Software Design Spring 2008 For today you should have: 1) prepared for a quiz on UML diagrams 2) written a project proposal For next time: 1) finish any pending homeworks 2) have a good break! Model-view-controller --------------------- Poker.py contains an example of a pattern called MVC http://en.wikipedia.org/wiki/Model-view-controller Model: the data we want to represent, in this case PokerHand View: a presentation of the data, in this case Table Controller: an intermediary between model and view, in this case HandView The motivation is that the PokerHand class should not have the details of Table embedded in it, especially if there are multiple views (graphical, textual, remote) Also, the Table shouldn't have to know about all the things that might be displayed on it (PokerHand, CribbageHand, etc.) (For example, imagine that PokerHand and Table were written by different people; you have to make them work together but you are not allowed to modify them!) HandView isolates each from the other; all of the overlap is encapsulated in one class. class HandView: def draw(self, x=0, y=0): self.tag = 'HandView%d' % id(self) for card in self.hand.cards: self.draw_card(card, x, y) x += 1 self.table.create_text([x, y], self.hand.label) To make this design even cleaner, I would replace attribute access like self.hand.cards with method invocations. Why? GUI Concepts ------------ Hierarchy of widgets. Automatic layout. Event-driven programming. Callbacks. Callbacks --------- 1) In widget_demo.py, I use the simplest form of a callback, a simple function: def press_me(): text = en.get() la2.configure(text=text) bu = g.bu(text='Press me', command=press_me) Most important idea: distinguishing between setup time and event-processing time. 2) In World.py, I use bound methods: self.bu(text='Hello', command=self.hello) self.bu(text='Quit', command=self.quit) A bound method is a method associated with a particular object, so when it is invoked, it is invoked on the object. In effect, you get one parameter free! 3) Another way to associate arguments with a callback is to wrap up the function and its arguments in an object. The Callable class is defined in Gui.py def set_color(color): ca.itemconfig(item2, fill=color) for color in ['red', 'green', 'blue']: g.mi(mb, color, command=Callable(set_color, color)) All three menu items use set_color as their callback, but they associate different arguments with it. Binding and event-handling -------------------------- A binding is a relationship between a widget, an event and an event-handler. When an event (mouse-click, key press, etc.) occurs, Tk decides which widget gets the event, and which of the widget's event-handlers should be invoked. Example: def setup(self): self.canvas = self.ca(...) self.canvas.bind('', canvas_click) The event handler can be the name of a function, a bound method, or a Callback object. You can also bind items within a canvas, as in the Draggable example in the book. Gather/scatter -------------- If a function takes a variable number of arguments, you can "gather" the arguments with the * operator. Try this in the interpreter: def foo(*args): print args foo(1) foo(1, 2) foo(1, 2, 3) You can also combine required and optional parameters: def bar(required, optional=-1, *the_rest): print required print optional print the_rest bar(1) bar(1, 2) bar(1, 2, 3) bar(1, 2, 3, 4) scatter ------- If you pass a sequence as an argument, it gets assigned to a single variable. def baz(a, b, c): print a, b, c t = (1, 2, 3) baz(t) Oops! Now try this: baz(*t) This can be handy, but it can also be confusing, since the * operator works differently in the context of an argument list (scatter) or a parameter list (gather).