CSCI 233 Python Exercise 11b

Let us try one more time to finish the “Life” simulation’s interface.

Review from class

The new things from class on Monday had to with changing existing widgets and with generating repeated events.

Changing existing widgets To change an existing widget, you call its config() method, using the same keyword arguments that are used when creating it. Here are some useful keywords (properties) for many widgets:

For example, to disable a button self.b, you would call

self.b.config(state=DISABLED)

See Lab 9 for more attributes, listed by widget class.

Generating repeated events In order to make the simulation run continuously, it is tempting to simply write a loop that repeatedly calls the step() method, like:

    def run(self):
        while True:
            self.step()

There are several problems with this approach. First, it might not get the display updated every time; we can force that by calling the update() method on the root window each time through the loop. But then we still have not provided any way to stop the simulation. We could try to fix that by introducing another instance variable in the GUI for whether it is running, so that the “stop” button could reset that. That would give us something like this:

    def run(self):
        self.running = True
        while self.running:
            self.step()
    self.root.update()
    def stop(self):
        self.running = False

The problem that remains is that while our program is running, it does not get around to checking for user actions (such as pressing the “stop” button!). So what we need is a way to structure our repetition as a series of events that are treated in the same way as user-generated events.

We do this by replacing our loop with an event that does one step, then schedules another step as an event to occur in the future. We can do this latter bit of scheduling by calling the after() method on the root window, giving it a number of milliseconds to wait and an event handler (method) to call when the time comes. Our “loop” then ends up looking like this:

    def run(self):
        self.running = True
self.runstep()
    def runstep():
        if self.running:
            self.step()
    self.root.update()
    self.root.after(self.delay, self.runstep)
    def stop(self):
        self.running = False

That will work provided we have set self.delay to the milliseconds of pause that we want between steps. (That in turn suggests a way to provide a speed control…)

Dialogs

The previous handout described how to get the simplest kind of dialog, using functions from the tkMessageBox module. To build a more interesting dialog, you import the tkSimpleDialog module and extend its Dialog class.

The Dialog class is designed for extension; what you have to provide are the methods that lay out the body of the dialog, that do checking for validity of input, and that do something when the “OK” button is pressed. You can copy a simple example program from

/cslab/class/csci233/lab11/dialog.py

What you will find there is a class DemoDialog that extends Dialog. The three methods provided are:

The last of these is optional: if you leave it out, all input will be treated as valid.

If you look in the application’s pressDialog() method, you’ll see how this is used. Constructing a DemoDialog pops up the window; any results are passed back by setting instance variables in the dialog, which can be checked after it returns.

Hiding, discarding, or replacing a widget

When you want to get rid of a widget, you use one of two grid methods on it. If you want it to disappear temporarily, you call its grid remove() method; to make it reappar, call grid() again (no arguments needed). If you want to get rid of the widget permanently, call grid forget() instead. So the usual sequence to replace a widget self.w at row r and column c with a new instance of its class would look something like:

    self.w.grid_forget()
    self.w = create the new widget
    self.w.grid(row=r, column=c)

Extra fun

If you need something else to do, think about how you could make your program same a simulation’s state in a file or read a simulation state from a file. The things that you would need to do for this are:

The last one is made fairly simple, because there is a module tkFileDialog that provides functions askopenfile() and asksaveasfile() that return an open file for reading or writing, respectively. The (keyword) options for those functions are described at

http://www.pythonware.com/library/tkinter/introduction/x1164-data-entry.htm

Turning it in

Polish your program in life.py and lifegui.py. Write your reflections in life.txt.

Turn in a snapshot of where you are at the end of lab today, using

/cslab/class/csci233/bin/handin P11 filenames

Turn in the final version before lab on next Thursday, using the same command.