We’re going to start by working on a real wxPython program, albeit a simple one. We won’t create anything complicated, yet. For now, we’re going to lead you step-by-step through the process of creating your very first wxPython program. Let’s begin by making sure you’ve got everything installed. Table 1 shows everything you’ll need in order to run wxPython.
Table 1 Everything you’ll need to run wxPython on your own computer
|The right operating system||This is an easy one—you have a lot of options. Specifically, you must be running one of the following:
|The Python programming language||Available for download at www.python.org. Any version 2.3 or higher will work. Many Linux distributions include a version of Python, as does Mac OS X 10.3 or higher. Even so, you might still want to download the latest version.|
|The wxPython Toolkit||
Available for download at www.wxpython.org.
There are different versions, depending on your operating system and Python version. Be sure to download the runtime installer that matches your platform, Python version, and Unicode preference. Download the packages for the demos and documentation as well.
If you’ve installed other software on your system, you should find that installing the wxPython packages works very similarly. Again, recent versions of Mac OS X and some Linux distributions already include wxPython, but you should download the latest version if you can.
|A text editor||
We recommend an editor that recognizes Python syntax and can do things like colorize the code to make it more readable. Most of the popular editors have support for Python code, so use the editor you prefer.
If you have no strong feelings for any particular editor, try IDLE, the integrated development environment included with Python, which includes a source code editor, interactive shell, debugger, and other tools.
The Python web site has a list of Python-aware editors at www.python.org/editors.
Once the installations are complete, get ready to type. We’re going to create a program that displays a single image file. This will happen in three steps:
- We’ll start with the bare minimum required for a working wxPython program.
- We’ll make that code more structured and sophisticated.
- We’ll end with a version that can display the wxPython logo.
Figures 1, 2, and 3, illustrate what the final program will look like, depending on your platform.
Figure 1 Running hello.py on Windows
Figure 2 Running hello.py on Linux
Figure 3 Running hello.py on Mac OS X
Creating the bare minimum wxPython program
Let’s begin with the simplest possible wxPython program that will run successfully. Create a file named “bare.py” and type in the following code. Remember, in Python, the spacing at the start of each line is significant.
import wx class App(wx.App): def OnInit(self): frame = wx.Frame(parent=None, title='Bare') frame.Show() return True app = App() app.MainLoop()
There’s not much to it, is there? Even at only eight lines of code (not counting blank lines) this program might seem like a waste of space, as it does little more than display an empty frame. But bear with us, as we’ll soon revise it, making it something more useful.
The real purpose of this program is to make sure you can create a Python source file, verify that wxPython is installed properly, and allow us to introduce more complex aspects of wxPython programming one step at a time. So humor us: create a file, type in the code, save the file with a name “bare.py,” run it, and make sure it works for you.
The mechanism for running the program depends on your operating system. You can usually run this program by sending it as a command line argument to the Python interpreter from an operating system prompt, using one of the following commands:
python bare.py pythonw bare.py
Figures 4, 5, and 6 show what the program looks like running on various operating systems.
Figure 4 Running bare.py on Windows.
Figure 5 Running bare.py on Linux.
Figure 6 Running bare.py on Mac OS X.
|Jargon: It Looks Like a Window|
|When most people look at this running program, they see something they would call a “window.” However, wxPython does not call this a window. It calls this a “frame.” In wxPython,”window” is the generic term for any object that displays on the screen (what other toolkits might call a “widget”). So, a wxPython programmer will often refer to objects such as buttons or text boxes as “windows.” This may seem confusing, but the usage dates to the earliest days of the original C++ toolkit, and it’s unlikely to change now. In this book, we’ll try to avoid the use of window as a generic term, because it’s confusing and also because it’s the name of a big product from a major corporation. We’ll use widget as the generic term. When we’re specifically referring to the operating system of similar name, we’ll do it with a capital “W.”|
While this bare-minimum program does little more than create and display an empty frame, all of its code is essential; remove any line of code and the program will not work. This basic wxPython program illustrates the five basic steps you must complete for every wxPython program you develop:
- Import the necessary wxPython package
- Subclass the wxPython application class
- Define an application initialization method
- Create an application class instance
- Enter the application’s main event loop
Let’s examine this bare minimum program step-by-step to see how each one was accomplished.
The first thing you need to do is import the main wxPython package, which is named wx:
Once that package is imported, you can refer to wxPython classes, functions, and constants using the wx package name as a prefix, like this:
|Jargon: It Looks Like a Window|
During the writing of this book the name of the wxPython package changed. Since the old naming convention is still supported, you will probably encounter wxPython code written in the old style. So, we’ll digress briefly to explain the older style and why it was changed. The old package name was wxPython and it contained an internal module named wx. There were two common ways to import the needed code—you could import the wx module from the wxPython package:
from wxPython import wx #DEPRECATED—DON'T DO THIS ANY MORE
Or, you could import everything from the wx module directly.
from wxPython.wx import * #DEPRECATED—DON'T DO THIS ANY MORE
Both import methods had serious drawbacks. Using the second method of import * is generally discouraged in Python because of the possibility of namespace conflicts. The old wx module avoided this problem by putting a wx prefix on nearly all of its attributes. Even with this safeguard, import * still had the potential to cause problems, but many wxPython programmers preferred this style, and you’ll see it used quite often in older code. One downside of this style was that class names began with a lowercase letter, while most of the wxPython methods begin with an uppercase letter—the exact opposite of the normal Python programming convention.
However, if you tried to avoid the namespace bloat caused by import * by doing from wxPython import wx, you now had to type “wx” twice for each class, function, or constant name—once as the package prefix and once as the “normal” prefix, such as wx.wxWindow. This got old fast. Many wxPython programmers saw this dilemma as a wart that should be removed, and eventually, it was. If you’re interested, you can search the wxPython mailing list archives to read more of the details surrounding this change.
One more thing to know about importing wxPython: you must import wx before you import anything else from wxPython. In general, the order of imports in Python is irrelevant, meaning you can import modules in any order. However, wxPython, although it looks like a single module, is actually a complex set of modules (many of which are automatically generated by a tool called the Simplified Wrapper and Interface Generator, or SWIG) that wrap the functionality provided by the underlying wxWidgets C++ toolkit (we’ll discuss wxWidgets in more detail in section 1.7). When you import the wx module for the first time, wxPython performs some initialization that is vital to other wxPython modules. As a result, some of the wxPython subpackages, such as the xrc module, might not work properly unless the wx module has already been imported:
import wx # Always import wx before from wx import xrc # any other wxPython packages, from wx import html # just to be on the safe side.
This requirement applies only to the wxPython modules; you can still import other Python modules as you always have, and those modules can be imported before or after the wxPython modules. For instance, this example is valid:
import sys import wx import os from wx import xrc import urllib
Working with applications and frames
Once you’ve imported the wx module, you can create your application and frame objects. Every wxPython program must have one application object and at least one frame object. These objects will be discussed in detail in chapter 2. For now, you just need to know that the application object must be an instance of wx.App or of a subclass you define where you declare an OnInit() method. The OnInit() method will be called by the wx.App parent class when your application starts.
Subclass the wxPython application class
Here is how we defined our wx.App subclass:
class MyApp(wx.App): def OnInit(self): frame = wx.Frame(parent=None, id=-1, title="Bare") frame.Show() return True
We named our class “MyApp,” which is a common convention, but any valid Python class name would do.
The OnInit() method is where you’ll most often create frame objects. But you won’t usually directly create instances of wx.Frame as we did here. Instead, you’ll define your own wx.Frame subclass the same way we defined our own wx.App subclass. (You’ll see an example in the next section.) We’ll explore frames in detail in the next chapter, so for now we’ll simply point out that the wx.Frame constructor accepts several parameters. Of the three we supplied, only the first is required, while the rest have default values.
Invoking the Show() method makes the frame visible. If we had left that out, the frame would have been created, but we wouldn’t be able to see it. We can toggle the frame’s visibility by calling Show() with a Boolean parameter:
frame.Show(False) # Make the frame invisible. frame.Show(True) # True is the default parameter value. frame.Hide() # Equivalent to frame.Show(False).
Define an application initialization method
Notice that we didn’t define an __init__() method for our application class. In Python, this means that the parent method, wx.App.__init__(), is automatically invoked on object creation. This is a good thing. If you define an __init__() method of your own, don’t forget to call the __init__() of the base class, like this:
class App(wx.App): def __init__(self): # Call the base class constructor. wx.App.__init__(self) # Do something here...
If you forget to do so, wxPython won’t be initialized and your OnInit() method won’t get called.
Create an application class instance and enter its main event loop
The final step is to create an instance of the wx.App subclass, and invoke its Main-Loop() method:
app = App() app.MainLoop()
That’s it. Once the application’s main event loop processing takes over, control passes to wxPython. Unlike procedural programs, a wxPython GUI program primarily responds to the events taking place around it, mostly determined by a human user clicking with a mouse and typing at the keyboard. When all the frames in an application have been closed, the app.MainLoop() method will return and the program will exit.
Extending the bare-minimum wxPython program
We showed you a bare-minimum wxPython program to give you a comfortable start, but something that small isn’t useful for anything but discussion. By oversimplifying the code, we produced a Python program that was easy to understand, but difficult to extend—which is not how we would encourage you to create serious wxPython programs.
So now we’re going to enhance this minimal program until it has a reasonable amount of functionality, incorporates common Python programming standards, and can serve as a proper foundation for your own programs. Listing 1.2 shows the next iteration, which we named spare.py.
This version is still quite small, only 14 lines of code, but we added several important items that get us closer to what we would consider good, solid code.
Developer.com Note:The numbers in the following list indicate the “callout” numbers in the listing above.
- The first line in the file is now a shebang line. It looks like a Python comment, which it is, but on some operating systems, such as Linux and Unix, the shebang tells the operating system how to find the interpreter that will execute the program file. If this program file was then given executable privileges (using the chmod command, for example) we could run the program from the operating system command line by simply supplying the program name:
The shebang line is a convenience for Unix and Mac OS X users and is simply ignored on other platforms. Even if you aren’t using one of those systems, it’s polite to include it on a script that might be executed cross-platform.
- We added a module docstring (documentation string). When the first statement in a module is a string, that string becomes the docstring for the module and is stored in the module’s __doc__ attribute. You can access the docstring in your code, some development environments, and even the Python interpreter running in interactive mode:
>>> import spare >>> print spare.__doc__ Spare.py is a starting point for simple wxPython programs. >>>
Docstrings are but one example of Python’s powerful introspection capabilities, and we will encourage you to provide them for modules, classes, methods, functions, and any other place that Python supports. Python development tools, such as PyCrust, are able to use the docstring to provide useful information to a developer while you are coding.
- We changed the way we created the frame object. The “bare” version of this program simply created an instance of the wx.Frame class. In the “spare” version we defined our own Frame class as a subclass of wx.Frame. At this point it hasn’t made any difference to the final results, but you’ll want your own Frame class if you want anything interesting, such as text, buttons, and menus, to appear in your frame. Introducing your own custom Frame class now sets the stage for future iterations. In fact, once your Frame class becomes complicated, you’ll probably want to move it into its own module and import it into your main program.
- We added a reference to the frame instance as an attribute of the application class instance. Again, we’re setting the stage for things to come, as well as demonstrating how easy it is to add attributes to Python classes. It makes no difference that the attribute is a reference to a complex, graphical object, such as a frame. To Python, an object is an object is an object.
- Inside the OnInit() method we called the App class’s own SetTopWindow() method,
passing it our newly created frame instance. We didn’t have to define the SetTopWindow() method because it was inherited from the wx.App parent class. It’s an optional method that lets wxPython know which frame or dialog should be considered the main one. A wxPython program can have several frames, with one designated as the top window for the application. In this case the choice was easy since we have but one frame.
- The final addition to the program represents a common idiom in Python programs
used to test whether the module is being run as a program or was imported
by another module. We do that by examining the module’s __name__ attribute:
if __name__ == '__main__': app = App() app.MainLoop()
If the module was imported, its __name__ attribute will be the same as its filename (without the extension), like this:
>>> import spare >>> spare.__name__ 'spare' >>>
But if the module is being executed, rather than imported, Python overrides the default naming convention and sets the module’s __name__ attribute to ‘__main__‘, giving us the chance to have the module behave differently when executed directly. We take advantage of this feature by creating an application instance and entering its main event-loop only if the module is being executed as a program.
If we didn’t perform this test, and created an application instance even when this module was imported, it could conflict with code in the module doing the importing—especially if the importing module has already started the wxPython event loop. It would also be quite difficult to test (especially since there can only be one application instance active at one time in a wxPython program, and once we enter the event loop, control passes to wxPython.) By not starting our own application when the module is imported, we make our frame and app classes readily available to other Python programs, facilitating the reuse of existing code.
Creating the final hello.py program
Now that you’ve got the basics under your belt, let’s create the final version of the program we showed at the beginning of this chapter. Create a file named hello.py and enter in the code shown in listing 2.
Listing 2 The final hello.py
Developer.com Note:The numbers in the following list indicate the “callout” numbers in the listing above.
- The shebang line allows this program to be an executable script under Linux and other Unix-like operating systems.
- Defining a custom Frame class that subclasses wx.Frame lets us more easily control the Frame’s contents and appearance.
- We added an image parameter to our Frame’s constructor. This value is provided by our application class when it creates a Frame instance. As long as we can pass the required values to wx.Frame.__init__(), there’s no reason we can’t add more parameters to our subclass’s constructor.
- We’re going to display the image in a wx.StaticBitmap control, which requires a bitmap. So we convert the image to a bitmap. We also create a size tuple, using the width and height of the bitmap. The size tuple is supplied to the wx.Frame.__init__() call, so that the frame size matches the bitmap size.
- Defining a wx.App subclass with an OnInit() method is a minimum requirement for any wxPython application.
- We create an image object, using a wxPython.jpg file stored in the same directory as hello.py. You can get this file from the Manning web site, or substitute one of your own. A more sophisticated version of this program would accept the name of a file from the command line. We pass our image object as a parameter when we create the frame.
- The main() function creates an application instance and starts the wxPython event loop.
- Checking if this module is the main module allows it to be used in two different ways: run from the command line or imported by another module.
What happened when you ran your version of hello.py? Did you see a frame sized to match the graphic you provided? If not, brush yourself off and try again. If so, congratulations! You’re ready to move on to the next exciting steps.
But before you rush into the next article, we’re going to talk about wxPython a little more broadly, what it’s capable of, and how it came to be. If that doesn’t interest you, feel free to jump to the next chapter and continue coding—the rest of the introduction will still be here.
About the Authors
Noel Rappin is a senior software engineer at a large technology company, and has extensive Python experience. He has a Ph.D. from the Georgia Institute of Technology, where he studied educational software and user interface design. Noel co-authored the book Jython Essentials.
Robin Dunn, the creator and maintainer of wxPython, has been working in the software industry for 18 years on a wide variety of applications. He discovered both wxWindows and Python in 1995 while looking for a cross platform toolkit and has never (willingly) looked back. Robin was awarded the ActiveState Programmers? Choice Award at the 2002 O?Reilly Open Source Convention. Robin also worked for the Open Source Applications Foundation, improving wxPython for use in their flagship product, Chandler.
Source of This Material
wxPython in Action
By Noel Rappin and Robin Dunn