Getting Started with wxPython
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|
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:
% spare.pyThe 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.
Page 2 of 3