Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Python (2005)

.pdf
Скачиваний:
159
Добавлен:
17.08.2013
Размер:
15.78 Mб
Скачать

Writing a GUI with Python

Thread.__init__(self) self.button = button self.doCount = False self.count = 0 self.setDaemon(True)

def run(self): self.doCount = True while self.doCount:

self.gui.gui_queue_append(“incrementCount”, [self.button, self.count])

self.count += 1 time.sleep(1)

self.gui.gui_queue_append(“resetCount”, [self.button]) self.count = 0

if __name__ == ‘__main__’: CountUpGUI()

try:

gtk.threads_init() except:

print “No threading was enabled when you compiled pyGTK!” import sys

sys.exit(1) gtk.threads_enter() gtk.main() gtk.threads_leave()

You can see how this looks in Figure 13-3.

Figure 13-3

How It Works

When you click the button the first time, it initializes a CountingThread object. This thread object spends most of its time sleeping, but it wakes up every second to update the label with a new number. If it were updating the label directly, then to avoid freezing the program, it would have to know to call gtk.threads_enter before calling incrementCount, and to call gtk.threads_leave afterward. Instead, it puts an incrementCount command onto the gui_queue object. The main thread (which called gtk.threads_enter before entering the main body of the code) retrieves this command from the queue and executes it. The other thread can change the GUI without having to know any of the details of pyGTK or its thread handling.

221

TEAM LinG

Chapter 13

Widget Packing

So far, all of our examples have explored GUI concepts with a GUI consisting only of a single button. Needless to say, most real GUI applications are more complicated. You might be tempted to create a GUI with multiple widgets by simply creating the widgets and attaching them to the window:

#This is bad code! Don’t actually try it! button1=gtk.Button(“Button 1”)

window.add(button1) button2=gtk.Button(“Button 2”)

window.add(button2)

If you try this code, you’ll notice that only the first button shows up. This is because a window can only contain one widget. Once you associate the first button with the window, you can’t associate anything else with it. How, then, are complex GUIs possible? The answer is a technique called widget packing.

Widget packing makes use of boxes and tables, virtual widgets that don’t necessarily show up on the screen the way a button does. A window can only have one child widget, but if that widget happens to be a box or table, it can contain a number of child widgets, and display them all, either beside or on top of each other. As you’ll see, you can put boxes inside boxes to get the exact layout you need.

Here’s TwoButtonsGUI.py, a script that is like our original “Hello World” application SingleButtonGUI.py (in which clicking the button does nothing), but this time there are two buttons instead of one:

#!/usr/bin/env python import findgtk import gtk

class TwoButtonsGUI:

def __init__(self, msg1=”Hello World”, msg2=”Hello Again”): #Set up the window and the button within. self.window=gtk.Window()

self.box = gtk.VBox()

self.window.add(self.box)

The window widget only has space for one child widget: If we put one of our buttons directly in the window, there wouldn’t be anywhere to put the other one. Instead, we put a box widget in the window. This widget doesn’t show anything onscreen, but it can contain more than one child widget. We use a VBox, which means that widgets will be packed vertically into the box, one on top of the other. The alternative is an HBox, which packs widgets next to each other horizontally:

self.button1 = gtk.Button(msg1) self.button2 = gtk.Button(msg2)

self.box.pack_start(self.button1) self.box.pack_start(self.button2)

222

TEAM LinG

Writing a GUI with Python

We create our two buttons and put each one in the box. Next we must show all four widgets: the two buttons, the box, and the window. Remember that if you don’t show a widget, neither it nor any of its children appear on the screen:

#Show the GUI self.button1.show() self.button2.show() self.box.show() self.window.show()

if __name__ == ‘__main__’: TwoButtonsGUI() gtk.main()

See Figure 13-4 for an example of what this code will look like when its run.

Figure 13-4

As you can see, adding just one more GUI widget greatly increased the amount of design and programming work we had to do. For complex layouts, it gets even worse. Writing GUIs by hand in this day and age is insane. Fortunately, we don’t have to: The best way to lay out a GUI is graphically. This is as far as we’re going to go with hand-coded GUIs. From this point on, we’ll use a GUI builder tool called Glade.

Glade: a GUI Builder for pyGTK

The great thing about pyGTK is that you almost never have to write the GUI by hand. That’s what Glade is for. Glade is a GUI construction kit: It provides a GUI you can use to design your own GUI. Once you’re done, it writes a description of a GUI layout to an XML file (see Chapter 15 for more information about XML). A library called libglade can then read that XML file and render the corresponding GUI. Instead of instantiating a bunch of Python objects and calling show on all of them, you just feed a file describing your GUI into libglade.

To give you some idea of the sorts of complex applications you can build with Glade, Figure 13-5 shows a screenshot of CANVAS 5.4 running on Windows XP. It runs identically on Linux — and, of course, integrates with the standard GTK themes.

Here you can see many of the widgets you’ll shortly know how to use: a notebook container for the log and debug information panes, several list and tree views, a horizontal scale for the covertness bar, text entries, a menu bar, and a button with an icon in it.

Given a little practice, you too could lay out this complex application in Glade in just a few moments. Of course, the real payoff is when you want to move parts of your application around. Rather than regenerate code, you simply change the pieces you want changed in Glade, and click Save. No code changes need to be made at all.

223

TEAM LinG

Chapter 13

Figure 13-5

GUI Builders for Other GUI Frameworks

Writing GUI code is painful — no matter which toolkit you use, you’re going to want to use some sort of generator for as much of it as possible. All the non-TK toolkits include a construction kit. If you’re using wxPython to run your GUI, you should consider using Boa Constructor, wxDesigner, or wxGlade to build it. QT has the KDE builder tools to work with, and of course pyGTK has Glade.

You’ll want to avoid any GUI builder that actually generates GUI code (for instance, anything that generates all those Python objects and calls to show). A good code generator will go to an intermediate language, such as XML, which your graphics toolkit will load and parse at runtime. By this token, wxGlade would be preferable to Boa Constructor. wxGlade generates XRC: the wxWindows XML language.

224

TEAM LinG

Writing a GUI with Python

For programmers used to HyperCard, wxPython also offers PythonCard. You can obtain more information from the developer’s SourceForge page at http://pythoncard.sourceforge.net/.

Using libGlade with Python

Libglade is a library that reads in an XML file and makes the corresponding calls to GTK to create a GUI. Glade (actually Glade-2.exe or glade2) will present you with a GUI for creating these XML files. There are many advantages for doing your GUI in this way:

You can change your GUI quickly and easily.

You can have multiple GUIs that drive the same underlying application.

You spend your time designing the GUI, and not debugging the GUI creation code.

As Immunity developed CANVAS, we also strove as much as possible to isolate the code from the GUI altogether. Although we liked the pyGTK model, there was a distinct possibility that someday we would want to port to a platform that GTK did not support, such as the Sharp Zaurus. Good application design specifies that you actually build another layer in between your GUI and your application code, such that you have three layers:

The XML file that describes your GUI.

An application file (called something like mygui.py), which loads the GUI and application, and other application files as needed for major GUI components to allow for testing them independently of the rest of the application. These are the only files that use GTK functionality directly.

The application logic itself, which should never import GTK or any GUI toolkit library. All calls to the GUI should be through the code in mygui.py.

Using this design will save you years of time later trying to debug some small threading error. All of Immunity CANVAS (not a trivial application) was ported from GTK version 1 (and pyGTK version 1) to GTK version 2 (and corresponding pyGTK) within one day. Because GTK development is proceeding quite quickly, this sort of capability is going to be key to maintaining compatibility with the library itself.

A Glade Walkthrough

If you’ve never used a GUI builder, then you can’t fully appreciate how easy Glade has made the whole process. If you have, you’ll find that Glade offers the same kind of features you’re used to.

Installing Glade

If you’re using Windows, you can download a port of Glade from http://gladewin32

.sourceforge.net/. The package systems of Unix-like operating systems (including Mac OS X) usually make a glade, glade2, or python-glade2 package available. See the web site for the book for more information.

225

TEAM LinG

Chapter 13

Starting Glade

Start glade by running glade-2.exe, or on Unix-like systems (including Mac OS X), a simple glade or glade-2 will do. Glade starts up with three windows: a project window as in Figure 13-6, a palette of widgets you can use to build your GUI as in Figure 13-7, and a property sheet displaying information on the currently selected GUI widget as in Figure 13-8. Because you have no GUI widgets yet, the property sheet is blank.

Figure 13-6

Figure 13-7

226

TEAM LinG

Writing a GUI with Python

Figure 13-8

Creating a Project

First, start a new Glade project called GladeTwoButtonsGUI.

Glade might ask you some questions at this point, but your answers don’t matter much. Glade might offer you options for two types of projects: GTK or Gnome projects. A Gnome project would use features unique to the Gnome desktop environment, which is usually available on Linux, but is not a good idea for cross-platform projects. You want something portable, so choose a GTK project instead. If Glade asks you to pick a language (for instance, C, C++, or Ada), choose any of them; it doesn’t matter. You’re not generating the GUI code from Glade, You’re going to be using only the XML file that Glade generates to describe the GUI layout.

Save your project now, and you can start creating a GUI with the palette.

Using the Palette to Create a Window

The Glade widget palette is one of the most important tools you’ll be using. You can see an image of the palette in Figure 13-9. Each icon on the palette corresponds to a type of widget or widget container. To create a widget, you click its icon and then the container in which you want to place that widget.

Of course, you don’t start out with anywhere to put any of the widgets. Let’s change that by creating a new window. Click the top-left icon on the palette (it’s called Window and it looks like a little empty window) to create a root window for your application.

227

TEAM LinG

Chapter 13

Figure 13-9

The Window and Dialog widgets are top-level widgets: They have their own GUI windows and they don’t need to go into a widget container. Every other widget in your application will have a Window widget or a Dialog widget as its ultimate parent.

This window starts out with the name of “window1,” but you can change this from its property sheet. You’ll find that Glade selects names for each widget based on a simple incrementing number plan. The first text view widget you create is called “textview1”, the first window is “window1”, and so on. If you hover over an icon on the palette, or click to select it, Glade will tell you what kind of widget that icon represents.

Putting Widgets into the Window

The window you just created provides a container in which you can place a widget (or another container). Let’s use this to recreate the two-button GUI from the earlier example.

Recall that a window can only contain one child widget. Before you can place any buttons, you need to fill the window with a box: a virtual widget that can contain multiple child widgets. Click the Vertical Box icon on the palette and then click on the window to place a vertical box in the window. You’ll be asked how many rows you want in the box. Because you’re going to place two buttons, enter 2. Now you have a window that contains a vertical box (see Figure 13-10), which itself can contain up to two widgets.

The presence of the vertical box is denoted graphically by a white line partitioning the window into two parts. When the GUI is actually run, though, all you’ll see are the widgets inside the vertical box. Remember that virtual widgets such as boxes don’t show up in the GUI; they just determine how the other widgets appear.

Let’s put buttons in the box. Click the Button icon on the palette and then click on the top partition of the vertical box. Repeat this to put another button in the bottom partition of the box. Resize the window if necessary, and you should have something that looks almost like our other two-button example (see Figure 13-11).

228

TEAM LinG

Writing a GUI with Python

Figure 13-10

Figure 13-11

Use the properties sheet for each button to change its label, and the illusion will be complete (see Figure 13-12). If you can’t find the window with the properties sheet, select View Show Property Editor in the main Glade window to make it show up.

Figure 13-12

229

TEAM LinG

Chapter 13

By setting these properties, you’ll get a window with two buttons that have changed according to the text you’ve entered (see Figure 13-13).

Figure 13-13

Glade Creates an XML Representation of the GUI

It should already be clear how powerful Glade is. With it, you can construct a GUI visually instead of by writing code. But how do you get this GUI into a representation that Python can understand?

Save your Glade project, and then look in the project’s directory. You should find a GladeTwoButtonsGUI.glade file that contains an XML representation of the GUI you just created. That XML representation will look something like this (although a lot of it has been edited out for clarity):

<?xml version=”1.0” standalone=”no”?> <!--*- mode: xml -*-->

<!DOCTYPE glade-interface SYSTEM “http://glade.gnome.org/glade-2.0.dtd”>

<glade-interface>

<widget class=”GtkWindow” id=”window1”> <child>

<widget class=”GtkVBox” id=”vbox1”> <child>

<widget class=”GtkButton” id=”button1”>

<property name=”label” translatable=”yes”>Hello World</property> </widget>

</child>

<child>

<widget class=”GtkButton” id=”button2”>

<property name=”label” translatable=”yes”>Hello Again</property> </widget>

</child>

</widget>

</child>

</widget>

</glade-interface>

If this looks like gibberish to you, consult Chapter 15 for more information on XML. If you can read XML, notice that this data structure defines a tree of tags that corresponds to the tree structure of the GUI. The interface as a whole contains a window (GtkWindow), which contains a vertical box

(GtkVBox), which contains two buttons (GtkButton). The buttons have customized label properties, just as you defined them in Glade.

230

TEAM LinG