Chapter 19. GUI Programming

In this chapter, we will give you a brief introduction to the subject of graphical user interface (GUI) programming. If you are somewhat new to this area or want to learn more about it, or if you want to see how it is done in Python, then this chapter is for you. We cannot show you everything about GUI application development here in this one chapter, but we will give you a very solid introduction to it. The primary GUI toolkit we will be using is Tk, Python’s default GUI, and we will access Tk from its Python interface called Tkinter (short for “Tk interface”).

Tk is not the “latest and greatest” nor does it have the most robust set of GUI building blocks, but it is fairly simple to use and will allow you to build GUIs that run on most platforms. We will present several simple and intermediate examples using Tkinter, followed by a few examples using other toolkits. Once you have completed this chapter, you will have the skills to build more complex applications and/or move to a more modern graphical toolkit. Python has bindings or adapters to most of the major toolkits out there, including commercial systems.

19.1 Introduction

19.1.1 What Are Tcl, Tk, and Tkinter?

Tkinter is Python’s default GUI library. It is based on the Tk toolkit, originally designed for the Tool Command Language (Tcl). Due to Tk’s popularity, it has been ported to a variety of other scripting languages, including Perl (Perl/Tk), Ruby (Ruby/Tk), and Python (Tkinter). With the GUI development portability and flexibility of Tk, along with the simplicity of scripting language integrated with the power of systems language, you are given the tools to rapidly design and implement a wide variety of commercial-quality GUI applications.

If you are new to GUI programming, you will be pleasantly surprised at how easy it is. You will also find that Python, along with Tkinter, provides a fast and exciting way to build applications that are fun (and perhaps useful) and that would have taken much longer if you had had to program directly in C/C++ with the native windowing system’s libraries. Once you have designed the application and the look and feel that goes along with your program, you will use basic building blocks known as widgets to piece together the desired components, and finally, to attach functionality to “make it real.”

If you are an old hand at using Tk, either with Tcl or Perl, you will find Python a refreshing way to program GUIs. On top of that, it provides an even faster rapid prototyping system for building them. Remember that you also have Python’s system accessibility, networking functionality, XML, numerical and visual processing, database access, and all the other standard library and third-party extension modules.

Once you get Tkinter up on your system, it will take less than 15 minutes to get your first GUI application running.

19.1.2 Getting Tkinter Installed and Working

Like threading, Tkinter is not necessarily turned on by default on your system. You can tell whether Tkinter is available for your Python interpreter by attempting to import the Tkinter module. If Tkinter is available, then no errors occur:

>>> import Tkinter
>>>

If your Python interpreter was not compiled with Tkinter enabled, the module import fails:

image

You may have to recompile your Python interpreter to get access to Tkinter. This usually involves editing the Modules/Setup file and enabling all the correct settings to compile your Python interpreter with hooks to Tkinter or choosing to have Tk installed on your system. Check the README file for your Python distribution for specific instructions on getting Tkinter to compile on your system. Be sure, after your compilation, that you start the new Python interpreter you just created; otherwise, it will act just like your old one without Tkinter (and in fact, it is your old one).

19.1.3 Client/Server Architecture—Take Two

In the earlier chapter on network programming, we introduced the notion of client/server computing. A windowing system is another example of a software server. These run on a machine with an attached display, such as a monitor of some sort. There are clients, too—programs that require a windowing environment to execute, also known as GUI applications. Such applications cannot run without a windows system.

The architecture becomes even more interesting when networking comes into play. Usually when a GUI application is executed, it displays to the machine that it started on (via the windowing server), but it is possible in some networked windowing environments, such as the X Window system on Unix, to choose another machine’s window server to display to. In such situations, you can be running a GUI program on one machine, but have it displayed on another!

19.2 Tkinter and Python Programming

19.2.1 Tkinter Module: Adding Tk to your Applications

So what do you need to do to have Tkinter as part of your application? Well, first of all, it is not necessary to have an application already. You can create a pure GUI if you want, but it probably isn’t too useful without some underlying software that does something interesting.

There are basically five main steps that are required to get your GUI up and running:

  1. Import the Tkinter module (or from Tkinter import *).
  2. Create a top-level windowing object that contains your entire GUI application.
  3. Build all your GUI components (and functionality) on top (or “inside”) of your top-level windowing object.
  4. Connect these GUI components to the underlying application code.
  5. Enter the main event loop.

The first step is trivial: All GUIs that use Tkinter must import the Tkinter module. Getting access to Tkinter is the first step (see Section 19.1.2).

19.2.2 Introduction to GUI Programming

Before going to the examples, we will give you a brief introduction to GUI application development in general. This will provide you with some of the background you need to move forward.

Setting up a GUI application is similar to an artist’s producing a painting. Conventionally, there is a single canvas onto which the artist must put all the work. The way it works is like this: You start with a clean slate, a “top-level” windowing object on which you build the rest of your components. Think of it as a foundation to a house or the easel for an artist. In other words, you have to pour the concrete or set up your easel before putting together the actual structure or canvas on top of it. In Tkinter, this foundation is known as the top-level window object.

In GUI programming, a top-level root windowing object contains all of the little windowing objects that will be part of your complete GUI application. These can be text labels, buttons, list boxes, etc. These individual little GUI components are known as widgets. So when we say create a top-level window, we just mean that you need such a thing as a place where you put all your widgets. In Python, this would typically look like this line:

top = Tkinter.Tk() # or just Tk() with “from Tkinter import *”

The object returned by Tkinter.Tk() is usually referred to as the root window, hence the reason why some applications use root rather than top to indicate as such. Top-level windows are those that show up standalone as part of your application. You may have more than one top-level window for your GUI, but only one of them should be your root window. You may choose to completely design all your widgets first, then add the real functionality, or do a little of this and a little of that along the way. (This means mixing and matching steps 3 and 4 from our list.)

Widgets may be standalone or be containers. If a widget “contains” other widgets, it is considered the parent of those widgets. Accordingly, if a widget is “contained” in another widget, it’s considered a child of the parent, the parent being the next immediate enclosing container widget.

Usually, widgets have some associated behaviors, such as when a button is pressed, or text is filled into a text field. These types of user behaviors are called events, and the actions that the GUI takes to respond to such events are known as callbacks.

Actions may include the actual button press (and release), mouse movement, hitting the RETURN or Enter key, etc. All of these are known to the system literally as events. The entire system of events that occurs from the beginning to the end of a GUI application is what drives it. This is known as event-driven processing.

One example of an event with a callback is a simple mouse move. Let’s say the mouse pointer is sitting somewhere on top of your GUI application. If the mouse is moved to another part of your application, something has to cause the movement of the mouse on your screen so that it looks as if it is moving to another location. These are mouse move events that the system must process to give you the illusion (and reality) that your mouse is moving across the window. When you release the mouse, there are no more events to process, so everything just sits there quietly on the screen again.

The event-driven processing nature of GUIs fits right in with client/server architecture. When you start a GUI application, it must perform some setup procedures to prepare for the core execution, just as when a network server has to allocate a socket and bind it to a local address. The GUI application must establish all the GUI components, then draw (aka render or paint) them to the screen. Tk has a couple of geometry managers that help position the widget in the right place; the main one that you will use is called Pack, aka the packer. Another geometry manager is Grid—this is where you specify GUI widgets to be placed in grid coordinates, and Grid will render each object in the GUI in their grid position. For us, we will stick with the packer.

Once the packer has determined the sizes and alignments of all your widgets, it will then place them on the screen for you. When all of the widgets, including the top-level window, finally appear on your screen, your GUI application then enters a “server-like” infinite loop. This infinite loop involves waiting for a GUI event, processing it, then going back to wait for the next event.

The final step we described above says to enter the main loop once all the widgets are ready. This is the “server” infinite loop we have been referring to. In Tkinter, the code that does this is:

Tkinter.mainloop()

This is normally the last piece of sequential code your program runs. When the main loop is entered, the GUI takes over execution from there. All other action is via callbacks, even exiting your application. When you pull down the File menu to click on the Exit menu option or close the window directly, a callback must be invoked to end your GUI application.

19.2.3 Top-Level Window: Tkinter.Tk()

We mentioned above that all main widgets are built into the top-level window object. This object is created by the Tk class in Tkinter and is created via the normal instantiation:

>>> import Tkinter
>>> top = Tkinter.Tk()

Within this window, you place individual widgets or multiple-component pieces together to form your GUI. So what kinds of widgets are there? We will now introduce the Tk widgets.

19.2.4 Tk Widgets

There are currently 15 types of widgets in Tk. We describe these widgets in Table 19.1.

Table 19.1. Tk Widgets

image

image

We won’t go over the Tk widgets in detail as there is plenty of good documentation available on them, either from the Tkinter topics page at the main Python Web site or the abundant number of Tcl/Tk printed and online resources (some of which are available in Appendix B). However, we will present several simple examples to help you get started.

Core Note: Default arguments are your friend

image

GUI development really takes advantage of default arguments in Python because there are numerous default actions in Tkinter widgets. Unless you know every single option available to you for every single widget you are using, it’s best to start out by setting only the parameters you are aware of and letting the system handle the rest. These defaults were chosen carefully. If you do not provide these values, do not worry about your applications appearing odd on the screen. They were created with an optimized set of default arguments as a general rule, and only when you know how to exactly customize your widgets should you use values other than the default.

19.3 Tkinter Examples

19.3.1 Label Widget

In Example 19.1, we present tkhello1.py, the Tkinter version of “Hello World!” In particular, it shows you how a Tkinter application is set up and highlights the Label widget.

Example 19.1. Label Widget Demo (tkhello1.py)

Our first Tkinter example is ... what else? “Hello World!” In particular, we introduce our first widget, the Label.

image

In the first line, we create our top-level window. That is followed by our Label widget containing the all-too-famous string. We instruct the packer to manage and display our widget, and finally call mainloop() to run our GUI application. Figure 19-1 shows what you will see when you run this GUI application.

Figure 19-1. Tkinter Label widget (tkhello1.py)

image

19.3.2 Button Widget

The next example is pretty much the same as the first. However, instead of a simple text label, we will create a button instead. In Example 19.2 is the source code for tkhello2.py.

Example 19.2. Button Widget Demo (tkhello2.py)

This example is exactly the same as tkhello1.py except that rather than using a Label widget, we create a Button widget.

image

The first few lines are identical. Things differ only when we create the Button widget. Our button has one additional parameter, the Tkinter.quit() method. This installs a callback to our button so that if it is pressed (and released), the entire application will exit. The final two lines are the usual pack() and entering of the mainloop(). This simple button application is shown in Figure 19-2.

Figure 19-2. Tkinter Label widget (tkhello1.py)

image

19.3.3 Label and Button Widgets

We combine tkhello1.py and tkhello2.py into tkhello3.py, a script that has both a label and a button. In addition, we are providing more parameters now than before when we were comfortable using all the default arguments that are automatically set for us. The source for tkhello3.py is given in Example 19.3.

Example 19.3. Label and Button Widget Demo (tkhello3.py)

This example features both a Label and a Button widget. Rather than primarily using default arguments when creating the widget, we are able to specify more now that we know more about Button widgets and how to configure them.

image

Besides additional parameters for the widgets, we also see some arguments for the packer. The fill parameter tells the packer to let the QUIT button take up the rest of the horizontal real estate, and the expand parameter directs the packer to visually fill out the entire horizontal landscape, stretching the button to the left and right sides of the window.

As you can see in Figure 19-3, without any other instructions to the packer, the widgets are placed vertically (on top of each other). Horizontal placement requires creating a new Frame object with which to add the buttons. That frame will take the place of the parent object as a single child object (see the buttons in the listdir.py module, Example 19.6 in Section 19.3.6).

Figure 19-3. Tkinter Label and Button widgets (tkhello3.py)

image

19.3.4 Label, Button, and Scale Widgets

Our final trivial example, tkhello4.py, involves the addition of a Scale widget. In particular, the Scale is used to interact with the Label widget. The Scale slider is a tool which controls the size of the text font in the Label widget. The greater the slider position, the larger the font, and the same goes for a lesser position, meaning a smaller font. The code for tkhello4.py is given in Example 19.4

Example 19.4. Label, Button, and Scale Demo (tkhello4.py)

Our final introductory widget example introduces the Scale widget and highlights how widgets can “communicate” with each other using callbacks [such as resize()]. The text in the Label widget is affected by actions taken on the Scale widget.

image

New features of this script include a resize() callback function (lines 5-7), which is attached to the Scale. This is the code that is activated when the slider on the Scale is moved, resizing the size of the text in the Label.

We also define the size (250 × 150) of the top-level window (line 10). The final difference between this script and the first three is that we import the attributes from the Tkinter module into our namespace with “from Tkinter import *.” Although not recommended because it “pollutes” your namespace, we do it here mainly because this application involves a great number of references to Tkinter attributes. This would require use of their fully qualified names for each and every attribute access. By using the undesired shortcut, we are able to access attributes with less typing and have code that is easier to read, at some cost.

As you can see from Figure 19-4, both the slider mechanism as well as the current set value show up in the main part of the window. Figure 19-4 shows the state of the GUI after the user moves the scale/slider to avalue of 36.

Figure 19-4. Tkinter Label, Button, and Scale widgets (tkhello4.py)

image

As you can see from the code, the initial setting for the scale when the application starts is 12 (line 18).

19.3.5 Partial Function Application Example

Before looking a longer GUI application, we wanted to review the Partial Function Application (PFA) as introduced back in Section 11.7.3 of Chapter 11.

PFAs were added to Python in version 2.5 and are one piece in a series of significant improvements in functional programming.

image

PFAs allow you to “cache” function parameters by effectively “freezing” those predetermined arguments, and then at runtime, when you have the remaining arguments you need, you can thaw them out, send in the final arguments, and have that function called with all parameters.

Best of all, PFAs are not limited to just functions. They will work with any “callable,” any object that has a functional interface just by using parentheses, i.e., classes, methods, or callable instances. The use of PFAs fits perfectly into a situation where there are many callables and many of the calls feature the same arguments over and over again.

GUI programming makes a great use case because there is good probability that you want some consistency in GUI widget look-and-feel, and this consistency comes about when the same parameters are used to create like objects. We are now going to present an application where multiple buttons will have the same foreground and background colors. It would be a waste of typing to give the same arguments to the same instantiators every time we wanted a slightly different button: the foreground and background colors are the same, but only the text is slightly different.

We are going to use traffic road signs as our example with our application attempts creating textual versions of road signs by dividing them up into various categories of sign types like critical, warning, or informational (just like logging levels). The type of the sign determines their color layout when the signs are created. For example, critical signs have the text in bright red with a white backdrop, warning signs are in black text on a goldenrod background, and informational or regulatory signs feature black text on a white background. We have the “Do Not Enter” and “Wrong Way” signs, which are both “critical,” plus “Merging Traffic” and “Railroad Crossing,” both of which are warnings. Finally, we have the regulatory “Speed Limit” and “One Way” signs.

The application creates the “signs,” which are just buttons. When users press the buttons, they just pop up the corresponding Tk dialog, critical/error, warning, or informational. It is not too exciting, but how the buttons are built is. You will find our application featured here in Example 19.5.

Example 19.5. Road Signs PFA GUI Application (pfaGUI2.py)

Create road signs with the appropriate foreground and background colors based on sign type. Use PFAs to help “templatize” common GUI parameters.

image

image

When you execute this application, you will get a GUI that will look something like Figure 19.5.

Figure 19-5. Road signs PFA GUI application on XDarwin in MacOS X (pfaGUI2.py)

image

Line-by-Line Explanation

Lines 1–18

We begin our application by importing functools.partial(), a few Tkinter attributes, and the Tk dialogs (lines 1-5). Next, we define some signs along with their categories (lines 7-18).

Lines 20–28

The Tk dialogs are assigned as button callbacks, which we will use for each button created (lines 20-23). We then launch Tk, set the title, and create a QUIT button (lines 25-28).

Lines 30–33

These lines represent our PFA magic. We use two levels of PFA. The first templatizes the Button class and the root window top. What this does is that every time we call MyButton, it will call Button (Tkinter.Button() creates a button.) with top as its first argument. We have “frozen” this into MyButton.

The second level of PFA is where we use our first one, MyButton, and templatize that. We create separate button types for each of our sign categories. When users create a critical button CritButton (by calling it, e.g., CritButton()), it will then call MyButton along with the appropriate button callback and background and foreground colors, which means calling Button with top, callback, and colors. Do you see how it unwinds and goes down the layers until at the very bottom, it has the call that you would have originally had to make if this feature did not exist yet? We repeat with Warn-Button and ReguButton.

Lines 35–42

With the setup completed, we look at our list of signs and create them. We put together a Python evaluatable string consisting of the correct button name, pass in the button label as the text argument, and pack() it. If it is a critical sign, then we CAPITALIZE the button text, otherwise we titlecase it. This last bit is done in line 39, demonstrating another feature introduced in Python 2.5, the temporary operator. Then we take each button creation string and execute it with eval(), creating the buttons one at a time and resulting in the graphic seen previously. Finally, we start the GUI by entering the main event loop.

This application uses several Python 2.5 features, so you will not be able to run this with an older version.

19.3.6 Intermediate Tkinter Example

We conclude this section with a larger example, listdir.py. This application is a directory tree traversal tool. It starts in the current directory and provides a file listing. Double-clicking on any other directory in the list causes the tool to change to the new directory as well as replace the original file listing with the files from the new directory. The source code is given as Example 19.6.

Example 19.6. File System Traversal GUI (listdir.py)

This slightly more advanced GUI expands on the use of widgets, adding listboxes, text entry fields, and scrollbars to our repertoire. There are also a good number of callbacks such as mouse clicks, key presses, and scrollbar action.

image

image

image

image

In Figure 19-6, we present what this GUI looks like in a Windows environment.

Figure 19-6. List directory GUI application in Windows (listdir.py)

image

The Unix version of this application is given in Figure 19-7.

Figure 19-7. List directory GUI application in Unix (listdir.py)

image

Line-by-Line Explanation

Lines 1–5

These first few lines contain the usual Unix startup line and importation of the os module, the time.sleep() function, and all attributes of the Tkinter module.

Lines 9–13

These lines define the constructor for the DirList class, an object that represents our application. The first Label we create contains the main title of the application and the version number.

Lines 15–19

We declare a Tk variable named cwd to hold the name of the directory we are on—we will see where this comes in handy later. Another Label is created to display the name of the current directory.

Lines 21–29

This section defines the core part of our GUI, (the Listbox) dirs, which contain the list of files of the directory that is being listed. A Scrollbar is employed to allow the user to move through a listing if the number of files exceeds the size of the Listbox. Both of these widgets are contained in a Frame widget. Listbox entries have a callback (setDirAndGo) tied to them using the Listbox bind() method.

Binding means to tie a keystroke, mouse action, or some other event to a callback to be executed when such an event is generated by the user. setDirAndGo() will be called if any item in the Listbox is doubleclicked. The Scrollbar is tied to the Listbox by calling the Scrollbar.config() method.

Lines 31–34

We then create a text Entry field for the user to enter the name of the directory he or she wants to traverse and see its files listed in the Listbox. We add a RETURN or Enter key binding to this text entry field so that the user can hit RETURN as an alternative to pressing a button. The same applies for the mouse binding we saw above in the Listbox. When the user doubleclicks on a Listbox item, it has the same effect as the user’s entering the directory name manually into the text Entry field and pressing the “go” button.

Lines 36–53

We then define a Button frame (bfm) to hold our three buttons, a “clear” button (clr), a “go” button (ls), and a “quit” button (quit). Each button has its own different configuration and callbacks, if pressed.

Lines 55–57

The final part of the constructor initializes the GUI program, starting with the current working directory.

Lines 59–60

The clrDir() method clears the cwd Tk string variable, which contains the current directory that is “active.” This variable is used to keep track of what directory we are in and, more important, helps keep track of the previous directory in case errors arise. You will notice the ev variables in the callback functions with a default value of None. Any such values would be passed in by the windowing system. They may or may not be used in your callback.

Lines 62–69

The setDirAndGo() method sets the directory to traverse to and issues the call to the method that makes it all happen, doLS().

Lines 71–108

doLS() is, by far, the key to this entire GUI application. It performs all the safety checks (e.g., is the destination a directory and does it exist?). If there is an error, the last directory is reset to be the current directory. If all goes well, it calls os.listdir() to get the actual set of files and replaces the listing in the Listbox. While the background work is going on to pull in the new directory’s information, the highlighted blue bar becomes bright red. When the new directory has been installed, it reverts to blue.

Lines 110–115

The last pieces of code in listdir.py represent the main part of the code. main() is executed only if this script is invoked directly, and when main() runs, it creates the GUI application, then calls mainloop() to start the GUI, which is passed control of the application.

We leave all other aspects of the application as an exercise to the reader, recommending that it is easier to view the entire application as a combination of a set of widgets and functionality. If you see the individual pieces clearly, then the entire script will not appear as daunting.

We hope that we have given you a good introduction to GUI programming with Python and Tkinter. Remember that the best way to get familiar with Tkinter programming is by practicing and stealing a few examples! The Python distribution comes with a large number of demonstration applications that you can study.

If you download the source code, you will find Tkinter demo code in Lib/lib-tk, Lib/idlelib, and Demo/tkinter. If you have installed the Win32 version of Python and C:Python2x, then you can get access to the demo code in Liblib-tk and Libidlelib. The latter directory contains the most significant sample Tkinter application: the IDLE IDE itself. For further reference, there are several books on Tk programming, one specifically on Tkinter.

19.4 Brief Tour of Other GUIs

We hope to eventually develop an independent chapter on general GUI development using many of the abundant number of graphical toolkits that exist under Python, but alas, that is for the future. As a proxy, we would like to present a single simple GUI application written using four of the more popular and available toolkits out there: Tix (Tk Interface eXtensions), Pmw (Python MegaWidgets Tkinter extension), wxPython (Python binding to wxWidgets), and PyGTK (Python binding to GTK+). Links to where you can get more information and/or download these toolkits can be found in the reference section at the end of this chapter.

The Tix module is already available in the Python standard library. You must download the others, which are third party. Since Pmw is just an extension to Tkinter, it is the easiest to install (just extract into your site packages). wxPython and PyGTK involve the download of more than one file and building (unless you opt for the Win32 versions where binaries are usually available). Once the toolkits are installed and verified, we can begin. Rather than just sticking with the widgets we’ve already seen in this chapter, we’d like to introduce a few more complex widgets for these examples.

In addition to the Label and Button widgets we have seen before, we would like to introduce the Control or SpinButton and ComboBox. The Control widget is a combination of a text widget with a value inside being “controlled” or “spun up or down” by a set of arrow buttons close by, and the ComboBox is usually a text widget and a pulldown menu of options where the currently active or selected item in the list is displayed in the text widget.

Our application is fairly basic: pairs of animals are being moved around, and the number of total animals can range from a pair to a dozen max. The Control is used to keep track of the total number while the ComboBox is a menu containing the various types of animals that can be selected. In Figure 19-8, each image shows the state of the GUI application immediately after launching. Note that the default number of animals is two, and no animal type has been selected yet.

Figure 19-8. Application using various GUIs under Win32 (animal*.pyw)

image

Things are different once we start to play around with the application, as evidenced in Figure 19-9 after we have modified some of the elements in the Tix application.

Figure 19-9. After modifying the Tix GUI version of our application (animalTix.pyw)

image

Below, you will find the code for all four versions of our GUI. You will note that although relatively similar, each one differs in its own special way. Also, we use the .pyw extension to suppress the popping up of the Dos command or terminal window.

19.4.1 Tk Interface eXtensions (Tix)

We start with an example (Example 19.7) of using the Tix module. Tix is an extension library for Tcl/T that adds many new widgets, image types, and other commands that keep Tk a viable GUI development toolkit. Let’s take look at how to use Tix with Python.

Example 19.7. Tix GUI Demo (animalTix.pyw)

Our first example uses the Tix module. Tix comes with Python!

image

Line-by-Line Explanation

Lines 1–7

This is all the setup code, module imports, and basic GUI infrastructure. Line 7 asserts that the Tix module is available to the application.

Lines 8–27

These lines create all the widgets: Label (lines 9-11), Control (lines 13-16), ComboBox (lines 18-21), and quit Button (lines 23-25). The constructors and arguments for the widgets are fairly self-explanatory and do not require elaboration. Finally, we enter the main GUI event loop in line 27.

19.4.2 Python MegaWidgets (PMW)

Next we take a look at Python MegaWidgets as shown in Example 19.8. This module was created to address the aging Tkinter. It basically helps the extend its longevity by adding more modern widgets to the GUI palette.

Example 19.8. Pmw GUI Demo (animalPmw.pyw)

Our second example uses the Python MegaWidgets package.

image

The Pmw example is so similar to our Tix example that we leave line-by-line analysis to the reader. The line of code that differs the most is the constructor for the control widget, the Pmw Counter. It provides for entry validation. Instead of specifying the smallest and largest possible values as keyword arguments to the widget constructor, Pmw uses a “validator” to ensure that the values do not fall outside our accepted range.

Now, we are finally going to leave the Tk world behind. Tix and Pmw are extensions to Tk and Tkinter, respectively, but now we are going to change gears to look at completely different toolkits, wxWidgets and GTK+. You will notice that the number of lines of code starts to increase as we start programming in a more object-oriented way when using these more modern and robust GUI toolkits.

19.4.3 wxWidgets and wxPython

wxWidgets (formerly known as wxWindows) is a cross-platform toolkit used to build graphical user applications. It is implemented using C++ and is available on a wide number of platforms to which wxWidgets defines a consistent and common API. The best part of all is that wxWidgets uses the native GUI on each platform, so your program will have the same look-and-feel as all the other applications on your desktop. Another feature is that you are not restricted to developing wxWidgets applications in C++. There are interfaces to both Python and Perl. Example 19.9 shows our animal application using wxPython.

Example 19.9. wxPython GUI Demo (animalWx.pyw)

Our third example uses wxPython (and wxWidgets). Note that we have placed all our widgets inside a “sizer” for organization and the more object-oriented nature of this application.

image

image

Line-by-Line Explanation

Lines 5–37

Here we instantiate a Frame class (lines 5-8), of which the sole member is the constructor. This method’s only purpose in life is to create our widgets. Inside the frame, we have a Panel. Inside the panel we use a BoxSizer to contain and layout all of our widgets (lines 10, 36), which consist of a Label (lines 12-14), SpinCtrl (lines 16-20), ComboBox (lines 22-27), and quit Button (lines 29-34).

We have to manually add Labels to the SpinCtrl and ComboBox widgets because they apparently do not come with them. Once we have them all, we add them to the sizer, set the sizer to our panel, and lay everything out. On line 10, you will note that the sizer is vertically oriented, meaning that our widgets will be placed top to bottom.

One weakness of the SpinCtrl widget is that it does not support “step” functionality. With the other three examples, we are able to click an arrow selector and have it increment or decrement by units of two, but that is not possible with this widget.

Lines 39–51

Our application class instantiates the Frame object we just designed, renders it to the screen, and sets it as the top-most window of our application. Finally, the setup lines just instantiate our GUI application and start it running.

19.4.4 GTK+ and PyGTK

Finally, we have the PyGTK version, which is quite similar to the wxPython GUI (See Example 19.10). The biggest difference is that we use only one class, and it seems more tedious to set the foreground and background colors of objects, buttons in particular.

Example 19.10. PyGTK GUI Demo (animalGtk.pyw)

Our final example uses PyGTK (and GTK+). Like the wxPython example, this one also uses a class for our application. It is interesting to note how similar yet different all of our GUI applications are. This is not surprising and allows programmers to switch between toolkits with relative ease.

image

image

Line-by-Line Explanation

Lines 1–6

We import three different modules and packages, PyGTK, GTK, and Pango, a library for layout and rendering of text, specifically for I18N purposes. We need it here because it represents the core of text and font handling for GTK+ (2.x).

Lines 8–51

The GTKapp class represents all the widgets of our application. The topmost window is created (with handlers for closing it via the window manager), and a vertically oriented sizer (VBox) is created to hold our primary widgets. This is exactly what we did in the wxPython GUI.

However, wanting the static labels for the SpinButton and ComboBox-Entry to be next to them (unlike above them for the wxPython example), we create little horizontally oriented boxes to contain the label-widget pairs (lines 18-36), and placed those HBoxes into the all-encompassing VBox.

After creating the quit Button and adding the VBox to our topmost window, we render everything on-screen. You will notice that we create the button with an empty label at first. We do this so that a Label (child) object will be created as part of the button. Then on lines 45-46, we get access to the label and set the text with white font color.

The reason why we do this is because if you set the style foreground, i.e., in the loop and auxiliary code on lines 41-44, the foreground only affects the button’s foreground and not the label—for example, if you set the foreground style to white and highlight the button (by pressing TAB until it is “selected”) you will see that the inside dotted box identifying the selected widget is white, but the label text would still be black if you did not alter it like we did with the markup on line 46.

Lines 53–55

Here we create our application and enter the main event loop.

19.5 Related Modules and Other GUIs

There are other GUI development systems that can be used with Python. We present the appropriate modules along with their corresponding window systems in Table 19.2.

Table 19.2. GUI Systems Available for Python

image

image

image

image

You can find out more about all GUIs related to Python from the general GUI Programming page on the Python wiki at http://wiki.python.org/moin/GuiProgramming.

19.6 Exercises

19-1. Client/Server Architecture. Describe the roles of a windows (or windowing) server and a windows client.

19-2. Object-Oriented Programming. Describe the relationship between child and parent widgets.

19-3. Label Widgets. Update the tkhello1.py script to display your own message instead of “Hello World!”

19-4. Label and Button Widgets. Update the tkhello3.py script so that there are three new buttons in addition to the QUIT button. Pressing any of the three buttons will result in changing the text label so that it will then contain the text of the Button (widget) that was pressed. Hint: you will need three separate handlers or, customize one handler with arguments preset (still three function objects).

19-5. Label, Button, and Radiobutton Widgets. Modify your solution to the previous problem so that there are three Radiobuttons presenting the choices of text for the Label. There are two buttons: the QUIT button and an “Update” button. When the Update button is pressed, the text label will then be changed to contain the text of the selected Radiobutton. If no Radiobutton has been checked, the Label will remain unchanged.

19-6. Label, Button, and Entry Widgets. Modify your solution to the previous problem so that the three Radiobuttons are replaced by a single Entry text field widget with a default value of “Hello World!” (to reflect the initial string in the Label). The Entry field can be edited by the user with a new text string for the Label which will be updated if the Update button is pressed.

19-7. Label and Entry Widgets and Python I/O. Create a GUI application that provides an Entry field where the user can provide the name of a text file. Open the file and read it, displaying its contents in a Label.

Extra Credit (Menus): Replace the Entry widget with a menu that has a File Open option that pops up a window to allow the user to specify the file to read. Also add an Exit or Quit option to the menu rather than having a QUIT button.

19-8. Simple Text Editor. Use your solution to the previous problem to create a simple text editor. A file can be created from scratch or read and displayed into a Text widget that can be edited by the user. When the user quits the application (either with the QUIT button or the Quit/Exit menu option), the user is prompted whether to save the changes.

Extra Credit: Interface your script to a spellchecker and add a button or menu option to spellcheck the file. The words that are misspelled should be highlighted by using a different foreground or background color in the Text widget.

19-9. Multithreaded Chat Applications. The chat programs from Chapters 13, 16, and 17 need completion. Create a fully-functional multithreaded chat server. A GUI is not really necessary for the server unless you want to create one as a front-end to its configuration, i.e., port number, name, connection to a name server, etc. Create a multithreaded chat client that has separate threads to monitor user input (and sends the message to the server for broadcast) and another thread to accept incoming messages to display to the user. The client front-end GUI should have two portions of the chat window: a larger section with multiple lines to hold all the dialog, and a smaller text entry field to accept input from the user.

19-10. Using Other GUIs. The example GUI applications using the various toolkits in Chapter 18.4 are very similar; however, they are not the same. Although it is impossible to make them all look exactly alike, tweak them so that they are more consistent than they are now.

19-11. Using GUI builders. Download either Boa Constructor (for wxWidgets) or Glade (for GTK+) [or both!], and implement the “animal” GUI by just dragging and dropping the widgets from the corresponding palette. Hook up your new GUIs with callbacks so that they behave just like the sample applications we looked at in that chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset