Refactoring question

kyosohma at gmail.com kyosohma at gmail.com
Wed Apr 4 15:57:46 CEST 2007


On Apr 3, 11:41 pm, "ginstrom" <ginst... at tree.odn.ne.jp> wrote:
> > On Behalf Of Kevin Walzer
> > What's the best way to do this? Can anyone point me in the
> > right direction? How could, for instance, the top snippet be
> > rewritten to separate the Tkinter parts from the generic stuff?
>
> I like to use the broadcaster/broker recipe athttp://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81983
>
> I added a few features (such as decorators for listener functions --
> see below sig).
> There are other recipes out there, such as PyDispatcher.
>
> Basically, I have GUI events translated into broadcaster events (by
> passing lambdas that call broadcaster.Broadcast() to the event
> binders), which are received by controller functions/classes.
>
> Feedback (status/progress) is also communicated via broadcaster
> events.
>
> Something I tried on my current project, which is going fairly well,
> is first writing a command-line version, then writing a GUI version,
> with the same controller back-end used in each case, and the
> controller communicating progress/results via the same interface. This
> approach has made me keep presentation and logic very loosely
> coupled.
>
> So for instance, the view class would send request for processing,
> which the controller gets.
> The controller performs the requested action, sending broadcasts of
> progress (note that the controller can start a worker thread for the
> processing, but the broadcasts should be made on the GUI thread...)
>
>     broadcaster.Broadcast( "progress", "start", (100, "Doing your
> bidding now..." ) ) # number of items we will process
>
>     # ...
>     broadcaster.Broadcast( "progress", "progress", (i, "Working on
> item %i" % i ) ) # current item
>     # ...
>
>     broadcaster.Broadcast( "progress", "end", (100, "Done!") )
>
> Depending on who is showing the progress, this might go onto a status
> bar, progress dialog, the console, a log file, and so on, or some
> combination thereof -- the controller doesn't know or care.
>
> When the controller is finished, it asks the broker for a view, and
> calls show results on the view
>
>     view = broker.Request( "view" )
>     view.ShowResults( results )
>
> That could have been done equally with the broadcaster, but for some
> reason I like the broker here (it makes the view "dumber").
>
> Regards,
> Ryan
>
> --
> Ryan Ginstrom
>
> ==================
> # listener decorators
>
> def BrokerRequestHandler( title ):
>     """A decorator for broker listeners
>
>     @param title: the title to provide
>
>     The decorated function must take no arguments
>     (it can retrieve them using CurrentData())
>     """
>
>     def decorator(func):
>         broker.Register( title, func )
>         return func
>     return decorator
>
> def BroadcasterEventHandler( source, title ):
>     """A decorator for broadcaster event handlers
>
>     @param source: the broadcast source
>     @param title: the title of the broadcast
>
>     The decorated function must take no arguments
>     (it can retrieve them using CurrentData())
>     """
>
>     def decorator(func):
>         broadcaster.Register( func, source, title )
>         return func
>     return decorator
>
> # example ...
> @BrokerRequestHandler( "meaning of life" )
> def getMeaningOfLife():
>     return 42
>
> ## A little more complicated for class methods. I stole this technique
> from WCK
>
> # Lifted shamelessly from WCK (effbot)'s wckTkinter.bind
> def EventHandler( source, title ):
>     """Dectorator for event-handling methods"""
>
>     def decorator(func):
>         func.BroadcasterEvent = (source, title)
>         return func
>     return decorator
>
> class FrameController:
>     """Controller for the main frame window"""
>
>     def __init__( self ):
>
>         for key in dir(self):
>             method = getattr(self, key)
>             if hasattr(method, "BroadcasterEvent") and
> callable(method):
>                 source, title = method.BroadcasterEvent
>                 broadcaster.Register( method,
>                                      source=source,
>                                      title=title )
>
>     @EventHandler( "event", "onExport" )
>     def onExport( self ):
>         """Handles the onExport broadcast by exporting the database to
> the requested format"""
>
>         format = broadcaster.CurrentData()
>         # Perform export...

I usually put all my GUI code into their own methods(s) and call those
methods from the __init__(). I do the same with the logic (where
applicable). This makes the code easier to manipulate and/or import.

In wxPython, you can also use XRC to define most of the common
elements of you GUI. I've found this method to be very helpful in
keeping my code short and easy to read, for GUI code.

Mike




More information about the Python-list mailing list