The wxPython Linux Tutorial

Table of Contents:

Foreword

The purpose of this tutorial is to get you started with the wxPython toolkit, from the basics to the advanced topics. It has lots of code examples, not much talking. After that, you will be able to dig in yourself.

There are three decent toolkits for the python programming language:

Note that this tutorial is done on Linux. Some scripts do not work correctly on windows.

Icons used in this tutorial: icons.tgz Images used in this tutorial: images.tgz

jan bodnar 2005 - 2007

status update. (april 2007) All my work on wxPython tutorial has been moved to my website http://www.zetcode.com/wxpython here I shall not add any more examples. If I find myself some time, I will do some polishing.

wxPython API

wxPython API is a set of functions and widgets. Widgets are essential building blocks of a GUI application. Under Windows widgets are calles controls. We can roughly divide programmers into two groups. They code applications or libraries. In our case, wxPython is a library that is used by application programmers to code applications. Technically, wxPython is a wrapper over a C++ GUI API called wxWidgets. So it is not a native API. e.g. not written directly in Python. The only native GUI library for an interpreted language that I know is Java's Swing library.

In wxPython we have lot's of widgets. These can be divided into some logical groups.

Base Widgets

These widgets provide basic functionality for derived widgets. They are usually not used directly.

Top level Widgets

These widgets exist independently of each other.

Containers

Containers contain other widgets. These widgets are called children.

Dynamic Widgets

These widgets can be edited by users.

Static Widgets

These widgets display informatin. They cannot be edited by user.

Other Widgets

These widgets implement statusbar, toolbar and menubar in an application.

First Steps

We start with a simple example.

   1 #!/usr/bin/python
   2 
   3 # simple.py
   4 
   5 import wx
   6 
   7 app = wx.App()
   8 
   9 frame = wx.Frame(None, -1, 'simple.py')
  10 frame.Show()
  11 
  12 app.MainLoop()

In every wxPython application, we must import the wx library.

import wx

An application object is created by initiating class wx.App.

app = wx.App()

We create a frame widget. The window pops up only if we call Show() method on a widget.

frame = wx.Frame(None, -1, "simple.py")
frame.Show()

The last line enters a Mainloop. A mainloop is an endless cycle that catches up all events coming up to your application. It is an integral part of any windows GUI application.

app.MainLoop()

Although the code is very simple, you can do a lot of things with your window. You can maximize it, minimize it, move it, resize it. All these things have been already done.

simple.png

Figure: simple.py

wx.Window

wx.Window is a base class out of which many widgets inherit. For instance, the wx.Frame widget inherits from wx.Window. Technically it means that we can use wx.Window's methods for all descendants. We will introduce here some of its methods.

   1 #!/usr/bin/python
   2 
   3 # simple2.py
   4 
   5 import wx
   6 
   7 app = wx.App()
   8 
   9 frame = wx.Frame(None, -1, '')
  10 frame.SetToolTip(wx.ToolTip('This is a frame'))
  11 frame.SetCursor(wx.StockCursor(wx.CURSOR_MAGNIFIER))
  12 frame.SetPosition(wx.Point(0,0))
  13 frame.SetSize(wx.Size(300,250))
  14 frame.SetTitle('simple2.py')
  15 frame.Show()
  16 
  17 app.MainLoop()

We create a 'This is a frame' tooltip. The cursor is set to a magnifier cursor. Possible cursor id's: are listed below We position the window to the upper left corner and size it to 300x250 pixels. Title is set to 'simple2.py'.

wx.Frame

wx.Frame is a container widget. It means that it can contain other widgets. It has the following constructor:

wx.Frame(wx.Window parent, id, string title,
         wx.Point pos = wx.DefaultPosition, wx.Size size = wx.DefaultSize,
         style = wx.DEFAULT_FRAME_STYLE, string name = 'frame')

A constructor is a special kind of a function. It is called when an object is created. For us it is only important that when we want to create a new widget, we simply call its constructor. Python enables parameters with default values. So the only obligatory parameters in wx.Frame are parent, id and title. If you specify all values of the parameters in order, you don't need to specify the parameter names. For example you want to create a wx.Frame widget, which has no parent, its identifier is 100, the title is 'Title', the position is (100,50) and the size is (100,100).

frame = wx.Frame(None, 100, 'Title', wx.Point(100,50), wx.Size(100,100))

Here we have omitted the pos parameter. So we must provide explicitly the size parameter.

frame = wx.Frame(None, 100, 'Title', size = wx.Size(100,100))

In the following example we will use other useful features.

   1 #!/usr/bin/python
   2 
   3 # icon.py
   4 
   5 import wx
   6 
   7 def main():
   8     app = wx.App()
   9 
  10     frame = wx.Frame(None, title='Icon', pos=(350,300))
  11     frame.SetIcon(wx.Icon('tipi.ico', wx.BITMAP_TYPE_ICO))
  12     frame.Center()
  13     frame.Show()
  14     app.MainLoop()
  15 
  16 if __name__ == '__main__':
  17     main()

Icon's name is Tipi.ico. The icon is located in current directory. First parameter of an icon's constructor is the file name. Second parameter specifies the file type.

As you have noticed, the structure of our application has changed. This is a standard in Python programs. In Python programs __name__ is a special variable. More complicated programs consist of several files. There is usually only one file, which launches the application. For this file, Python sets the __name__ variable to '__main__'. This happens when you launch an application from the command line or when you click on the file. So when you click on the icon.py file or you launch it from the command line, the __name__ variable equals '__main__'. Then function main() is called.

icon.png

Figure: icon.py

wx.MenuBar

To set up a menubar in your wxPython application is pretty simple. We will discuss adding menus to menubar, adding submenus to existing menus. Each menu consists of menuitems. Menuitems can be normal items, check items or radio items.

First thing to do is to create a menubar.

menubar = wx.MenuBar()

Then we create our menus.

file = wx.Menu()
edit = wx.Menu()
help = wx.Menu()

Then we add some items into the menu. This can be done in two ways.

file.Append(101, '&Open', 'Open a new document')
file.Append(102, '&Save', 'Save the document')

We can separate logical sections in menus with a horizontal line.

file.AppendSeparator()

If you want to have some icons in your menus, you need to create MenuItem objects manually.

quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application')
quit.SetBitmap(wx.Image('stock_exit-16.png',wx.BITMAP_TYPE_PNG).ConvertToBitmap())
file.AppendItem(quit)

wxPython toolkit can only put bitmaps into menus. Therefore we need to convert our PNG files into bitmaps.

Menus are then added into the menubar.

menubar.Append(file, '&File')
menubar.Append(edit, '&Edit')
menubar.Append(help, '&Help')

Finally we set up our menubar in our application class.

self.SetMenuBar(menubar)

Let's sum it up in a small script.

   1 #!/usr/bin/python
   2 
   3 # menu1.py
   4 
   5 import wx
   6 
   7 class MyMenu(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(200, 150))
  10 
  11         menubar = wx.MenuBar()
  12         file = wx.Menu()
  13         edit = wx.Menu()
  14         help = wx.Menu()
  15         file.Append(101, '&Open', 'Open a new document')
  16         file.Append(102, '&Save', 'Save the document')
  17         file.AppendSeparator()
  18         quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application')
  19         quit.SetBitmap(wx.Image('stock_exit-16.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap())
  20         file.AppendItem(quit)
  21 
  22         menubar.Append(file, '&File')
  23         menubar.Append(edit, '&Edit')
  24         menubar.Append(help, '&Help')
  25         self.SetMenuBar(menubar)
  26         self.CreateStatusBar()
  27 
  28 class MyApp(wx.App):
  29     def OnInit(self):
  30         frame = MyMenu(None, -1, 'menu1.py')
  31         frame.Show(True)
  32         return True
  33 
  34 app = MyApp(0)
  35 app.MainLoop()

So far we have seen the default, normal menu items. Here we see, how we can explicitly define check items, normal items or radio items.

edit.Append(201, 'check item1', '', wx.ITEM_CHECK)
edit.Append(202, 'check item2', '', kind=wx.ITEM_CHECK)

or

quit = wxMenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application', wx.ITEM_NORMAL)

The parameter is called kind.

Possible wx.ItemKind-s

If you want to create a submenu, you create a menu first.

submenu = wx.Menu()

Then append some menu items into this submenu.

submenu.Append(301, 'radio item1', kind=wx.ITEM_RADIO)
submenu.Append(302, 'radio item2', kind=wx.ITEM_RADIO)
submenu.Append(302, 'radio item3', kind=wx.ITEM_RADIO)

You finish with adding a submenu into a menu object.

edit.AppendMenu(203, 'submenu', submenu)

We now discuss how to respond to user actions. We will touch on it only briefly and explain it later in more detail.

So when a user of our application selects a menu item, an event is generated. We must provide an event handler, that will react to this event accordingly. Handling events in wxPython is the most elegant and simple that I have seen so far. When we look into the reference book, we find wx.EVT_MENU handler under Event handling section.

Suppose we want to add an event handler to our quit menu item.

wx.EVT_MENU(self, 105, self.OnQuit )

We need to provide three pieces of information. The object, where we bind our event handler. In our case self, the application's main object. The id of the corresponding menu item. And the method name, which will do our job.

The method which will react to user actions has two parameters. The first one is the object where this method is defined. The second one is the generated event. This time, we do without it. We simply close our application.

def OnQuit(self, event):
    self.Close()

The following script demonstrates various menu items, submenu and one simple event handler. I hate when my application window pops up somewhere in the corner by the will of the allmighty window manager. So I added

self.Centre()

so that the window pops up in the center of the screen.

menu1.png

Figure: menu1.py

   1 #!/usr/bin/python
   2 
   3 # menu2.py
   4 
   5 import wx
   6 
   7 class MyMenu(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(380, 250))
  10 
  11         menubar = wx.MenuBar()
  12         file = wx.Menu()
  13         edit = wx.Menu()
  14         help = wx.Menu()
  15         file.Append(101, '&Open', 'Open a new document')
  16         file.Append(102, '&Save', 'Save the document')
  17         file.AppendSeparator()
  18         quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application')
  19         quit.SetBitmap(wx.Image('stock_exit-16.png',wx.BITMAP_TYPE_PNG).ConvertToBitmap())
  20         file.AppendItem(quit)
  21         edit.Append(201, 'check item1', '', wx.ITEM_CHECK)
  22         edit.Append(202, 'check item2', kind=wx.ITEM_CHECK)
  23         submenu = wx.Menu()
  24         submenu.Append(301, 'radio item1', kind=wx.ITEM_RADIO)
  25         submenu.Append(302, 'radio item2', kind=wx.ITEM_RADIO)
  26         submenu.Append(303, 'radio item3', kind=wx.ITEM_RADIO)
  27         edit.AppendMenu(203, 'submenu', submenu)
  28         menubar.Append(file, '&File')
  29         menubar.Append(edit, '&Edit')
  30         menubar.Append(help, '&Help')
  31         self.SetMenuBar(menubar)
  32         self.Centre()
  33         self.Bind(wx.EVT_MENU, self.OnQuit, id=105)
  34 
  35     def OnQuit(self, event):
  36         self.Close()
  37 
  38 class MyApp(wx.App):
  39     def OnInit(self):
  40         frame = MyMenu(None, -1, 'menu2.py')
  41         frame.Show(True)
  42         return True
  43 
  44 app = MyApp(0)
  45 app.MainLoop()

menu2.png

Figure: menu2.py

wx.ToolBar

Toolbar is a widget that groups the most common used commands or actions of your application. Typically save, open, cut, copy, paste, undo, redo etc. Its purpose is to save time. You need one click to do an action from the toolbar and two clicks from the menu.

   1 #!/usr/bin/python
   2 
   3 # toolbar.py
   4 
   5 import wx
   6 
   7 class MyToolBar(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(350, 250))
  10 
  11         vbox = wx.BoxSizer(wx.VERTICAL)
  12         toolbar = wx.ToolBar(self, -1, style=wx.TB_HORIZONTAL | wx.NO_BORDER)
  13         toolbar.AddSimpleTool(1, wx.Image('stock_new.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'New', '')
  14         toolbar.AddSimpleTool(2, wx.Image('stock_open.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Open', '')
  15         toolbar.AddSimpleTool(3, wx.Image('stock_save.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Save', '')
  16         toolbar.AddSeparator()
  17         toolbar.AddSimpleTool(4, wx.Image('stock_exit.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Exit', '')
  18         toolbar.Realize()
  19         vbox.Add(toolbar, 0, border=5)
  20         self.SetSizer(vbox)
  21         self.statusbar = self.CreateStatusBar()
  22         self.Centre()
  23 
  24         self.Bind(wx.EVT_TOOL, self.OnNew, id=1)
  25         self.Bind(wx.EVT_TOOL, self.OnOpen, id=2)
  26         self.Bind(wx.EVT_TOOL, self.OnSave, id=3)
  27         self.Bind(wx.EVT_TOOL, self.OnExit, id=4)
  28 
  29     def OnNew(self, event):
  30         self.statusbar.SetStatusText('New Command')
  31 
  32     def OnOpen(self, event):
  33         self.statusbar.SetStatusText('Open Command')
  34 
  35     def OnSave(self, event):
  36         self.statusbar.SetStatusText('Save Command')
  37 
  38     def OnExit(self, event):
  39         self.Close()
  40 
  41 class MyApp(wx.App):
  42     def OnInit(self):
  43         frame = MyToolBar(None, -1, 'toolbar.py')
  44         frame.Show(True)
  45         return True
  46 
  47 app = MyApp(0)
  48 app.MainLoop()

wx.BoxSizer will be explained later in layout section. Toolbar widget is created in three steps.

Firstly, we create a toolbar object.

toolbar = wx.ToolBar(self, -1, style=wx.TB_HORIZONTAL | wx.NO_BORDER)

Then we add some tools to the toolbar with the AddSimpleTool() method. You don't find this method in the reference book. It is a wxPython 'extension'. This is a curse and also a blessing. It makes python programming easier. But on the other hand, these extensions are undocumented. You have to look at the wrapper code, demo example or ask on the mailing list.

toolbar.AddSimpleTool(1, wx.Image('stock_new.png',  wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'New', '')

In the end, we call the Realize() method. This method shows or renders the toolbar widget.

toolbar.Realize()

The toolbar widget has several event handlers. When you click on a toolbar icon a wx.EVT_COMMAND_TOOL_CLICKED event is generated. We bind this event to a specified method with the wx.EVT_TOOL handler.

In order to show some meaningful output to our events, we have set up a statusbar.

self.statusbar = self.CreateStatusBar()

This is yet another wxPython extension. So when we click on a toolbar button, a message is displayed on the statusbar. This is done with the SetStatusText() method.

toolbar.png

Figure: toolbar.py

Layout Management

There are basically two methods for layout of our widgets. The first method is manual. We place widgets by specifying the position in the constructor of the widget.

   1 #!/usr/bin/python
   2 
   3 # layout.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(250, 50))
  10 
  11         panel = wx.Panel(self, -1)
  12         wx.Button(panel, -1, "Button1", (0,0))
  13         wx.Button(panel, -1, "Button2", (80,0))
  14         wx.Button(panel, -1, "Button3", (160,0))
  15 
  16 class MyApp(wx.App):
  17     def OnInit(self):
  18         frame = MyFrame(None, -1, 'layout.py')
  19         frame.Show(True)
  20         frame.Centre()
  21         return True
  22 
  23 app = MyApp(0)
  24 app.MainLoop()

When the window is resized, the size and the position of buttons do not change. This is one of the main features of the manual positioning of the widgets.

layout.png

Figure: layout.py

The second method is to use layout managers. This method is prevalent in real programs. Basically you use sizers. We will discuss

wx.BoxSizer

Let's make a program in which three buttons will occupy one row placed at the top of the window. These buttons will resize when the window is resized.

   1 #!/usr/bin/python
   2 
   3 # wxboxsizer.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, (-1, -1), wx.Size(250, 50))
  10         panel = wx.Panel(self, -1)
  11         box = wx.BoxSizer(wx.HORIZONTAL)
  12         box.Add(wx.Button(panel, -1, 'Button1'), 1 )
  13         box.Add(wx.Button(panel, -1, 'Button2'), 1 )
  14         box.Add(wx.Button(panel, -1, 'Button3'), 1 )
  15         panel.SetSizer(box)
  16         self.Centre()
  17 
  18 class MyApp(wx.App):
  19      def OnInit(self):
  20          frame = MyFrame(None, -1, 'wxboxsizer.py')
  21          frame.Show(True)
  22          return True
  23 
  24 app = MyApp(0)
  25 app.MainLoop()

We can place widgets vertically or horizontally.

box = wx.BoxSizer(integer orient)

where orientation can be wx.VERTICAL or wx.HORIZONTAL. Adding widgets into the wx.BoxSizer is done via the Add() method. In order to understand it, we need to look at its parameters.

Add(wx.Window window, integer proportion=0, integer flag = 0, integer border = 0)

wxboxsizer.png

Figure: wxboxsizer.py

The proportion parameter defines the share or ratio of available sizer space that the widget will occupy in the direction of the defined orientation. Let's assume we have three buttons with the proportions 0, 1, and 2. They are added into a horizontal wx.BoxSizer. The button with proportion 0 will not change at all when the sizer's width (horizontal size) changes (i.e. the button will always be the same width). The rest of the width of the sizer is split into 3 (2+1) shares. The button with proportion 2 will always occupy 2 of those 3 shares (its width will be 2/3 of the available width), and the button with proportion 1 will always occupy 1 of those shares.

With the flag parameter, you can further configure the behaviour of the widgets within a wx.BoxSizer. We can control the border (though "padding" would be a more accurate name than "border") between the widgets. We add some space between widgets in pixels. In order to apply border, we need to define which sides will use the border. We can choose between these flags:

We can combine them with the | operator. e.g wx.LEFT | wx.BOTTOM. If we use wx.EXPAND flag, our widget will use all the space that is available in the direction perpendicular to the sizer's orient direction. Lastly, we can also define the alignment of our widgets. We do it with the following flags :

An example:

   1 #!/usr/bin/python
   2 
   3 # layout3.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, (-1, -1), wx.Size(450, 300))
  10 
  11         panel = wx.Panel(self, -1)
  12         box = wx.BoxSizer(wx.HORIZONTAL)
  13         box.Add(wx.Button(panel, -1, 'Button1'), 1, wx.ALL, 5)
  14         box.Add(wx.Button(panel, -1, 'Button2'), 0, wx.EXPAND)
  15         box.Add(wx.Button(panel, -1, 'Button3'), 0, wx.ALIGN_CENTER)
  16         panel.SetSizer(box)
  17         self.Centre()
  18 
  19 class MyApp(wx.App):
  20     def OnInit(self):
  21         frame = MyFrame(None, -1, 'layout3.py')
  22         frame.Show(True)
  23         return True
  24 
  25 app = MyApp(0)
  26 app.MainLoop()

In our example we again have three buttons. The first one has some border around all its sides. It is the only one that changes in the horizontal dimension when the main window is resized. The second one occupies all space alloted to it in the vertical direction. The third one is aligned in the centre.

We can combine various wx.BoxSizer-s. For example, we can put several horizontal wx.BoxSizer-s into a vertical wx.BoxSizer and vice versa. This way we can make complex layouts.

   1 #!/usr/bin/python
   2 
   3 # borders.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9 
  10         wx.Frame.__init__(self, parent, id, title)
  11         hbox = wx.BoxSizer(wx.HORIZONTAL)
  12         pnl1 = wx.Panel(self, -1, style=wx.SIMPLE_BORDER)
  13         pnl2 = wx.Panel(self, -1, style=wx.RAISED_BORDER)
  14         pnl3 = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
  15         pnl4 = wx.Panel(self, -1, style=wx.NO_BORDER)
  16 
  17         hbox.Add(pnl1, 1, wx.EXPAND | wx.ALL, 3)
  18         hbox.Add(pnl2, 1, wx.EXPAND | wx.ALL, 3)
  19         hbox.Add(pnl3, 1, wx.EXPAND | wx.ALL, 3)
  20         hbox.Add(pnl4, 1, wx.EXPAND | wx.ALL, 3)
  21 
  22         self.SetSize((400, 120))
  23         self.SetSizer(hbox)
  24         self.Centre()
  25 
  26 class MyApp(wx.App):
  27     def OnInit(self):
  28         frame = MyFrame(None, -1, 'borders.py')
  29         frame.Show(True)
  30         return True
  31 
  32 app = MyApp(0)
  33 app.MainLoop()

We show four various border styles available in wxPython. Border is a simple window decoration.

Available Borders:

borders.png

wx.GridSizer

wx.GridSizer lays out its children in a two-dimensional table. The width of each field is the width of the widest child. The height of each field is the height of the tallest child.

wx.GridSizer(integer rows, integer cols, integer vgap, integer hgap)

In the constructor we provide the number of rows and the number of columns of our table and the horizontal and vertical gap between the children widgets. We insert our widgets into the table with the AddMany() method. Children are inserted from left to right, top to bottom.

   1 #!/usr/bin/python
   2 
   3 # calculator.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9 
  10         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(300, 250))
  11         self.formula = False
  12         menubar = wx.MenuBar()
  13         file = wx.Menu()
  14         file.Append(22, '&Quit', 'Exit Calculator')
  15         menubar.Append(file, '&File')
  16         self.SetMenuBar(menubar)
  17         wx.EVT_MENU(self, 22, self.OnClose)
  18         sizer = wx.BoxSizer(wx.VERTICAL)
  19         self.display = wx.TextCtrl(self, -1, '',  style=wx.TE_RIGHT)
  20         sizer.Add(self.display, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 4)
  21 
  22         gs = wx.GridSizer(4, 4, 3, 3)
  23         gs.AddMany([(wx.Button(self, 20, 'Cls'), 0, wx.EXPAND),
  24                         (wx.Button(self, 21, 'Bck'), 0, wx.EXPAND),
  25                         (wx.StaticText(self, -1, ''), 0, wx.EXPAND),
  26                         (wx.Button(self, 22, 'Close'), 0, wx.EXPAND),
  27                         (wx.Button(self, 1, '7'), 0, wx.EXPAND),
  28                         (wx.Button(self, 2, '8'), 0, wx.EXPAND),
  29                         (wx.Button(self, 3, '9'), 0, wx.EXPAND),
  30                         (wx.Button(self, 4, '/'), 0, wx.EXPAND),
  31                         (wx.Button(self, 5, '4'), 0, wx.EXPAND),
  32                         (wx.Button(self, 6, '5'), 0, wx.EXPAND),
  33                         (wx.Button(self, 7, '6'), 0, wx.EXPAND),
  34                         (wx.Button(self, 8, '*'), 0, wx.EXPAND),
  35                         (wx.Button(self, 9, '1'), 0, wx.EXPAND),
  36                         (wx.Button(self, 10, '2'), 0, wx.EXPAND),
  37                         (wx.Button(self, 11, '3'), 0, wx.EXPAND),
  38                         (wx.Button(self, 12, '-'), 0, wx.EXPAND),
  39                         (wx.Button(self, 13, '0'), 0, wx.EXPAND),
  40                         (wx.Button(self, 14, '.'), 0, wx.EXPAND),
  41                         (wx.Button(self, 15, '='), 0, wx.EXPAND),
  42                         (wx.Button(self, 16, '+'), 0, wx.EXPAND) ])
  43 
  44         sizer.Add(gs, 1, wx.EXPAND)
  45 
  46         self.SetSizer(sizer)
  47         self.Centre()
  48 
  49         self.Bind(wx.EVT_BUTTON, self.OnClear, id=20)
  50         self.Bind(wx.EVT_BUTTON, self.OnBackspace, id=21)
  51         self.Bind(wx.EVT_BUTTON, self.OnClose, id=22)
  52         self.Bind(wx.EVT_BUTTON, self.OnSeven, id=1)
  53         self.Bind(wx.EVT_BUTTON, self.OnEight, id=2)
  54         self.Bind(wx.EVT_BUTTON, self.OnNine, id=3)
  55         self.Bind(wx.EVT_BUTTON, self.OnDivide, id=4)
  56         self.Bind(wx.EVT_BUTTON, self.OnFour, id=5)
  57         self.Bind(wx.EVT_BUTTON, self.OnFive, id=6)
  58         self.Bind(wx.EVT_BUTTON, self.OnSix, id=7)
  59         self.Bind(wx.EVT_BUTTON, self.OnMultiply, id=8)
  60         self.Bind(wx.EVT_BUTTON, self.OnOne, id=9)
  61         self.Bind(wx.EVT_BUTTON, self.OnTwo, id=10)
  62         self.Bind(wx.EVT_BUTTON, self.OnThree, id=11)
  63         self.Bind(wx.EVT_BUTTON, self.OnMinus, id=12)
  64         self.Bind(wx.EVT_BUTTON, self.OnZero, id=13)
  65         self.Bind(wx.EVT_BUTTON, self.OnDot, id=14)
  66         self.Bind(wx.EVT_BUTTON, self.OnEqual, id=15)
  67         self.Bind(wx.EVT_BUTTON, self.OnPlus, id=16)
  68 
  69     def OnClear(self, event):
  70         self.display.Clear()
  71 
  72     def OnBackspace(self, event):
  73         formula = self.display.GetValue()
  74         self.display.Clear()
  75         self.display.SetValue(formula[:-1])
  76 
  77     def OnClose(self, event):
  78         self.Close()
  79 
  80     def OnDivide(self, event):
  81         if self.formula:
  82             return
  83         self.display.AppendText('/')
  84 
  85     def OnMultiply(self, event):
  86         if self.formula:
  87             return
  88         self.display.AppendText('*')
  89 
  90     def OnMinus(self, event):
  91         if self.formula:
  92             return
  93         self.display.AppendText('-')
  94 
  95     def OnPlus(self, event):
  96         if self.formula:
  97             return
  98         self.display.AppendText('+')
  99 
 100     def OnDot(self, event):
 101         if self.formula:
 102             return
 103         self.display.AppendText('.')
 104 
 105     def OnEqual(self, event):
 106         if self.formula:
 107             return
 108         formula = self.display.GetValue()
 109         self.formula = False
 110         try:
 111             self.display.Clear()
 112             output = eval(formula)
 113             self.display.AppendText(str(output))
 114         except StandardError:
 115             self.display.AppendText("Error")
 116 
 117     def OnZero(self, event):
 118         if self.formula:
 119             self.display.Clear()
 120             self.formula = False
 121         self.display.AppendText('0')
 122 
 123     def OnOne(self, event):
 124         if self.formula:
 125             self.display.Clear()
 126             self.formula = False
 127         self.display.AppendText('1')
 128 
 129     def OnTwo(self, event):
 130         if self.formula:
 131             self.display.Clear()
 132             self.formula = False
 133         self.display.AppendText('2')
 134 
 135     def OnThree(self, event):
 136         if self.formula:
 137             self.display.Clear()
 138             self.formula = False
 139         self.display.AppendText('3')
 140 
 141     def OnFour(self, event):
 142         if self.formula:
 143             self.display.Clear()
 144             self.formula = False
 145         self.display.AppendText('4')
 146 
 147     def OnFive(self, event):
 148         if self.formula:
 149             self.display.Clear()
 150             self.formula = False
 151         self.display.AppendText('5')
 152 
 153     def OnSix(self, event):
 154         if self.formula:
 155             self.display.Clear()
 156             self.formula = False
 157         self.display.AppendText('6')
 158 
 159     def OnSeven(self, event):
 160         if self.formula:
 161             self.display.Clear()
 162             self.formula = False
 163         self.display.AppendText('7')
 164 
 165     def OnEight(self, event):
 166         if self.formula:
 167             self.display.Clear()
 168             self.formula = False
 169         self.display.AppendText('8')
 170 
 171     def OnNine(self, event):
 172         if self.formula:
 173             self.display.Clear()
 174             self.formula = False
 175         self.display.AppendText('9')
 176 
 177 class MyApp(wx.App):
 178     def OnInit(self):
 179         frame = MyFrame(None, -1, 'calculator.py')
 180         frame.Show(True)
 181         self.SetTopWindow(frame)
 182         return True
 183 
 184 app = MyApp(0)
 185 app.MainLoop()

The formula we input is processed by the eval built-in python function.

output = eval(formula)

If we make an error in our formula, an error message is displayed. Notice how we managed to put a space between the Bck and Close buttons. We simply put an empty wx.StaticText there. Such tricks are quite common.

calculator.png

Figure: calculator.py

wx.GridBagSizer

The most complicated sizer in wxPython. It enables explicit positioning of the items. Items can also optionally span more than one row and/or column. wx.GridBagSizer has a simple constructor.

wx.GridBagSizer(integer vgap, integer hgap)

The vertical and the horizontal gap defines the space in pixels used between children. You add items to grid with the Add() method.

Add(self, item, tuple pos, tuple span=wx.DefaultSpan, integer flag=0, integer border=0, userData=None)

Item is a widget that you insert into the grid. pos specifies the position in the virtual grid. The topleft cell has pos of (0, 0). span is an optional spanning of the widget. e.g. span of (3, 2) spans a widget across 3 rows and 2 columns. flag and border were discussed earlier by wx.BoxSizer.

The items in the grid can change their size or keep the default size, when the window is resized. If you want your items to grow and shrink, you can use these two methods.

AddGrowableRow(integer row)
AddGrowableCol(integer col)

   1 #!/usr/bin/python
   2 
   3 # wxgridbagsizer.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition)
  10 
  11         sizer = wx.GridBagSizer(9, 9)
  12         sizer.Add(wx.Button(self,-1, "Button"), (0, 0), wx.DefaultSpan,  wx.ALL, 5)
  13         sizer.Add(wx.Button(self,-1, "Button"), (1, 1), (1,7), wx.EXPAND)
  14         sizer.Add(wx.Button(self,-1, "Button"), (6, 6), (3,3), wx.EXPAND)
  15         sizer.Add(wx.Button(self,-1, "Button"), (3, 0), (1,1), wx.ALIGN_CENTER)
  16         sizer.Add(wx.Button(self,-1, "Button"), (4, 0), (1,1), wx.ALIGN_LEFT)
  17         sizer.Add(wx.Button(self,-1, "Button"), (5, 0), (1,1), wx.ALIGN_RIGHT)
  18         sizer.AddGrowableRow(6)
  19         sizer.AddGrowableCol(6)
  20 
  21         self.SetSizerAndFit(sizer)
  22         self.Centre()
  23 
  24 class MyApp(wx.App):
  25     def OnInit(self):
  26         frame = MyFrame(None, -1, "wxgridbagsizer.py")
  27         frame.Show(True)
  28         self.SetTopWindow(frame)
  29         return True
  30 
  31 app = MyApp(0)
  32 app.MainLoop()

If you want your item to span more than one cell, you must provide wx.EXPAND flag.

self.SetSizerAndFit(sizer)

This method is same as SetSizer() except that it also sends size hints to the window. All buttons are displayed on the window.

Basic Objects

wxPython is a collection of various objects. We can divide them into two groups.

Examples of visual objects are: widgets, fonts, colours or cursors. Non-visual objects: sizers, timers or events.

Cursors

A cursor is a simple graphical object. It is used to indicate position on the monitor or any other display device. It usually dynamically changes itself. Typically, when you hover a mouse pointer over a hypertext, the cursor changes to a hand.

In the next code example, we create a grid of nine wx.Panels. Each panel shows a different cursor.

   1 #!/usr/bin/python
   2 
   3 # cursors.py
   4 
   5 import wx
   6 
   7 class Cursors(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title)
  10 
  11         vbox = wx.BoxSizer(wx.VERTICAL)
  12         sizer = wx.GridSizer(3, 3, 2, 2)
  13 
  14         cursors = [ wx.CURSOR_ARROW, wx.CURSOR_HAND, wx.CURSOR_WATCH, wx.CURSOR_SPRAYCAN, wx.CURSOR_PENCIL,
  15                     wx.CURSOR_CROSS, wx.CURSOR_QUESTION_ARROW, wx.CURSOR_POINT_LEFT, wx.CURSOR_SIZING]
  16 
  17         for i in cursors:
  18             panel = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
  19             panel.SetCursor(wx.StockCursor(i))
  20             sizer.Add(panel, flag=wx.EXPAND)
  21 
  22         vbox.Add(sizer, 1, wx.EXPAND | wx.TOP, 5)
  23         self.SetSizer(vbox)
  24 
  25         self.Centre()
  26         self.Show()
  27 
  28 
  29 app = wx.App(0)
  30 Cursors(None, -1, 'Cursors.py')
  31 app.MainLoop()

Various predefined cursors: Listed below.

Fonts

We create different kinds of fonts with the wx.Font object. It has the following constructor:

wx.Font(integer pointSize, wx.FontFamily family, integer style, integer weight,
        bool underline = false, string faceName = '',
        wx.FontEncoding encoding = wx.FONTENCODING_DEFAULT)

The specified parameters can have the following options:

family:

style:

weight:

fonts.py script shows three different fonts.

   1 #!/usr/bin/python
   2 
   3 # fonts.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(325, 320))
  10 
  11         panel = wx.Panel(self, -1)
  12 
  13         text1 = "Now listen to me mama\nMama mama\nYou're taking away my last chance\nDon't take it away"
  14 
  15         text2 = '''You won't cry for my absence, I know -
  16 You forgot me long ago.
  17 Am I that unimportant...?
  18 Am I so insignificant...?
  19 Isn't something missing?
  20 Isn't someone missing me?'''
  21 
  22         text3 = '''But if I had one wish fulfilled tonight
  23 I'd ask for the sun to never rise
  24 If God passed a mic to me to speak
  25 I'd say stay in bed, world
  26 Sleep in peace'''
  27 
  28         font1 = wx.Font(10, wx.NORMAL, wx.ITALIC, wx.NORMAL)
  29         font2 = wx.Font(10, wx.ROMAN, wx.NORMAL, wx.NORMAL)
  30         font3 = wx.Font(10, wx.MODERN, wx.NORMAL, wx.BOLD)
  31         lyrics1 = wx.StaticText(panel, -1, text1,(30,15), style=wx.ALIGN_CENTRE)
  32         lyrics1.SetFont(font1)
  33         lyrics2 = wx.StaticText(panel, -1, text2,(30,100), style=wx.ALIGN_CENTRE)
  34         lyrics2.SetFont(font2)
  35         lyrics3 = wx.StaticText(panel, -1, text3,(5,220), style=wx.ALIGN_CENTRE)
  36         lyrics3.SetFont(font3)
  37         self.Center()
  38 
  39 class MyApp(wx.App):
  40     def OnInit(self):
  41         frame = MyFrame(None, -1, 'fonts.py')
  42         frame.Show(True)
  43         self.SetTopWindow(frame)
  44         return True
  45 
  46 app = MyApp(0)
  47 app.MainLoop()

fonts.png

Figure: fonts.py

Colours

A colour is an object representing a combination of Red, Green, and Blue (RGB) intensity values. Valid RGB values are in the range 0 to 255.

There are three ways for setting colours. We can create a wx.Colour object, use a predefined colour name or use hex value string.

SetBackgroundColour(wx.Colour(0,0,255))
SetBackgroundColour('BLUE')
SetBackgroundColour('#0000FF')

Predefined colour names in wxPython:

Standard colour database:

Listed below.

colours.py script shows eight different colours. wxBlack is simple. sea green is poetic and #0000FF technical. It is up to the developer, what to choose.

   1 #!/usr/bin/python
   2 
   3 # colours.py
   4 
   5 import wx
   6 
   7 
   8 class Colours(wx.Dialog):
   9     def __init__(self, parent, id, title):
  10 
  11         wx.Dialog.__init__(self, parent, id, title, size=(300, 300))
  12         vbox = wx.BoxSizer(wx.VERTICAL)
  13         self.pnl1 = wx.Panel(self, -1)
  14         self.pnl2 = wx.Panel(self, -1)
  15         self.pnl3 = wx.Panel(self, -1)
  16         self.pnl4 = wx.Panel(self, -1)
  17         self.pnl5 = wx.Panel(self, -1)
  18         self.pnl6 = wx.Panel(self, -1)
  19         self.pnl7 = wx.Panel(self, -1)
  20         self.pnl8 = wx.Panel(self, -1)
  21 
  22         gs = wx.GridSizer(4,2,3,3)
  23         gs.AddMany([ (self.pnl1, 0 ,wx.EXPAND),
  24             (self.pnl2, 0, wx.EXPAND),
  25             (self.pnl3, 0, wx.EXPAND),
  26             (self.pnl4, 0, wx.EXPAND),
  27             (self.pnl5, 0, wx.EXPAND),
  28             (self.pnl6, 0, wx.EXPAND),
  29             (self.pnl7, 0, wx.EXPAND),
  30             (self.pnl8, 0, wx.EXPAND) ])
  31 
  32         vbox.Add(gs, 1, wx.EXPAND | wx.TOP, 5)
  33         self.SetSizer(vbox)
  34         self.SetColors()
  35         self.Centre()
  36         self.ShowModal()
  37         self.Destroy()
  38 
  39     def SetColors(self):
  40         self.pnl1.SetBackgroundColour(wx.BLACK)
  41         self.pnl2.SetBackgroundColour(wx.Colour(139,105,20))
  42         self.pnl3.SetBackgroundColour(wx.RED)
  43         self.pnl4.SetBackgroundColour('#0000FF')
  44         self.pnl5.SetBackgroundColour('sea green')
  45         self.pnl6.SetBackgroundColour('midnight blue')
  46         self.pnl7.SetBackgroundColour(wx.LIGHT_GREY)
  47         self.pnl8.SetBackgroundColour('plum')
  48 
  49 app = wx.App(0)
  50 Colours(None, -1, 'colours.py')
  51 app.MainLoop()

colours.png

Figure: colours.py

The full database has currently about 630 different colour names. The list can be found in the colourdb.py file. It is also shown in the wxPython demo. In randomcolours.py script we have a single window. We change the background colour of the window to the randomly chosen colour.

   1 #!/usr/bin/python
   2 
   3 # randomcolours.py
   4 
   5 import wx
   6 
   7 from random import randrange
   8 from wx.lib.colourdb import *
   9 
  10 class MyFrame(wx.Frame):
  11     def __init__(self, parent, id, title):
  12         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(400, 350))
  13 
  14         self.panel = wx.Panel(self, -1)
  15         self.colors = getColourList()
  16         self.timer = wx.Timer(self)
  17         self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
  18         self.timer.Start(1500)
  19         self.col_num = len(self.colors)
  20         self.Centre()
  21 
  22     def OnTimer(self, event):
  23         self.panel.SetBackgroundColour(wx.RED)
  24         position = randrange(0, self.col_num-1, 1)
  25         self.panel.SetBackgroundColour(self.colors[position])
  26         self.panel.Refresh()
  27 
  28 class MyApp(wx.App):
  29     def OnInit(self):
  30         updateColourDB()
  31         frame = MyFrame(None, -1, 'randomcolours.py')
  32         frame.Show(True)
  33         self.SetTopWindow(frame)
  34         return True
  35 
  36 app = MyApp(0)
  37 app.MainLoop()

Bitmaps

There are two kinds of graphics. Vector and bitmap. With vector graphics, images are created with mathematical formulas that define all the shapes of the image. Geometric objects like curves and polygons are used. A bitmap or a bit map is a collection of bits that form an image. It is a grid of individual dots stored in memory or in a file. Each dot has its own colour. When the image is displayed, the computer transfers a bit map into pixels on monitors or ink dots on printers. The quality of a bitmap is determined by the resolution and the color depth of the image. The resolution is the total number of pixels in the image. The Color depth is the amount of information in each pixel.

There are various kinds of bitmaps:

   1 #!/usr/bin/python
   2 
   3 # bitmap.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, size = (270, 270))
  10 
  11         self.bitmap = wx.Bitmap('memento.jpg')
  12         wx.EVT_PAINT(self, self.OnPaint)
  13 
  14         self.Centre()
  15 
  16     def OnPaint(self, event):
  17         dc = wx.PaintDC(self)
  18         dc.DrawBitmap(self.bitmap, 60, 20)
  19 
  20 
  21 class MyApp(wx.App):
  22     def OnInit(self):
  23         frame = MyFrame(None, -1, 'Memento')
  24         frame.Show(True)
  25         self.SetTopWindow(frame)
  26         return True
  27 
  28 app = MyApp(0)
  29 app.MainLoop()

memento.png

Figure: bitmap.py

Events

Events are integral part of every GUI application. All GUI applications are event-driven. An application reacts to different event types which are generated during its life. Events are generated mainly by the user of an application. But they can be generated by other means as well. e.g. internet connection, window manager, timer. So when we call MainLoop() method, our application waits for events to be generated. The MainLoop() method ends when we exit the application.

Working with events is straightforward in wxPython. There are three steps:

In wxPython we say to bind a method to an event. Sometimes a word hook is used.

You bind an event by calling the Bind() method. The method has the following parameters:

Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY)

Note that method Bind() is defined in class EvtHandler. It is the class, from which wx.Window inherits. wx.Window is a base class for most widgets in wxPython. There is also a reverse process. If we want to unbind a method from an event, we call the Unbind() method. It has the same paremeters as the above one.

Possible events

wx.Event

the event base class

wx.ActivateEvent

a window or application activation event

wx.CloseEvent

a close window or end session event

wx.EraseEvent

an erase background event

wx.FocusEvent

a window focus event

wx.KeyEvent

a keypress event

wx.IdleEvent

an idle event

wx.InitDialogEvent

a dialog initialisation event

wx.JoystickEvent

a joystick event

wx.MenuEvent

a menu event

wx.MouseEvent

a mouse event

wx.MoveEvent

a move event

wx.PaintEvent

a paint event

wx.QueryLayoutInfoEvent

used to query layout information

wx.SetCursorEvent

used for special cursor processing based on current mouse position

wx.SizeEvent

a size event

wx.ScrollWinEvent

a scroll event sent by a built-in Scrollbar

wx.ScrollEvent

a scroll event sent by a stand-alone scrollbar

wx.SysColourChangedEvent

a system colour change event

Examples

The following code is an example of a wx.ScrollWinEvent. This event is generated, when we click on a built in Scrollbar. Built-in Scrollbar is activated with the SetScrollbar() method call. For stand-alone Scrollbars, there is another event type, namely wx.ScrollEvent.

   1 #!/usr/bin/python
   2 
   3 # myscrollwinevent.py
   4 
   5 import wx
   6 
   7 class MyScrollWinEvent(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title)
  10         panel = wx.Panel(self, -1)
  11         self.st = wx.StaticText(panel, -1, '0', (30,0))
  12         panel.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
  13         panel.SetScrollbar(wx.VERTICAL, 0, 6, 50);
  14         self.Centre()
  15 
  16     def OnScroll(self, evt):
  17         y = evt.GetPosition()
  18         self.st.SetLabel(str(y))
  19 
  20 class MyApp(wx.App):
  21     def OnInit(self):
  22         msw = MyScrollWinEvent(None, -1, 'myscrollwinevent.py')
  23         msw.Show(True)
  24         return True
  25 
  26 app = MyApp(0)
  27 app.MainLoop()

The wx.SizeEvent is generated, when our window is resized. In our example, we show the size of the window in the titlebar.

   1 #!/usr/bin/python
   2 
   3 # sizeevent.py
   4 
   5 import wx
   6 
   7 class SizeEvent(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title)
  10 
  11         self.Bind(wx.EVT_SIZE, self.OnSize)
  12         self.Centre()
  13 
  14     def OnSize(self, event):
  15         self.SetTitle(str(event.GetSize()))
  16 
  17 class MyApp(wx.App):
  18     def OnInit(self):
  19         se = SizeEvent(None, -1, 'sizeevent.py')
  20         se.Show(True)
  21         return True
  22 
  23 app = MyApp(0)
  24 app.MainLoop()

   1 #!/usr/bin/python
   2 
   3 # moveevent.py
   4 
   5 import wx
   6 
   7 class MoveEvent(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title)
  10 
  11         wx.StaticText(self, -1, 'x:', (10,0))
  12         wx.StaticText(self, -1, 'y:', (10,20))
  13         self.st1 = wx.StaticText(self, -1, '', (30, 0))
  14         self.st2 = wx.StaticText(self, -1, '', (30, 20))
  15         self.Bind(wx.EVT_MOVE, self.OnMove)
  16         self.Centre()
  17 
  18     def OnMove(self, event):
  19         x, y = event.GetPosition()
  20         self.st1.SetLabel(str(x))
  21         self.st2.SetLabel(str(y))
  22 
  23 class MyApp(wx.App):
  24     def OnInit(self):
  25         me = MoveEvent(None, -1, 'moveevent.py')
  26         me.Show(True)
  27         return True
  28 
  29 app = MyApp(0)
  30 app.MainLoop()

A paint event is generated when a window is redrawn. This happens when we resize window, maximize it. A paint event can be generated programatically as well. For example, when we call SetLabel() method to change a wxStaticText widget. Note that when we minimize a window, no paint event is generated.

   1 #!/usr/bin/python
   2 
   3 # paintevent.py
   4 
   5 import wx
   6 
   7 class PaintEvent(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title)
  10 
  11         self.Bind(wx.EVT_PAINT, self.OnPaint)
  12         self.Centre()
  13 
  14     def OnPaint(self, event):
  15         wx.Bell()
  16 
  17 class MyApp(wx.App):
  18     def OnInit(self):
  19         pe = PaintEvent(None, -1, 'paintevent.py')
  20         pe.Show(True)
  21         return True
  22 
  23 app = MyApp(0)
  24 app.MainLoop()

When we press a key on our keyboard, wx.KeyEvent is generated. There are three different key handlers:

A common request is to close application, when Esc key is pressed.

   1 #!/usr/bin/python
   2 
   3 # keyevent.py
   4 
   5 import wx
   6 
   7 
   8 class KeyEvent(wx.Frame):
   9     def __init__(self, parent, id, title):
  10         wx.Frame.__init__(self, parent, id, title)
  11 
  12         panel = wx.Panel(self, -1)
  13         panel.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
  14         panel.SetFocus()
  15 
  16         self.Centre()
  17         self.Show(True)
  18 
  19 
  20     def OnKeyDown(self, event):
  21         keycode = event.GetKeyCode()
  22         if keycode == wx.WXK_ESCAPE:
  23             ret  = wx.MessageBox('Are you sure to quit?', 'Question', wx.YES_NO | wx.CENTRE |
  24  wx.NO_DEFAULT, self)
  25             if ret == wx.YES:
  26                 self.Close()
  27         event.Skip()
  28 
  29 
  30 app = wx.App()
  31 KeyEvent(None, -1, 'keyevent.py')
  32 app.MainLoop()

We find out, which key was pressed by calling GetKeyCode() method. In our case, keycode is wx.WXK_ESCAPE.

keycode = event.GetKeyCode()

Other keycodes are listed below.

Dialogs

In wxPython you can use predefined dialogs or create your own dialogs. You can also create dialog based applications.

The example shows a skeleton of a dialog based application in wxPython.

   1 #!/usr/bin/python
   2 # simpledialog.py
   3 
   4 import wx
   5 
   6 class MyDialog(wx.Dialog):
   7     def __init__(self, parent, id, title):
   8         wx.Dialog.__init__(self, parent, id, title)
   9 
  10 class MyApp(wx.App):
  11     def OnInit(self):
  12         dia = MyDialog(None, -1, "simpledialog.py")
  13         dia.ShowModal()
  14         dia.Destroy()
  15         return True
  16 
  17 app = MyApp(0)
  18 app.MainLoop()

Notice that you cannot resize the dialog window. The Destroy() method is necessary. It deletes the dialog from the memory. Otherwise, the script would not exit properly.

There are two types of dialogs. Modal and modeless. Modal dialog does not allow a user to work with the rest of the application until it is destroyed. Modal dialogs are created with the ShowModal() method. Dialogs are modeless when called with Show().

Custom dialogs

There are two methods that simplify the creation of dialogs. Both return a specific sizer object.

CreateTextSizer(self, string message)
CreateButtonSizer(self, long flags)

CreateTextSizer() method creates a text sizer. In the following example, we add some buttons into the sizer to demonstrate it.

   1 #!/usr/bin/python
   2 
   3 # customdialog1.py
   4 
   5 import wx
   6 
   7 class MyDialog(wx.Dialog):
   8     def __init__(self, parent, id, title):
   9         wx.Dialog.__init__(self, parent, id, title, size=(350,300))
  10 
  11         sizer =  self.CreateTextSizer('My Buttons')
  12         sizer.Add(wx.Button(self, -1, 'Button'), 0, wx.ALL, 5)
  13         sizer.Add(wx.Button(self, -1, 'Button'), 0, wx.ALL, 5)
  14         sizer.Add(wx.Button(self, -1, 'Button'), 0, wx.ALL, 5)
  15         sizer.Add(wx.Button(self, -1, 'Button'), 0, wx.ALL|wx.ALIGN_CENTER, 5)
  16         sizer.Add(wx.Button(self, -1, 'Button'), 0, wx.ALL|wx.EXPAND, 5)
  17         self.SetSizer(sizer)
  18 
  19 class MyFrame(wx.Frame):
  20     def __init__(self, parent, id, title):
  21         wx.Frame.__init__(self, parent, id, title, size=(550,500))
  22 
  23         panel = wx.Panel(self, -1)
  24         wx.Button(panel, 1, 'Show Custom Dialog', (100,100))
  25         self.Bind (wx.EVT_BUTTON, self.OnShowCustomDialog, id=1)
  26 
  27     def OnShowCustomDialog(self, event):
  28         dia = MyDialog(self, -1, 'buttons')
  29         dia.ShowModal()
  30         dia.Destroy()
  31 
  32 class MyApp(wx.App):
  33     def OnInit(self):
  34         frame = MyFrame(None, -1, 'customdialog1.py')
  35         frame.Show(True)
  36         frame.Centre()
  37         return True
  38 
  39 app = MyApp(0)
  40 app.MainLoop()

CreateButtonSizer() method creates a row of buttons. You can specify button types with different flags. CreateButtonSizer() method can take the following flags:

   1 #!/usr/bin/python
   2 
   3 # customdialog2.py
   4 
   5 import wx
   6 
   7 class MyDialog(wx.Dialog):
   8     def __init__(self, parent, id, title):
   9         wx.Dialog.__init__(self, parent, id, title)
  10 
  11         vbox = wx.BoxSizer(wx.VERTICAL)
  12         stline = wx.StaticText(self, 11, 'Discipline ist Macht.')
  13         vbox.Add(stline, 1, wx.ALIGN_CENTER|wx.TOP, 45)
  14         sizer =  self.CreateButtonSizer(wx.NO|wx.YES|wx.HELP)
  15         vbox.Add(sizer, 0, wx.ALIGN_CENTER)
  16         self.SetSizer(vbox)
  17         self.Bind(wx.EVT_BUTTON, self.OnYes, id=wx.ID_YES)
  18 
  19     def OnYes(self, event):
  20         self.Close()
  21 
  22 class MyFrame(wx.Frame):
  23     def __init__(self, parent, id, title):
  24         wx.Frame.__init__(self, parent, id, title)
  25         panel = wx.Panel(self, -1)
  26         wx.Button(panel, 1, 'Show custom Dialog', (50,50))
  27         self.Bind(wx.EVT_BUTTON, self.OnShowCustomDialog, id=1)
  28 
  29     def OnShowCustomDialog(self, event):
  30         dia = MyDialog(self, -1, '')
  31         val = dia.ShowModal()
  32         dia.Destroy()
  33 
  34 class MyApp(wx.App):
  35     def OnInit(self):
  36         frame = MyFrame(None, -1, 'customdialog2.py')
  37         frame.Show(True)
  38         frame.Centre()
  39         return True
  40 
  41 app = MyApp(0)
  42 app.MainLoop()

Note that wxPython does not take the order of flags into account.

sizer =  self.CreateButtonSizer(wxNO|wxYES|wxHELP)

The buttons will be created according to the standards.

Common Predefined Dialogs

wxPython provides several common dialogs. They save programmers a lot of work. They also help promote standards in applications. We will show these ones:

   1 #!/usr/bin/python
   2 
   3 # commondialogs.py
   4 
   5 import wx
   6 import os, sys
   7 
   8 class MyFrame(wx.Frame):
   9     def __init__(self, parent, id, title):
  10       wx.Frame.__init__(self, parent, id, title)
  11 
  12       self.CreateStatusBar()
  13       menuBar = wx.MenuBar()
  14       menu = wx.Menu()
  15       menu.Append(99,  "&Message Dialog", "Shows a Message Dialog")
  16       menu.Append(100, "&Color Dialog", "Shows a Color Dialog")
  17       menu.Append(101, "&File Dialog", "Shows a File Dialog")
  18       menu.Append(102, "&Page Setup Dialog", "Shows a Page Setup Dialog")
  19       menu.Append(103, "&Font Dialog", "Shows a Font Dialog")
  20       menu.Append(104, "&Directory Dialog", "Shows a Directory Dialog")
  21       menu.Append(105, "&SingleChoice Dialog", "Shows a SingleChoice Dialog")
  22       menu.Append(106, "&TextEntry Dialog", "Shows a TextEntry Dialog")
  23       menuBar.Append(menu, "&Dialogs")
  24       self.SetMenuBar(menuBar)
  25 
  26       self.Bind(wx.EVT_MENU, self.message, id=99)
  27       self.Bind(wx.EVT_MENU, self.choosecolor, id=100)
  28       self.Bind(wx.EVT_MENU, self.openfile, id=101)
  29       self.Bind(wx.EVT_MENU, self.pagesetup, id=102)
  30       self.Bind(wx.EVT_MENU, self.choosefont, id=103)
  31       self.Bind(wx.EVT_MENU, self.opendir, id=104)
  32       self.Bind(wx.EVT_MENU, self.singlechoice, id=105)
  33       self.Bind(wx.EVT_MENU, self.textentry, id=106)
  34 
  35     def message(self, event):
  36         dlg = wx.MessageDialog(self, 'To save one life is as if you have saved the world.', 'Talmud', wx.OK|wx.ICON_INFORMATION)
  37         dlg.ShowModal()
  38         dlg.Destroy()
  39 
  40     def choosecolor(self, event):
  41         dlg = wx.ColourDialog(self)
  42         dlg.GetColourData().SetChooseFull(True)
  43         if dlg.ShowModal() == wx.ID_OK:
  44             data = dlg.GetColourData()
  45             self.SetStatusText('You selected: %s\n' % str(data.GetColour().Get()))
  46         dlg.Destroy()
  47 
  48     def openfile(self, event):
  49        dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.*", wx.OPEN)
  50        if dlg.ShowModal() == wx.ID_OK:
  51                 path = dlg.GetPath()
  52                 mypath = os.path.basename(path)
  53                 self.SetStatusText("You selected: %s" % mypath)
  54        dlg.Destroy()
  55 
  56     def pagesetup(self, event):
  57         dlg = wx.PageSetupDialog(self)
  58         if dlg.ShowModal() == wx.ID_OK:
  59             data = dlg.GetPageSetupData()
  60             tl = data.GetMarginTopLeft()
  61             br = data.GetMarginBottomRight()
  62             self.SetStatusText('Margins are: %s %s' % (str(tl), str(br)))
  63         dlg.Destroy()
  64 
  65     def choosefont(self, event):
  66         default_font = wx.Font(10, wx.SWISS , wx.NORMAL, wx.NORMAL, False, "Verdana")
  67         data = wx.FontData()
  68         if sys.platform == 'win32':
  69             data.EnableEffects(True)
  70         data.SetAllowSymbols(False)
  71         data.SetInitialFont(default_font)
  72         data.SetRange(10, 30)
  73         dlg = wx.FontDialog(self, data)
  74         if dlg.ShowModal() == wx.ID_OK:
  75             data = dlg.GetFontData()
  76             font = data.GetChosenFont()
  77             color = data.GetColour()
  78             text = 'Face: %s, Size: %d, Color: %s' % (font.GetFaceName(), font.GetPointSize(),  color.Get())
  79             self.SetStatusText(text)
  80         dlg.Destroy()
  81 
  82     def opendir(self, event):
  83         dlg = wx.DirDialog(self, "Choose a directory:", style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
  84         if dlg.ShowModal() == wx.ID_OK:
  85             self.SetStatusText('You selected: %s\n' % dlg.GetPath())
  86         dlg.Destroy()
  87 
  88     def singlechoice(self, event):
  89         sins = ['Greed', 'Lust', 'Gluttony', 'Pride', 'Sloth', 'Envy', 'Wrath']
  90         dlg = wx.SingleChoiceDialog(self, 'Seven deadly sins', 'Which one?', sins, wx.CHOICEDLG_STYLE)
  91         if dlg.ShowModal() == wx.ID_OK:
  92             self.SetStatusText('You chose: %s\n' % dlg.GetStringSelection())
  93         dlg.Destroy()
  94 
  95     def textentry(self, event):
  96         dlg = wx.TextEntryDialog(self, 'Enter some text','Text Entry')
  97         dlg.SetValue("Default")
  98         if dlg.ShowModal() == wx.ID_OK:
  99             self.SetStatusText('You entered: %s\n' % dlg.GetValue())
 100         dlg.Destroy()
 101 
 102 class MyApp(wx.App):
 103     def OnInit(self):
 104         myframe = MyFrame(None, -1, "commondialogs.py")
 105         myframe.CenterOnScreen()
 106         myframe.Show(True)
 107         return True
 108 
 109 app = MyApp(0)
 110 app.MainLoop()

The script shows eight different common dialogs. All the output is displayed on the statusbar.

messagedialog.png textentrydialog.png

fontdialog.png colordialog.png

directorydialog.png filedialog.png

pagesetupdialog.png singlechoicedialog.png

Core Widgets

In this section, we will introduce basic widgets in wxPython. Each widget will have a small code example.

wx.Button

wx.Button is a simple widget. It contains a text string. It is used to trigger an action.

wx.Button styles

wx.Button methods

SetDefault()

set the button to be the default item on a window

wx.Size GetDefaultSize()

get the default button size on a platform

   1 #!/usr/bin/python
   2 
   3 # buttons.py
   4 
   5 import wx
   6 import random
   7 
   8 APP_SIZE_X = 300
   9 APP_SIZE_Y = 200
  10 
  11 class MyButtons(wx.Dialog):
  12     def __init__(self, parent, id, title):
  13         wx.Dialog.__init__(self, parent, id, title, size=(APP_SIZE_X, APP_SIZE_Y))
  14 
  15         wx.Button(self, 1, 'Close', (50, 130))
  16         wx.Button(self, 2, 'Random Move', (150, 130), (110, -1))
  17 
  18         self.Bind(wx.EVT_BUTTON, self.OnClose, id=1)
  19         self.Bind(wx.EVT_BUTTON, self.OnRandomMove, id=2)
  20 
  21         self.Centre()
  22         self.ShowModal()
  23         self.Destroy()
  24 
  25     def OnClose(self, event):
  26         self.Close(True)
  27 
  28     def OnRandomMove(self, event):
  29         screensize = wx.GetDisplaySize()
  30         randx = random.randrange(0, screensize.x - APP_SIZE_X)
  31         randy = random.randrange(0, screensize.y - APP_SIZE_Y)
  32         self.Move((randx, randy))
  33 
  34 app = wx.App(0)
  35 MyButtons(None, -1, 'buttons.py')
  36 app.MainLoop()

buttons.png

Figure: buttons.py

wx.ToggleButton

wx.ToggleButton is a button that has two states. Pressed and not pressed. You toggle between these two states by clicking on it. There are situations where this functionality fits well.

wx.ToggleButton methods

SetValue(bool value)

set a state of a toggle button

bool GetValue()

get a state of the toggle button

SetLabel(string label)

set a label for the button

   1 #!/usr/bin/python
   2 
   3 # togglebuttons.py
   4 
   5 import wx
   6 
   7 class ToggleButtons(wx.Dialog):
   8     def __init__(self, parent, id, title):
   9         wx.Dialog.__init__(self, parent, id, title, size=(300, 200))
  10 
  11         self.colour = wx.Colour(0, 0, 0)
  12 
  13         wx.ToggleButton(self, 1, 'red', (20, 25))
  14         wx.ToggleButton(self, 2, 'green', (20, 60))
  15         wx.ToggleButton(self, 3, 'blue', (20, 100))
  16 
  17         self.panel  = wx.Panel(self, -1, (150, 20), (110, 110), style=wx.SUNKEN_BORDER)
  18         self.panel.SetBackgroundColour(self.colour)
  19 
  20         self.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleRed, id=1)
  21         self.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleGreen, id=2)
  22         self.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleBlue, id=3)
  23 
  24         self.Centre()
  25         self.ShowModal()
  26         self.Destroy()
  27 
  28     def ToggleRed(self, event):
  29         green = self.colour.Green()
  30         blue = self.colour.Blue()
  31         if  self.colour.Red():
  32             self.colour.Set(0, green, blue)
  33         else:
  34             self.colour.Set(255, green, blue)
  35         self.panel.SetBackgroundColour(self.colour)
  36 
  37     def ToggleGreen(self, event):
  38         red = self.colour.Red()
  39         blue = self.colour.Blue()
  40         if  self.colour.Green():
  41             self.colour.Set(red, 0, blue)
  42         else:
  43             self.colour.Set(red, 255, blue)
  44         self.panel.SetBackgroundColour(self.colour)
  45 
  46     def ToggleBlue(self, event):
  47         red = self.colour.Red()
  48         green = self.colour.Green()
  49         if  self.colour.Blue():
  50             self.colour.Set(red, green, 0)
  51         else:
  52             self.colour.Set(red, green, 255)
  53         self.panel.SetBackgroundColour(self.colour)
  54 
  55 
  56 app = wx.App(0)
  57 ToggleButtons(None, -1, 'togglebuttons.py')
  58 app.MainLoop()

togglebuttons.png

Figure: togglebuttons.py

wx.BitmapButton

A bitmap button is a button, that displays a bitmap. A bitmap button can have three other states. Selected, focused and displayed. We can set a specific bitmap for those states.

wx.BitmapButton methods

wx.Bitmap GetBitmapLabel()

return the label bitmap

SetBitmapLabel(wx.Bitmap bitmap)

set the bitmap label for the button

wx.Bitmap GetBitmapFocus()

return the bitmap for the focused state

wx.Bitmap GetBitmapDisabled()

return the bitmap for the disabled state

wx.Bitmap GetBitmapSelected()

return the bitmap for the selected state

SetBitmapFocus(wx.Bitmap bitmap)

set the bitmap for the focused state

SetBitmapSelected(wx.Bitmap bitmap)

set the bitmap for the selected state

SetBitmapDisabled(wx.Bitmap bitmap)

set the bitmap for the disabled state

SetMargins(int x, int y)

not implemented

int GetMarginX()

not implemented

int GetMarginY()

not implemented

A video player is a nice example, where bitmap buttons are used. We can see play, pause, next, previous and volume bitmap buttons there. So we create a skeleton of a video player in our next example.

   1 #!/usr/bin/python
   2 
   3 # player.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, size=(350, 300))
  10         panel = wx.Panel(self, -1)
  11 
  12         pnl1 = wx.Panel(self, -1)
  13         pnl1.SetBackgroundColour(wx.BLACK)
  14         pnl2 = wx.Panel(self, -1 )
  15 
  16         menubar = wx.MenuBar()
  17         file = wx.Menu()
  18         play = wx.Menu()
  19         view = wx.Menu()
  20         tools = wx.Menu()
  21         favorites = wx.Menu()
  22         help = wx.Menu()
  23 
  24         file.Append(101, '&quit', 'Quit application')
  25 
  26         menubar.Append(file, '&File')
  27         menubar.Append(play, '&Play')
  28         menubar.Append(view, '&View')
  29         menubar.Append(tools, '&Tools')
  30         menubar.Append(favorites, 'F&avorites')
  31         menubar.Append(help, '&Help')
  32 
  33         slider1 = wx.Slider(pnl2, -1, 0, 0, 1000)
  34         pause = wx.BitmapButton(pnl2, -1, wx.Bitmap('icons/stock_media-pause.png'))
  35         play  = wx.BitmapButton(pnl2, -1, wx.Bitmap('icons/stock_media-play.png'))
  36         next  = wx.BitmapButton(pnl2, -1, wx.Bitmap('icons/stock_media-next.png'))
  37         prev  = wx.BitmapButton(pnl2, -1, wx.Bitmap('icons/stock_media-prev.png'))
  38         volume = wx.BitmapButton(pnl2, -1, wx.Bitmap('icons/volume.png'))
  39         slider2 = wx.Slider(pnl2, -1, 0, 0, 100, size=(120, -1))
  40 
  41         vbox = wx.BoxSizer(wx.VERTICAL)
  42         hbox1 = wx.BoxSizer(wx.HORIZONTAL)
  43         hbox2 = wx.BoxSizer(wx.HORIZONTAL)
  44 
  45         hbox1.Add(slider1, 1)
  46         hbox2.Add(pause)
  47         hbox2.Add(play, flag=wx.RIGHT, border=5)
  48         hbox2.Add(next, flag=wx.LEFT, border=5)
  49         hbox2.Add(prev)
  50         hbox2.Add((150, -1), 1, flag=wx.EXPAND | wx.ALIGN_RIGHT)
  51         hbox2.Add(volume, flag=wx.ALIGN_RIGHT)
  52         hbox2.Add(slider2, flag=wx.ALIGN_RIGHT | wx.TOP | wx.LEFT, border=5)
  53 
  54         vbox.Add(hbox1, 1, wx.EXPAND | wx.BOTTOM, 10)
  55         vbox.Add(hbox2, 1, wx.EXPAND)
  56         pnl2.SetSizer(vbox)
  57 
  58         sizer = wx.BoxSizer(wx.VERTICAL)
  59         sizer.Add(pnl1, 1, flag=wx.EXPAND)
  60         sizer.Add(pnl2, flag=wx.EXPAND | wx.BOTTOM | wx.TOP, border=10)
  61 
  62         self.SetMinSize((350, 300))
  63         self.SetMenuBar(menubar)
  64         self.CreateStatusBar()
  65         self.SetSizer(sizer)
  66         self.Centre()
  67 
  68 class MyApp(wx.App):
  69     def OnInit(self):
  70         frame = MyFrame(None, -1, 'Player')
  71         frame.Show(True)
  72         self.SetTopWindow(frame)
  73         return True
  74 
  75 app = MyApp(0)
  76 app.MainLoop()

player.png

Figure: player.py

wx.StaticLine

This widget displays a simple line on the window. It can be horizontal or vertical.

wx.StaticLine styles

wx.StaticLine methods

bool IsVertical()

check if the line is vertical

integer GetDefaultSize()

return the size of the line

   1 #!/usr/bin/python
   2 
   3 # centraleurope.py
   4 
   5 import wx
   6 
   7 class MyDialog(wx.Dialog):
   8     def __init__ (self, parent, ID, title):
   9         wx.Dialog.__init__(self, parent, ID, title, size=(360, 370))
  10 
  11         font = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD)
  12         heading = wx.StaticText(self, -1, 'The Central Europe', (130, 15))
  13         heading.SetFont(font)
  14 
  15         wx.StaticLine(self, -1, (25, 50), (300,1))
  16 
  17         wx.StaticText(self, -1, 'Slovakia', (25, 80), style=wx.ALIGN_RIGHT)
  18         wx.StaticText(self, -1, 'Hungary', (25, 100), style=wx.ALIGN_RIGHT)
  19         wx.StaticText(self, -1, 'Poland', (25, 120), style=wx.ALIGN_RIGHT)
  20         wx.StaticText(self, -1, 'Czech Republic', (25, 140))
  21         wx.StaticText(self, -1, 'Germany', (25, 160))
  22         wx.StaticText(self, -1, 'Slovenia', (25, 180))
  23         wx.StaticText(self, -1, 'Austria', (25, 200))
  24         wx.StaticText(self, -1, 'Switzerland', (25, 220))
  25 
  26         wx.StaticText(self, -1, '5 379 000', (250, 80))
  27         wx.StaticText(self, -1, '10 084 000', (250, 100))
  28         wx.StaticText(self, -1, '38 635 000', (250, 120))
  29         wx.StaticText(self, -1, '10 240 000', (250, 140))
  30         wx.StaticText(self, -1, '82 443 000', (250, 160))
  31         wx.StaticText(self, -1, '2 001 000', (250, 180))
  32         wx.StaticText(self, -1, '8 032 000', (250, 200))
  33         wx.StaticText(self, -1, '7 288 000', (250, 220))
  34 
  35         wx.StaticLine(self, -1, (25, 260), (300,1))
  36 
  37         sum = wx.StaticText(self, -1, '164 102 000', (240, 280))
  38         sum_font = sum.GetFont()
  39         sum_font.SetWeight(wx.BOLD)
  40         sum.SetFont(sum_font)
  41 
  42         wx.Button(self, 1, 'Ok', (140, 310), (60, 30))
  43 
  44         self.Bind(wx.EVT_BUTTON, self.OnOk, id=1)
  45         self.Centre()
  46 
  47     def OnOk(self, event):
  48         self.Close()
  49 
  50 class MyApp(wx.App):
  51     def OnInit(self):
  52         dia = MyDialog(None, -1, 'centraleurope.py')
  53         dia.ShowModal()
  54         dia.Destroy()
  55         return True
  56 
  57 app = MyApp()
  58 app.MainLoop()

centraleurope.py script displays central european countries and their population. Thewx.StatLine makes it look more visually attractive.

centraleurope.png

Figure: centraleurope.py

wx.StaticText

Awx.StaticText widget displays one or more lines of read-only text.

wx.StaticText Styles

wx.StaticText methods

Wrap(int width)

if possible set each line of text to width pixels. if width is negative, no wrapping is done.

   1 #!/usr/bin/python
   2 
   3 # statictext.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(320, 350))
  10 
  11         lyrics1 = '''I'm giving up the ghost of love
  12 in the shadows cast on devotion
  13 She is the one that I adore
  14 creed of my silent suffocation
  15 Break this bittersweet spell on me
  16 lost in the arms of destiny'''
  17 
  18         lyrics2 = '''There is something in the way
  19 You're always somewhere else
  20 Feelings have deserted me
  21 To a point of no return
  22 I don't believe in God
  23 But I pray for you'''
  24 
  25         panel = wx.Panel(self, -1)
  26         wx.StaticText(panel, -1, lyrics1, (45, 25), style=wx.ALIGN_CENTRE)
  27         wx.StaticText(panel, -1, lyrics2, (45, 190), style=wx.ALIGN_CENTRE)
  28         self.Centre()
  29 
  30 class MyApp(wx.App):
  31     def OnInit(self):
  32         frame = MyFrame(None, -1, 'statictext.py')
  33         frame.Show(True)
  34         self.SetTopWindow(frame)
  35         return True
  36 
  37 app = MyApp(0)
  38 app.MainLoop()

statictext.png

Figure statictext.py

wx.StaticBox

This is a kind of a decorator widget. It is used to logically group various widgets. Note that this widget must be created before the widgets that it contains, and that those widgets should be siblings, not children, of the static box.

   1 #!/usr/bin/python
   2 
   3 # staticbox.py
   4 
   5 import wx
   6 
   7 class MyDialog(wx.Dialog):
   8     def __init__(self, parent, id, title):
   9         wx.Dialog.__init__(self, parent, id, title, size=(250, 230))
  10 
  11         wx.StaticBox(self, -1, 'Personal Info', (5, 5), size=(240, 170))
  12         wx.CheckBox(self, -1 ,'Male', (15, 30))
  13         wx.CheckBox(self, -1 ,'Married', (15, 55))
  14         wx.StaticText(self, -1, 'Age', (15, 95))
  15         wx.SpinCtrl(self, -1, '1', (55, 90), (60, -1), min=1, max=120)
  16         wx.Button(self, 1, 'Ok', (90, 185), (60, -1))
  17 
  18         self.Bind(wx.EVT_BUTTON, self.OnClose, id=1)
  19 
  20         self.Centre()
  21         self.ShowModal()
  22         self.Destroy()
  23 
  24     def OnClose(self, event):
  25         self.Close()
  26 
  27 app = wx.App(0)
  28 MyDialog(None, -1, 'staticbox.py')
  29 app.MainLoop()

staticbox.png

Figure staticbox.py

wx.ComboBox

wx.ComboBox is a combination of a single line text field, a button with a down arrow image and a listbox. When you press the button, a listbox appears. User can select only one option from the supplied string list.

wx.ComboBox has the following constructor:

wx.ComboBox(int id, string value='', wx.Point pos=wx.DefaultPosition, wx.Size size=wx.DefaultSize,
            wx.List choices=wx.EmptyList, int style=0, wx.Validator validator=wx.DefaultValidator,
            string name=wx.ComboBoxNameStr)

wx.ComboBox styles

wx.ComboBox methods

string GetValue()

return the current value

SetValue(string value)

set the value into the textfield of the combobox

Copy()

copy the selected value to the clipboard

Cut()

cut the selected value to the clipboard

Paste()

paste text from the clipboard to the combobox's text field

SetInsertionPoint(int pos)

set the insertion point in the combobox's text field

int GetInsertionPoint()

get the insertion point for the combobox's text field

int GetLastPosition()

return the last position in the combobox's text field

Replace(int from, int to, string value)

replace the text between from and to positions in the combobox's text field

SetSelection(int n)

select the item at position n

SetMark(int from, int to)

select the text between from and to positions in the combobox's text field

(int from, int to) GetMark()

return the from and to positions of the selected text in the combobox's text field

int GetCurrentSelection()

return the current selection

bool SetStringSelection(string string)

select the item with the specified string

int SetString(int n, string string)

set the label for the item at position n

bool SetEditable(bool editable)

toggle readonly flag for the combobox's text field

int SetInsertionPointEnd()

set the insertion point at the end of the combobox's text field.

Remove(int from, int to)

remove the text between the two positions in the combobox's text field

bool IsEditable()

return true if the combobox is editable

SelectAll(int from, int to)

select all the text in the combo's text field

   1 #!/usr/bin/python
   2 
   3 # combobox.py
   4 
   5 import wx
   6 
   7 class MyDialog(wx.Dialog):
   8     def __init__(self, parent, id, title):
   9         wx.Dialog.__init__(self, parent, id, title, size=(250, 270))
  10 
  11         panel = wx.Panel(self, -1, (75, 20), (100, 127),  style=wx.SUNKEN_BORDER)
  12         self.picture = wx.StaticBitmap(panel)
  13         panel.SetBackgroundColour(wx.WHITE)
  14 
  15         self.images = ['tolstoy.jpg', 'feuchtwanger.jpg', 'balzac.jpg', 'pasternak.jpg', 'galsworthy.jpg', 'wolfe.jpg', 'zweig.jpg']
  16         authors = ['Leo Tolstoy', 'Lion Feuchtwanger', 'Honore de Balzac', 'Boris Pasternak', 'John Galsworthy', 'Tom Wolfe', 'Stefan Zweig' ]
  17 
  18 
  19         wx.ComboBox(self, -1, pos=(50, 170), size=(150, -1), choices=authors, style=wx.CB_READONLY)
  20         wx.Button(self, 1, 'Close', (80, 220))
  21 
  22         self.Bind(wx.EVT_BUTTON, self.OnClose, id=1)
  23         self.Bind(wx.EVT_COMBOBOX, self.OnSelect)
  24 
  25         self.Centre()
  26 
  27     def OnClose(self, event):
  28         self.Close()
  29 
  30     def OnSelect(self, event):
  31         item = event.GetSelection()
  32         self.picture.SetFocus()
  33         self.picture.SetBitmap(wx.Bitmap('images/' + self.images[item]))
  34 
  35 
  36 class MyApp(wx.App):
  37     def OnInit(self):
  38         dlg = MyDialog(None, -1, 'combobox.py')
  39         dlg.ShowModal()
  40         dlg.Destroy()
  41         return True
  42 
  43 app = MyApp(0)
  44 app.MainLoop()

combobox.png

Figure: combobox.py

wx.CheckBox

wx.CheckBox is a widget that has two states. On and Off. It is a box with a label. The label can be set to the right or to the left of the box. If the checkbox is checked, it is represented by a tick in a box.

wx.CheckBox Styles

wx.CheckBox methods

bool GetValue()

get the state of the checkbox

bool IsChecked()

determine the checkbox state

SetValue(bool state)

set the state of the checkbox

   1 #!/usr/bin/python
   2 
   3 # checkbox.py
   4 
   5 import wx
   6 
   7 class MyCheckBox(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, size=(250, 170))
  10 
  11         panel = wx.Panel(self, -1)
  12         self.cb = wx.CheckBox(panel, -1, 'Show Title', (10, 10))
  13         self.cb.SetValue(True)
  14 
  15         wx.EVT_CHECKBOX(self, self.cb.GetId(), self.ShowTitle)
  16 
  17         self.Show()
  18         self.Centre()
  19 
  20     def ShowTitle(self, event):
  21         if self.cb.GetValue():
  22             self.SetTitle('checkbox.py')
  23         else: self.SetTitle('')
  24 
  25 
  26 app = wx.App(0)
  27 MyCheckBox(None, -1, 'checkbox.py')
  28 app.MainLoop()

In our script we toggle the visibility of the title.

checkbox.png

Figure: checkbox.py

wx.StatusBar

As its name indicates, thewx.StatusBar widget is used to display application status information. It can be divided into several parts to show different kind of information. We can insert other widgets into the wx.StatusBar. It can be used as an alternative to dialogs, since dialogs are ofted abused and are disliked by most users. We can create a

wx.StatusBar in two ways. We can manually create our own wx.StatusBar and call SetStatusBar() method or we can simply call a CreateStatusBar() method. The latter method creates a default wx.StatusBar for us. In our first example, we have a

wx.Frame widget and five other widgets. If we hover a mouse pointer over a widget, its description is shown on the wx.StatusBar

   1 #!/usr/bin/python
   2 
   3 # statusbar.py
   4 
   5 import wx
   6 
   7 class MyStatusBar(wx.Frame):
   8         def __init__(self, parent, id, title):
   9                 wx.Frame.__init__(self, parent, id, title, size=(250, 200), style=wx.CAPTION | wx.SYSTEM_MENU | wx.CLOSE_BOX)
  10 
  11                 panel = wx.Panel(self, 1)
  12 
  13                 button = wx.Button(panel, 2, 'Button', (20, 20))
  14                 text = wx.CheckBox(panel, 3, 'CheckBox', (20, 90))
  15                 combo = wx.ComboBox(panel, 4, '', (120, 22))
  16                 slider = wx.Slider(panel, 5, 6, 1, 10, (120, 90), (110, -1))
  17 
  18 
  19                 panel.Bind(wx.EVT_ENTER_WINDOW, self.EnterPanel, id=1)
  20                 button.Bind(wx.EVT_ENTER_WINDOW, self.EnterButton, id=2)
  21                 text.Bind(wx.EVT_ENTER_WINDOW, self.EnterText, id=3)
  22                 combo.Bind(wx.EVT_ENTER_WINDOW, self.EnterCombo, id=4)
  23                 slider.Bind(wx.EVT_ENTER_WINDOW, self.EnterSlider, id=5)
  24 
  25 
  26                 self.sb = self.CreateStatusBar()
  27                 self.SetMaxSize((250, 200))
  28                 self.SetMinSize((250, 200))
  29                 self.Centre()
  30 
  31 
  32         def EnterButton(self, event):
  33                 self.sb.SetStatusText('Button widget')
  34                 event.Skip()
  35 
  36         def EnterPanel(self, event):
  37                 self.sb.SetStatusText('Panel widget')
  38                 event.Skip()
  39 
  40         def EnterText(self, event):
  41                 self.sb.SetStatusText('CheckBox widget')
  42                 event.Skip()
  43 
  44         def EnterCombo(self, event):
  45                 self.sb.SetStatusText('ComboBox widget')
  46                 event.Skip()
  47 
  48         def EnterSlider(self, event):
  49                 self.sb.SetStatusText('Slider widget')
  50                 event.Skip()
  51 
  52 class MyApp(wx.App):
  53         def OnInit(self):
  54                 frame = MyStatusBar(None, -1, 'statusbar.py')
  55                 frame.Show(True)
  56                 return True
  57 
  58 app = MyApp(0)
  59 app.MainLoop()

statusbar.png

Figure: statusbar.py

wx.RadioButton

wx.RadioButton  is a widget that allows the user to select a single exclusive choice from a group of options. A group of radio buttons is defined by having the first RadioButton in the group contain the wx.RB_GROUP style. All other RadioButtons defined after the first RadioButton with this style flag is set will be added to the function group of the first RadioButton. Declaring another RadioButton with the wx.RB_GROUP flag will start a new radio button group.

wx.RadioButton Styles

wx.RadioButton methods

bool GetValue()

returns True or False depending on the selection state

SetValue(bool state)

RadioButton

   1 #!/usr/bin/python
   2 
   3 # radiobuttons.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(200, 150))
  10         panel = wx.Panel(self, -1)
  11         self.rb1 = wx.RadioButton(panel, -1, 'Value A', (10, 10), style=wx.RB_GROUP)
  12         self.rb2 = wx.RadioButton(panel, -1, 'Value B', (10, 30))
  13         self.rb3 = wx.RadioButton(panel, -1, 'Value C', (10, 50))
  14         self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id=self.rb1.GetId())
  15         self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id=self.rb2.GetId())
  16         self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id=self.rb3.GetId())
  17         self.statusbar = self.CreateStatusBar(3)
  18         self.SetVal(True)
  19 
  20     def SetVal(self, event):
  21         state1 = str(self.rb1.GetValue())
  22         state2 = str(self.rb2.GetValue())
  23         state3 = str(self.rb3.GetValue())
  24         self.statusbar.SetStatusText(state1,0)
  25         self.statusbar.SetStatusText(state2,1)
  26         self.statusbar.SetStatusText(state3,2)
  27 
  28 class MyApp(wx.App):
  29     def OnInit(self):
  30         frame = MyFrame(None, -1, 'radiobuttons.py')
  31         frame.Show(True)
  32         frame.Center()
  33         return True
  34 
  35 app = MyApp(0)
  36 app.MainLoop()

radiobuttons.png

Figure: radiobuttons.py

wx.Gauge

wx.Gauge is a widget that is used, when we process lengthy tasks.

wx.Gauge styles

wx.Gauge methods

SetRange(integer range)

set the maximum value of the gauge

integer GetRange()

get the maximum value of the gauge

SetValue(integer position)

set the position of the gauge

integer GetValue()

get the position of the gauge

bool IsVertical()

check if the gauge is vertical

   1 #!/usr/bin/python
   2 
   3 # gauge.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title)
  10 
  11         self.timer = wx.Timer(self, 1)
  12         self.count = 0
  13         self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
  14         panel = wx.Panel(self, -1)
  15         vbox = wx.BoxSizer(wx.VERTICAL)
  16         hbox1 = wx.BoxSizer(wx.HORIZONTAL)
  17         hbox2 = wx.BoxSizer(wx.HORIZONTAL)
  18         hbox3 = wx.BoxSizer(wx.HORIZONTAL)
  19 
  20         self.gauge = wx.Gauge(panel, -1, 50, size=(250, 25))
  21         self.btn1 = wx.Button(panel, wx.ID_OK)
  22         self.btn2 = wx.Button(panel, wx.ID_STOP)
  23         self.text = wx.StaticText(panel, -1, 'Task to be done')
  24 
  25         self.Bind(wx.EVT_BUTTON, self.OnOk, self.btn1)
  26         self.Bind(wx.EVT_BUTTON, self.OnStop, self.btn2)
  27 
  28         hbox1.Add(self.gauge, 1, wx.ALIGN_CENTRE)
  29         hbox2.Add(self.btn1, 1, wx.RIGHT, 10)
  30         hbox2.Add(self.btn2, 1)
  31         hbox3.Add(self.text, 1)
  32         vbox.Add((0, 50), 0)
  33         vbox.Add(hbox1, 0, wx.ALIGN_CENTRE)
  34         vbox.Add((0, 30), 0)
  35         vbox.Add(hbox2, 1, wx.ALIGN_CENTRE)
  36         vbox.Add(hbox3, 1, wx.ALIGN_CENTRE)
  37         panel.SetSizer(vbox)
  38         self.Centre()
  39 
  40     def OnOk(self, event):
  41         if self.count >= 50:
  42             return
  43         self.timer.Start(100)
  44         self.text.SetLabel('Task in Progress')
  45 
  46     def OnStop(self, event):
  47         if self.count == 0 or self.count >= 50 or not self.timer.IsRunning():
  48             return
  49         self.timer.Stop()
  50         self.text.SetLabel('Task Interrupted')
  51         wx.Bell()
  52 
  53     def OnTimer(self, event):
  54         self.count = self.count +1
  55         self.gauge.SetValue(self.count)
  56         if self.count == 50:
  57             self.timer.Stop()
  58             self.text.SetLabel('Task Completed')
  59 
  60 class MyApp(wx.App):
  61     def OnInit(self):
  62         frame = MyFrame(None, -1, 'gauge.py')
  63         frame.Show(True)
  64         return True
  65 
  66 app = MyApp(0)
  67 app.MainLoop()

gauge.png

Figure: gauge.py

wx.Slider

wx.Slider is a widget that has a simple handle. This handle can be pulled back and forth. This way we are choosing a value for a specific task. Say we want to input into our application the age of a customer. For this purpose, wx.Slider might be a good choice.

wx.Slider styles

wx.Slider methods

integer GetValue()

get the current slider value

SetValue(integer value)

set the slider position

SetRange(integer min, integer max)

set the minimum and maximum slider value

integer GetMin()

get the minimum slider value

integer GetMax()

get the maximum slider value

SetMin(integer min)

set the minimum slider value

integer SetMax(integer max)

set the maximum slider value

SetLineSize(integer size)

set the line size for the slider

SetPageSize(integer pageSize)

set the page size for the slider


pageSize - the number of steps the slider moves when the user pages up or down.

   1 #!/usr/bin/python
   2 
   3 # slider.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9 
  10         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, (300, 150))
  11         panel = wx.Panel(self, -1)
  12 
  13         vbox = wx.BoxSizer(wx.VERTICAL)
  14         hbox = wx.BoxSizer(wx.HORIZONTAL)
  15         self.sld = wx.Slider(panel, -1, 200, 150, 500, wx.DefaultPosition, (250, -1),
  16                               wx.SL_AUTOTICKS | wx.SL_HORIZONTAL | wx.SL_LABELS)
  17         btn1 = wx.Button(panel, 8, 'Adjust')
  18         btn2 = wx.Button(panel, 9, 'Close')
  19 
  20         wx.EVT_BUTTON(self, 8, self.OnAdjust)
  21         wx.EVT_BUTTON(self, 9, self.OnClose)
  22         vbox.Add(self.sld, 1, wx.ALIGN_CENTRE)
  23         hbox.Add(btn1, 1, wx.RIGHT, 10)
  24         hbox.Add(btn2, 1)
  25         vbox.Add(hbox, 0, wx.ALIGN_CENTRE | wx.ALL, 20)
  26         panel.SetSizer(vbox)
  27 
  28     def OnAdjust(self, event):
  29         val = self.sld.GetValue()
  30 
  31         self.SetSize((val*2, val))
  32     def OnClose(self, event):
  33         self.Close()
  34 
  35 class MyApp(wx.App):
  36     def OnInit(self):
  37         frame = MyFrame(None, -1, 'slider.py')
  38         frame.Show(True)
  39         frame.Centre()
  40         return True
  41 
  42 app = MyApp(0)
  43 app.MainLoop()

In this example we have a slider and two buttons. Slider initial position is set to 200. The min value is 150, max value is 500. When you click adjust button, the frame size is changed. The height is set to value chosen by slider, the width is set to 2 x value.

slider.png

Figure: slider.py

wx.ListBox

wx.Listbox is a widget that consists of a scrolling box and a list of items. User can select one or more items from that list. It depends on whether it is created as a single or multiple selection box. Selected items are marked.

   1 #!/usr/bin/python
   2 # listbox.py
   3 
   4 import wx
   5 
   6 from time import *
   7 
   8 class MyFrame(wx.Frame):
   9     def __init__(self, parent, id, title):
  10         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, (550, 350))
  11 
  12         zone_list = ['CET', 'GMT', 'MSK', 'EST', 'PST', 'EDT']
  13         self.full_list = {
  14             'CET': 'Central European Time',
  15             'GMT': 'Greenwich Mean Time',
  16             'MSK': 'Moscow Time',
  17             'EST': 'Eastern Standard Time',
  18             'PST': 'Pacific Standard Time',
  19             'EDT': 'Eastern Daylight Time'
  20         }
  21         self.time_diff = {
  22             'CET' : 1,
  23             'GMT' : 0,
  24             'MSK': 3,
  25             'EST': -5,
  26             'PST': -8,
  27             'EDT': -4
  28         }
  29 
  30         vbox = wx.BoxSizer(wx.VERTICAL)
  31         hbox1 = wx.BoxSizer(wx.HORIZONTAL)
  32         hbox2 = wx.BoxSizer(wx.HORIZONTAL)
  33         hbox3 = wx.BoxSizer(wx.HORIZONTAL)
  34 
  35         self.timer = wx.Timer(self, 1)
  36         self.diff = 0
  37         panel = wx.Panel(self, -1)
  38         self.time_zones = wx.ListBox(panel, 26, wx.DefaultPosition, (170, 130), zone_list, wx.LB_SINGLE)
  39         self.time_zones.SetSelection(0)
  40         self.text = wx.TextCtrl(panel, -1, 'Central European Time', size=(200, 130), style=wx.TE_MULTILINE)
  41         self.time = wx.StaticText(panel, -1, '')
  42         btn = wx.Button(panel, wx.ID_CLOSE, 'Close')
  43         hbox1.Add(self.time_zones, 0, wx.TOP, 40)
  44         hbox1.Add(self.text, 1, wx.LEFT | wx.TOP, 40)
  45         hbox2.Add(self.time, 1, wx.ALIGN_CENTRE)
  46         hbox3.Add(btn, 0, wx.ALIGN_CENTRE)
  47         vbox.Add(hbox1, 0, wx.ALIGN_CENTRE)
  48         vbox.Add(hbox2, 1, wx.ALIGN_CENTRE)
  49         vbox.Add(hbox3, 1, wx.ALIGN_CENTRE)
  50         panel.SetSizer(vbox)
  51         self.timer.Start(100)
  52 
  53         self.Bind(wx.EVT_BUTTON, self.OnClose, id=wx.ID_CLOSE)
  54         self.Bind(wx.EVT_LISTBOX, self.OnSelect, id=26)
  55         self.Bind(wx.EVT_TIMER, self.OnTimer, id=1)
  56 
  57     def OnClose(self, event):
  58         self.Close()
  59 
  60     def OnSelect(self, event):
  61         index = event.GetSelection()
  62         time_zone = self.time_zones.GetString(index)
  63         self.diff = self.time_diff[time_zone]
  64         self.text.SetValue(self.full_list[time_zone])
  65 
  66     def OnTimer(self, event):
  67         ct = gmtime()
  68         print_time = (ct[0], ct[1], ct[2], ct[3]+self.diff, ct[4], ct[5], ct[6], ct[7], -1)
  69         self.time.SetLabel(strftime("%H:%M:%S", print_time))
  70 
  71 class MyApp(wx.App):
  72     def OnInit(self):
  73         frame = MyFrame(None, -1, 'listbox.py')
  74         frame.Centre()
  75         frame.Show(True)
  76         return True
  77 
  78 app = MyApp(0)
  79 app.MainLoop()

listbox.py example consists of four different widgets.wx.Listbox, wx.TextCtrl, wx.StaticText and wx.Button. Widgets are organized with wx.BoxSizer-s. wx.Listbox has a list of six different world times. These abbreviations are explained in the wx.TextCtrl. Current time is displayed in the wx.StaticText widget. wx.Timer widget is used to update the time every 100 miliseconds.

listbox.png

Figure: listbox.py

wx.SpinCtrl

This widget lets you increment and decrement a value. It has two up and down arrow buttons for this purpose. User can enter a value into a box or increment/decrement it by these two arrows.

wx.SpinCtrl styles

wx.SpinCtrl methods

integer GetValue()

get the current value

SetValue(integer value)

set the current value

SetValueString(string value)

something

SetRange(integer min, integer max)

set the min and max values

integer GetMin()

get the minimum value

integer GetMax()

get the maximum value

   1 #!/usr/bin/python
   2 
   3 # spinctrl.py
   4 
   5 import wx
   6 
   7 class MyDialog(wx.Dialog):
   8     def __init__(self, parent, id, title):
   9         wx.Dialog.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(350, 310))
  10 
  11         wx.StaticText(self, -1, 'Convert Fahrenheit temperature to Celsius', (20,20))
  12         wx.StaticText(self, -1, 'Fahrenheit: ', (20, 80))
  13         wx.StaticText(self, -1, 'Celsius: ', (20, 150))
  14 
  15         self.celsius =  wx.StaticText(self, -1, '', (150, 150))
  16         self.sc = wx.SpinCtrl(self, -1, '',  (150, 75), (60, -1))
  17         self.sc.SetRange(-459, 1000)
  18         self.sc.SetValue(0)
  19 
  20         compute_btn = wx.Button(self, 1, 'Compute', (70, 250))
  21         compute_btn.SetFocus()
  22         clear_btn = wx.Button(self, 2, 'Close', (185, 250))
  23 
  24         self.Bind(wx.EVT_BUTTON, self.OnCompute, id=1)
  25         self.Bind(wx.EVT_BUTTON, self.OnClose, id=2)
  26         self.Bind(wx.EVT_CLOSE, self.OnClose)
  27 
  28     def OnCompute(self, event):
  29         fahr = self.sc.GetValue()
  30         cels = round((fahr-32)*5/9.0, 2)
  31         self.celsius.SetLabel(str(cels))
  32 
  33     def OnClose(self, event):
  34         self.Destroy()
  35 
  36 class MyApp(wx.App):
  37     def OnInit(self):
  38         dlg = MyDialog(None, -1, 'spinctrl.py')
  39         dlg.Show(True)
  40         dlg.Centre()
  41         return True
  42 
  43 app = MyApp(0)
  44 app.MainLoop()

spinctrl.py is a dialog-based script. Our main class inherits fromwx.Dialog instead of wx.Frame. The main difference is that we cannot resize the window and we call Destroy() method instead of Close(), when we quit the application. spinctrl.py script converts Fahrenheit temperature to Celsius. This example is very popular and can be found in most programming primer books.

spinctrl.png

Figure spinctrl.py

wx.ListCtrl

wx.ListCtrl creates lists in the following formats:

In our example, we key in the states and their capitals and add them into the list widget. We use report view.

   1 #!/usr/bin/python
   2 
   3 # capitals.py
   4 
   5 import wx
   6 
   7 class MyDialog(wx.Dialog):
   8     def __init__(self, parent, id, title):
   9         wx.Dialog.__init__(self, parent, id, title, size=(600,500), style=wx.DEFAULT_DIALOG_STYLE)
  10 
  11         hbox  = wx.BoxSizer(wx.HORIZONTAL)
  12         vbox1 = wx.BoxSizer(wx.VERTICAL)
  13         vbox2 = wx.BoxSizer(wx.VERTICAL)
  14         vbox3 = wx.GridSizer(2,2,0,0)
  15         vbox4 = wx.BoxSizer(wx.VERTICAL)
  16         pnl1 = wx.Panel(self, -1, style=wx.SIMPLE_BORDER)
  17         pnl2 = wx.Panel(self, -1, style=wx.SIMPLE_BORDER)
  18         self.lc = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
  19         self.lc.InsertColumn(0, 'State')
  20         self.lc.InsertColumn(1, 'Capital')
  21         self.lc.SetColumnWidth(0, 140)
  22         self.lc.SetColumnWidth(1, 153)
  23         vbox1.Add(pnl1, 1, wx.EXPAND | wx.ALL, 3)
  24         vbox1.Add(pnl2, 1, wx.EXPAND | wx.ALL, 3)
  25         vbox2.Add(self.lc, 1, wx.EXPAND | wx.ALL, 3)
  26         self.tc1 = wx.TextCtrl(pnl1, -1)
  27         self.tc2 = wx.TextCtrl(pnl1, -1)
  28         vbox3.AddMany([ (wx.StaticText(pnl1, -1, 'State'),0, wx.ALIGN_CENTER),
  29                         (self.tc1, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL),
  30                         (wx.StaticText(pnl1, -1, 'Capital'),0, wx.ALIGN_CENTER_HORIZONTAL),
  31                         (self.tc2,0)])
  32         pnl1.SetSizer(vbox3)
  33         vbox4.Add(wx.Button(pnl2, 10, 'Add'),   0, wx.ALIGN_CENTER| wx.TOP, 45)
  34         vbox4.Add(wx.Button(pnl2, 11, 'Remove'), 0, wx.ALIGN_CENTER|wx.TOP, 15)
  35         vbox4.Add(wx.Button(pnl2, 12, 'Clear'), 0, wx.ALIGN_CENTER| wx.TOP, 15)
  36         vbox4.Add(wx.Button(pnl2, 13, 'Close'), 0, wx.ALIGN_CENTER| wx.TOP, 15)
  37         pnl2.SetSizer(vbox4)
  38         self.Bind (wx.EVT_BUTTON, self.OnAdd, id=10)
  39         self.Bind (wx.EVT_BUTTON, self.OnRemove, id=11)
  40         self.Bind (wx.EVT_BUTTON, self.OnClear, id=12)
  41         self.Bind (wx.EVT_BUTTON, self.OnClose, id=13)
  42         hbox.Add(vbox1, 1, wx.EXPAND)
  43         hbox.Add(vbox2, 1, wx.EXPAND)
  44         self.SetSizer(hbox)
  45 
  46     def OnAdd(self, event):
  47         if not self.tc1.GetValue() or not self.tc2.GetValue():
  48             return
  49         num_items = self.lc.GetItemCount()
  50         self.lc.InsertStringItem(num_items, self.tc1.GetValue())
  51         self.lc.SetStringItem(num_items, 1, self.tc2.GetValue())
  52         self.tc1.Clear()
  53         self.tc2.Clear()
  54 
  55     def OnRemove(self, event):
  56         index = self.lc.GetFocusedItem()
  57         self.lc.DeleteItem(index)
  58 
  59     def OnClose(self, event):
  60         self.Close()
  61 
  62     def OnClear(self, event):
  63         self.lc.DeleteAllItems()
  64 
  65 class MyApp(wx.App):
  66     def OnInit(self):
  67         dia = MyDialog(None, -1, 'capitals.py')
  68         dia.ShowModal()
  69         dia.Destroy()
  70         return True
  71 
  72 app = MyApp(0)
  73 app.MainLoop()

capitals1.png

Figure: capitals.py

wx.SplitterWindow

This widget enables to split the main area of an application into parts. The user can dynamically resize those parts with the mouse pointer. Such a solution can be seen in mail clients (evolution) or in burning software (k3b). You can split an area vertically or horizontally.

   1 #!/usr/bin/python
   2 # splitterwindow.py
   3 import wx
   4 class MyFrame(wx.Frame):
   5     def __init__(self, parent, id, title):
   6         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(350, 300))
   7 
   8         splitter = wx.SplitterWindow(self, -1)
   9         panel1 = wx.Panel(splitter, -1)
  10         wx.StaticText(panel1, -1,
  11                     "Whether you think that you can, or that you can't, you are usually right."
  12                     "\n\n Henry Ford",
  13             (100,100), style=wx.ALIGN_CENTRE)
  14         panel1.SetBackgroundColour(wx.LIGHT_GREY)
  15         panel2 = wx.Panel(splitter, -1)
  16         panel2.SetBackgroundColour(wx.WHITE)
  17         splitter.SplitVertically(panel1, panel2)
  18         self.Centre()
  19 
  20 class MyApp(wx.App):
  21     def OnInit(self):
  22         frame = MyFrame(None, -1, 'splitterwindow.py')
  23         frame.Show(True)
  24         self.SetTopWindow(frame)
  25         return True
  26 
  27 app = MyApp(0)
  28 app.MainLoop()

wx.ScrolledWindow

This is one of the container widgets. It can be useful, when we have a larger area than a window can display. In our example, we demonstrate such a case. We place a large image into our window. When the window is smaller than our image, Scrollbars are displayed automatically.

wx.ScrolledWindow methods

SetScrollbars()

set scrollbars for a window

Scroll(int x, int y)

scroll a window

int GetScrollPageSize(int orient)

get the scroll page size

SetScrollPageSize(int orient, int pageSize)

set the scroll page size

SetScrollRate(int xstep, int ystep)

set scrolling rate

wx.Size GetScrollPixelsPerUnit()

get the size of one logical unit in physical units

SetScrollRate(int xstep, int ystep)

set scrolling rate

EnableScrolling(bool x, bool y)

enable or disable scrolling

wxPoint GetViewStart()

get the view start

SetTargetWindow(wx.Window target)

set the target window for scrolling

wx.Window GetTargetWindow()

get the scrolling target window

AdjustScrollbars()

adjust scrollbars

   1 #!/usr/bin/python
   2 
   3 # myscrolledwindow.py
   4 
   5 import wx
   6 
   7 class MyScrolledWindow(wx.Frame):
   8    def __init__(self, parent, id, title):
   9        wx.Frame.__init__(self, parent, id, title, size=(500, 400))
  10 
  11        sw = wx.ScrolledWindow(self)
  12        bmp = wx.Image('aliens.jpg',wx.BITMAP_TYPE_JPEG).ConvertToBitmap()
  13        wx.StaticBitmap(sw, -1, bmp)
  14        sw.SetScrollbars(20,20,55,40)
  15 
  16 class MyApp(wx.App):
  17    def OnInit(self):
  18        frame = MyScrolledWindow(None, -1, 'Aliens')
  19        frame.Show(True)
  20        frame.Centre()
  21        return True
  22 
  23 app = MyApp(0)
  24 app.MainLoop()

scrolledwindow.png

wx.TreeCtrl

wx.TreeCtrl displays data in a hierarchy.

   1 #!/usr/bin/python
   2 
   3 # treectrl.py
   4 
   5 import wx
   6 
   7 class MyFrame(wx.Frame):
   8     def __init__(self, parent, id, title):
   9         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(450, 350))
  10 
  11         hbox = wx.BoxSizer(wx.HORIZONTAL)
  12         vbox = wx.BoxSizer(wx.VERTICAL)
  13         panel1 = wx.Panel(self, -1)
  14         panel2 = wx.Panel(self, -1)
  15 
  16         self.tree = wx.TreeCtrl(panel1, 1, wx.DefaultPosition, (-1,-1), wx.TR_HIDE_ROOT|wx.TR_HAS_BUTTONS)
  17         root = self.tree.AddRoot('Programmer')
  18         os = self.tree.AppendItem(root, 'Operating Systems')
  19         pl = self.tree.AppendItem(root, 'Programming Languages')
  20         tk = self.tree.AppendItem(root, 'Toolkits')
  21         self.tree.AppendItem(os, 'Linux')
  22         self.tree.AppendItem(os, 'FreeBSD')
  23         self.tree.AppendItem(os, 'OpenBSD')
  24         self.tree.AppendItem(os, 'NetBSD')
  25         self.tree.AppendItem(os, 'Solaris')
  26         cl = self.tree.AppendItem(pl, 'Compiled languages')
  27         sl = self.tree.AppendItem(pl, 'Scripting languages')
  28         self.tree.AppendItem(cl, 'Java')
  29         self.tree.AppendItem(cl, 'C++')
  30         self.tree.AppendItem(cl, 'C')
  31         self.tree.AppendItem(cl, 'Pascal')
  32         self.tree.AppendItem(sl, 'Python')
  33         self.tree.AppendItem(sl, 'Ruby')
  34         self.tree.AppendItem(sl, 'Tcl')
  35         self.tree.AppendItem(sl, 'PHP')
  36         self.tree.AppendItem(tk, 'Qt')
  37         self.tree.AppendItem(tk, 'MFC')
  38         self.tree.AppendItem(tk, 'wxPython')
  39         self.tree.AppendItem(tk, 'GTK+')
  40         self.tree.AppendItem(tk, 'Swing')
  41         self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=1)
  42         self.display = wx.StaticText(panel2, -1, '',(10,10), style=wx.ALIGN_CENTRE)
  43         vbox.Add(self.tree, 1, wx.EXPAND)
  44         hbox.Add(panel1, 1, wx.EXPAND)
  45         hbox.Add(panel2, 1, wx.EXPAND)
  46         panel1.SetSizer(vbox)
  47         self.SetSizer(hbox)
  48         self.Centre()
  49 
  50     def OnSelChanged(self, event):
  51         item =  event.GetItem()
  52         self.display.SetLabel(self.tree.GetItemText(item))
  53 
  54 class MyApp(wx.App):
  55     def OnInit(self):
  56         frame = MyFrame(None, -1, 'treectrl.py')
  57         frame.Show(True)
  58         self.SetTopWindow(frame)
  59         return True
  60 
  61 app = MyApp(0)
  62 app.MainLoop()

First of all we must create a root item.

root = self.tree.AddRoot('Programmer')

In our case, root item will not be displayed, because we used wx.TR_HIDE_ROOT flag in our constructor.

self.tree = wx.TreeCtrl(panel1, 1, wx.DefaultPosition, (-1,-1), wx.TR_HIDE_ROOT|wx.TR_HAS_BUTTONS)

We add items to the root item withAppendItem() method.

os = self.tree.AppendItem(root, 'Operating Systems')
pl = self.tree.AppendItem(root, 'Programming Languages')
tk = self.tree.AppendItem(root, 'Toolkits')

We can similarly create several levels by simply adding items to existing items.

We catch events with wx.EVT_TREE_SEL_CHANGED() event handler.

wx.EVT_TREE_SEL_CHANGED(self.tree, 1, self.OnSelChanged)

Various constructor style flags:

treecontrol.png

Figure: treectrl.py

wx.Notebook

wx.Notebook widget joins multiple windows with corresponding tabs.

You can position the Notebook widget using the following style flags:

The default position is wx.NB_TOP.

wx.Notebook methods

integer GetRowCount()

get the number of rows

SetPadding(wx.Size padding)

set the amount of space around each page's icon and label

SetTabSize(wx.Size size)

set the tab size

(tab, where) HitTest(wx.Point point)

return the tab which is hit

wx.Size CalcSizeFromPage(wx.Size size)

something

add bookcontrolbase methods where return value of the

HitTest() method can be one of:

In our example, we mimic the look of a spreadsheet.

   1 #!/usr/bin/python
   2 
   3 # notebook.py
   4 
   5 import wx
   6 import wx.lib.sheet as sheet
   7 
   8 class MySheet(sheet.CSheet):
   9     def __init__(self, parent):
  10         sheet.CSheet.__init__(self, parent)
  11 
  12         self.SetLabelBackgroundColour('#DBD4D4')
  13         self.SetNumberRows(50)
  14         self.SetNumberCols(50)
  15 
  16 class Notebook(wx.Frame):
  17     def __init__(self, parent, id, title):
  18         wx.Frame.__init__(self, parent, id, title, size=(600, 500))
  19 
  20         menubar = wx.MenuBar()
  21         file = wx.Menu()
  22         file.Append(101, 'Quit', '' )
  23         menubar.Append(file, "&File")
  24         self.SetMenuBar(menubar)
  25         wx.EVT_MENU(self, 101, self.OnQuit)
  26         nb = wx.Notebook(self, -1, style=wx.NB_BOTTOM)
  27         self.sheet1 = MySheet(nb)
  28         self.sheet2 = MySheet(nb)
  29         self.sheet3 = MySheet(nb)
  30         nb.AddPage(self.sheet1, "Sheet1")
  31         nb.AddPage(self.sheet2, "Sheet2")
  32         nb.AddPage(self.sheet3, "Sheet3")
  33         self.sheet1.SetFocus()
  34         self.StatusBar()
  35 
  36     def StatusBar(self):
  37         self.statusbar = self.CreateStatusBar()
  38 
  39     def OnQuit(self, event):
  40         self.Close()
  41 
  42 class MyApp(wx.App):
  43     def OnInit(self):
  44          frame = Notebook(None, -1, 'notebook.py')
  45          frame.Show(True)
  46          frame.Centre()
  47          return True
  48 
  49 app = MyApp(0)
  50 app.MainLoop()

notebook.png

Figure: notebook.py

wx.lib Classes

Under lib directory we can find other widgets, improvements or extensions. Here we will show some more useful classes.

Mouse Gestures

We can find mouse gestures in such successfull applications like Firefox or Opera. They really help users save their time while browsing on the Interent. Mouse gestures are created with

wx.lib.gestures.MouseGestures class in wxPython.

Possible methods

AddGesture(string gesture, action)

registers a mouse gesture

RemoveGesture(string gesture)

removes a mouse gesture

SetGesturePen(wx.Colour colour, integer width)

sets the colour and width of the line drawn to visually represent each gesture

SetGesturesVisible(boolean vis)

sets wether a line is drawn to visually represent each gesture

SetWobbleTolerance(integer wobbletolerance)

sets the intensity to trigger a gesture

SetModifiers(list modifiers)

takes a list of wx.Key constants (Control, Shift, and/or Alt)

SetMouseButton(integer flag)

takes the wx constant for the target mousebutton

Available gestures:

If you wonder why these numbers were chosen, have a look at the numerical pad. Mouse gestures can be combined. This way 'RDLU' is a mouse gesture triggered, when we do a square with a mouse pointer.

Possible flags are:

   1 #!/usr/bin/python
   2 
   3 # mousegestures.py
   4 
   5 import wx
   6 import wx.lib.gestures as gest
   7 
   8 class MyMouseGestures(wx.Frame):
   9     def __init__ (self, parent, id, title):
  10         wx.Frame.__init__(self, parent, id, title, size=(600, 500))
  11 
  12         panel = wx.Panel(self, -1)
  13         mg = gest.MouseGestures(panel, True, wx.MOUSE_BTN_LEFT)
  14         mg.SetGesturePen(wx.Colour(255, 0, 0), 2)
  15         mg.SetGesturesVisible(True)
  16         mg.AddGesture('DR', self.OnDownRight)
  17 
  18     def OnDownRight(self):
  19           self.Close()
  20 
  21 class MyApp(wx.App):
  22     def OnInit(self):
  23         frame = MyMouseGestures(None, -1, "mousegestures.py")
  24         frame.Show(True)
  25         frame.Centre()
  26         return True
  27 
  28 app = MyApp(0)
  29 app.MainLoop()

In our example, we have registered a mouse gesture for a panel. Mouse gesture is triggered, when a left button is pressed and we go down and right with a cursor. As in letter 'L'. Our mouse gesture will close the application. If we want to use mouse gestures, we have to create a

MouseGesture object. The first parameter is a window, where the mouse gesture is registered. Second parameter defines a way to register a gesture. True is for automatic, False for manual. Manual is not fully implemented and we are happy with the automatic way. Last parameter defines a mouse button, which will be pressed when triggering gestures. The button can be later changed with the SetMouseButton() method.

mg = gest.MouseGestures(panel, True, wx.MOUSE_BTN_LEFT)

Our gestures will be painted as red lines. They will be 2 pixels wide.

mg.SetGesturePen(wx.Colour(255, 0, 0), 2)

We set this gesture visible with theSetGesturesVisible() method.

mg.SetGesturesVisible(True)

We register a mouse gesture with theAddGesture() method. The first parameter is the gesture. Second parameter is the method triggered by the gesture.

mg.AddGesture('DR', self.OnDownRight)

AnalogClockWindow

Simple analog clock.

   1 #!/usr/bin/python
   2 
   3 # analogclock.py
   4 
   5 import wx
   6 from wx.lib import analogclock as ac
   7 
   8 class MyFrame(wx.Frame):
   9     def __init__(self, parent, id, title):
  10         wx.Frame.__init__(self, parent, id, title)
  11 
  12         clock = ac.AnalogClockWindow(self)
  13         clock.SetBackgroundColour('gray')
  14         clock.SetHandColours('black')
  15         clock.SetTickColours('WHITE')
  16         clock.SetTickSizes(h=5, m=2)
  17         clock.SetTickStyles(ac.TICKS_ROMAN)
  18         self.SetSize((400,350))
  19 
  20 class MyApp(wx.App):
  21     def OnInit(self):
  22         frame = MyFrame(None, -1, 'analogclock.py')
  23         frame.Show(True)
  24         frame.Centre()
  25         return True
  26 
  27 app = MyApp(0)
  28 app.MainLoop()

Various clock styles:

SetClockStyle()

Various ticks styles:

SetTickStyles()

analogclock.png

Figure: analogclock.py

Bitmap Text Buttons

We cannot use wx.Button class if we want to display text buttons with images. For this task, we have awx.GenBitmapTextButton class.

The constructor is:

wx.GenBitmapTextButton(parent, ID=-1, bitmap=wx.NullBitmap, label='', pos = wx.DefaultPosition, size = wx.DefaultSize,
                 style = 0, validator = wx.DefaultValidator, name = 'genbutton')

This class is found under wx.lib.buttons.