[Tutor] critique my script!

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Wed Jun 21 04:10:30 CEST 2006



On Tue, 20 Jun 2006, Christopher Spears wrote:

> Here is a little gui I created:

[gui code cut]

Hi Christopher,

Looks ok so far.

One approach that has a lot of popularity behind it is called the 
"model-view-controller" approach.  The idea is that we should be able to 
build up the graphical part, the part that does all the windows and 
buttons and textboxes.  We should be able to develop this "view" 
independently of the part that does the real work, the model.  We should 
then be able to control these two parts and explicitely link them 
together.


Concretely, we can take the program you showed us, and make it temporarily 
less useful.  *grin*

Here's an example of this:

##################################################################
class View:
     def __init__(self):
         self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
         self.window.set_title("Get Current Working Dir")
         self.window.set_border_width(10)
         self.window.connect("delete_event", self.delete_event)

         self.box = gtk.HBox(False, 0)
         self.window.add(self.box)
         self.button = gtk.Button("Current Working Directory")

         self.button.connect("clicked", self.button_pressed)
         self.box.pack_start(self.button, True, True, 0)

         def do_nothing():
             pass
         self.on_button_pressed = do_nothing

         self.button.show()
         self.box.show()
         self.window.show()

     def button_pressed(self, widget, data=None):
         self.on_button_pressed()

     def delete_event(self, widget, event, data=None):
         gtk.main_quit()
         return False

     def main(self):
         gtk.main()

##################################################################

It's basically your program, minus any of the useful stuff that does 
getcwd().  There's a small level of indirection here: the button is hooked 
up to button_pressed, which calls on_button_pressed, which does_nothing. 
And of course, this is useless!  We can bring up this view, and press its 
buttons, but nothing happens.


However, we can fix that: we can rewire up the view so that 
on_button_pressed doesn't do_nothing(), but does something more 
interesting, something like:

#########################################
view = View()
view.on_button_pressed = os.getcwd
#########################################


If we do then, then we can patch up our main entry point and get back the 
original behavior of the program:

######################################
if __name__ == '__main__':
     view = View()
     view.on_button_pressed = os.getcwd
     view.main()
######################################


But why bother with this?


The advantage of this approach is that it becomes easy to rewire this same 
view with different models.  For example:

######
view = View()

def say_hello():
     print "hello world"

view.on_button_pressed = say_hello
######

Bam.  Now we have a GUI that should say hello when we press the button.


Of course, the title bars and button text are all wrong.  But that's 
something that can be fixed by making the View more general and passing in 
the explicit text strings into the constructor.

###############################################################
class OneButtonView:
     def __init__(self, title, button_text):
         self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
         self.window.set_title(title)
         self.window.set_border_width(10)
         self.window.connect("delete_event", self.delete_event)

         self.box = gtk.HBox(False, 0)
         self.window.add(self.box)
         self.button = gtk.Button(button_text)

         self.button.connect("clicked", self.button_pressed)
         self.box.pack_start(self.button, True, True, 0)

         def do_nothing():
             pass
         self.on_button_pressed = do_nothing

         self.button.show()
         self.box.show()
         self.window.show()

     def button_pressed(self, widget, data=None):
         self.on_button_pressed()

     def delete_event(self, widget, event, data=None):
         gtk.main_quit()
         return False

     def main(self):
         gtk.main()
##############################################################


And now we can either have the original program:


###############################################################
def cwd_gui_program():
     view = OneButtonView("Get Current Working Directory",
                          "Current Working Directory")
     view.on_button_pressed = os.getcwd
     view.main()
###############################################################


Or we can have a happy hello world gui:

###############################################################
def hello_world_gui_program():
     view = OneButtonView("Hello World", "Press me please")
     def say_hello():
         print "hello happy world"
     view.on_button_pressed = say_hello
     view.main()
###############################################################


The point is that if we break things up like this, we now have a general 
OneButtonView GUI that shows up a button, and lets the user press it to 
activate a function.

Does this make sense?


Best of wishes!


More information about the Tutor mailing list