Software Design Spring 2008 For today you should have: 1) worked on hw07 2) generated project ideas For lab: 1) Read chapter 19 and the notes below For Thursday: 1) prepare for a quiz on UML diagrams Project idea generation ----------------------- Assume that you will have the following at your disposal: 1) 2-D and 3-D graphics and animation 2) graphical user interface 3) modules for talking to network servers, especially web servers, and tools for parsing what you get back 4) remote object invocation for network programming, including client-server and peer-to-peer. 5) sound/music Some different kinds of projects 1) feature-driven: choose a project that lets you explore a topic 2) tool-building: write something useful to you 3) client-driven: write something that serves the needs of a client Today: two rounds 1) generate ideas without thinking about who's doing what 2) start to choose projects/groups Proposals due on Thursday. See http://wb/sd/project/ Also feel free to use the class mailing list and http://mrwiki.olin.edu/mrwiki/Software_Design_Spring_2008 Homework 6 Solutions -------------------- sd_hw06_soln.py is available from the class web page. If you create a new class that inherits from exception: class Missed(Exception): """this the exception raised when a turtle tries to tag someone too far away""" You can use the class object in a raise statement (no need to instantiate a Missed object): def apply_tag(self, other): """try to tag the other turtle. if it is too far away, let the other turtle flee and raise an exception""" if self.distance_from(other) < 10: self.not_it() other.youre_it() else: other.flee(self) raise Missed Optionally, you can provide additional information: raise Missed, 'nah, nah!' Either way, you can catch the exception: if self.it: target = self.closest(world.animals) try: self.apply_tag(target) except Missed: self.chase(target) closest demonstrates a variation on the DSU pattern: def closest(self, others): """return the animal in the list that is closest to self (not including self!)""" t = [(self.distance_from(animal), animal) for animal in others if animal is not self] (distance, animal) = min(t) return animal And a new feature called a "list comprehension". tkinter ------- Tk is a toolkit for building GUIs. The interface from Python to Tk is called tkinter. My program, Gui.py is built on top of tkinter. The primary good thing about Gui.py is that it makes it relatively easy to create a GUI. The bad thing is that you will have to read a lot of documentation (and code!) to get started. BUT, learning how to read documentation and code is a good thing! Widgets ------- GUIs are composed of widgets, which include: Button, Canvas, Entry, Frame, Label, Listbox, Menu, Menubutton, etc. The tkinter documentation for these classes is in An Introduction to Tkinter at http://www.pythonware.com/library/tkinter/introduction/ Geometry Managers ----------------- In tkinter, adding a widget to a Gui is a two-step process 1) create the widget 2) lay out the widget There are three Geometry Managers that lay out widgets: 1) Pack: complicated but powerful 2) Grid: simpler, but limited to grid configurations 3) Place: simplest, but requires the user to determine absolute sizes and locations. Widgets are nested, which means that every widget is embedded in a parent or master widget. A Frame is a (usually) invisible widget that is only used to contain and organize other widgets. A Gui object is also a Frame. The Gui Module -------------- Gui.py provides a simplified interface to the Pack and Grid geometry managers. It provides wrapper methods that create widgets and lay them out at the same time. It also keeps track of the current frame; when you create a new widget, its parent is the current frame. Frames are normally invisible, but if you initialize Gui with the debug flag, it makes the frames visible. g = Gui(debug=True) See widget_demo.py for examples. mainloop -------- GUI programming consists of two phases: 1) setup: the widgets that compose the GUI are created and layed out 2) mainloop: the system waits for the user to do something, then invokes the appropriate code Callbacks --------- During the setup phase, you specify the functions that will be invoked from the mainloop. These functions are called "callbacks" because when you call Tkinter, you give it a function, and then later, Tkinter calls you back. A simple use of a callback is the command parameter of Gui.bu self.bu(text='Hello', command=self.hello) self.bu(text='Quit', command=self.quit) self.hello and self.quit are bound methods we are using as callbacks. Notice that they don't have () after them, because we are not calling them now (at button-creation time). We are just naming them. Tkinter will call them later, when the user presses a button. Later we will see ways to 1) build more complicated Callback objects 2) "bind" callbacks to other widgets (not just Buttons) and other events (not just button clicks). The canvas widget ----------------- The Canvas widget is a little different from the others because you can draw "items" on it. The GuiCanvas class in Gui.py is a wrapper I wrote for Canvas. 1) It adds a new capability (coordinate transformations), and 2) It provides a simpler interface to some of the item creation methods. For example: ca = g.ca(width=200, height=200) item1 = ca.circle([0, 0], 70, 'red') item2 = ca.rectangle([[0, 0], [60, 60]], 'blue') item3 = ca.text([0, 0], 'This is a canvas.', 'white') After you make an item, you can reconfigure it: ca.itemconfig(item2, fill='yellow') The documentation of the Canvas items is also in "An Introduction to Tkinter". OMG this sucks -------------- When you start playing with this, you probably won't like it, especially if you have used a GUI-builder. So why am I inflicting this on you? 1) GUI-builders are fine if you know ahead of time what all the widgets will be, but often you have to generate widgets dynamically. For example, you might write a loop that generates a sequence of widgets: for i in range(1, 10): g.bu(text=str(i)) 2) Most GUIs have structure; writing code to generate a GUI allows you to take advantage of that structure. Also, behind every GUI is a data structure. Writing code allows you to develop the data structure along with the GUI. 3) Tk's packer is overgeneralized; that is, it has sacrificed ease of use for power. When you are confronted with a library like that, you should look for opportunities to build helper functions. I have done some of that in Gui.py. Gui.py is an example of the kind of layered design I will be looking for in your projects. (That said, Gui.py is a work in progress -- not everything in there is meant to be a _good_ example!) 4) Dynamic layout is an important technique for a lot of applications. I want you to get a sense of how it is implemented.