next up previous
Next: Exercises Up: Adding a Graphical User Previous: GTK

Implementing a GTK controller

The controller is where the GTK main loop will be running, and all callback functions will be in the controller. It is important now that we distinguish the difference between the graphics that the visual python renderer displays and the graphics that the controller displays. In our program, the subjects are the simulator or minimiser. The GTK controller does not display any of the data that they calculate, therefore it is not a view. Rather, it controls the behaviour of the subjects and the views, and so it is a controller.

<gtkController.py>=
import pygtk
pygtk.require('2.0')
import gtk
import Simulate
import Minimise
import TextRenderer
import VPRenderer

<GTK Setup>
<MD Setup>

gtk.main()
This code is written to a file (or else not used).

When setting up the molecular dynamics part of the simulator, we don't have to worry yet about what the subject is for each renderer, nor do we have to worry about the initial values. This will all be decided once the user has entered the appropriate data, and set when the user clicks ``Start''.

<MD Setup>=
simulator = Simulate.Simulate()
minimise = Minimise.Minimise()
vpRenderer = VPRenderer.VPRenderer()
textRenderer = TextRenderer.TextRenderer()
Used above.

If the interface is built in a script (and not a class) then the callback functions need to be defined first, otherwise the python interpreter will raise an error when connecting the functions.

<GTK Setup>=
<Start Callback>
<Quit Callback>
<Build Interface>
Used above.

Now we will create the widgets. At the top we will have a drop down menu to choose the function, or subject, it will have two options, ``Simulate'' and ``Minimise''. Below that we will have four text entries, nside, rside, tstep and nstep. These will each be labelled, they will be placed in a four by two table, the labels in the first column, and the entries in the second. At the bottom there will be a start button, and a quit button. The drop down menu, text entry table, and buttons, will be placed in a vertical box, for simplicity.

<Build Interface>=
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.connect("destroy", quit)
vbox = gtk.VBox(gtk.FALSE, 0)

menu = gtk.Menu()
item = gtk.MenuItem("Simulate")
item.show()
menu.append(item)
item = gtk.MenuItem("Minimise")
item.show()
menu.append(item)
menu.show()
function = gtk.OptionMenu()
function.set_menu(menu)
function.set_history(0)
function.show()
vbox.pack_start(function, gtk.FALSE, gtk.FALSE, 2)

nside = gtk.Entry(max=2)
nside.set_width_chars(5)
nside.set_text("3")
nside.show()
rside = gtk.Entry(max=8)
rside.set_width_chars(5)
rside.set_text("3.0")
rside.show()
tstep = gtk.Entry(max=5)
tstep.set_width_chars(5)
tstep.set_text("0.01")
tstep.show()
nstep = gtk.Entry(max=5)
nstep.set_width_chars(5)
nstep.set_text("1000")
nstep.show()
table = gtk.Table(rows=4, columns=2, homogeneous=gtk.FALSE)
label = gtk.Label("nside:")
label.show()
table.attach(label, 0, 1, 0, 1, gtk.EXPAND, gtk.EXPAND, 2, 2)
table.attach(nside, 1, 2, 0, 1, gtk.EXPAND, gtk.EXPAND, 2, 2)
label = gtk.Label("rside:")
label.show()
table.attach(label, 0, 1, 1, 2, gtk.EXPAND, gtk.EXPAND, 2, 2)
table.attach(rside, 1, 2, 1, 2, gtk.EXPAND, gtk.EXPAND, 2, 2)
label = gtk.Label("tstep:")
label.show()
table.attach(label, 0, 1, 2, 3, gtk.EXPAND, gtk.EXPAND, 2, 2)
table.attach(tstep, 1, 2, 2, 3, gtk.EXPAND, gtk.EXPAND, 2, 2)
label = gtk.Label("nstep:")
label.show()
table.attach(label, 0, 1, 3, 4, gtk.EXPAND, gtk.EXPAND, 2, 2)
table.attach(nstep, 1, 2, 3, 4, gtk.EXPAND, gtk.EXPAND, 2, 2)
table.show()
vbox.pack_start(table, gtk.TRUE, gtk.TRUE, 2)
startButton = gtk.Button("Start")
startButton.connect("clicked", startClicked, None)
startButton.show()
vbox.pack_start(startButton, gtk.TRUE, gtk.TRUE, 2)
quitButton = gtk.Button("Quit")
quitButton.connect("clicked", quit, None)
quitButton.show()
vbox.pack_start(quitButton, gtk.TRUE, gtk.TRUE, 2)
vbox.show()
window.add(vbox)
window.show()
Used above.

The start callback needs to first determine the subject, then it gives the subject it's data, tells the renderers which subject to view, and then starts the subject. Note the very basic error checking, should the user enter a value that isn't a number, the start button will do nothing.

<Start Callback>=
def startClicked(widget, data=None):
    if function.get_history() == 0:
        subject = simulator
    elif function.get_history() == 1:
        subject = minimise
    try:
        n = int(nside.get_text())
        subject.nside = (n, n, n)
        r = float(rside.get_text())
        subject.rside = (r, r, r)
        subject.tstep = float(tstep.get_text())
        subject.nstep = int(nstep.get_text())
    except ValueError:
        pass
    else:
        vpRenderer.setSubject(subject)
        textRenderer.setSubject(subject)
        subject.run()
Used above.

The quit callback simply kills the GTK main loop.

<Quit Callback>=
def quit(widget, data=None):
    gtk.main_quit()
Used above.


next up previous
Next: Exercises Up: Adding a Graphical User Previous: GTK
James Roper 2004-02-12