Blogroll

Forums

python

Creating a GUI using Python, WxWidgets, and wxPython

After putting off this tutorial for as long as I possibly could (I’m not sure why) I have finally decided to buckle down and learn how to use WxWidgets using the WxPython Python bindings.

Installing

Getting WxWidgets and WxPython installed on my Debian Linux computer was as simple as installing the python-wxgtk2.6 Debian package. If you have any problems installing wxPython on your computer I suggest that you follow the instructions in the wxPython FAQ. Or you can follow the Getting Started instructions on the wxPython wiki.

Setting up the Window

Now that you have WxWidgets and WxPython installed it’s time to write a quick wxPython application. Start up your favourite python editor and enter the following:

[code lang=”python”]
#!/usr/bin/env python

import wx

if __name__ == “__main__”:
app = wx.App()
app.MainLoop()
[/code]

Save the file as wxHello.py, or whatever you would like. You can run the file if you want but you won’t actually see anything. The first thing that you will notice is that we import wx, this will import wxPython into your program.

The next thing that we do is create a wx.App and then call it’s MainLoop function, which starts the main GUI event loop. Here is a description of the wx.App class from the wxPython documentation :

The wx.App class represents the application and is used to:

bootstrap the wxPython system and initialize the underlying gui toolkit

set and get application-wide properties

implement the windowing system main message or event loop, and to dispatch events to window instances

etc.

Every application must have a wx.App instance, and all creation of UI objects should be delayed until after the wx.App object has been created in order to ensure that the gui platform and wxWidgets have been fully initialized.

Normally you would derive from this class and implement an OnInit method that creates a frame and then calls self.SetTopWindow(frame).

Notice the last little instruction there:

Normally you would derive from this class and implement an OnInit method that creates a frame and then calls self.SetTopWindow(frame).

So that is our next step, derive a class from wx.App and override the OnInit member:

Now instead of creating a wx.App we create a wxHelloApp that is derived from a wx.App. All that the wxHelloApp does is override the OnInit() function. In that function it creates a wx.Frame (from the wxWidget docs):

A frame is a window whose size and position can (usually) be changed by the user. It usually has thick borders and a title bar, and can optionally contain a menu bar, toolbar and status bar. A frame can contain any window that is not a frame or dialog.

When we create the frame we set its parent to be nothing, and it’s title to be “wxHello”. If you run the wxHello.py file now you will be greeted by the following:

It’s not much but it’s a start. Now we are going to do the same thing to the wx.Frame class that we did to the wx.App class:

[code lang=”python”]
#!/usr/bin/env python

import wx

class wxHelloFrame(wx.Frame):
“””This is the frame for our application, it is derived from
the wx.Frame element.”””

Now if you run this code the result will be identical to the previous run, the only difference is in the internal code. Instead of creating a wx.Frame directly we create an instance of our wx.Frame derived class wxHelloFrame.

wxHelloFrame does nothing except initialize its parent class and then call the create_controls() function which is where we will create our widgets in the future but for now does nothing.

Adding the Widgets

Now we are going to start adding the widgets to the frame in the create_controls() function. Since we want our window to be resizeable (which they are in wxPython by default) we are going to use wx.Sizer objects so that our widgets also resize (from the WxPython docs):

wx.Sizer is the abstract base class used for laying out subwindows in a window. You cannot use wx.Sizer directly; instead, you will have to use one of the sizer classes derived from it such as wx.BoxSizer, wx.StaticBoxSizer, wx.GridSizer, wx.FlexGridSizer and wx.GridBagSizer.

The concept implemented by sizers in wxWidgets is closely related to layout tools in other GUI toolkits, such as Java’s AWT, the GTK toolkit or the Qt toolkit. It is based upon the idea of the individual subwindows reporting their minimal required size and their ability to get stretched if the size of the parent window has changed. This will most often mean that the programmer does not set the original size of a dialog in the beginning, rather the dialog will assigned a sizer and this sizer will be queried about the recommended size. The sizer in turn will query its children, which can be normal windows or contorls, empty space or other sizers, so that a hierarchy of sizers can be constructed. Note that wxSizer does not derive from wxWindow and thus do not interfere with tab ordering and requires very little resources compared to a real window on screen.

We are going to use wx.BoxSizer objects to lay out our widgets (from the wxPython docs):

The basic idea behind a box sizer is that windows will most often be laid out in rather simple basic geometry, typically in a row or a column or nested hierarchies of either. A wx.BoxSizer will lay out its items in a simple row or column, depending on the orientation parameter passed to the constructor.

It is the unique feature of a box sizer, that it can grow in both directions (height and width) but can distribute its growth in the main direction (horizontal for a row) unevenly among its children. This is determined by the proportion parameter give to items when they are added to the sizer. It is interpreted as a weight factor, i.e. it can be zero, indicating that the window may not be resized at all, or above zero. If several windows have a value above zero, the value is interpreted relative to the sum of all weight factors of the sizer, so when adding two windows with a value of 1, they will both get resized equally and each will receive half of the available space after the fixed size items have been sized. If the items have unequal proportion settings then they will receive a coresondingly unequal allotment of the free space.

Whew, that’s a lot of documentation, fortunately wx.Sizer’s aren’t that difficult to use. So first we need to create a horizontal wx.BoxSizer (so that our widgets will resize horizontally) and then add wx.StaticText and wx.TextCtrl widgets to the sizer. After that we simply need to set the wx.boxSizer as the frame’s sizer and we are done.

[code lang=”python”]
def create_controls(self):
“””Called when the controls on Window are to be created”””
# Horizontal sizer
self.h_sizer = wx.BoxSizer(wx.HORIZONTAL)

#Add to horizontal sizer
#add the static text to the sizer, tell it not to resize
self.h_sizer.Add(self.text, 0,)
#Add 5 pixels between the static text and the edit
self.h_sizer.AddSpacer((5,0))
#Add Edit
self.h_sizer.Add(self.edit, 1)

#Set the sizer
self.SetSizer(self.h_sizer)
[/code]

So, as you can see creating the wx.BoxSize and the wx.StaticText widgets is pretty straightforward. When we create the wx.TextCtrl widget you’ll notice that we are setting the default size of the widget using a wx.Size object. We are setting the default width to be 250 and the default height to be -1. When you use -1 as the value for a parameter in wxWidgets it generally means that that parameter should be ignored. So since we don’t want to set the height of the edit field, we want wxWidgets to do that for us automatically, we set the height to be -1.

You’ll also notice that the first parameter when we created the wx.StaticText and wx.TextCtrl widgets is self, or our wxHelloFrame. This means that the wxHelloFrame will be the parent window of the widgets.

We then add the two widgets to the wx.BoxSizer using the wx.Sizer.Add function. When we add the wx.Statictext we set the resize proportion to be 0 since we don’t want it to resize, and when we add the wx.TextCtrl we set its resize proportion to be 1, since we want it to resize directly with the window as it resizes.

You’ll also notice that we add a spacer in in between the wx.StaticText and wx.TextCtrl widgets using the wx.Sizer.AddSpacer() function. We do this to add a 5 pixel horizontal buffer between the two widgets just so that things look nicer.

Finally we set the wxHelloFrame’s sizer to be the horizontal sizer and we are done. If you run this code you will see the following:

So that’s pretty neat, you have a window with some widgets that resize. The next thing that we are going to do is add a wx.Button underneath the wx.StaticText widget.

To do this we will need to use another wx.BoxSizer, this one being a vertical sizer. Then we will add our horizontal wx.BoxSizer to the vertical sizer and then add the button. This will place the contents of the horizontal box sizer over top of the button.

[code lang=”python”]
def create_controls(self):
“””Called when the controls on Window are to be created”””

#Add to horizontal sizer
#add the static text to the sizer, tell it not to resize
self.h_sizer.Add(self.text, 0,)
#Add 5 pixels between the static text and the edit
self.h_sizer.AddSpacer((5,0))
#Add Edit
self.h_sizer.Add(self.edit, 1)

So now we create both a horizontal and vertical wx.BoxSizer and a wx.Button widget with specific label text. We then add the horizontal size to the vertical sizer, and then we add the button creating two rows of widgets. We set the resize proportion of both the button and the horizontal sizer to be 0 since we don’t want them resizing vertically. However we set the horizontal sizer’s flag to be wx.EXPAND which means:

The item will be expanded to fill the space allotted to the item.

We do this so that the horizontal sizer will continue to resize horizontally. Finally we set the sizer of the wxHelloFrame to be the vertical sizer. If you run the code you will see the following:

One thing that you will notice when you run the program is that the window starts off too big for the widgets and you can resize the window smaller then the size of the widgets. This isn’t very nice so we need to add in some code to set the initial size of the window and to set the minimum size of the window.

The other thing that we need to do is do something when the button is clicked. We will make it so that when the button is clicked a message box comes up displaying whatever text was typed in the wx.TextCtrl.

[code lang=”python”]
def create_controls(self):
“””Called when the controls on Window are to be created”””

#Add to horizontal sizer
#add the static text to the sizer, tell it not to resize
self.h_sizer.Add(self.text, 0,)
#Add 5 pixels between the static text and the edit
self.h_sizer.AddSpacer((5,0))
#Add Edit
self.h_sizer.Add(self.edit, 1)

This function is pretty straight forward, first we get the text from the wx.TextCtrl widget using the wx.TextCtrl.GetValue() function. Then we create a wx.MessageDialog instance. The wx.MessageDialog is “a simple dialog that shows a single or multi-line message, with a choice of OK, Yes, No and/or Cancel buttons.” We set the parent, title, text of the dialog and use the following flags to control how it looks and functions:

wx.OK – Show an OK button.

wx.ICON_EXCLAMATION – Shows an exclamation mark icon.

wx.CENTER – Is supposed to center the dialog on it’s parent.

Then we show the dialog using the wx.Dialog.ShowModal() function.

Here is the all the code:

[code lang=”python”]
#!/usr/bin/env python

import wx

class wxHelloFrame(wx.Frame):
“””This is the frame for our application, it is derived from
the wx.Frame element.”””

#Add to horizontal sizer
#add the static text to the sizer, tell it not to resize
self.h_sizer.Add(self.text, 0,)
#Add 5 pixels between the static text and the edit
self.h_sizer.AddSpacer((5,0))
#Add Edit
self.h_sizer.Add(self.edit, 1)

Wrap up

So that’s it for this simple wxWidgets tutorial I hope everything makes sense. All-in-all I found my first experience with wxPython to be quite enjoyable, I would have liked the wxPython documentation to be a bit more robust but using it and some of the other examples on the Internet was enough for me to muddle my way through.

I still think that I prefer pyGTK to wxPython but the fact that I have more experience with pyGTK probably plays a large role in that. I also like that the fact the pyGTk seems to take a different approach to the other GUI tool kits that I have used making it able to do some really neat things. wxWidgets on the other hand seems to be more similar to other toolkits, which made me instantly more familiar with some of its functionality.

As always if you have any questions or comments, or if you notice any bugs or style irregularities feel free to add a comment below.

If you have any of your own wxPython tips or want to ask some wxPython questions why don’t you do so in the learningPython fourms?

26 thoughts on “Creating a GUI using Python, WxWidgets, and wxPython”

I’ve been researching a lot about cross-platform UIs. I’m looking into XUL, and even some of the Adobe offerings. Have you seen much on the actual packaging of something written with wxPython and distribution for Mac OSX and Windows? (I haven’t really found much on this, and I’m curious how the end user experience would be for this.)

Thanks for the article, also liked your one on PyGTK. I’m more of the wxPython fan myself. I’ve found it has a very active user community with lots of open source widgets. A plus when it comes to making all sorts of fancy programs with minimal effort.

I have not looked into the packaging of a wxPython application all that much. I did stumble upon a short py2exe tutorial on the wxPython website. For OSX you you might want to read this Optimizing for Mac OS X from the wxPython site. It describes using py2app to create stand-alone applications for OSX.

I just found this article from http://wiki.wxpython.org/FrontPage. This was the best presentation on sizers that I have read. Your examples made it very easy to understand a concept that was so difficult for me before. Thank you very much!

I wish I could find more of these elementary tutorials to speed up my
learning cure. Basically, these tutorials are necessary due to a lacking
comprehensive wxwidgets documention. that is really bottom-up.
Nonetheless, thank you.
Siggi

Thank you for this great tutorial. I already had some experience with PyGTK and now I want to familiarize me with wxPython to start developing a bit for Mac OSX. Your tutorial helped me a lot to understand the “basic wxPython”.

You really make it seem so easy with your presentation
but I find this topic to be actually something which I think
I would never understand. It seems too complex and
very broad for me. I am looking forward for your next post, I will try to get the hang of
it!

I would recommend changing one line of this “good” tutorial. The section that describes the adding of widgets to the create_controls method/function begins with the line:
“Now we are going to start adding the widgets to the frame in the create_controls() function”.

However, in a tutorial that is aimed at people new to wxPython a better wording would, in my opinion, have been:
“Now we are going to start adding the widgets to the application by modifying the create_controls() function defined in the wxHelloFrame class.

I know this is being very, very picky, but I have seen people define a second create_controls function outside of the class definition and get very frustrated. I hope you are not irritated by this observation.

Thanks for the wxpython article. I copied the dialog function and was able to quickly
add it into an existing script. It answered some questions I had about using a dialog.
Now, I won’t be so slow to add them in when needed.

Hi, if you unzip the template and lucanh main.py (not MainFrame.py) the program should run without any modification. If the program works try to change the code inside the event handlers and lucanh it again. Launch main.py from the console, so you can see the traceback in case of errors. When the application is finished and debugged, rename main.py to main.pyw to execute the application without the console.

com, a Web Marketing House that deals with SEO,
SEM and Online Marketing. Solicit customer testimonials and provide
a page for them. They also tend to be searched more unlike others in subsequent pages.

According to com – Score, US online sales rose by 13 percent in the 3rd quarter this
year compared to that same quarter in 2010. ‘ HUBSPOT BLOG TOPIC GENERATOR Know what you want to
write about but clueless on how to address it.