Refactoring question

ginstrom ginstrom at tree.odn.ne.jp
Wed Apr 4 00:41:32 EDT 2007


> 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 at
http://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...




More information about the Python-list mailing list