Abstract

wxPython represents the python bindings to wxwidgets GUI toolkit. It enables you to write cross-platform Graphical User Interfaces or GUIs in python. Unlike other toolkits wxPython allows you to create GUIs that will look native on all platforms it supports (Windows, Linux, OSX).

For more information on GUI toolkits check The GUI Toolkit, Framework Page and python.org's GuiProgramming

For more information about wxPython history check WxPythonHistory

Prerequisites

Here is what you need before we start:

Hello wxpython

   1 import wx
   2 app = wx.App() # creation of the wx.App object (initialisation of the wxpython toolkit)
   3 frame = wx.Frame(None, title="Hello World") # creation of a Frame with "Hello World" as title
   4 frame.Show() # frames are invisible by default so we use Show() to make them visible
   5 app.MainLoop() # here the app enters a loop waiting for user input

Although there are only 5 lines this app does everything a GUI is supposed to to: presents information (the "Hello World" title) and reacts to user input (you can drag the frame around the screen, you can resize it and you can minimize, maximize or close it).

Minimal App

Most of wxpython apps will go through the following scenario:

  1. an app object gets created, its creation will take care of the initialization of the toolkit.
  2. a custom frame/dialog/window gets created and shown.
  3. the app's MainLoop() gets called and this will put the app in a loop where events generated by widgets will be dispatched to their handlers.

In most of the apps, steps 1 and 3 will be just 2 lines: step 1 will be something like app = wx.App(0) and step 3 will be something like app.MainLoop().

Step 2 will involve:

Step 2.2 is rather straightforward: identify the event you want to react to using the documentation and then use widget.Bind(...) to create a link between the event generated by that widget and your custom handler.

Step 2.1 will be most of the GUI related work you'll do on your app. Most of the problems you'll encounter will be here. It will be either that your widgets don't look/behave as you want them to or maybe they aren't laid out properly, this is why it is a good idea to have the docs and the demo open at all time and to learn at least one way of laying out widgets.

The recommended way of laying out widgets is by using sizers. The sizer is a class that will compute the location and size a widget should have. Of course it requires some hints like the order in which the widgets are laid out, if a widget should enlarge as more space is available or if it should stay the same size. Sizers will automatically handle situations where the layout of the frame/dialog/window needs to change. For example, when you resize the frame and more space becomes available, the sizer will automatically recompute widgets size and location and distribute the available space to the managed widgets according to the hints that were given.

wx.BoxSizer is one of the most used sizers. It is very easy to use and with several BoxSizers you can create quite complex layouts. It simply lays out the widgets one after the other from top to bottom if the orientation is wx.VERTICAL or from left to right if the orientation is wx.HORIZONTAL. Adding widgets to the sizer means that the sizer will manage their size and position. When adding the widgets you can also give the sizer certain hints about how the widget should react to changes in layout. The first hint is the proportion, it could be 0 or 1 and above and it tells the sizer if it should expand the widget in the direction of the layout as more space is available. So if you use a BoxSizer with wx.VERTICAL as orientation a proportion of 1 will cause the widget to use all the available vertical space. If more than one widget has 1 as proportion they will share (divide equally) the available space. Using different numbers for proportions will cause the space to be divided proportionally between the widgets. The next attribute is the flag. Here you could tell the sizer if the widget should expand in the other direction, if the widgets should be aligned in the available space or if there should be a border around the widget. More about it in the wxDocs, look for wxSizer, the parent of BoxSizer. Also look at the sizer examples from the Demo.

Hello Sizers App:

   1 import wx 
   2 import random
   3 
   4 def GetRandColor():
   5     red = random.randint(0,255)
   6     green = random.randint(0,255)
   7     blue = random.randint(0,255)
   8     return (red, green, blue)
   9 
  10 class ColorPanel(wx.Panel):
  11     def __init__(self, parent, color='rand'):
  12         wx.Panel.__init__(self, parent)
  13         if color == 'rand':
  14             color = GetRandColor()
  15             self.Bind(wx.EVT_MOTION, self.OnMouseMove)
  16         self.SetBackgroundColour(color)
  17     
  18     def OnMouseMove(self, evt):
  19         self.SetBackgroundColour(GetRandColor())
  20         self.Refresh()
  21         evt.Skip()
  22         
  23 class MyFrame(wx.Frame):
  24     def __init__(self):
  25         wx.Frame.__init__(self, None, title="Hello Sizers")
  26         vSizer =wx.BoxSizer(wx.VERTICAL)
  27         hSizer = wx.BoxSizer(wx.HORIZONTAL)
  28         red = ColorPanel(self, 'red')
  29         blue = ColorPanel(self, 'blue')
  30         green = ColorPanel(self, 'green')
  31         rand = ColorPanel(self)
  32         
  33         red.SetSize((100, 100))
  34         rand.SetSize((100, 100))
  35         green.SetSize((-1, 100))
  36         blue.SetSize((-1, 100))
  37         
  38         vSizer.Add(rand, 0, wx.EXPAND)
  39         vSizer.Add(green, 1, wx.EXPAND)
  40         vSizer.Add(blue, 2, wx.EXPAND)
  41         
  42         hSizer.Add(red, 0, wx.EXPAND)
  43         hSizer.Add(vSizer, 1, wx.EXPAND)
  44         
  45         self.SetSizer(hSizer)
  46         self.Fit()
  47         
  48         self.Show()
  49         self.Bind(wx.EVT_CLOSE, self.OnClose)
  50     
  51     def OnClose(self, evt):
  52         dlg = wx.MessageDialog(self, 'Are you sure you want to close My World?',
  53                         'Closing...', wx.YES_NO | wx.ICON_QUESTION)
  54         ret = dlg.ShowModal()
  55         dlg.Destroy()
  56         
  57         if ret == wx.ID_YES:
  58             evt.Skip()
  59         
  60 app = wx.App(0) 
  61 MyFrame() 
  62 app.MainLoop() 

Now, for some clarifications on the above app:

ColorPanel is a custom wx.Panel that can receive a color at initialization, color that will become its background color. If it doesn't receive a color it defaults to 'rand' and beside setting the background color to some random color it also installs a handler for mouse movement so that when the mouse moves OVER the panel the panel changes its background color. As you can see SetBackgroundColour is not fussy, it accepts a named color, a wx.Color object, a predefined wx.Color object like wx.RED or a tuple of 3 integers representing the intensity of red, green and blue.

The layout is taken care of in the Frame constructor. There we create 4 ColorPanels and with the help of 2 BoxSizers we place them in a configuration with the red panel on the left, expanding as the frame grows vertically. The next 3 panels are placed on the right one on top of the other with green and blue expanding vertically in a proportion of 1:2.

The sizer talks to the children (the widgets it manages) and to the parent. From the parent it gets information about available space and from the children it gets information about what sizes they prefer. When you have problems with the layout the first step in debugging is to look for what information the sizer receives. The information about the size is ALWAYS overwritten by the hints you give to the sizer. In the above example we set the size of the red panel to 100x100 pixels but when we add it to the sizer we say that proportion should be 0 and that it should EXPAND. So the sizer (being a HORIZONTAL BoxSizer) honors the information about the width that it receives from the panel due to proportion 0 (as the widget wants) BUT it overrides the information about height due to wx.EXPAND hint.

The green panel has both the proportion set to 1 and the wx.EXPAND flag so it looks like none of its size hints are gonna be honored by the sizer... well not true, having proportion of 1 makes it a reference so when we later ask the frame to Fit the following happens:

Let's see now what happens when we resize the frame:

Next Level

Here are few things you might wanna try next:

GettingStarted (last edited 2008-03-11 10:50:31 by localhost)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.