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 but the scripts work correctly on Windows :

- Tested py3.x, wx4.x and Win10).

Icons and images :

- 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

https://github.com/janbodnar/wxPython-examples

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 #---------------------------------------------------------------------------
   8 
   9 app = wx.App()
  10 
  11 frame = wx.Frame(None, -1, 'Simple.py')
  12 frame.Show()
  13 
  14 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 #---------------------------------------------------------------------------
   8 
   9 app = wx.App()
  10 
  11 frame = wx.Frame(None, -1, '')
  12 frame.SetToolTip(wx.ToolTip('This is a frame'))
  13 frame.SetCursor(wx.Cursor(wx.CURSOR_MAGNIFIER))
  14 frame.SetPosition(wx.Point(0,0))
  15 frame.SetSize(wx.Size(300,250))
  16 frame.SetTitle('Simple2.py')
  17 frame.Show()
  18 
  19 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

Link :

https://wiki.wxpython.org/How%20to%20create%20a%20frame%20%28Phoenix%29

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.Append(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 #---------------------------------------------------------------------------
   8 
   9 class MyMenu(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title,
  12                           wx.DefaultPosition, wx.Size(300, 200))
  13 
  14         menubar = wx.MenuBar()
  15 
  16         file = wx.Menu()
  17         edit = wx.Menu()
  18         help = wx.Menu()
  19 
  20         file.Append(101, '&Open', 'Open a new document')
  21         file.Append(102, '&Save', 'Save the document')
  22         file.AppendSeparator()
  23         quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application')
  24         quit.SetBitmap(wx.Image('stock_exit-16.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap())
  25         file.Append(quit)
  26 
  27         menubar.Append(file, '&File')
  28         menubar.Append(edit, '&Edit')
  29         menubar.Append(help, '&Help')
  30 
  31         self.SetMenuBar(menubar)
  32 
  33         #------------
  34 
  35         self.Bind(wx.EVT_MENU, self.OnQuit, id=105)
  36 
  37         #------------
  38 
  39         self.CreateStatusBar()
  40 
  41     #-----------------------------------------------------------------------
  42 
  43     def OnQuit(self, event):
  44         self.Close()
  45 
  46 #---------------------------------------------------------------------------
  47 
  48 class MyApp(wx.App):
  49     def OnInit(self):
  50         frame = MyMenu(None, -1, 'Menu1.py')
  51         frame.Show(True)
  52 
  53         return True
  54 
  55 #---------------------------------------------------------------------------
  56 
  57 app = MyApp(0)
  58 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 = wx.MenuItem(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.Append(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.

self.Bind(wx.EVT_MENU, self.OnQuit, id=105)

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 #---------------------------------------------------------------------------
   8 
   9 class MyMenu(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title,
  12                           wx.DefaultPosition, wx.Size(380, 250))
  13 
  14         menubar = wx.MenuBar()
  15 
  16         file = wx.Menu()
  17         edit = wx.Menu()
  18         help = wx.Menu()
  19 
  20         file.Append(101, '&Open', 'Open a new document')
  21         file.Append(102, '&Save', 'Save the document')
  22         file.AppendSeparator()
  23         quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application')
  24         quit.SetBitmap(wx.Image('stock_exit-16.png',wx.BITMAP_TYPE_PNG).ConvertToBitmap())
  25         file.Append(quit)
  26 
  27         edit.Append(201, 'check item1', '', wx.ITEM_CHECK)
  28         edit.Append(202, 'check item2', kind=wx.ITEM_CHECK)
  29         submenu = wx.Menu()
  30         submenu.Append(301, 'radio item1', kind=wx.ITEM_RADIO)
  31         submenu.Append(302, 'radio item2', kind=wx.ITEM_RADIO)
  32         submenu.Append(303, 'radio item3', kind=wx.ITEM_RADIO)
  33         edit.Append(203, 'submenu', submenu)
  34 
  35         menubar.Append(file, '&File')
  36         menubar.Append(edit, '&Edit')
  37         menubar.Append(help, '&Help')
  38 
  39         self.SetMenuBar(menubar)
  40 
  41         #------------
  42 
  43         self.Bind(wx.EVT_MENU, self.OnQuit, id=105)
  44 
  45         #------------
  46 
  47         self.Centre()
  48 
  49     #-----------------------------------------------------------------------
  50 
  51     def OnQuit(self, event):
  52         self.Close()
  53 
  54 #---------------------------------------------------------------------------
  55 
  56 class MyApp(wx.App):
  57     def OnInit(self):
  58         frame = MyMenu(None, -1, 'Menu2.py')
  59         frame.Show(True)
  60 
  61         return True
  62 
  63 #---------------------------------------------------------------------------
  64 
  65 app = MyApp(0)
  66 app.MainLoop()

menu2.png

Figure: menu2.py

wx.ToolBar

Link : https://wiki.wxpython.org/How%20to%20create%20a%20tool%20bar%20%28Phoenix%29

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 #---------------------------------------------------------------------------
   8 
   9 class MyFrame(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title,
  12                           wx.DefaultPosition, wx.Size(300, 100))
  13 
  14         panel = wx.Panel(self, -1)
  15 
  16         wx.Button(panel, -1, "Button1", (0, 0))
  17         wx.Button(panel, -1, "Button2", (80, 0))
  18         wx.Button(panel, -1, "Button3", (160, 0))
  19 
  20 #---------------------------------------------------------------------------
  21 
  22 class MyApp(wx.App):
  23     def OnInit(self):
  24         frame = MyFrame(None, -1, 'Layout.py')
  25         frame.Show(True)
  26         frame.Centre()
  27 
  28         return True
  29 
  30 #---------------------------------------------------------------------------
  31 
  32 app = MyApp(0)
  33 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

Link : https://wiki.wxpython.org/How%20to%20create%20a%20box%20sizer%20%28Phoenix%29

wxboxsizer.png

Figure: wxboxsizer.py

Borders

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

Available Borders:

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

borders.png

Figure: borders.py

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

Link : https://wiki.wxpython.org/How%20to%20create%20a%20grid%20bag%20sizer%20%28Phoenix%29

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 #---------------------------------------------------------------------------
   8 
   9 class Cursors(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title)
  12 
  13         cursors = [wx.CURSOR_ARROW, wx.CURSOR_HAND, wx.CURSOR_WATCH,
  14                    wx.CURSOR_SPRAYCAN, wx.CURSOR_PENCIL, wx.CURSOR_CROSS,
  15                    wx.CURSOR_QUESTION_ARROW, wx.CURSOR_POINT_LEFT,
  16                    wx.CURSOR_SIZING]
  17 
  18         #------------
  19 
  20         vbox = wx.BoxSizer(wx.VERTICAL)
  21         sizer = wx.GridSizer(3, 3, 2, 2)
  22 
  23         for i in cursors:
  24             panel = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
  25             panel.SetCursor(wx.Cursor(i))
  26             sizer.Add(panel, flag=wx.EXPAND)
  27 
  28         vbox.Add(sizer, 1, wx.EXPAND | wx.TOP, 5)
  29         self.SetSizer(vbox)
  30 
  31         #------------
  32 
  33         self.Centre()
  34 
  35         #------------
  36 
  37         self.Show(True)
  38 
  39 #---------------------------------------------------------------------------
  40 
  41 app = wx.App(0)
  42 Cursors(None, -1, 'Cursors.py')
  43 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 #---------------------------------------------------------------------------
   8 
   9 text1 = "Now listen to me mama\nMama mama\nYou're taking away my last chance\nDon't take it away"
  10 
  11 text2 = '''You won't cry for my absence, I know -
  12 You forgot me long ago.
  13 Am I that unimportant...?
  14 Am I so insignificant...?
  15 Isn't something missing?
  16 Isn't someone missing me?'''
  17 
  18 text3 = '''But if I had one wish fulfilled tonight
  19 I'd ask for the sun to never rise
  20 If God passed a mic to me to speak
  21 I'd say stay in bed, world
  22 Sleep in peace'''
  23 
  24 #---------------------------------------------------------------------------
  25 
  26 class MyFrame(wx.Frame):
  27     def __init__(self, parent, id, title):
  28         wx.Frame.__init__(self, parent, id, title,
  29                           wx.DefaultPosition, wx.Size(350, 360))
  30 
  31         panel = wx.Panel(self, -1)
  32 
  33         font1 = wx.Font(10, wx.SWISS, wx.ITALIC, wx.NORMAL)
  34         font2 = wx.Font(10, wx.ROMAN, wx.NORMAL, wx.NORMAL)
  35         font3 = wx.Font(10, wx.MODERN, wx.NORMAL, wx.BOLD)
  36 
  37         lyrics1 = wx.StaticText(panel, -1, text1,(70, 15), style=wx.ALIGN_CENTRE)
  38         lyrics1.SetFont(font1)
  39 
  40         lyrics2 = wx.StaticText(panel, -1, text2,(70, 100), style=wx.ALIGN_CENTRE)
  41         lyrics2.SetFont(font2)
  42 
  43         lyrics3 = wx.StaticText(panel, -1, text3,(10, 220), style=wx.ALIGN_CENTRE)
  44         lyrics3.SetFont(font3)
  45 
  46         #------------
  47 
  48         self.Center()
  49 
  50 #---------------------------------------------------------------------------
  51 
  52 class MyApp(wx.App):
  53     def OnInit(self):
  54         frame = MyFrame(None, -1, 'Fonts.py')
  55         frame.Show(True)
  56         self.SetTopWindow(frame)
  57 
  58         return True
  59 
  60 #---------------------------------------------------------------------------
  61 
  62 app = MyApp(0)
  63 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')              # A predefined colour name .
SetBackgroundColour('#0000FF')           # Hex value string.

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 
   9 class Colours(wx.Dialog):
  10     def __init__(self, parent, id, title):
  11         wx.Dialog.__init__(self, parent, id, title, size=(300, 300))
  12 
  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         #------------
  23 
  24         vbox = wx.BoxSizer(wx.VERTICAL)
  25 
  26         gs = wx.GridSizer(4,2,3,3)
  27         gs.AddMany([ (self.pnl1, 0 ,wx.EXPAND),
  28             (self.pnl2, 0, wx.EXPAND),
  29             (self.pnl3, 0, wx.EXPAND),
  30             (self.pnl4, 0, wx.EXPAND),
  31             (self.pnl5, 0, wx.EXPAND),
  32             (self.pnl6, 0, wx.EXPAND),
  33             (self.pnl7, 0, wx.EXPAND),
  34             (self.pnl8, 0, wx.EXPAND) ])
  35 
  36         vbox.Add(gs, 1, wx.EXPAND | wx.TOP, 5)
  37         self.SetSizer(vbox)
  38 
  39         #------------
  40 
  41         self.SetColors()
  42 
  43         #------------
  44 
  45         self.Centre()
  46 
  47         #------------
  48 
  49         self.ShowModal()
  50         self.Destroy()
  51 
  52     #-----------------------------------------------------------------------
  53 
  54     def SetColors(self):
  55         self.pnl1.SetBackgroundColour(wx.BLACK)
  56         self.pnl2.SetBackgroundColour(wx.Colour(139,105,20))
  57         self.pnl3.SetBackgroundColour(wx.RED)
  58         self.pnl4.SetBackgroundColour('#0000FF')
  59         self.pnl5.SetBackgroundColour('sea green')
  60         self.pnl6.SetBackgroundColour('midnight blue')
  61         self.pnl7.SetBackgroundColour(wx.LIGHT_GREY)
  62         self.pnl8.SetBackgroundColour('plum')
  63 
  64 #---------------------------------------------------------------------------
  65 
  66 app = wx.App(0)
  67 Colours(None, -1, 'Colours.py')
  68 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 #---------------------------------------------------------------------------
  11 
  12 class MyFrame(wx.Frame):
  13     def __init__(self, parent, id, title):
  14         wx.Frame.__init__(self, parent, id, title,
  15                           wx.DefaultPosition, wx.Size(400, 350))
  16 
  17         self.panel = wx.Panel(self, -1)
  18 
  19         self.colors = getColourList()
  20         self.col_num = len(self.colors)
  21 
  22         #------------
  23 
  24         self.timer = wx.Timer(self)
  25         self.timer.Start(1500)
  26         self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
  27 
  28         #------------
  29 
  30         self.Centre()
  31 
  32     #-----------------------------------------------------------------------
  33 
  34     def OnTimer(self, event):
  35         self.panel.SetBackgroundColour(wx.RED)
  36         position = randrange(0, self.col_num-1, 1)
  37         self.panel.SetBackgroundColour(self.colors[position])
  38         self.panel.Refresh()
  39 
  40 #---------------------------------------------------------------------------
  41 
  42 class MyApp(wx.App):
  43     def OnInit(self):
  44         updateColourDB()
  45         frame = MyFrame(None, -1, 'Randomcolours.py')
  46         frame.Show(True)
  47         self.SetTopWindow(frame)
  48 
  49         return True
  50 
  51 #---------------------------------------------------------------------------
  52 
  53 app = MyApp(0)
  54 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 #---------------------------------------------------------------------------
   8 
   9 class MyFrame(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title, size=(320, 300))
  12 
  13         self.bitmap = wx.Bitmap('memento.jpg')
  14 
  15         self.Bind(wx.EVT_PAINT, self.OnPaint)
  16 
  17         self.Centre()
  18 
  19     #-----------------------------------------------------------------------
  20 
  21     def OnPaint(self, event):
  22         dc = wx.PaintDC(self)
  23         dc.DrawBitmap(self.bitmap, 80, 20)
  24 
  25 #---------------------------------------------------------------------------
  26 
  27 class MyApp(wx.App):
  28     def OnInit(self):
  29         frame = MyFrame(None, -1, 'bitmap.py (Memento)')
  30         frame.Show(True)
  31         self.SetTopWindow(frame)
  32 
  33         return True
  34 
  35 #---------------------------------------------------------------------------
  36 
  37 app = MyApp(0)
  38 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 #---------------------------------------------------------------------------
   8 
   9 class MyScrollWinEvent(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title)
  12 
  13         panel = wx.Panel(self, -1)
  14         panel.SetScrollbar(wx.VERTICAL, 0, 6, 50)
  15         panel.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
  16 
  17         self.st = wx.StaticText(panel, -1, '0', (30, 0))
  18 
  19         #------------
  20 
  21         self.Centre()
  22 
  23     #-----------------------------------------------------------------------
  24 
  25     def OnScroll(self, evt):
  26         y = evt.GetPosition()
  27         self.st.SetLabel(str(y))
  28 
  29 #---------------------------------------------------------------------------
  30 
  31 class MyApp(wx.App):
  32     def OnInit(self):
  33         msw = MyScrollWinEvent(None, -1, 'Myscrollwinevent.py')
  34         msw.Show(True)
  35 
  36         return True
  37 
  38 #---------------------------------------------------------------------------
  39 
  40 app = MyApp(0)
  41 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 #---------------------------------------------------------------------------
   8 
   9 class SizeEvent(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title)
  12 
  13         #------------
  14 
  15         self.Bind(wx.EVT_SIZE, self.OnSize)
  16 
  17         #------------
  18 
  19         self.Centre()
  20 
  21     #-----------------------------------------------------------------------
  22 
  23     def OnSize(self, event):
  24         self.SetTitle(str("Size : %s" % event.GetSize()))
  25 
  26 #---------------------------------------------------------------------------
  27 
  28 class MyApp(wx.App):
  29     def OnInit(self):
  30         se = SizeEvent(None, -1, 'Sizeevent.py')
  31         se.Show(True)
  32 
  33         return True
  34 
  35 #---------------------------------------------------------------------------
  36 
  37 app = MyApp(0)
  38 app.MainLoop()

   1 #!/usr/bin/python
   2 
   3 # moveevent.py
   4 
   5 import wx
   6 
   7 #---------------------------------------------------------------------------
   8 
   9 class MoveEvent(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title)
  12 
  13         wx.StaticText(self, -1, 'x:', (10,0))
  14         wx.StaticText(self, -1, 'y:', (10,20))
  15 
  16         self.st1 = wx.StaticText(self, -1, '', (30, 0))
  17         self.st2 = wx.StaticText(self, -1, '', (30, 20))
  18 
  19         #------------
  20 
  21         self.Bind(wx.EVT_MOVE, self.OnMove)
  22 
  23         #------------
  24 
  25         self.Centre()
  26 
  27     #-----------------------------------------------------------------------
  28 
  29     def OnMove(self, event):
  30         x, y = event.GetPosition()
  31         self.st1.SetLabel(str(x))
  32         self.st2.SetLabel(str(y))
  33 
  34 #---------------------------------------------------------------------------
  35 
  36 class MyApp(wx.App):
  37     def OnInit(self):
  38         me = MoveEvent(None, -1, 'Moveevent.py')
  39         me.Show(True)
  40 
  41         return True
  42 
  43 #---------------------------------------------------------------------------
  44 
  45 app = MyApp(0)
  46 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 #---------------------------------------------------------------------------
   8 
   9 class PaintEvent(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title)
  12 
  13         self.Bind(wx.EVT_PAINT, self.OnPaint)
  14 
  15         #------------
  16 
  17         self.Centre()
  18 
  19     #-----------------------------------------------------------------------
  20 
  21     def OnPaint(self, event):
  22         wx.Bell()
  23 
  24 #---------------------------------------------------------------------------
  25 
  26 class MyApp(wx.App):
  27     def OnInit(self):
  28         pe = PaintEvent(None, -1, 'Paintevent.py')
  29         pe.Show(True)
  30 
  31         return True
  32 
  33 #---------------------------------------------------------------------------
  34 
  35 app = MyApp(0)
  36 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 
   9 class KeyEvent(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title)
  12 
  13         panel = wx.Panel(self, -1)
  14         panel.SetFocus()
  15         panel.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
  16 
  17         #------------
  18 
  19         self.Centre()
  20 
  21         #------------
  22 
  23         self.Show(True)
  24 
  25     #-----------------------------------------------------------------------
  26 
  27     def OnKeyDown(self, event):
  28         keycode = event.GetKeyCode()
  29         if keycode == wx.WXK_ESCAPE:
  30             ret  = wx.MessageBox('Are you sure to quit?',
  31                                  'Question',
  32                                  wx.YES_NO | wx.CENTRE |
  33                                  wx.NO_DEFAULT,
  34                                  self)
  35             if ret == wx.YES:
  36                 self.Close()
  37         event.Skip()
  38 
  39 #---------------------------------------------------------------------------
  40 
  41 app = wx.App()
  42 KeyEvent(None, -1, 'Keyevent.py')
  43 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 
   3 # simpledialog.py
   4 
   5 import wx
   6 
   7 #---------------------------------------------------------------------------
   8 
   9 class MyDialog(wx.Dialog):
  10     def __init__(self, parent, id, title):
  11         wx.Dialog.__init__(self, parent, id, title)
  12 
  13 #---------------------------------------------------------------------------
  14 
  15 class MyApp(wx.App):
  16     def OnInit(self):
  17         dia = MyDialog(None, -1, "Simpledialog.py")
  18         dia.ShowModal()
  19         dia.Destroy()
  20 
  21         return True
  22 
  23 #---------------------------------------------------------------------------
  24 
  25 app = MyApp(0)
  26 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 #---------------------------------------------------------------------------
   8 
   9 class MyDialog(wx.Dialog):
  10     def __init__(self, parent, id, title):
  11         wx.Dialog.__init__(self, parent, id, title, size=(300, 250))
  12 
  13         sizer = self.CreateTextSizer(' My Buttons :')
  14 
  15         sizer.Add(wx.Button(self, -1, 'Button 1'), 0, wx.ALL, 5)
  16         sizer.Add(wx.Button(self, -1, 'Button 2'), 0, wx.ALL, 5)
  17         sizer.Add(wx.Button(self, -1, 'Button 3'), 0, wx.ALL, 5)
  18         sizer.Add(wx.Button(self, -1, 'Button 4'), 0, wx.ALL|wx.ALIGN_CENTER, 5)
  19         sizer.Add(wx.Button(self, -1, 'Button 5'), 0, wx.ALL|wx.EXPAND, 5)
  20 
  21         self.SetSizer(sizer)
  22 
  23 #---------------------------------------------------------------------------
  24 
  25 class MyFrame(wx.Frame):
  26     def __init__(self, parent, id, title):
  27         wx.Frame.__init__(self, parent, id, title, size=(350, 300))
  28 
  29         panel = wx.Panel(self, -1)
  30 
  31         wx.Button(panel, 1, 'Show Custom Dialog', (100, 100))
  32 
  33         self.Bind (wx.EVT_BUTTON, self.OnShowCustomDialog, id=1)
  34 
  35     #-----------------------------------------------------------------------
  36 
  37     def OnShowCustomDialog(self, event):
  38         dia = MyDialog(self, -1, 'buttons')
  39         dia.ShowModal()
  40         dia.Destroy()
  41 
  42 #---------------------------------------------------------------------------
  43 
  44 class MyApp(wx.App):
  45     def OnInit(self):
  46         frame = MyFrame(None, -1, 'Customdialog1.py')
  47         frame.Centre()
  48         frame.Show(True)
  49 
  50         return True
  51 
  52 #---------------------------------------------------------------------------
  53 
  54 app = MyApp(0)
  55 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 #---------------------------------------------------------------------------
   8 
   9 class MyDialog(wx.Dialog):
  10     def __init__(self, parent, id, title):
  11         wx.Dialog.__init__(self, parent, id, title)
  12 
  13         stline = wx.StaticText(self, -1, 'Discipline ist Macht.')
  14 
  15         sizer =  self.CreateButtonSizer(wx.NO | wx.YES | wx.HELP)
  16 
  17         #------------
  18 
  19         vbox = wx.BoxSizer(wx.VERTICAL)
  20 
  21         vbox.Add(stline, 1, wx.ALIGN_CENTER|wx.TOP, 45)
  22         vbox.Add(sizer, 0, wx.ALIGN_CENTER | wx.ALL, 10)
  23 
  24         self.SetSizer(vbox)
  25 
  26         #------------
  27 
  28         self.Bind(wx.EVT_BUTTON, self.OnYes, id=wx.ID_YES)
  29 
  30     #-----------------------------------------------------------------------
  31 
  32     def OnYes(self, event):
  33         self.Destroy()
  34 
  35 #---------------------------------------------------------------------------
  36 
  37 class MyFrame(wx.Frame):
  38     def __init__(self, parent, id, title):
  39         wx.Frame.__init__(self, parent, id, title)
  40 
  41         panel = wx.Panel(self, -1)
  42 
  43         wx.Button(panel, 1, '&Show custom Dialog', (50, 50))
  44 
  45         self.Bind(wx.EVT_BUTTON, self.OnShowCustomDialog, id=1)
  46 
  47     #-----------------------------------------------------------------------
  48 
  49     def OnShowCustomDialog(self, event):
  50         dia = MyDialog(self, -1, '')
  51         val = dia.ShowModal()
  52         dia.Destroy()
  53 
  54 #---------------------------------------------------------------------------
  55 
  56 class MyApp(wx.App):
  57     def OnInit(self):
  58         frame = MyFrame(None, -1, 'Customdialog2.py')
  59         frame.Centre()
  60         frame.Show(True)
  61 
  62         return True
  63 
  64 #---------------------------------------------------------------------------
  65 
  66 app = MyApp(0)
  67 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 #---------------------------------------------------------------------------
   9 
  10 class MyFrame(wx.Frame):
  11     def __init__(self, parent, id, title):
  12         wx.Frame.__init__(self, parent, id, title)
  13 
  14         self.CreateStatusBar()
  15 
  16         #------------
  17 
  18         menuBar = wx.MenuBar()
  19 
  20         menu = wx.Menu()
  21         menu.Append(99,  "&Message Dialog", "Shows a Message Dialog")
  22         menu.Append(100, "&Color Dialog", "Shows a Color Dialog")
  23         menu.Append(101, "&File Dialog", "Shows a File Dialog")
  24         menu.Append(102, "&Page Setup Dialog", "Shows a Page Setup Dialog")
  25         menu.Append(103, "&Font Dialog", "Shows a Font Dialog")
  26         menu.Append(104, "&Directory Dialog", "Shows a Directory Dialog")
  27         menu.Append(105, "&SingleChoice Dialog", "Shows a SingleChoice Dialog")
  28         menu.Append(106, "&TextEntry Dialog", "Shows a TextEntry Dialog")
  29         menuBar.Append(menu, "&Dialogs")
  30 
  31         self.SetMenuBar(menuBar)
  32 
  33         #------------
  34 
  35         self.Bind(wx.EVT_MENU, self.message, id=99)
  36         self.Bind(wx.EVT_MENU, self.choosecolor, id=100)
  37         self.Bind(wx.EVT_MENU, self.openfile, id=101)
  38         self.Bind(wx.EVT_MENU, self.pagesetup, id=102)
  39         self.Bind(wx.EVT_MENU, self.choosefont, id=103)
  40         self.Bind(wx.EVT_MENU, self.opendir, id=104)
  41         self.Bind(wx.EVT_MENU, self.singlechoice, id=105)
  42         self.Bind(wx.EVT_MENU, self.textentry, id=106)
  43 
  44     #-----------------------------------------------------------------------
  45 
  46     def message(self, event):
  47         dlg = wx.MessageDialog(self, 'To save one life is as if you have saved the world.', 'Talmud', wx.OK|wx.ICON_INFORMATION)
  48         dlg.ShowModal()
  49         dlg.Destroy()
  50 
  51 
  52     def choosecolor(self, event):
  53         dlg = wx.ColourDialog(self)
  54         dlg.GetColourData().SetChooseFull(True)
  55         if dlg.ShowModal() == wx.ID_OK:
  56             data = dlg.GetColourData()
  57             self.SetStatusText('You selected: %s\n' % str(data.GetColour().Get()))
  58         dlg.Destroy()
  59 
  60 
  61     def openfile(self, event):
  62         dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.*", wx.FD_OPEN)
  63         if dlg.ShowModal() == wx.ID_OK:
  64             path = dlg.GetPath()
  65             mypath = os.path.basename(path)
  66             self.SetStatusText("You selected: %s" % mypath)
  67         dlg.Destroy()
  68 
  69 
  70     def pagesetup(self, event):
  71         dlg = wx.PageSetupDialog(self)
  72         if dlg.ShowModal() == wx.ID_OK:
  73             data = dlg.GetPageSetupData()
  74             tl = data.GetMarginTopLeft()
  75             br = data.GetMarginBottomRight()
  76             self.SetStatusText('Margins are: %s %s' % (str(tl), str(br)))
  77         dlg.Destroy()
  78 
  79 
  80     def choosefont(self, event):
  81         default_font = wx.Font(10, wx.SWISS , wx.NORMAL, wx.NORMAL, False, "Verdana")
  82         data = wx.FontData()
  83         if sys.platform == 'win32':
  84             data.EnableEffects(True)
  85         data.SetAllowSymbols(False)
  86         data.SetInitialFont(default_font)
  87         data.SetRange(10, 30)
  88         dlg = wx.FontDialog(self, data)
  89         if dlg.ShowModal() == wx.ID_OK:
  90             data = dlg.GetFontData()
  91             font = data.GetChosenFont()
  92             color = data.GetColour()
  93             text = 'Face: %s, Size: %d, Color: %s' % (font.GetFaceName(), font.GetPointSize(),  color.Get())
  94             self.SetStatusText(text)
  95         dlg.Destroy()
  96 
  97 
  98     def opendir(self, event):
  99         dlg = wx.DirDialog(self, "Choose a directory:", style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
 100         if dlg.ShowModal() == wx.ID_OK:
 101             self.SetStatusText('You selected: %s\n' % dlg.GetPath())
 102         dlg.Destroy()
 103 
 104 
 105     def singlechoice(self, event):
 106         sins = ['Greed', 'Lust', 'Gluttony', 'Pride', 'Sloth', 'Envy', 'Wrath']
 107         dlg = wx.SingleChoiceDialog(self, 'Seven deadly sins', 'Which one?', sins, wx.CHOICEDLG_STYLE)
 108         if dlg.ShowModal() == wx.ID_OK:
 109             self.SetStatusText('You chose: %s\n' % dlg.GetStringSelection())
 110         dlg.Destroy()
 111 
 112 
 113     def textentry(self, event):
 114         dlg = wx.TextEntryDialog(self, 'Enter some text','Text Entry')
 115         dlg.SetValue("Default")
 116         if dlg.ShowModal() == wx.ID_OK:
 117             self.SetStatusText('You entered: %s\n' % dlg.GetValue())
 118         dlg.Destroy()
 119 
 120 #---------------------------------------------------------------------------
 121 
 122 class MyApp(wx.App):
 123     def OnInit(self):
 124         myframe = MyFrame(None, -1, "Commondialogs.py")
 125         myframe.CenterOnScreen()
 126         myframe.Show(True)
 127 
 128         return True
 129 
 130 #---------------------------------------------------------------------------
 131 
 132 app = MyApp(0)
 133 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

Figure: commondialogs.py

Core Widgets

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

wx.Button

link : https://wiki.wxpython.org/How%20to%20create%20a%20button%20%28Phoenix%29

buttons.png

Figure: buttons.py

wx.ToggleButton

Link : https://wiki.wxpython.org/How%20to%20create%20a%20toggle%20button%20%28Phoenix%29

togglebuttons.png

Figure: togglebuttons.py

wx.BitmapButton

Link : https://wiki.wxpython.org/How%20to%20create%20a%20bitmap%20button%20%28Phoenix%29

player.png

Figure: player.py

wx.StaticLine

Link : https://wiki.wxpython.org/How%20to%20create%20a%20static%20line%20%28Phoenix%29

centraleurope.png

Figure: centraleurope.py

wx.StaticText

Link : https://wiki.wxpython.org/How%20to%20create%20a%20static%20text%20%28Phoenix%29

statictext.png

Figure: statictext.py

wx.StaticBox

Link : https://wiki.wxpython.org/How%20to%20create%20a%20static%20box%20%28Phoenix%29

staticbox.png

Figure: staticbox.py

wx.ComboBox

Link : https://wiki.wxpython.org/How%20to%20create%20a%20combo%20box%20%28Phoenix%29

combobox.png

Figure: combobox.py

wx.CheckBox

Link : https://wiki.wxpython.org/How%20to%20create%20a%20check%20box%20%28Phoenix%29

checkbox.png

Figure: checkbox.py

wx.StatusBar

Link : https://wiki.wxpython.org/How%20to%20create%20a%20status%20bar%20%28Phoenix%29

statusbar.png

Figure: statusbar.py

wx.RadioButton

Link : https://wiki.wxpython.org/How%20to%20create%20a%20radio%20button%20%28Phoenix%29

radiobuttons.png

Figure: radiobuttons.py

wx.Gauge

Link : https://wiki.wxpython.org/How%20to%20create%20a%20gauge%20%28Phoenix%29

gauge.png

Figure: gauge.py

wx.Slider

Link : https://wiki.wxpython.org/How%20to%20create%20a%20slider%20%28Phoenix%29

slider.png

Figure: slider.py

wx.ListBox

Link : https://wiki.wxpython.org/How%20to%20create%20a%20list%20box%20%28Phoenix%29

listbox.png

Figure: listbox.py

wx.SpinCtrl

Link : https://wiki.wxpython.org/How%20to%20create%20a%20spin%20control%20%28Phoenix%29

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 #---------------------------------------------------------------------------
   8 
   9 class MyDialog(wx.Dialog):
  10     def __init__(self, parent, id, title):
  11         wx.Dialog.__init__(self, parent, id, title,
  12                            size=(600, 500), style=wx.DEFAULT_DIALOG_STYLE)
  13 
  14         pnl1 = wx.Panel(self, -1, style=wx.SIMPLE_BORDER)
  15         pnl2 = wx.Panel(self, -1, style=wx.SIMPLE_BORDER)
  16 
  17         self.lc = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
  18         self.lc.InsertColumn(0, 'State')
  19         self.lc.InsertColumn(1, 'Capital')
  20         self.lc.SetColumnWidth(0, 140)
  21         self.lc.SetColumnWidth(1, 153)
  22         self.lc.SetBackgroundColour("#eceade")
  23 
  24         self.tc1 = wx.TextCtrl(pnl1, -1)
  25         self.tc2 = wx.TextCtrl(pnl1, -1)
  26 
  27         #------------
  28 
  29         hbox  = wx.BoxSizer(wx.HORIZONTAL)
  30         vbox1 = wx.BoxSizer(wx.VERTICAL)
  31         vbox2 = wx.BoxSizer(wx.VERTICAL)
  32         vbox3 = wx.GridSizer(2,2,0,0)
  33         vbox4 = wx.BoxSizer(wx.VERTICAL)
  34 
  35         vbox1.Add(pnl1, 1, wx.EXPAND | wx.ALL, 3)
  36         vbox1.Add(pnl2, 1, wx.EXPAND | wx.ALL, 3)
  37 
  38         vbox2.Add(self.lc, 1, wx.EXPAND | wx.ALL, 3)
  39 
  40         vbox3.AddMany([ (wx.StaticText(pnl1, -1, 'State :'),0, wx.ALIGN_CENTER),
  41                         (self.tc1, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL),
  42                         (wx.StaticText(pnl1, -1, 'Capital :'),0, wx.ALIGN_CENTER_HORIZONTAL),
  43                         (self.tc2,0)])
  44 
  45         vbox4.Add(wx.Button(pnl2, 10, '&Add'),   0, wx.ALIGN_CENTER| wx.TOP, 45)
  46         vbox4.Add(wx.Button(pnl2, 11, '&Remove'), 0, wx.ALIGN_CENTER|wx.TOP, 15)
  47         vbox4.Add(wx.Button(pnl2, 12, '&Clear'), 0, wx.ALIGN_CENTER| wx.TOP, 15)
  48         vbox4.Add(wx.Button(pnl2, 13, '&Quit'), 0, wx.ALIGN_CENTER| wx.TOP, 15)
  49 
  50         hbox.Add(vbox1, 1, wx.EXPAND)
  51         hbox.Add(vbox2, 1, wx.EXPAND)
  52 
  53         pnl1.SetSizer(vbox3)
  54         pnl2.SetSizer(vbox4)
  55         self.SetSizer(hbox)
  56 
  57         #------------
  58 
  59         self.Bind (wx.EVT_BUTTON, self.OnAdd, id=10)
  60         self.Bind (wx.EVT_BUTTON, self.OnRemove, id=11)
  61         self.Bind (wx.EVT_BUTTON, self.OnClear, id=12)
  62         self.Bind (wx.EVT_BUTTON, self.OnClose, id=13)
  63 
  64     #-----------------------------------------------------------------------
  65 
  66     def OnAdd(self, event):
  67         if not self.tc1.GetValue() or not self.tc2.GetValue():
  68             return
  69         num_items = self.lc.GetItemCount()
  70         self.lc.InsertItem(num_items, self.tc1.GetValue())
  71         self.lc.SetItem(num_items, 1, self.tc2.GetValue())
  72         self.tc1.Clear()
  73         self.tc2.Clear()
  74 
  75 
  76     def OnRemove(self, event):
  77         index = self.lc.GetFocusedItem()
  78         self.lc.DeleteItem(index)
  79 
  80 
  81     def OnClose(self, event):
  82         self.Close()
  83 
  84 
  85     def OnClear(self, event):
  86         self.lc.DeleteAllItems()
  87 
  88 #---------------------------------------------------------------------------
  89 
  90 class MyApp(wx.App):
  91     def OnInit(self):
  92         dia = MyDialog(None, -1, 'Capitals.py')
  93         dia.ShowModal()
  94         dia.Destroy()
  95 
  96         return True
  97 
  98 #---------------------------------------------------------------------------
  99 
 100 app = MyApp(0)
 101 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 
   3 # splitterwindow.py
   4 
   5 import wx
   6 
   7 #---------------------------------------------------------------------------
   8 
   9 class MyFrame(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title,
  12                           wx.DefaultPosition, wx.Size(350, 300))
  13 
  14         splitter = wx.SplitterWindow(self, -1)
  15         splitter.SetMinimumPaneSize(20)
  16 
  17         panel1 = wx.Panel(splitter, -1)
  18         panel1.SetBackgroundColour(wx.LIGHT_GREY)
  19 
  20         wx.StaticText(panel1, -1,
  21                       "Whether you think that you can, or that you can't, you are usually right."
  22                       "\n\n Henry Ford",
  23                       (100,100), style=wx.ALIGN_CENTRE)
  24 
  25         panel2 = wx.Panel(splitter, -1)
  26         panel2.SetBackgroundColour(wx.YELLOW)
  27 
  28         splitter.SplitVertically(panel1, panel2)
  29 
  30         #------------
  31 
  32         self.Centre()
  33 
  34 #---------------------------------------------------------------------------
  35 
  36 class MyApp(wx.App):
  37     def OnInit(self):
  38         frame = MyFrame(None, -1, 'Splitterwindow.py')
  39         frame.Show(True)
  40         self.SetTopWindow(frame)
  41 
  42         return True
  43 
  44 #---------------------------------------------------------------------------
  45 
  46 app = MyApp(0)
  47 app.MainLoop()

wx.ScrolledWindow

Link : https://wiki.wxpython.org/How%20to%20create%20a%20scrolled%20window%20%28Phoenix%29

scrolledwindow.png

Figure: myscrolledwindow.py

wx.TreeCtrl

wx.TreeCtrl displays data in a hierarchy.

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

Link : https://wiki.wxpython.org/How%20to%20create%20a%20notebook%20%28Phoenix%29

notebook.png

Figure: notebook.py

wx.lib Classes

Link : https://wiki.wxpython.org/wxPython%20snippets%2C%20examples%2C%20tutorials%2C%20links...%20%28Phoenix%29

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 #---------------------------------------------------------------------------
   9 
  10 class MyMouseGestures(wx.Frame):
  11     def __init__ (self, parent, id, title):
  12         wx.Frame.__init__(self, parent, id, title, size=(600, 500))
  13 
  14         panel = wx.Panel(self, -1)
  15 
  16         mg = gest.MouseGestures(panel, True, wx.MOUSE_BTN_LEFT)
  17         mg.SetGesturePen(wx.Colour(255, 0, 0), 2)
  18         mg.SetGesturesVisible(True)
  19         mg.AddGesture('DR', self.OnDownRight)
  20 
  21     #-----------------------------------------------------------------------
  22 
  23     def OnDownRight(self):
  24           self.Close()
  25 
  26 #---------------------------------------------------------------------------
  27 
  28 class MyApp(wx.App):
  29     def OnInit(self):
  30         frame = MyMouseGestures(None, -1, "Mousegestures.py")
  31         frame.Show(True)
  32         frame.Centre()
  33 
  34         return True
  35 
  36 #---------------------------------------------------------------------------
  37 
  38 app = MyApp(0)
  39 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 #---------------------------------------------------------------------------
   9 
  10 class MyFrame(wx.Frame):
  11     def __init__(self, parent, id, title):
  12         wx.Frame.__init__(self, parent, id, title)
  13 
  14         #------------
  15 
  16         if wx.Platform == "__WXMSW__":
  17             self.SetDoubleBuffered(True)
  18 
  19         #------------
  20 
  21         clock = ac.AnalogClockWindow(self)
  22         clock.SetBackgroundColour('gray')
  23         clock.SetHandColours('black')
  24         clock.SetTickColours('WHITE')
  25         clock.SetTickSizes(h=5, m=2)
  26         clock.SetTickStyles(ac.TICKS_ROMAN)
  27 
  28         #------------
  29 
  30         self.SetSize((400,350))
  31 
  32 #---------------------------------------------------------------------------
  33 
  34 class MyApp(wx.App):
  35     def OnInit(self):
  36         frame = MyFrame(None, -1, 'Analogclock.py')
  37         frame.Centre()
  38         frame.Show(True)
  39 
  40         return True
  41 
  42 #---------------------------------------------------------------------------
  43 
  44 app = MyApp(0)
  45 app.MainLoop()

Various clock styles:

SetClockStyle()

Various ticks styles:

SetTickStyles()

analogclock.png

Figure: analogclock.py

Bitmap Text Buttons

Link : https://wiki.wxpython.org/How%20to%20create%20a%20gen%20bitmap%20text%20button%20%28Phoenix%29

genbitmaptextbutton.png

Figure: genbitmaptextbutton.py

Advanced Widgets

In this section we will discuss some advanced widgets. They are found in lib subdirectory which is in wx directory. These widgets are written in python. They address needs not covered by the underlying C++ wx library.

CalendarCtrl

CalendarCtrl is a handy widget for working with dates.

   1 #!/usr/bin/python
   2 
   3 # calendar.py
   4 
   5 import wx
   6 import wx.adv
   7 from wx.adv import CalendarCtrl, GenericCalendarCtrl, CalendarDateAttr
   8 
   9 #---------------------------------------------------------------------------
  10 
  11 class Calendar(wx.Dialog):
  12     def __init__(self, parent, id, title):
  13         wx.Dialog.__init__(self, parent, id, title)
  14 
  15         # calend = CalendarCtrl(self, -1, wx.DateTime().Today(),
  16         #                       style=wx.adv.CAL_SHOW_HOLIDAYS |
  17         #                             wx.adv.CAL_SEQUENTIAL_MONTH_SELECTION)
  18         # Or
  19         #
  20         calend = GenericCalendarCtrl(self, -1, wx.DateTime().Today(),
  21                                      style=wx.adv.CAL_SHOW_HOLIDAYS |
  22                                            wx.adv.CAL_SUNDAY_FIRST |
  23                                            wx.adv.CAL_SEQUENTIAL_MONTH_SELECTION)
  24 
  25         #------------
  26 
  27         self.text = wx.StaticText(self, -1, '&Date')
  28         btn = wx.Button(self, -1, '&Ok')
  29 
  30         #------------
  31 
  32         vbox = wx.BoxSizer(wx.VERTICAL)
  33         hbox = wx.BoxSizer(wx.HORIZONTAL)
  34         hbox2 = wx.BoxSizer(wx.HORIZONTAL)
  35 
  36         hbox.Add(self.text)
  37 
  38         hbox2.Add(btn, 1)
  39 
  40         vbox.Add(calend, 0, wx.EXPAND | wx.ALL, 20)
  41         vbox.Add((-1, 20))
  42         vbox.Add(hbox, 0,  wx.LEFT, 8)
  43         vbox.Add(hbox2, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 20)
  44 
  45         self.SetSizerAndFit(vbox)
  46 
  47         #------------
  48 
  49         self.Bind(wx.adv.EVT_CALENDAR, self.OnCalSelected, id=calend.GetId())
  50         self.Bind(wx.EVT_BUTTON, self.OnQuit, id=btn.GetId())
  51         self.Bind(wx.EVT_CLOSE, self.OnQuit)
  52 
  53         #------------
  54 
  55         self.Centre()
  56 
  57         #------------
  58 
  59         self.Show(True)
  60 
  61     #-----------------------------------------------------------------------
  62 
  63     def OnCalSelected(self, event):
  64         date = event.GetDate()
  65         dt = str(date).split(' ')
  66         s = ' '.join(str(s) for s in dt)
  67         self.text.SetLabel(s)
  68 
  69 
  70     def OnQuit(self, event):
  71         self.Destroy()
  72 
  73 #---------------------------------------------------------------------------
  74 
  75 app = wx.App()
  76 Calendar(None, -1, 'Calendar.py')
  77 app.MainLoop()

Possible calendar styles:

Listed below.

calendar.png

Figure: calendar.py

LEDNumberCtrl

In the previous example we had an analog clock example.LEDNumberCtrl widget displays a digital clock.

In our example we show current local time in a simple wx.Frame.

   1 #!/usr/bin/python
   2 
   3 # lednumber.py
   4 
   5 import time
   6 import wx
   7 import wx.gizmos as gizmos
   8 
   9 #---------------------------------------------------------------------------
  10 
  11 class MyFrame(wx.Frame):
  12     def __init__(self, parent, id, title):
  13         wx.Frame.__init__(self, parent, id, title,
  14                           wx.DefaultPosition, wx.Size(450, 100))
  15 
  16         #------------
  17 
  18         if wx.Platform == "__WXMSW__":
  19             self.SetDoubleBuffered(True)
  20 
  21         #------------
  22 
  23         self.led = gizmos.LEDNumberCtrl(self, -1, wx.DefaultPosition,
  24                                         wx.DefaultSize, gizmos.LED_ALIGN_CENTER)
  25 
  26         self.OnTimer(None)
  27         self.timer = wx.Timer(self, -1)
  28         self.timer.Start(1000)
  29         self.Bind(wx.EVT_TIMER, self.OnTimer)
  30 
  31         #------------
  32 
  33         self.Centre()
  34 
  35     #-----------------------------------------------------------------------
  36 
  37     def OnTimer(self, event):
  38         t = time.localtime(time.time())
  39         st = time.strftime("%I:%M:%S", t)
  40         self.led.SetValue(st)
  41 
  42 #---------------------------------------------------------------------------
  43 
  44 class MyApp(wx.App):
  45     def OnInit(self):
  46         frame = MyFrame(None, -1, 'Lednumber.py')
  47         frame.Show(True)
  48         self.SetTopWindow(frame)
  49 
  50         return True
  51 
  52 #---------------------------------------------------------------------------
  53 
  54 app = MyApp(0)
  55 app.MainLoop()

Possible flags:

Windows users, putwx.CallAfter() in your code:

self.led = gizmos.LEDNumberCtrl(self, -1, style=gizmos.LED_ALIGN_CENTER)
wx.CallAfter(self.OnTimer, None)
self.timer = wx.Timer(self, -1)

lednumber.png

Figure: lednumber.py

Creating a taskbar application

Link : https://wiki.wxpython.org/How%20to%20create%20a%20task%20bar%20icon%20%28Phoenix%29

taskbaricon.png

Figure: mytaskbaricon.py

wx.TheClipboard

From wikipedia:

In computing, the clipboard is a portion of memory where information that has been copied or cut from a computer application is stored. This storage is meant as a short-term volatile place to keep information that will be used again shortly.

wx.TheClipboard is a class for manipulating clipboard in wxPython. Fist we must call the Open() method to get ownership of the clipboard. If successful, the method returns true. Then we put data on the clipboard with the

SetData() method. This method accepts a simple data object.

We have three predefined simple data objects:

To retrieve data from Clipboard you call methodGetData(). It accepts simple data object as well. In the end we close the clipboard with Close() method and relinquish ownership of it.

clipboard.py example shows a simple usage of the the clipboard in wxPython.

   1 #!/usr/bin/python
   2 
   3 # clipboard.py
   4 
   5 import wx
   6 
   7 #---------------------------------------------------------------------------
   8 
   9 class MyFrame(wx.Frame):
  10     def __init__(self, parent, id, title):
  11         wx.Frame.__init__(self, parent, id, title,
  12                           wx.DefaultPosition, wx.Size(320, 300))
  13 
  14         panel1 = wx.Panel(self, -1)
  15 
  16         self.tc1 = wx.TextCtrl(panel1, -1, '', (50,50), (85, -1))
  17         self.tc2 = wx.TextCtrl(panel1, -1, '', (180,50), (85, -1))
  18 
  19         wx.Button(panel1, 1, '&Copy', (50,200))
  20         wx.Button(panel1, 2, '&Paste', (180,200))
  21 
  22         #------------
  23 
  24         self.Bind(wx.EVT_BUTTON, self.OnCopy, id=1)
  25         self.Bind(wx.EVT_BUTTON, self.OnPaste, id=2)
  26 
  27         #------------
  28 
  29         self.Centre()
  30 
  31     #-----------------------------------------------------------------------
  32 
  33     def OnCopy(self, event):
  34         text = self.tc1.GetValue()
  35         if wx.TheClipboard.Open():
  36             wx.TheClipboard.Clear()
  37             wx.TheClipboard.SetData(wx.TextDataObject(text))
  38             wx.TheClipboard.Close()
  39 
  40 
  41     def OnPaste(self, event):
  42         if wx.TheClipboard.Open():
  43             td = wx.TextDataObject()
  44             success = wx.TheClipboard.GetData(td)
  45             wx.TheClipboard.Close()
  46             if not success: return
  47             text = td.GetText()
  48             if text: self.tc2.SetValue(text)
  49 
  50 #---------------------------------------------------------------------------
  51 
  52 class MyApp(wx.App):
  53     def OnInit(self):
  54         frame = MyFrame(None, -1, 'Clipboard.py')
  55         frame.Show(True)
  56         self.SetTopWindow(frame)
  57 
  58         return True
  59 
  60 #---------------------------------------------------------------------------
  61 
  62 app = MyApp(0)
  63 app.MainLoop()

We have two textcontrols and two buttons. We input some text into the first textcontrol and paste in the the second one with our two buttons. Notice how we retrieve data from thewxTextDataObject:

text = td.GetText()

Drag and Drop

Wikipedia:

In computer graphical user interfaces, drag-and-drop is the action of (or support for the action of) clicking on a virtual object and dragging it to a different location or onto another virtual object. In general, it can be used to invoke many kinds of actions, or create various types of associations between two abstract objects. I think everyone is familiar with drag

& drop. Here is an example in wxPython:

   1 #!/usr/bin/python
   2 
   3 # dragdrop.py
   4 
   5 import os
   6 import wx
   7 
   8 #---------------------------------------------------------------------------
   9 
  10 class MyTextDropTarget(wx.TextDropTarget):
  11     def __init__(self, object):
  12         wx.TextDropTarget.__init__(self)
  13 
  14         self.object = object
  15 
  16     #-----------------------------------------------------------------------
  17 
  18     def OnDropText(self, x, y, data):
  19         self.object.InsertItem(0, data)
  20 
  21         return True
  22 
  23 #---------------------------------------------------------------------------
  24 
  25 class MyFrame(wx.Frame):
  26     def __init__(self, parent, id, title):
  27         wx.Frame.__init__(self, parent, id, title,
  28                           wx.DefaultPosition, wx.Size(600, 450))
  29 
  30         splitter1 = wx.SplitterWindow(self, -1, style=wx.SP_3D)
  31         splitter1.SetMinimumPaneSize(20)
  32 
  33         splitter2 = wx.SplitterWindow(splitter1, -1, style=wx.SP_3D)
  34         splitter2.SetMinimumPaneSize(20)
  35 
  36         #------------
  37 
  38         if wx.Platform == "__WXGTK__":
  39             self.dir = wx.GenericDirCtrl(splitter1, -1, dir='/home/', style=wx.DIRCTRL_DIR_ONLY)
  40 
  41         elif wx.Platform == "__WXMSW__":
  42             self.dir = wx.GenericDirCtrl(splitter1, -1, dir='C:/', style=wx.DIRCTRL_DIR_ONLY)
  43 
  44         #------------
  45 
  46         self.lc1 = wx.ListCtrl(splitter2, -1, style=wx.LC_LIST)
  47         self.lc2 = wx.ListCtrl(splitter2, -1, style=wx.LC_LIST)
  48 
  49         #------------
  50 
  51         dt = MyTextDropTarget(self.lc2)
  52         self.lc2.SetDropTarget(dt)
  53 
  54         #------------
  55 
  56         tree = self.dir.GetTreeCtrl()
  57 
  58         #------------
  59 
  60         splitter2.SplitHorizontally(self.lc1, self.lc2)
  61         splitter1.SplitVertically(self.dir, splitter2)
  62 
  63         #------------
  64 
  65         self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnDragInit, id=self.lc1.GetId())
  66         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelect, id=tree.GetId())
  67 
  68         #------------
  69 
  70         self.OnSelect(0)
  71 
  72         #------------
  73 
  74         self.Centre()
  75 
  76     #-----------------------------------------------------------------------
  77 
  78     def OnSelect(self, event):
  79         list = os.listdir(self.dir.GetPath())
  80         print("List dir :", list)
  81         self.lc1.ClearAll()
  82         self.lc2.ClearAll()
  83 
  84         for i in range(len(list)):
  85             if list[i][0] != '.':
  86                 self.lc1.InsertItem(0, list[i])
  87 
  88 
  89     def OnDragInit(self, event):
  90         text = self.lc1.GetItemText(event.GetIndex())
  91         tdo = wx.TextDataObject(text)
  92         tds = wx.DropSource(self.lc1)
  93         tds.SetData(tdo)
  94         tds.DoDragDrop(True)
  95 
  96 #---------------------------------------------------------------------------
  97 
  98 class MyApp(wx.App):
  99     def OnInit(self):
 100         frame = MyFrame(None, -1, "Dragdrop.py")
 101         frame.Show(True)
 102         self.SetTopWindow(frame)
 103 
 104         return True
 105 
 106 #---------------------------------------------------------------------------
 107 
 108 app = MyApp(0)
 109 app.MainLoop()

dragdrop.png

Figure: dragdrop.py

One of the advantages of the GUI over the console is its intuitiveness. You can learn a GUI program easier than a console application. You often do not need a manual. On the other hand, some graphical operations are too complex. For example, deleting a file by dragging it and droping it to the trash basket is very intuitive and easy to understand, but actually most people just press the delete key. (shift + delete) It is more effective. In our next example we explore a graphical operation, that is very handy. In most GUI text editors, you can open a file by simply dragging it from the file manager and dropping it on the editor.

   1 #!/usr/bin/python
   2 
   3 # filedrop.py
   4 
   5 import wx
   6 
   7 #---------------------------------------------------------------------------
   8 
   9 class FileDrop(wx.FileDropTarget):
  10     def __init__(self, window):
  11         wx.FileDropTarget.__init__(self)
  12 
  13         self.window = window
  14 
  15     #-----------------------------------------------------------------------
  16 
  17     def OnDropFiles(self, x, y, filenames):
  18         for name in filenames:
  19             try:
  20                 file = open(name, 'r', encoding='utf-8')
  21                 text = file.read()
  22                 self.window.WriteText(text)
  23                 file.close()
  24             except IOError as error:
  25                 dlg = wx.MessageDialog(None, 'Error opening file\n' + str(error))
  26                 dlg.ShowModal()
  27             except UnicodeDecodeError as error:
  28                 dlg = wx.MessageDialog(None, 'Cannot open non ascii files\n' + str(error))
  29                 dlg.ShowModal()
  30 
  31         return True
  32 
  33 #---------------------------------------------------------------------------
  34 
  35 class MyFrame(wx.Frame):
  36     def __init__(self, parent, id, title):
  37         wx.Frame.__init__(self, parent, id, title,
  38                           wx.DefaultPosition, wx.Size(450, 400))
  39 
  40         self.text = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE)
  41         dt = FileDrop(self.text)
  42         self.text.SetDropTarget(dt)
  43 
  44         self.Centre()
  45 
  46 #---------------------------------------------------------------------------
  47 
  48 class MyApp(wx.App):
  49     def OnInit(self):
  50         frame = MyFrame(None, -1, '')
  51         frame.Show(True)
  52         self.SetTopWindow(frame)
  53 
  54         return True
  55 
  56 #---------------------------------------------------------------------------
  57 
  58 app = MyApp(0)
  59 app.MainLoop()

Plotting

Link : https://wiki.wxpython.org/How%20to%20use%20Plot%20-%20Part%203%20(Phoenix)

scattergraph.png linegraph.png bargraph.png

Figures: scatter, line, bar graphs

Configuring application settings

Many applications allow users to configure their settings. Users can toggle tooltips on and of, change fonts, default download paths etc. Mostly they have a menu option called preferences. Application settings are saved to the hard disk, so that users do not have to change the settings each time the application starts.

In wxPython we have wx.Config class to do our job.

On Linux, settings are stored in a simple hidden file. This file is located in the home user directory by default. The location of the configuration file can be changed. The name of the file is specified in the constructor of the wx.Config class.

Various wx.Config methods

string Read(string key, string defaultVal='')

return a string value of a key if it exists, defaultVal otherwise

int ReadInt(string key, int defaultVal=0)

return an integer value of the key if it exists, defaultVal otherwise

float ReadFloat(string key, float defaultVal=0.0)

return a float value of a key if it exists, defaultVal otherwise

bool ReadBool(string key, bool defaultVal=False)

return a boolean value of a key if it exists, defaultVal otherwise

bool Write(string key, string value)

write a string value, return True on success

bool WriteInt(string key, int value)

write an integer value, return True on success

bool WriteFloat(string key, float value)

write a float value, return True on success

bool WriteBool(string key, bool value)

write a boolean value, return True on success

bool Exists(string value)

return True if a key with a given name exists

In the following code example, we can cofigure the size of the window. If there is no configuration file, the height and the width of the window is set to the defaul 250 px value. We can set these values to a range from 200 - 500px. After we save our values and restart the application, the window size is set to our preffered values.

   1 #!/usr/bin/python
   2 
   3 # myconfig.py
   4 
   5 import wx
   6 
   7 #---------------------------------------------------------------------------
   8 
   9 class MyFrame(wx.Frame):
  10     def __init__(self, parent, id, title):
  11 
  12         self.cfg = wx.Config('myconfig')
  13 
  14         if self.cfg.Exists('width'):
  15             w, h = self.cfg.ReadInt('width'), self.cfg.ReadInt('height')
  16         else:
  17             (w, h) = (250, 250)
  18 
  19         wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(w, h))
  20 
  21         #------------
  22 
  23         wx.StaticText(self, -1, 'Width :', (20, 20))
  24         wx.StaticText(self, -1, 'Height :', (20, 70))
  25 
  26         self.sc1 = wx.SpinCtrl(self, -1, str(w), (80, 15), (60, -1), min=200, max=500)
  27         self.sc2 = wx.SpinCtrl(self, -1, str(h), (80, 65), (60, -1), min=200, max=500)
  28 
  29         wx.Button(self, 1, '&Save', (20, 120))
  30 
  31         #------------
  32 
  33         self.Bind(wx.EVT_BUTTON, self.OnSave, id=1)
  34 
  35         #------------
  36 
  37         self.statusbar = self.CreateStatusBar()
  38 
  39         #------------
  40 
  41         self.Centre()
  42 
  43     #-----------------------------------------------------------------------
  44 
  45     def OnSave(self, event):
  46         self.cfg.WriteInt("width", self.sc1.GetValue())
  47         self.cfg.WriteInt("height", self.sc2.GetValue())
  48         self.statusbar.SetStatusText('Configuration saved, %s ' % wx.Now())
  49 
  50 #---------------------------------------------------------------------------
  51 
  52 class MyApp(wx.App):
  53     def OnInit(self):
  54         frame = MyFrame(None, -1, 'Myconfig.py')
  55         frame.Show(True)
  56         self.SetTopWindow(frame)
  57 
  58         return True
  59 
  60 #---------------------------------------------------------------------------
  61 
  62 app = MyApp(0)
  63 app.MainLoop()

Here we have the contents of a configuration file to our code example . It consists of two key, value pairs.

$ cat .myconfig
height=220
width=340

myconfig.png

Figure: myconfig.py

wxPython functions

wxPython provides several useful functions. We can implement them easily in our programs. Technically, these functions are module functions. e.g defined in a module scope. They resemble what we know as static methods in C++ or Java.

System functions

System functions

wx.Bell()

beep a sound

wx.GetDisplaySize()

return the current display mode

wx.GetEmailAddress()

return user's email address

wx.GetFullHostName()

return full host name

wx.GetHomeDir()

return the user's home direstory

wx.GetOsVersion()

return Os version

wx.GetOsDescription()

return a full Os description

wx.GetFreeMemory()

not working

wx.GetMousePosition()

return mouse position

wx.GetProcessId()

return a process id of our application

wx.GetUserName()

returns a user's name

wx.GetUTCTime()

return number of seconds

wx.Now()

return current timestamp

wx.Shell(string command)

execute a command in a shell

Dialog functions

These functions create dialogs. They do some common tasks.

Dialog functions

wx.GetTextFromUser()

get a text from user

wx.DirSelector()

select a directory

wx.FileSelector()

select a file

wx.GetNumberFromUser()

get a long number from user

wx.GetSingleChoice()

something

wx.GetSingleChoiceIndex()

something

wx.GetSingleChoiceIndex()

something

wx.LoadFileSelector()

load a file

wx.MessageBox()

show a message dialog

wx.SaveFileSelector()

saves a file

string wx.GetTextFromUser(string message, string caption='', string default_value='', wx.Window parent=None)
string wx.DirSelector(string message=wx.DirSelectorPromptStr, string defaultPath='', style=wx.DD_DEFAULT_STYLE,
                      wx.Point pos=wx.DefaultPosition, wx.Window parent=None)
string wx.FileSelector(string message=wx.FileSelectorPromptStr, string default_path='', string default_filename='',
                       string default_extension='', string wildcard=wx.FileSelectorDefaultWildcardStr,
                       integer flags=0, wx.Window parent=None, integer x=-1, integer y=-1)
long wx.GetNumberFromUser(string message, string prompt, string caption, long value, long min=0, long max=100,
                          wx.Window parent=None, wx.Point pos=wx.DefaultPosition)
string wx.GetSingleChoice(string message, string caption, integer choices, string choices_array,
                          wx.Window parent=None, integer x=-1, integer y=-1, bool centre=True, integer width=150,
                          integer height=200)
integer wx.GetSingleChoiceIndex(string message, string caption, integer choices, string choices_array,
                                wx.Window parent=None, integer x=-1, integer y=-1, bool centre=True,
                                integer width=150, integer height=200)
string wx.LoadFileSelector(string what, string extension, string default_name='', wx.Window parent=None)
integer wx.MessageBox(string message, string caption='', style=wx.OK | wx.CENTRE, wx.Window parent=None,
                      integer x=-1, integer y=-1)
string wx.SaveFileSelector(string what, string extension, string default_name='', wx.Window parent=None)

Other functions

Here is a list of various other functions

Other functions

wx.Exit()

exit application

bool wx.Yield()

yield to other applications or messages

bool wx.YieldIfNeeded()

something

bool SafeYield(wx.Window window=None, bool onlyIfNeeded=False)

something

wx.WakeUpIdle()

empty the message queue

wx.PostEvent(wx.EvtHandler dest, wx.Event event)

wx.EventHandler to be processed later

PyApp wx.GetApp()

return a reference to the current wx.App object

wx.SetDefaultPyEncoding(string encoding)

set the current encoding for working with wx.String

string wx.GetDefaultPyEncoding()

get the current encoding for working with wx.String

Using xml resource files

The idea behind xml resources is to separate the interface from the code of an application. Several GUI builders use this concept for creating interfaces. For example the famous Glade.

In our example we create a simple frame window with one button. We load resources from a file, load a panel and bind an event to a button.

   1 #!/usr/bin/python
   2 
   3 # xml.py
   4 
   5 import  wx
   6 import  wx.xrc  as  xrc
   7 
   8 #---------------------------------------------------------------------------
   9 
  10 class Xml(wx.Frame):
  11     def __init__(self, parent, id, title):
  12         wx.Frame.__init__(self, parent, id, title)
  13 
  14         res = xrc.XmlResource('resource.xrc')
  15         res.LoadPanel(self, 'MyPanel')
  16 
  17         self.Bind(wx.EVT_BUTTON, self.OnClose,  id=xrc.XRCID('CloseButton'))
  18 
  19         #------------
  20 
  21         self.Center()
  22 
  23         #------------
  24 
  25         self.Show(True)
  26 
  27     #-----------------------------------------------------------------------
  28 
  29     def OnClose(self, event):
  30         self.Close()
  31 
  32 #---------------------------------------------------------------------------
  33 
  34 app = wx.App()
  35 Xml(None, -1, 'xml.py')
  36 app.MainLoop()

This is resource file "resource.xrc". It is a xml file, where we define our widgets and their patterns. In this file, we use tags like<object></object>, <item></item> etc.

<?xml version="1.0" ?>
<resource>
  <object class="wxPanel" name="MyPanel">
   <object class="wxButton" name="CloseButton">
    <label>Close</label>
    <pos>150,100</pos>
   </object>
  </object>
</resource>

We use these two calls for working with widgets:

Skeletons

In this section, we will create some application skeletons. Our scripts will work out the interface but will not implement the functionality. The goal is to show, how several well known GUI interfaces could be done in wxPython. Most manuals, tutorials and books show only the basic usage of a widget. When I was a beginner, I always wondered how this or this could be done. And I think, many newbies think the same.

File Hunter

File Hunter is a skeleton of a file manager. It copies the lookout of the Krusader, the best file manager available on Unix systems.

   1 #!/usr/bin/python
   2 
   3 # filemanager.py
   4 
   5 """
   6 ZetCode wxPython tutorial
   7 
   8 This program creates a skeleton
   9 of a file manager UI.
  10 
  11 author: Jan Bodnar
  12 website: zetcode.com
  13 last edited: May 2018
  14 """
  15 
  16 import wx
  17 import os
  18 import time
  19 
  20 ID_BUTTON=100
  21 ID_EXIT=200
  22 ID_SPLITTER=300
  23 
  24 #---------------------------------------------------------------------------
  25 
  26 class MyListCtrl(wx.ListCtrl):
  27     def __init__(self, parent):
  28         wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT)
  29 
  30         images = ['images/empty.png', 'images/folder.png', 'images/source-py.png',
  31                   'images/image.png', 'images/pdf.png', 'images/up16.png']
  32 
  33         self.InsertColumn(0, 'Name')
  34         self.InsertColumn(1, 'Ext')
  35         self.InsertColumn(2, 'Size', wx.LIST_FORMAT_RIGHT)
  36         self.InsertColumn(3, 'Modified')
  37 
  38         self.SetColumnWidth(0, 120)
  39         self.SetColumnWidth(1, 35)
  40         self.SetColumnWidth(2, 60)
  41         self.SetColumnWidth(3, 420)
  42 
  43         self.il = wx.ImageList(16, 16)
  44 
  45         for i in images:
  46 
  47             self.il.Add(wx.Bitmap(i))
  48 
  49         self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
  50 
  51         j = 1
  52 
  53         self.InsertItem(0, '..')
  54         self.SetItemImage(0, 5)
  55 
  56         #------------
  57 
  58         files = os.listdir('.')
  59 
  60         for i in files:
  61             (name, ext) = os.path.splitext(i)
  62             ex = ext[1:]
  63             size = os.path.getsize(i)
  64             sec = os.path.getmtime(i)
  65 
  66             self.InsertItem(j, name)
  67             self.SetItem(j, 1, ex)
  68             self.SetItem(j, 2, str(size) + ' B')
  69             self.SetItem(j, 3, time.strftime('%Y-%m-%d %H:%M', time.localtime(sec)))
  70 
  71             if os.path.isdir(i):
  72                 self.SetItemImage(j, 1)
  73             elif ex == 'py':
  74                 self.SetItemImage(j, 2)
  75             elif ex == 'jpg':
  76                 self.SetItemImage(j, 3)
  77             elif ex == 'pdf':
  78                 self.SetItemImage(j, 4)
  79             else:
  80                 self.SetItemImage(j, 0)
  81 
  82             if (j % 2) == 0:
  83 
  84                 self.SetItemBackgroundColour(j, '#e6f1f5')
  85 
  86             j = j + 1
  87 
  88 #---------------------------------------------------------------------------
  89 
  90 class Example(wx.Frame):
  91     def __init__(self, *args, **kw):
  92         super(Example, self).__init__(*args, **kw)
  93 
  94         self.InitUI()
  95 
  96     #-----------------------------------------------------------------------
  97 
  98     def InitUI(self):
  99         self.SetTitle("File Hunter")
 100         self.SetMinSize((760, 360))
 101 
 102         #------------
 103 
 104         self.splitter = wx.SplitterWindow(self, ID_SPLITTER, style=wx.SP_BORDER)
 105         self.splitter.SetMinimumPaneSize(50)
 106 
 107         p1 = MyListCtrl(self.splitter)
 108         p2 = MyListCtrl(self.splitter)
 109 
 110         self.splitter.SplitVertically(p1, p2)
 111 
 112         #------------
 113 
 114         filemenu= wx.Menu()
 115         filemenu.Append(ID_EXIT, "E&xit"," Terminate the program")
 116         editmenu = wx.Menu()
 117         netmenu = wx.Menu()
 118         showmenu = wx.Menu()
 119         configmenu = wx.Menu()
 120         helpmenu = wx.Menu()
 121 
 122         menuBar = wx.MenuBar()
 123         menuBar.Append(filemenu, "&File")
 124         menuBar.Append(editmenu, "&Edit")
 125         menuBar.Append(netmenu, "&Net")
 126         menuBar.Append(showmenu, "&Show")
 127         menuBar.Append(configmenu, "&Config")
 128         menuBar.Append(helpmenu, "&Help")
 129         self.SetMenuBar(menuBar)
 130 
 131         #------------
 132 
 133         tb = self.CreateToolBar( wx.TB_HORIZONTAL | wx.NO_BORDER |
 134                       wx.TB_FLAT)
 135 
 136         tb.AddTool(10, 'Previous', wx.Bitmap('images/previous.png'), shortHelp='Previous')
 137         tb.AddTool(20, 'Up', wx.Bitmap('images/up.png'), shortHelp='Up one directory')
 138         tb.AddTool(30, 'Home', wx.Bitmap('images/home.png'), shortHelp='Home')
 139         tb.AddTool(40, 'Refresh', wx.Bitmap('images/refresh.png'), shortHelp='Refresh')
 140         tb.AddSeparator()
 141         tb.AddTool(50, 'Edit text', wx.Bitmap('images/textedit.png'), shortHelp='Edit text')
 142         tb.AddTool(60, 'Terminal', wx.Bitmap('images/terminal.png'), shortHelp='Terminal')
 143         tb.AddSeparator()
 144         tb.AddTool(70, 'Help', wx.Bitmap('images/help.png'), shortHelp='Show help')
 145         tb.Realize()
 146 
 147         #------------
 148 
 149         button1 = wx.Button(self, ID_BUTTON + 1, "F3 View")
 150         button2 = wx.Button(self, ID_BUTTON + 2, "F4 Edit")
 151         button3 = wx.Button(self, ID_BUTTON + 3, "F5 Copy")
 152         button4 = wx.Button(self, ID_BUTTON + 4, "F6 Move")
 153         button5 = wx.Button(self, ID_BUTTON + 5, "F7 Mkdir")
 154         button6 = wx.Button(self, ID_BUTTON + 6, "F8 Delete")
 155         button7 = wx.Button(self, ID_BUTTON + 7, "F9 Rename")
 156         button8 = wx.Button(self, ID_EXIT, "F10 Quit")
 157 
 158         #------------
 159 
 160         self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
 161         self.sizer = wx.BoxSizer(wx.VERTICAL)
 162 
 163         self.sizer2.Add(button1, 1, wx.EXPAND)
 164         self.sizer2.Add(button2, 1, wx.EXPAND)
 165         self.sizer2.Add(button3, 1, wx.EXPAND)
 166         self.sizer2.Add(button4, 1, wx.EXPAND)
 167         self.sizer2.Add(button5, 1, wx.EXPAND)
 168         self.sizer2.Add(button6, 1, wx.EXPAND)
 169         self.sizer2.Add(button7, 1, wx.EXPAND)
 170         self.sizer2.Add(button8, 1, wx.EXPAND)
 171 
 172         self.sizer.Add(self.splitter,1,wx.EXPAND)
 173         self.sizer.Add(self.sizer2,0,wx.EXPAND)
 174 
 175         self.SetSizer(self.sizer)
 176 
 177         # size = wx.DisplaySize()
 178         # self.SetSize(size)
 179 
 180         #------------
 181 
 182         self.Bind(wx.EVT_SIZE, self.OnSize)
 183         self.Bind(wx.EVT_SPLITTER_DCLICK, self.OnDoubleClick, id=ID_SPLITTER)
 184         self.Bind(wx.EVT_MENU, self.OnExit, id=ID_EXIT)
 185         self.Bind(wx.EVT_BUTTON, self.OnExit, id=ID_EXIT)
 186 
 187         #------------
 188 
 189         sb = self.CreateStatusBar()
 190         sb.SetStatusText(os.getcwd())
 191 
 192         #------------
 193 
 194         self.Center()
 195 
 196 
 197     def OnExit(self, event):
 198         self.Close(True)
 199 
 200 
 201     def OnSize(self, event):
 202         size = self.GetSize()
 203         self.splitter.SetSashPosition(int(size.x / 2))
 204 
 205         event.Skip()
 206 
 207 
 208     def OnDoubleClick(self, event):
 209         size = self.GetSize()
 210         self.splitter.SetSashPosition(int(size.x / 2))
 211 
 212 #---------------------------------------------------------------------------
 213 
 214 def main():
 215 
 216     app = wx.App()
 217     ex = Example(None)
 218     ex.Show()
 219     app.MainLoop()
 220 
 221 #---------------------------------------------------------------------------
 222 
 223 if __name__ == '__main__':
 224     main()

If you double click on the splitter widget, it will divide the File Hunter into two parts with the same width. The same happens, if you resize the main window.

filehunter.png

Figure: filemanager.py

SpreadSheet

Gnumeric, KSpread andOpenOffice Calc are famous spreadsheet applications available on Unix. The following example shows a skeleton of a spreadsheet application in wxPython.

   1 #!/usr/bin/python
   2 
   3 # spreadsheet.py
   4 
   5 
   6 """
   7 ZetCode wxPython tutorial
   8 
   9 This program creates a SpreadSheet UI.
  10 
  11 author: Jan Bodnar
  12 website: zetcode.com
  13 last edited: May 2018
  14 """
  15 
  16 from wx.lib import sheet
  17 import wx
  18 
  19 #---------------------------------------------------------------------------
  20 
  21 class MySheet(wx.grid.Grid):
  22     def __init__(self, *args, **kw):
  23         super(MySheet, self).__init__(*args, **kw)
  24 
  25         self.InitUI()
  26 
  27     #-----------------------------------------------------------------------
  28 
  29     def InitUI(self):
  30         nOfRows = 55
  31         nOfCols = 25
  32 
  33         self.row = self.col = 0
  34         self.CreateGrid(nOfRows, nOfCols)
  35 
  36         self.SetColLabelSize(20)
  37         self.SetRowLabelSize(50)
  38 
  39         self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.OnGridSelectCell)
  40 
  41         for i in range(nOfRows):
  42             self.SetRowSize(i, 20)
  43 
  44         for i in range(nOfCols):
  45             self.SetColSize(i, 75)
  46 
  47 
  48     def OnGridSelectCell(self, e):
  49         self.row, self.col = e.GetRow(), e.GetCol()
  50 
  51         control = self.GetParent().GetParent().position
  52         value =  self.GetColLabelValue(self.col) + self.GetRowLabelValue(self.row)
  53         control.SetValue(value)
  54 
  55         e.Skip()
  56 
  57 #---------------------------------------------------------------------------
  58 
  59 class Example(wx.Frame):
  60     def __init__(self, *args, **kw):
  61         super(Example, self).__init__(*args, **kw)
  62 
  63         self.InitUI()
  64 
  65     #-----------------------------------------------------------------------
  66 
  67     def InitUI(self):
  68         fonts = ['Times New Roman', 'Times', 'Courier', 'Courier New', 'Helvetica',
  69                 'Sans', 'verdana', 'utkal', 'aakar', 'Arial']
  70         font_sizes = ['10', '11', '12', '14', '16']
  71 
  72         #------------
  73 
  74         menuBar = wx.MenuBar()
  75 
  76         menu1 = wx.Menu()
  77         menuBar.Append(menu1, '&File')
  78         menu2 = wx.Menu()
  79         menuBar.Append(menu2, '&Edit')
  80         menu3 = wx.Menu()
  81         menuBar.Append(menu3, '&Edit')
  82         menu4 = wx.Menu()
  83         menuBar.Append(menu4, '&Insert')
  84         menu5 = wx.Menu()
  85         menuBar.Append(menu5, 'F&ormat')
  86         menu6 = wx.Menu()
  87         menuBar.Append(menu6, '&Tools')
  88         menu7 = wx.Menu()
  89         menuBar.Append(menu7, '&Data')
  90         menu8 = wx.Menu()
  91         menuBar.Append(menu8, '&Help')
  92 
  93         self.SetMenuBar(menuBar)
  94 
  95         #------------
  96 
  97         toolbar1 = wx.ToolBar(self, style= wx.TB_HORIZONTAL)
  98 
  99         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/new.png'))
 100         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/open.png'))
 101         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/save.png'))
 102 
 103         toolbar1.AddSeparator()
 104 
 105         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/cut.png'))
 106         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/copy.png'))
 107         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/paste.png'))
 108         toolbar1.AddTool(wx.ID_ANY, '',  wx.Bitmap('images/delete.png'))
 109 
 110         toolbar1.AddSeparator()
 111 
 112         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/undo.png'))
 113         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/redo.png'))
 114 
 115         toolbar1.AddSeparator()
 116 
 117         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/asc.png'))
 118         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/desc.png'))
 119 
 120         toolbar1.AddSeparator()
 121         toolbar1.AddTool(wx.ID_ANY, '', wx.Bitmap('images/chart.png'))
 122 
 123         toolbar1.AddSeparator()
 124         toolbar1.AddTool(wx.ID_ANY, '',  wx.Bitmap('images/exit.png'))
 125 
 126         toolbar1.Realize()
 127 
 128         toolbar2 = wx.ToolBar(self, wx.TB_HORIZONTAL | wx.TB_TEXT)
 129 
 130         self.position = wx.TextCtrl(toolbar2)
 131 
 132         font = wx.ComboBox(toolbar2, value='Times', choices=fonts, size=(100, -1),
 133                 style=wx.CB_DROPDOWN)
 134 
 135         font_height = wx.ComboBox(toolbar2, value='10', choices=font_sizes,
 136                 size=(50, -1), style=wx.CB_DROPDOWN)
 137 
 138         toolbar2.AddControl(self.position)
 139         toolbar2.AddControl(font)
 140         toolbar2.AddControl(font_height)
 141 
 142         toolbar2.AddSeparator()
 143 
 144         toolbar2.AddCheckTool(wx.ID_ANY, '', wx.Bitmap('images/text-bold.png'))
 145         toolbar2.AddCheckTool(wx.ID_ANY, '', wx.Bitmap('images/text-italic.png'))
 146         toolbar2.AddCheckTool(wx.ID_ANY, '', wx.Bitmap('images/text-underline.png'))
 147 
 148         toolbar2.AddSeparator()
 149 
 150         toolbar2.AddTool(wx.ID_ANY, '', wx.Bitmap('images/align-left.png'))
 151         toolbar2.AddTool(wx.ID_ANY, '', wx.Bitmap('images/align-center.png'))
 152         toolbar2.AddTool(wx.ID_ANY, '', wx.Bitmap('images/align-right.png'))
 153 
 154         toolbar2.Realize()
 155 
 156         #------------
 157 
 158         notebook = wx.Notebook(self, style=wx.RIGHT)
 159 
 160         sheet1 = MySheet(notebook)
 161         sheet2 = MySheet(notebook)
 162         sheet3 = MySheet(notebook)
 163         sheet1.SetFocus()
 164 
 165         notebook.AddPage(sheet1, 'Sheet1')
 166         notebook.AddPage(sheet2, 'Sheet2')
 167         notebook.AddPage(sheet3, 'Sheet3')
 168 
 169         #------------
 170 
 171         box = wx.BoxSizer(wx.VERTICAL)
 172 
 173         box.Add(toolbar1, border=5)
 174         box.Add((5,5) , 0)
 175         box.Add(toolbar2)
 176         box.Add((5,10) , 0)
 177         box.Add(notebook, 1, wx.EXPAND)
 178 
 179         self.SetSizer(box)
 180 
 181         #------------
 182 
 183         self.CreateStatusBar()
 184 
 185         #------------
 186 
 187         self.SetSize((550, 550))
 188         self.SetTitle("SpreadSheet")
 189 
 190         #------------
 191 
 192         self.Centre()
 193 
 194 #---------------------------------------------------------------------------
 195 
 196 def main():
 197 
 198     app = wx.App()
 199     ex = Example(None)
 200     ex.Show()
 201     app.MainLoop()
 202 
 203 #---------------------------------------------------------------------------
 204 
 205 if __name__ == '__main__':
 206     main()

spreadsheet.png

Figure: spreadsheet.py

Tips And Tricks

In this section we will show some tips in wxPython.

PopupMenu

The following code was committed by Chris Barker on the wxPython-users mailing list. Popup menu is here implemented in a separate class. This way, you don't have to manually check, if the events were already bound.

   1 #!/usr/bin/env python
   2 
   3 # popup.py
   4 
   5 import wx
   6 
   7 app = wx.App()
   8 
   9 #---------------------------------------------------------------------------
  10 
  11 class MyPopupMenu(wx.Menu):
  12     def __init__(self, WinName):
  13         wx.Menu.__init__(self)
  14 
  15         self.WinName = WinName
  16 
  17         #------------
  18 
  19         item1 = wx.MenuItem(self, -1, "Item One")
  20         self.Append(item1)
  21 
  22         item2 = wx.MenuItem(self, -1,"Item Two")
  23         self.Append(item2)
  24 
  25         item3 = wx.MenuItem(self, -1,"Item Three")
  26         self.Append(item3)
  27 
  28         #------------
  29 
  30         self.Bind(wx.EVT_MENU, self.OnItem1, item1)
  31         self.Bind(wx.EVT_MENU, self.OnItem2, item2)
  32         self.Bind(wx.EVT_MENU, self.OnItem3, item3)
  33 
  34     #-----------------------------------------------------------------------
  35 
  36     def OnItem1(self, event):
  37         print("Item One selected in the %s window" % self.WinName)
  38 
  39 
  40     def OnItem2(self, event):
  41         print("Item Two selected in the %s window" % self.WinName)
  42 
  43 
  44     def OnItem3(self, event):
  45         print("Item Three selected in the %s window" % self.WinName)
  46 
  47 #---------------------------------------------------------------------------
  48 
  49 class MyWindow(wx.Window):
  50     def __init__(self, parent, color):
  51         wx.Window.__init__(self, parent, -1)
  52 
  53         self.color = color
  54 
  55         self.SetBackgroundColour(color)
  56 
  57         self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
  58 
  59     #-----------------------------------------------------------------------
  60 
  61     def OnRightDown(self,event):
  62         self.PopupMenu(MyPopupMenu(self.color), event.GetPosition())
  63 
  64 #---------------------------------------------------------------------------
  65 
  66 class MyFrame(wx.Frame):
  67     def __init__(self):
  68         wx.Frame.__init__(self,None, -1, "Popup.py", size=(300, 200))
  69 
  70         sizer = wx.GridSizer(2,2,5,5)
  71         sizer.Add(MyWindow(self,"blue"),1,wx.GROW)
  72         sizer.Add(MyWindow(self,"yellow"),1,wx.GROW)
  73         sizer.Add(MyWindow(self,"red"),1,wx.GROW)
  74         sizer.Add(MyWindow(self,"green"),1,wx.GROW)
  75         self.SetSizer(sizer)
  76 
  77         self.Show()
  78 
  79 #---------------------------------------------------------------------------
  80 
  81 frame = MyFrame()
  82 app.SetTopWindow(frame)
  83 app.MainLoop()

The example is just a single frame. This frame is divided into four windows. If you right click on the frame, context menu pops up. Context menu consists of three commands. If you select any of them a message is sent to the console. It will say what item you selected plus the color of the window where you clicked with the mouse. This example shows the power of the object oriented programming. Imagine you would have to do it the other way, by calculating manually the position of the pointer!

Notice that the popup menu is implemented as a new class. This is a more elegant way of using popup menus as the following one, taken from the Demo application.

 def OnRightClick(self, event):
        # only do this part the first time so the events are only bound once
        if not hasattr(self, "popupID1"):
            self.popupID1 = wx.NewId()
            self.popupID2 = wx.NewId()
            self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1)
            self.Bind(wx.EVT_MENU, self.OnPopupTwo, id=self.popupID2)
        menu = wx.Menu()
        menu.Append(self.popupID1, "One")
        menu.Append(self.popupID2, "Two")
        self.PopupMenu(menu, event.GetPosition())
        menu.Destroy()
    def OnPopupOne(self, event):
        pass
    def OnPopupTwo(self, event):
        pass

There is one subtle thing in the code that needs some clarification. You only boud an event once. It resides in event table afterwards. If it is done in the constructor, everything is ok. But when you bound an event to a method in a method, you do it everytime when the method gets invoked. That's why we wrote a condition to ensure, we don't implement this overhead.

 if not hasattr(self, "popupID1"):
            self.popupID1 = wx.NewId()
            self.popupID2 = wx.NewId()
            self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1)
            self.Bind(wx.EVT_MENU, self.OnPopupTwo, id=self.popupID2)

The tiniest wxPython application

Feel free to contact me, if you can shorthen it. Even for one single character.

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

One-liner wxPython application

This is functionally equivalent to the tiniest wxPython application given above.

   1 #!/usr/bin/python
   2 
   3 # onelinerapp.py
   4 
   5 from wx import App, Frame
   6 
   7 #---------------------------------------------------------------------------
   8 
   9 type('', (App, object), {'OnInit': lambda self: Frame(None).Show()})().MainLoop()

Interactive Button

This tip shows how to program an interactive Button. This button reacts to users actions. In our case, the button changes its background colour. When you enter the area of the button widget with a mouse pointer,

EVT_ENTER_WINDOW event is generated. Simirarly, EVT_LEAVE_WINDOW event is generated, when you leave the area of the widget. So all you have to do is to bind those events to functions, that will change the colour/shape of the button widget appropriately.

   1 #!/usr/bin/python
   2 
   3 # interactivebutton.py
   4 
   5 import wx
   6 import wx.lib.buttons  as  buttons
   7 
   8 #---------------------------------------------------------------------------
   9 
  10 class MyPanel(wx.Panel):
  11     def __init__(self, parent, id):
  12         wx.Panel.__init__(self, parent, -1, wx.DefaultPosition)
  13 
  14         # Plain old text button based off GenButton().
  15         self.btn = buttons.GenButton(self, -1, "Button",
  16                                      pos=wx.Point(125, 100),
  17                                      size=(-1, -1))
  18         self.btn.SetFont(wx.Font(10, wx.FONTFAMILY_SWISS,
  19                                  wx.FONTSTYLE_NORMAL,
  20                                  wx.FONTWEIGHT_BOLD, False))
  21         self.btn.SetBezelWidth(1)
  22         self.btn.SetBackgroundColour(wx.LIGHT_GREY)
  23         self.btn.SetForegroundColour(wx.BLACK)
  24         self.btn.SetToolTip("This is a GenButton...")
  25 
  26         #------------
  27 
  28         self.btn.Bind(wx.EVT_ENTER_WINDOW, self.OnEnterButton)
  29         self.btn.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveButton)
  30 
  31     #-----------------------------------------------------------------------
  32 
  33     def OnEnterButton(self, event):
  34         self.btn.SetBackgroundColour(wx.Colour(128, 128, 128))
  35         self.btn.Refresh()
  36 
  37 
  38     def OnLeaveButton(self, event):
  39         self.btn.SetBackgroundColour(wx.Colour(212, 208, 200))
  40         self.btn.Refresh()
  41 
  42 #---------------------------------------------------------------------------
  43 
  44 class MyApp(wx.App):
  45     def OnInit(self):
  46         frame = wx.Frame(None, -1, "Interactivebutton.py",
  47                          wx.DefaultPosition, wx.Size(350, 300))
  48         mypanel = MyPanel(frame, -1)
  49         frame.Centre()
  50         frame.Show(True)
  51         self.SetTopWindow(frame)
  52 
  53         return True
  54 
  55 #---------------------------------------------------------------------------
  56 
  57 app = MyApp(0)
  58 app.MainLoop()

I have usedwx.GenButton instead of basic wx.Button. wx.GenButton enables to change border settigs, which I find attractive. But wx.Button would work as well.

Error handling without dialogs

Link : https://wiki.wxpython.org/How%20to%20create%20an%20alternative%20error%20messages%20(Phoenix)

isabelle.png

Figure: isabelle.py

UndoRedoFramework

Many applications have the ability to undo and redo the user's actions. The following example shows how it can be accomplished in wxPython.

   1 #!/usr/bin/python
   2 
   3 # undoredo.py
   4 
   5 """
   6 ZetCode wxPython tutorial
   7 
   8 In this example, we create two horizontal
   9 toolbars.
  10 
  11 author: Jan Bodnar
  12 website: www.zetcode.com
  13 last modified: April 2018
  14 """
  15 
  16 import wx
  17 
  18 #---------------------------------------------------------------------------
  19 
  20 class Example(wx.Frame):
  21     def __init__(self, *args, **kwargs):
  22         super(Example, self).__init__(*args, **kwargs)
  23 
  24         self.InitUI()
  25 
  26     #-----------------------------------------------------------------------
  27 
  28     def InitUI(self):
  29 
  30         self.count = 5
  31 
  32         #------------
  33 
  34         self.toolbar = self.CreateToolBar()
  35         tundo = self.toolbar.AddTool(wx.ID_UNDO, '', wx.Bitmap('tundo.png'))
  36         tredo = self.toolbar.AddTool(wx.ID_REDO, '', wx.Bitmap('tredo.png'))
  37         self.toolbar.EnableTool(wx.ID_REDO, False)
  38         self.toolbar.AddSeparator()
  39         texit = self.toolbar.AddTool(wx.ID_EXIT, '', wx.Bitmap('texit.png'))
  40         self.toolbar.Realize()
  41 
  42         #------------
  43 
  44         self.Bind(wx.EVT_TOOL, self.OnQuit, texit)
  45         self.Bind(wx.EVT_TOOL, self.OnUndo, tundo)
  46         self.Bind(wx.EVT_TOOL, self.OnRedo, tredo)
  47 
  48         #------------
  49 
  50         self.SetSize((350, 250))
  51         self.SetTitle('Undoredo.py')
  52 
  53         #------------
  54 
  55         self.Centre()
  56 
  57 
  58     def OnUndo(self, event):
  59         if self.count > 1 and self.count <= 5:
  60             self.count = self.count - 1
  61 
  62         if self.count == 1:
  63             self.toolbar.EnableTool(wx.ID_UNDO, False)
  64 
  65         if self.count == 4:
  66             self.toolbar.EnableTool(wx.ID_REDO, True)
  67 
  68 
  69     def OnRedo(self, event):
  70         if self.count < 5 and self.count >= 1:
  71             self.count = self.count + 1
  72 
  73         if self.count == 5:
  74             self.toolbar.EnableTool(wx.ID_REDO, False)
  75 
  76         if self.count == 2:
  77             self.toolbar.EnableTool(wx.ID_UNDO, True)
  78 
  79 
  80     def OnQuit(self, event):
  81         self.Close()
  82 
  83 #---------------------------------------------------------------------------
  84 
  85 def main():
  86     app = wx.App()
  87     ex = Example(None)
  88     ex.Show()
  89     app.MainLoop()
  90 
  91 #---------------------------------------------------------------------------
  92 
  93 if __name__ == '__main__':
  94     main()

In our example, we want to undo and redo cell text changes and column and row size changes. So we must bind relevant events to our methods. These bindings are not visible in our sample code. You can find them in sheet.py file, in CSheet class:

self.Bind(wx.grid.EVT_GRID_ROW_SIZE, self.OnRowSize)
self.Bind(wx.grid.EVT_GRID_COL_SIZE, self.OnColSize)
self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnCellChange)

In these three methods we create Undo objects. These objects areUndoText, UndoColSize and UndoRowSize. Each object has two methods. undo() and redo(). They are responsible for bringing to the state of the application before the change was done and vice versa. The objects are then appended to stockUndo list. This way we ensure, that all necessary changes are stored. Finally, when we press undo, redo buttons, we call OnUndo() and OnRedo() methods. The following method calls actually do the job:

a.undo()
a.redo()

The objects move between stockUndo and stockRedo lists accordingly. Also when there are no objects left, we disable a button with theEnableTool() method.

newt.png

Figure: newt.py

Gripts

In this section we will show some small, complete scripts. These graphical scripts or "gripts" will demonstrate various areas in programming. Programming in Python, wxPython is easier than in most other toolkits. But it is still a laborious task. There is a long, long way from easy scripts to professional applications.

Tom

Each application should have a good name. Short and easily remembered. So, we have Tom. A simple gript that sends an email.

   1 #!/usr/bin/python
   2 
   3 # Tom.py
   4 
   5 import wx
   6 import smtplib
   7 
   8 #---------------------------------------------------------------------------
   9 
  10 class Tom(wx.Dialog):
  11     def __init__(self, parent, id, title):
  12         wx.Dialog.__init__(self, parent, id, title,
  13                            wx.DefaultPosition, wx.Size(400, 420))
  14 
  15         panel = wx.Panel(self, -1)
  16 
  17         st1 = wx.StaticText(panel, -1, 'From : ')
  18         st2 = wx.StaticText(panel, -1, 'To : ')
  19         st3 = wx.StaticText(panel, -1, 'Subject : ')
  20         self.tc1 = wx.TextCtrl(panel, -1, size=(180, -1))
  21         self.tc2 = wx.TextCtrl(panel, -1, size=(180, -1))
  22         self.tc3 = wx.TextCtrl(panel, -1, size=(180, -1))
  23         self.write = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE)
  24         button_send = wx.Button(panel, 1, '&Send')
  25 
  26         #------------
  27 
  28         vbox = wx.BoxSizer(wx.VERTICAL)
  29         hbox1 = wx.BoxSizer(wx.HORIZONTAL)
  30         hbox2 = wx.BoxSizer(wx.HORIZONTAL)
  31         hbox3 = wx.BoxSizer(wx.HORIZONTAL)
  32 
  33         hbox1.Add(st1, 0, wx.LEFT, 10)
  34         hbox1.Add(self.tc1, 0, wx.LEFT, 20)
  35 
  36         hbox2.Add(st2, 0, wx.LEFT, 10)
  37         hbox2.Add(self.tc2, 0, wx.LEFT, 35)
  38 
  39         hbox3.Add(st3, 0, wx.LEFT, 10)
  40         hbox3.Add(self.tc3, 0, wx.LEFT, 9)
  41 
  42         vbox.Add(hbox1, 0, wx.TOP, 10)
  43         vbox.Add(hbox2, 0, wx.TOP, 10)
  44         vbox.Add(hbox3, 0, wx.TOP, 10)
  45         vbox.Add(self.write, 1, wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, 15)
  46         vbox.Add(button_send, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 20)
  47 
  48         panel.SetSizer(vbox)
  49 
  50         #------------
  51 
  52         self.Bind(wx.EVT_BUTTON, self.OnSend, id=1)
  53 
  54         #------------
  55 
  56         self.Centre()
  57 
  58     #-----------------------------------------------------------------------
  59 
  60     def OnSend(self, event):
  61         sender = self.tc1.GetValue()
  62         recipient = self.tc2.GetValue()
  63         subject = self.tc3.GetValue()
  64         text = self.write.GetValue()
  65         header = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n' % (sender, recipient, subject)
  66         message = header + text
  67 
  68         try:
  69             server = smtplib.SMTP('mail.chello.sk')
  70             server.sendmail(sender, recipient, message)
  71             server.quit()
  72             dlg = wx.MessageDialog(self, 'Email was successfully sent',
  73                                    'Success', wx.OK | wx.ICON_INFORMATION)
  74             dlg.ShowModal()
  75             dlg.Destroy()
  76 
  77         except smtplib.SMTPException as error:
  78             dlg = wx.MessageDialog(self, 'Failed to send email',
  79                                    'Error', wx.OK | wx.ICON_ERROR)
  80             dlg.ShowModal()
  81             dlg.Destroy()
  82 
  83 #---------------------------------------------------------------------------
  84 
  85 class MyApp(wx.App):
  86     def OnInit(self):
  87         frame = Tom(None, -1, 'Tom.py')
  88         frame.ShowModal()
  89         frame.Destroy()
  90 
  91         return True
  92 
  93 #---------------------------------------------------------------------------
  94 
  95 app = MyApp(0)
  96 app.MainLoop()

For working with emails we need to import smtp module.

import smtplib

From, To and Subject options must be separated by carriedge return and newline as shown here. This weird thing is requested by RFC 821 norm. So we must follow it.

header = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n' % (sender, recipient, subject)

Next we create an SMTP connection. Here you specify your settings. Each ISP gives you the name of the pop and smtp servers. In my case, 'mail.chello.sk' is a name for both. A mail is sent by calling the sendmail() method. Finally, we quit the connection with the quit() method.

server = smtplib.SMTP('mail.chello.sk')
server.sendmail(sender, recipient, message)
server.quit()

tom.png

Figure: tom.py

Editor

Link : https://wiki.wxpython.org/How%20to%20create%20a%20simple%20text%20editor%20(Phoenix)#Sample_three

editor.png

Figure: editor.py

Kika

Kika is a gript that connects to an ftp site. If a login is successfull, Kika shows a connected icon on the statusbar. Otherwise, a disconnected icon is displayed. We use an ftplib module from the python standard library. If you do not have an ftp account, you can try to login to some anonymous ftp sites.

   1 #!/usr/bin/python
   2 
   3 # kika.py
   4 
   5 from ftplib import FTP, all_errors
   6 import wx
   7 
   8 #---------------------------------------------------------------------------
   9 
  10 class MyStatusBar(wx.StatusBar):
  11     def __init__(self, parent):
  12         wx.StatusBar.__init__(self, parent)
  13 
  14         self.SetFieldsCount(2)
  15         self.SetStatusText('Welcome to Kika', 0)
  16         self.SetStatusWidths([-5, -2])
  17 
  18         #------------
  19 
  20         self.icon = wx.StaticBitmap(self, -1, wx.Bitmap('icons/disconnected.png'))
  21 
  22         #------------
  23 
  24         self.Bind(wx.EVT_SIZE, self.OnSize)
  25 
  26         #------------
  27 
  28         self.PlaceIcon()
  29 
  30     #-----------------------------------------------------------------------
  31 
  32     def PlaceIcon(self):
  33         rect = self.GetFieldRect(1)
  34         self.icon.SetPosition((rect.x+3, rect.y+3))
  35 
  36 
  37     def OnSize(self, event):
  38         self.PlaceIcon()
  39 
  40 #---------------------------------------------------------------------------
  41 
  42 class MyFrame(wx.Frame):
  43     def __init__(self, parent, id, title):
  44         wx.Frame.__init__(self, parent, id, title, size=(270, 270))
  45 
  46         self.ftp = None
  47 
  48         #------------
  49 
  50         self.SetMinSize((270, 270))
  51 
  52         #------------
  53 
  54         wx.StaticText(self, -1, 'Ftp site', (10, 20))
  55         wx.StaticText(self, -1, 'Login', (10, 60))
  56         wx.StaticText(self, -1, 'Password', (10, 100))
  57 
  58         self.ftpsite = wx.TextCtrl(self, -1, '',  (110, 15), (120, -1))
  59         self.login = wx.TextCtrl(self, -1, '',  (110, 55), (120, -1))
  60         self.password = wx.TextCtrl(self, -1, '',  (110, 95), (120, -1), style=wx.TE_PASSWORD)
  61 
  62         con = wx.Button(self, 1, 'Connect', (10, 160))
  63         discon = wx.Button(self, 2, 'DisConnect', (120, 160))
  64 
  65         #------------
  66 
  67         self.Bind(wx.EVT_BUTTON, self.OnConnect, id=1)
  68         self.Bind(wx.EVT_BUTTON, self.OnDisConnect, id=2)
  69 
  70         #------------
  71 
  72         self.statusbar = MyStatusBar(self)
  73         self.SetStatusBar(self.statusbar)
  74 
  75         #------------
  76 
  77         self.Centre()
  78 
  79     #-----------------------------------------------------------------------
  80 
  81     def OnConnect(self, event):
  82         if not self.ftp:
  83             ftpsite = self.ftpsite.GetValue()
  84             login = self.login.GetValue()
  85             password = self.password.GetValue()
  86 
  87             try:
  88                 self.ftp = FTP(ftpsite)
  89                 var = self.ftp.login(login, password)
  90                 self.statusbar.SetStatusText('User connected')
  91                 self.statusbar.icon.SetBitmap(wx.Bitmap('icons/connected.png'))
  92 
  93             except AttributeError:
  94                 self.statusbar.SetForegroundColour(wx.RED)
  95                 self.statusbar.SetStatusText('Incorrect params')
  96                 self.ftp = None
  97 
  98             except all_errors as err:
  99                 self.statusbar.SetStatusText(str(err))
 100                 self.ftp = None
 101 
 102 
 103     def OnDisConnect(self, event):
 104         if self.ftp:
 105             self.ftp.quit()
 106             self.ftp = None
 107             self.statusbar.SetStatusText('User disconnected')
 108             self.statusbar.icon.SetBitmap(wx.Bitmap('icons/disconnected.png'))
 109 
 110 #---------------------------------------------------------------------------
 111 
 112 class MyApp(wx.App):
 113     def OnInit(self):
 114         frame = MyFrame(None, -1, 'Kika.py')
 115         frame.Show(True)
 116         return True
 117 
 118 #---------------------------------------------------------------------------
 119 
 120 app = MyApp(0)
 121 app.MainLoop()

Notice that each time the window is resized, we must position our icon to a new place.

def PlaceIcon(self):
    rect = self.GetFieldRect(1)
    self.icon.SetPosition((rect.x+3, rect.y+3))

kika.png

Figure: kika.py

Appendix

Cursor IDs

wx.CURSOR_ARROW

wx.CURSOR_RIGHT_ARROW

wx.CURSOR_BLANK

wx.CURSOR_BULLSEYE

wx.CURSOR_CHAR

wx.CURSOR_CROSS

wx.CURSOR_HAND

wx.CURSOR_IBEAM

wx.CURSOR_LEFT_BUTTON

wx.CURSOR_MAGNIFIER

wx.CURSOR_MIDDLE_BUTTON

wx.CURSOR_NO_ENTRY

wx.CURSOR_PAINT_BRUSH

wx.CURSOR_PENCIL

wx.CURSOR_POINT_LEFT

wx.CURSOR_POINT_RIGHT

wx.CURSOR_QUESTION_ARROW

wx.CURSOR_RIGHT_BUTTON

wx.CURSOR_SIZENESW

wx.CURSOR_SIZENS

wx.CURSOR_SIZENWSE

wx.CURSOR_SIZEWE

wx.CURSOR_SIZING

wx.CURSOR_SPRAYCAN

wx.CURSOR_WAIT

wx.CURSOR_WATCH

wx.CURSOR_ARROWWAIT

wx.Frame styles

wx.DEFAULT_FRAME_STYLE

wx.ICONIZE

wx.FRAME_SHAPED

wx.CAPTION

wx.MINIMIZE

wx.MINIMIZE_BOX

wx.MAXIMIZE

wx.MAXIMIZE_BOX

wx.STAY_ON_TOP

wx.SYSTEM_MENU

wx.SIMPLE_BORDER

wx.RESIZE_BORDER

wx.FRAME_TOOL_WINDOW

wx.FRAME_NO_TASKBAR

wx.FRAME_FLOAT_ON_PARENT

wx.FRAME_EX_CONTEXTHELP

Standard Colour Database

AQUAMARINE

BLACK

BLUE

BLUE VIOLET

BROWN

CADET BLUE

CORAL

CORNFLOWER BLUE

CYAN

DARK GREY

DARK GREEN

DARK OLIVE GREEN

DARK ORCHID

DARK SLATE BLUE

DARK SLATE GREY

DARK TURQUOISE

DIM GREY

FIREBRICK

FOREST GREEN

GOLD

GOLDENROD

GREY

GREEN

GREEN YELLOW

INDIAN RED

KHAKI

LIGHT BLUE

LIGHT GREY

LIGHT STEEL BLUE

LIME GREEN

MAGENTA

MAROON

MEDIUM AQUAMARINE

MEDIUM BLUE

MEDIUM FOREST GREEN

MEDIUM GOLDENROD

MEDIUM ORCHID

MEDIUM SEA GREEN

MEDIUM SLATE BLUE

MEDIUM SPRING GREEN

MEDIUM TURQUOISE

MEDIUM VIOLET RED

MIDNIGHT BLUE

NAVY

ORANGE

ORANGE RED

ORCHID

PALE GREEN

PINK

PLUM

PURPLE

RED

SALMON

SEA GREEN

SIENNA

SKY BLUE

SLATE BLUE

SPRING GREEN

STEEL BLUE

TAN

THISTLE

TURQUOISE

VIOLET

VIOLET RED

WHEAT

WHITE

YELLOW

YELLOW GREEN

wx.Pen styles

wx.SOLID

wx.TRANSPARENT

wx.DOT

wx.LONG_DASH

wx.SHORT_DASH

wx.DOT_DASH

wx.STIPPLE

wx.USER_DASH

wx.BDIAGONAL_HATCH

wx.CROSSDIAG_HATCH

wx.FDIAGONAL_HATCH

wx.CROSS_HATCH

wx.HORIZONTAL_HATCH

wx.VERTICAL_HATCH

wx.Brush styles

wx.BLUE_BRUSH

wx.GREEN_BRUSH

wx.WHITE_BRUSH

wx.BLACK_BRUSH

wx.GREY_BRUSH

wx.MEDIUM_GREY_BRUSH

wx.LIGHT_GREY_BRUSH

wx.TRANSPARENT_BRUSH

wx.CYAN_BRUSH

wx.RED_BRUSH

CalendarCtrl styles

CAL_SUNDAY_FIRST

CAL_MONDAY_FIRST

CAL_SHOW_HOLIDAYS

CAL_NO_YEAR_CHANGE

CAL_NO_MONTH_CHANGE

CAL_SEQUENTIAL_MONTH_SELECTION

CAL_SHOW_SURROUNDING_WEEKS

CAL_HITTEST_NOWHERE

CAL_HITTEST_HEADER

CAL_HITTEST_DAY

CAL_HITTEST_INCMONTH

CAL_HITTEST_DECMONTH

CAL_HITTEST_SURROUNDING_WEEK

CAL_BORDER_NONE

CAL_BORDER_SQUARE

CAL_BORDER_ROUND

Keycodes

WXK_BACK

WXK_TAB

WXK_RETURN

WXK_ESCAPE

WXK_SPACE

WXK_DELETE

WXK_START

WXK_LBUTTON

WXK_RBUTTON

WXK_CANCEL

WXK_MBUTTON

WXK_CLEAR

WXK_SHIFT

WXK_CONTROL

WXK_MENU

WXK_PAUSE

WXK_CAPITAL

WXK_PRIOR

WXK_NEXT

WXK_END

WXK_HOME

WXK_LEFT

WXK_UP

WXK_RIGHT

WXK_DOWN

WXK_SELECT

WXK_PRINT

WXK_EXECUTE

WXK_SNAPSHOT

WXK_INSERT

WXK_HELP

WXK_NUMPAD0

WXK_NUMPAD1

WXK_NUMPAD2

WXK_NUMPAD3

WXK_NUMPAD4

WXK_NUMPAD5

WXK_NUMPAD6

WXK_NUMPAD7

WXK_NUMPAD8

WXK_NUMPAD9

WXK_MULTIPLY

WXK_ADD

WXK_SEPARATOR

WXK_SUBTRACT

WXK_DECIMAL

WXK_DIVIDE

WXK_F1

WXK_F2

WXK_F3

WXK_F4

WXK_F5

WXK_F6

WXK_F7

WXK_F8

WXK_F9

WXK_F10

WXK_F11

WXK_F12

WXK_F13

WXK_F14

WXK_F15

WXK_F16

WXK_F17

WXK_F18

WXK_F19

WXK_F20

WXK_F21

WXK_F22

WXK_F23

WXK_F24

WXK_NUMLOCK

WXK_SCROLL

WXK_PAGEUP

WXK_PAGEDOWN

WXK_NUMPAD_SPACE

WXK_NUMPAD_TAB

WXK_NUMPAD_ENTER

WXK_NUMPAD_F1

WXK_NUMPAD_F2

WXK_NUMPAD_F3

WXK_NUMPAD_F4

WXK_NUMPAD_HOME

WXK_NUMPAD_LEFT

WXK_NUMPAD_UP

WXK_NUMPAD_RIGHT

WXK_NUMPAD_DOWN

WXK_NUMPAD_PRIOR

WXK_NUMPAD_PAGEUP

WXK_NUMPAD_NEXT

WXK_NUMPAD_PAGEDOWN

WXK_NUMPAD_END

WXK_NUMPAD_BEGIN

WXK_NUMPAD_INSERT

WXK_NUMPAD_DELETE

WXK_NUMPAD_EQUAL

WXK_NUMPAD_MULTIPLY

WXK_NUMPAD_ADD

WXK_NUMPAD_SEPARATOR

WXK_NUMPAD_SUBTRACT

WXK_NUMPAD_DECIMAL

WXK_NUMPAD_DIVIDE

Comments...

Date (d/m/y) Person (bot) Comments :

??/04/05 - Jan Bodnar / ZetCode (Created page for wxPython).

??/??/?? - Adil Hasan (I've added a little example extending the wx.TreeCtrl example to create a window with a tree and moveable splitter window at AnotherTutorialTreeCtrlComment).

03/11/20 - Ecco (Updated examples for wxPython Phoenix (only scripts)).

Another tutorial (Phoenix) (last edited 2020-12-31 16:56:00 by Ecco)

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