468,458 Members | 1,858 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,458 developers. It's quick & easy.

Refactoring question

I currently have a GUI application (using Tkinter) in which all the code
is placed in a single script. This means that the GUI bits are a bit too
tightly bound to the "under-the-hood" logic of the app. Here's an
example snippet:

def installPackage(self):

self.package = self.infotable.getcurselection()
if not self.package:
showwarning('Error', 'Please select a package name.')
return
else:
self.packagename = self.package[0][1]
self.status.set('Installing %s' % self.packagename)
self.showProgress()
self.file = os.popen('echo %s | sudo -S /sw/bin/fink -y
install %s' % (self.passtext, self.packagename), 'r', os.O_NONBLOCK)
for line in self.file:
self.textdisplay.configure(state='normal')
self.textdisplay.insert(END, line)
self.update()
self.textdisplay.see(END)
self.textdisplay.configure(state='disabled')
self.endProgress()
self.categorytree.selection_set('All')
self.listCategoryPackages()

I'd like to refactor and separate the GUI code from the back-end code;
for instance, to move the "self.file = os.popen" bits into a separate
category. I want to do this so that I can redesign the GUI using another
toolkit, if I so choose, such as wxPython.

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?

--
Kevin Walzer
Code by Kevin
http://www.codebykevin.com
Apr 4 '07 #1
3 1073
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/Coo...n/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...

Apr 4 '07 #2
On Apr 3, 11:41 pm, "ginstrom" <ginst...@tree.odn.ne.jpwrote:
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

Apr 4 '07 #3
ky******@gmail.com wrote:
On Apr 3, 11:41 pm, "ginstrom" <ginst...@tree.odn.ne.jpwrote:
>>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
[lots of code that was pretty irrelevant to the reply]
>
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.
While talking about keeping things short and easy to read, maybe you
could be a bit more considerate in your quoting practices. We really
didn't need to see all the code quoted to hear what you had to say ...

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC/Ltd http://www.holdenweb.com
Skype: holdenweb http://del.icio.us/steve.holden
Recent Ramblings http://holdenweb.blogspot.com

Apr 4 '07 #4

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

9 posts views Thread by Peter Dembinski | last post: by
4 posts views Thread by | last post: by
reply views Thread by Andre Baresel | last post: by
2 posts views Thread by Sachin Garg | last post: by
11 posts views Thread by Master of C++ | last post: by
6 posts views Thread by Dean Ware | last post: by
8 posts views Thread by Frank Rizzo | last post: by
15 posts views Thread by Simon Cooke | last post: by
reply views Thread by bryan rasmussen | last post: by
reply views Thread by NPC403 | last post: by
reply views Thread by kmladenovski | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.