CSCI 233 Python Exercise 11

It is time to complete the “Life” simulation, pushing on toward re-using parts of our program for other simulations.

Recap

During lab last week, you should have completed the changes to Grid.display() that allow you to pass it a “displayer” object, one version of which prints the grid, as before. That will be the main change that you make to life.py.

Your GUI interface goes in lifegui.py. Your “application” class should create a pair of frames in the root window, one for the controls and one for the grid. You should create a class for the GUI interface to the grid itself. Either this class or your application will need to support the “displayer” interface.

The displayed grid is most easily made as a collection of buttons; you can use class extension so that each button “knows” its location in the grid and has a handler method that calls the toggle method on the corresponding cell when clicked. Each can also have a StringVar as an instance variable so that it knows what to display. That StringVar will need to be updated after you toggle a cell and by the “displayer” showElement() method.

Structure

You can think of your program as having two layers: there is the underlying simulation in life.py, and the GUI interface in lifegui.py. That means that lifegui will need to import from life.

For running the simulation with a GUI, it is lifegui that will be the main program; so you’ll end up with something that you can run (at the shell prompt) with

$ python lifegui.py

The “main” function or object in lifegui will take care of creating a Grid as well as the GUI objects. For the most part, you’ll have methods/functions from lifegui calling down into methods/functions in life. The exception will be that Grid.display() will call methods on the “displayer” instance passed to it—which will likely be the only time that something in life calls anything in lifegui.

This layered structure makes it easier to keep track of responsibilities. The exceptional calls going in the reverse direction are sometimes described as callbacks or upcalls.

Extensions

Once you have all of this working, there are several features you might want to add to your interface:

Any of these will involve learning more about

Additional GUI features

Sometimes you want to change an attribute of an widget after you have created it. You can do that by calling the config() method on the widget, giving it the same kinds of keyword arguments that you give when you create it. For example, If you have a label x, you could change its relief with

x.config(relief=RIDGE)

In addition to the attributes that were listed in Lab 9, you might find the following useful to change:

You can find more documentation about Tkinter at these three sites:

http://www.pythonware.com/library/tkinter/introduction/
http://effbot.org/tkinterbook/
http://infohost.nmt.edu/tcc/help/pubs/tkinter/

What is there is not complete, but does suggest things that you can do.

Repeating an action with alarms

If you set up a “run” button, what you want is for that button’s action to cause the simulation to step over and over. You can’t just write a loop that calls step(), however, as that would not allow the display to update or respond to user actions. The way around this is to use the idea of a scheduled alarm. So you might end up with a method like this on your application class:

    def runstep(self):
        self.step() # do a step of the simulation
        self.root.after(delay, self.runstep)

What the last line does is schedule another event (after delay milliseconds) that will call this method again.

That’s not quite everything you need: you probably want the “run” button to disable and enable various other controls, and you’ll need to have some way to make it stop. That’s a place for your creativity…

Dialogs

Sometimes you want to have a button pop-up a little window for further interaction; these windows are called dialogs. The easiest dialogs can be picked up from the tkMessageBox module, described under “Standard Dialogs” in the Tkinter documentation. An easy example, which just shows a message, would look like:

    import tkMessageBox

    ...

        tkMessageBox.showinfo("title", "message")

That just pops up a window and waits until the user hits “OK”.

You can get a little fancier with askyesno, which puts up two buttons, returning a boolean indicating the choice.

If you want to get a few inputs, you would use the tkSimpleDialog module, and make a class that extends tkSimpleDialog.Dialog. I’ll be making an example available.

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.