Software Design Spring 2007 For Thursday: 1) prepare for a quiz on UML class diagrams. 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 manager. 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. For example, Frame 3 in widget_demo.py looks like this: g.fr(LEFT) # outer frame g.fr(TOP) # inner frame la = g.la(TOP, text='List of colors:') # label lb = g.lb(LEFT) # listbox sb = g.sb(RIGHT, fill=Y) # scrollbar g.endfr() # end inner frame bu = g.bu(BOTTOM, text='Apply color') # button g.endfr() # end outer frame The first argument for each widget function controls the arrangement of widgets. Use LEFT to arrange widgets from left-to-right, and TOP to arrange them top-to-bottom. Frames are normally invisible, but if you initialize Gui with the debug flag, it makes the frames visible. g = Gui(debug=True) 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(LEFT, text='Hello', command=self.hello) self.bu(LEFT, 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". Oh my God! 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), sticky=NS) 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.