ModelButton: an attempt to clean up button enabling/disabling

Don Dwiggins ddwiggins at advpubtech.com
Fri Dec 1 10:32:36 PST 2006


I'm developing a panel with 8 buttons (hardly a record, I'm sure).  Each 
button should be enabled or disabled at various times while the user is 
working with the panel.  I've found the code to support this getting 
progressively messier, with lots of "extraneous" code growing in various 
event handlers.

I've just come up with a way of getting this under control, and I'm 
posting it here "as is" in the hope that someone might find it useful, 
or help me improve it, or point out a far better solution that I should 
have used in the first place.

The core of it is a subclass of Button that I call ModelButton, as a bow 
to MVC. The core of the idea is that each button is an observer of some 
object, gets notified by that object when some possibly significant 
event occurs, and can "ask" the object whether it should enable or 
disable itself.  Here's the implementation of the class:

> class ModelButton(wx.Button):
>     def __init__(self):
>         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() )

In addition to the usual event handler, I've added a callback that the 
button uses to set its enabled state.

To use this, the container class sets itself up as the observable for 
its buttons by declaring the following methods:

>     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()

Each button is set up by something like the following:

>         self.addinclbtn = xrc.XRCCTRL(self, "AddIncludeButton")
>         self.setModelButton(self.addinclbtn, self.testAddInclude,
>                             self.includeAdZones)

Here's an "enableTester":

>     def testAddInclude(self):
>         # Enable if there are available AdZones selected
>         return len( self.availAZlist.GetSelections() ) > 0

Then, in an event handler that changes the state of the panel, I added a 
call to "self.notifyButtons()"; this separates out the issue of changing 
the state from that of updating the "view" appropriately, something that 
MVC was originally invented for.

This is pretty minimal and simplistic, but it's already allowed me to 
prune out some crufty code and squash some bugs in the process.

-- 
Don Dwiggins
Advanced Publishing Technology





More information about the wxpython-users mailing list