[wxpython-users] Re: code structure - suggestions? (classes for gui, data, calculations...)

Don Dwiggins ddwiggins at advpubtech.com
Mon May 19 10:50:44 PDT 2008


Christopher Barker wrote:
> Don Dwiggins wrote:
>> One other MVC bit: I like to have "active" buttons on my forms, in the 
>> sense that each one is only enabled when it makes sense for the user 
>> to push it.  (E.g., a Save button should be disabled until the user 
>> makes a change worth saving.)
> 
> One usability issue here: I now it drives me crazy when a control is 
> grayed out, and I don't know why. It's not uncommon for me to think that 
> I should be able to performance given operation, and I can't. It's 
> usually because of some condition hasn't  been met, but if I don't know 
> what it is, all I'm left with is frustration.
> 
> If, on the other hand, I click the button, and get a dialog that says" 
> "You can't do X until you've set a value for Y", I know what to do.

A fair criticism; to some extent it's a matter of style, but should be 
evaluated on a case-by-case basis, being sensitive to the user's 
experience.  More generally, the design of the form (including the 
button labels) should always make it fairly clear under what 
circumstances each button should be used.  It might even be worth adding 
a tooltip to the button saying something like "Don't press this until 
you've set a value for Y".

I've had a request for the little framework I use for this, so here it 
is:  There's a ModelButton class that extends Button, and a mixin to use 
  with the panel or other class that contains the buttons.

Then, in setting up each button, you call <panel>.setModelButton; in the 
handler for each event that might cause a significant change (one that 
might cause a button to change state), you call <panel>.notifyButtons(). 
  (You can be pretty liberal in interpreting "significant" -- unless you 
have dozens of buttons with complex conditions, it won't cost much to 
process the notifications.)

Here's the code for the class and the mixin:
-----------------------------------------------------
"""
This is a subclass of wx.Button, designed to support enabling and
disabling of buttons in a container in an organized fashion, so that
event handlers and other state-changing methods don't have to
explicitly enable and disable buttons.

This class is designed to be an observer of a 'model' object, which
will notify the instance when its state has changed and the button
might need to be enabled or disabled.  When appropriate, the model
should call the 'notify' method, which takes no parameters.

As part of hooking up the instance, two handlers are passed:
- An 'enable tester' that takes no parameters and returns
   the desired enable status of the button.
- A typical wx event handler, to be called on EVT_BUTTON.
"""
class ModelButton(wx.Button):
     def __init__(self):
         """ See the module description """
         pre = wx.PreButton()
         self.PostCreate(pre)

     def SetHandlers(self, enableTester, eventHandler):
         """ Set the handlers that we'll use """
         self._enableTester = enableTester
         self._eventHandler = eventHandler
         self.Bind(wx.EVT_BUTTON, eventHandler)

     def notify(self):
         """ It's time to decide our enabled status """
         self.Enable( self._enableTester() )


"""
Mixin for use with ModelButton.  Inherit from this in the GUI class
that contains button instances; it allows the GUI class itself to
act as a model for the buttons.  Call its "init" anytime during the
GUI class' init (but before using the other methods here).

For each ModelButton: define an enableTester and an eventHandler method,
then call self.setModelButton(button, enableTester, enableHandler) --
this will take care of binding the event handler to EVT_BUTTON as well
as registering the button as an observer.

When a state change occurs that might affect the enabled status of
some button, call self.notifyButtons().
"""
class ModelButtonsMixin(object):
     def __init__(self):
         """ Set up to act as a simple observable """
         self.observers = []

     def setModelButton(self, button, enableTester, eventHandler):
         """ Set up the model button for operation with this class """
         button.SetHandlers(enableTester, eventHandler)
         self.observers.append(button)

     def notifyButtons(self):
         """ Send notification to all buttons of a state change """
         for button in self.observers:
             button.notify()



More information about the wxpython-users mailing list