The wxPython Linux Tutorial
Table of Contents:
Contents
-
The wxPython Linux Tutorial
- Foreword
- wxPython API
- First Steps
- Layout Management
- Basic Objects
- Events
- Dialogs
- Core Widgets
- wx.lib Classes
- Advanced Widgets
- Creating a taskbar application
- wx.TheClipboard
- Drag and Drop
- Plotting
- Configuring application settings
- wxPython functions
- Using xml resource files
- Skeletons
- Tips And Tricks
- Gripts
- Appendix
- Comments...
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.
- mailing list
- reference book
- source code of wxPython applications
- /home/vronskij/bin/wxPython/usr/lib/python2.4/site-packages/wx-2.6-gtk2-unicode/wx - the ultimate resource, on my Linux box
There are three decent toolkits for the python programming language:
wxPython
PyQt
PyGTK
Note that this tutorial is done on Linux. Some scripts do not work correctly on windows.
Icons used in this tutorial: icons.tgz Images used in this tutorial: images.tgz
jan bodnar 2005 - 2007
status update. (april 2007) All my work on wxPython tutorial has been moved to my website http://www.zetcode.com/wxpython here I shall not add any more examples. If I find myself some time, I will do some polishing.
wxPython API
wxPython API is a set of functions and widgets. Widgets are essential building blocks of a GUI application. Under Windows widgets are calles controls. We can roughly divide programmers into two groups. They code applications or libraries. In our case, wxPython is a library that is used by application programmers to code applications. Technically, wxPython is a wrapper over a C++ GUI API called wxWidgets. So it is not a native API. e.g. not written directly in Python. The only native GUI library for an interpreted language that I know is Java's Swing library.
In wxPython we have lot's of widgets. These can be divided into some logical groups.
Base Widgets
These widgets provide basic functionality for derived widgets. They are usually not used directly.
wx.Window
wx.Control
wx.ControlWithItem
Top level Widgets
These widgets exist independently of each other.
wx.Frame
wx.MDIParentFrame
wx.MDIChildFrame
wx.Dialog
wx.PopupWindow
Containers
Containers contain other widgets. These widgets are called children.
wx.Panel
wx.Notebook
wx.ScrolledWindow
wx.SplitterWindow
Dynamic Widgets
These widgets can be edited by users.
wx.Button
wx.BitmapButton
wx.Choice
wx.ComboBox
wx.CheckBox
wx.Grid
wx.ListBox
wx.RadioBox
wx.RadioButton
wx.ScrollBar
wx.SpinButton
wx.SpinCtrl
wx.Slider
wx.TextCtrl
wx.ToggleButton
Static Widgets
These widgets display informatin. They cannot be edited by user.
wx.Gauge
wx.StaticText
wx.StaticBitmap
wx.StaticLine
wx.StaticBox
Other Widgets
These widgets implement statusbar, toolbar and menubar in an application.
wx.MenuBar
wx.ToolBar
wx.StatusBar
First Steps
We start with a simple example.
1 #!/usr/bin/python
2
3 # simple.py
4
5 import wx
6
7 app = wx.App()
8
9 frame = wx.Frame(None, -1, 'simple.py')
10 frame.Show()
11
12 app.MainLoop()
In every wxPython application, we must import the wx library.
import wx
An application object is created by initiating class wx.App.
app = wx.App()
We create a frame widget. The window pops up only if we call Show() method on a widget.
frame = wx.Frame(None, -1, "simple.py") frame.Show()
The last line enters a Mainloop. A mainloop is an endless cycle that catches up all events coming up to your application. It is an integral part of any windows GUI application.
app.MainLoop()
Although the code is very simple, you can do a lot of things with your window. You can maximize it, minimize it, move it, resize it. All these things have been already done.
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 it's methods.
SetTitle(string title) - Sets the window's title. Applicable only to frames and dialogs.
SetToolTip(wx.ToolTip tip) - Attaches a tooltip to the window.
SetSize(wx.Size size) - Sets the size of the window.
SetPosition(wx.Point pos) - Positions the window to given coordinates
Show(show=True) - Shows or hides the window. show parameter can be True or False.
Move(wx.Point pos) - Moves the window to the given position.
SetCursor(wx.StockCursor id) - Sets the window's cursor.
1 #!/usr/bin/python
2
3 # simple2.py
4
5 import wx
6
7 app = wx.App()
8
9 frame = wx.Frame(None, -1, '')
10 frame.SetToolTip(wx.ToolTip('This is a frame'))
11 frame.SetCursor(wx.StockCursor(wx.CURSOR_MAGNIFIER))
12 frame.SetPosition(wx.Point(0,0))
13 frame.SetSize(wx.Size(300,250))
14 frame.SetTitle('simple2.py')
15 frame.Show()
16
17 app.MainLoop()
We create a 'This is a frame' tooltip. The cursor is set to a magnifier cursor. Possible cursor id's: are listed below We position the window to the upper left corner and size it to 300x250 pixels. Title is set to 'simple2.py'.
wx.Frame
wx.Frame is a container widget. It means that it can contain other widgets. It has the following constructor:
wx.Frame(wx.Window parent, id, string title,
wx.Point pos = wx.DefaultPosition, wx.Size size = wx.DefaultSize,
style = wx.DEFAULT_FRAME_STYLE, string name = 'frame')A constructor is a special kind of a function. It is called when an object is created. For us it is only important that when we want to create a new widget, we simply call it's constructor. Python enables parameters with default values. So the only obligatory parameters in wx.Frame are parent, id and title. If you specify all values of the parameters in order, you don't need to specify the parameter names. For example you want to create a wx.Frame widget, which has no parent, it's identifier is 100, the title is 'Title', the position is (100,50) and the size is (100,100).
frame = wx.Frame(None, 100, 'Title', wx.Point(100,50), wx.Size(100,100))
Here we have omitted the pos parameter. So we must provide explicitly the size parameter.
frame = wx.Frame(None, 100, 'Title', size = wx.Size(100,100))
In the following example we will use other useful features.
1 #!/usr/bin/python
2
3 # icon.py
4
5 import wx
6
7 def main():
8 app = wx.App()
9
10 frame = wx.Frame(None, title='Icon', pos=(350,300))
11 frame.SetIcon(wx.Icon('tipi.ico', wx.BITMAP_TYPE_ICO))
12 frame.Center()
13 frame.Show()
14 app.MainLoop()
15
16 if __name__ == '__main__':
17 main()
Icon's name is Tipi.ico. The icon is located in current directory. First parameter of an icon's constructor is the file name. Second parameter specifies the file type.
As you have noticed, the structure of our application has changed. This is a standard in Python programs. In Python programs name is a special variable. More complicated programs consist of several files. There is usually only one file, which launches the application. For this file, Python sets the name variable to 'main'. This happens when you launch an application from the command line or when you click on the file. So when you click on the icon.py file or you launch it from the command line, the name variable equals main. Then function main() is called.
Figure: icon.py
wx.MenuBar
To set up a menubar in your wxPython application is pretty simple. We will discuss adding menus to menubar, adding submenus to existing menus. Each menu consists of menuitems. Menuitems can be normal items, check items or radio items.
First thing to do is to create a menubar.
menubar = wx.MenuBar()
Then we create our menus.
file = wx.Menu() edit = wx.Menu() help = wx.Menu()
Then we add some items into the menu. This can be done in two ways.
file.Append(101, '&Open', 'Open a new document') file.Append(102, '&Save', 'Save the document')
We can separate logical sections in menus with a horizontal line.
file.AppendSeparator()
If you want to have some icons in your menus, you need to create MenuItem objects manually.
quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application')
quit.SetBitmap(wx.Image('stock_exit-16.png',wx.BITMAP_TYPE_PNG).ConvertToBitmap())
file.AppendItem(quit)wxPython toolkit can only put bitmaps into menus. Therefore we need to convert our PNG files into bitmaps.
Menus are then added into the menubar.
menubar.Append(file, '&File') menubar.Append(edit, '&Edit') menubar.Append(help, '&Help')
Finally we set up our menubar in our application class.
self.SetMenuBar(menubar)
Let's sum it up in a small script.
1 #!/usr/bin/python
2
3 # menu1.py
4
5 import wx
6
7 class MyMenu(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(200, 150))
10
11 menubar = wx.MenuBar()
12 file = wx.Menu()
13 edit = wx.Menu()
14 help = wx.Menu()
15 file.Append(101, '&Open', 'Open a new document')
16 file.Append(102, '&Save', 'Save the document')
17 file.AppendSeparator()
18 quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application')
19 quit.SetBitmap(wx.Image('stock_exit-16.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap())
20 file.AppendItem(quit)
21
22 menubar.Append(file, '&File')
23 menubar.Append(edit, '&Edit')
24 menubar.Append(help, '&Help')
25 self.SetMenuBar(menubar)
26 self.CreateStatusBar()
27
28 class MyApp(wx.App):
29 def OnInit(self):
30 frame = MyMenu(None, -1, 'menu1.py')
31 frame.Show(True)
32 return True
33
34 app = MyApp(0)
35 app.MainLoop()
So far we have seen the default, normal menu items. Here we see, how we can explicitly define check items, normal items or radio items.
edit.Append(201, 'check item1', '', wx.ITEM_CHECK) edit.Append(202, 'check item2', '', kind=wx.ITEM_CHECK)
or
quit = wxMenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application', wx.ITEM_NORMAL)
The parameter is called kind.
Possible wx.ItemKind-s
- wx.ITEM_NORMAL - default item
- wx.ITEM_CHECK - check item
- wx.ITEM_RADIO - radio item
If you want to create a submenu, you create a menu first.
submenu = wx.Menu()
Then append some menu items into this submenu.
submenu.Append(301, 'radio item1', kind=wx.ITEM_RADIO) submenu.Append(302, 'radio item2', kind=wx.ITEM_RADIO) submenu.Append(302, 'radio item3', kind=wx.ITEM_RADIO)
You finish with adding a submenu into a menu object.
edit.AppendMenu(203, 'submenu', submenu)
We now discuss how to respond to user actions. We will touch on it only briefly and explain it later in more detail.
So when a user of our application selects a menu item, an event is generated. We must provide an event handler, that will react to this event accordingly. Handling events in wxPython is the most elegant and simple that I have seen so far. When we look into the reference book, we find wx.EVT_MENU handler under Event handling section.
Suppose we want to add an event handler to our quit menu item.
wx.EVT_MENU(self, 105, self.OnQuit )
We need to provide three pieces of information. The object, where we bind our event handler. In our case self, the application's main object. The id of the corresponding menu item. And the method name, which will do our job.
The method which will react to user actions has two parameters. The first one is the object where this method is defined. The second one is the generated event. This time, we do without it. We simply close our application.
def OnQuit(self, event):
self.Close()The following script demonstrates various menu items, submenu and one simple event handler. I hate when my application window pops up somewhere in the corner by the will of the allmighty window manager. So I added
self.Centre()
so that the window pops up in the center of the screen.
Figure: menu1.py
1 #!/usr/bin/python
2
3 # menu2.py
4
5 import wx
6
7 class MyMenu(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(380, 250))
10
11 menubar = wx.MenuBar()
12 file = wx.Menu()
13 edit = wx.Menu()
14 help = wx.Menu()
15 file.Append(101, '&Open', 'Open a new document')
16 file.Append(102, '&Save', 'Save the document')
17 file.AppendSeparator()
18 quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application')
19 quit.SetBitmap(wx.Image('stock_exit-16.png',wx.BITMAP_TYPE_PNG).ConvertToBitmap())
20 file.AppendItem(quit)
21 edit.Append(201, 'check item1', '', wx.ITEM_CHECK)
22 edit.Append(202, 'check item2', kind=wx.ITEM_CHECK)
23 submenu = wx.Menu()
24 submenu.Append(301, 'radio item1', kind=wx.ITEM_RADIO)
25 submenu.Append(302, 'radio item2', kind=wx.ITEM_RADIO)
26 submenu.Append(303, 'radio item3', kind=wx.ITEM_RADIO)
27 edit.AppendMenu(203, 'submenu', submenu)
28 menubar.Append(file, '&File')
29 menubar.Append(edit, '&Edit')
30 menubar.Append(help, '&Help')
31 self.SetMenuBar(menubar)
32 self.Centre()
33 self.Bind(wx.EVT_MENU, self.OnQuit, id=105)
34
35 def OnQuit(self, event):
36 self.Close()
37
38 class MyApp(wx.App):
39 def OnInit(self):
40 frame = MyMenu(None, -1, 'menu2.py')
41 frame.Show(True)
42 return True
43
44 app = MyApp(0)
45 app.MainLoop()
Figure: menu2.py
wx.ToolBar
Toolbar is a widget that groups the most common used commands or actions of your application. Typically save, open, cut, copy, paste, undo, redo etc. It's purpose is to save time. You need one click to do an action from the toolbar and two clicks from the menu.
1 #!/usr/bin/python
2
3 # toolbar.py
4
5 import wx
6
7 class MyToolBar(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(350, 250))
10
11 vbox = wx.BoxSizer(wx.VERTICAL)
12 toolbar = wx.ToolBar(self, -1, style=wx.TB_HORIZONTAL | wx.NO_BORDER)
13 toolbar.AddSimpleTool(1, wx.Image('stock_new.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'New', '')
14 toolbar.AddSimpleTool(2, wx.Image('stock_open.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Open', '')
15 toolbar.AddSimpleTool(3, wx.Image('stock_save.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Save', '')
16 toolbar.AddSeparator()
17 toolbar.AddSimpleTool(4, wx.Image('stock_exit.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Exit', '')
18 toolbar.Realize()
19 vbox.Add(toolbar, 0, border=5)
20 self.SetSizer(vbox)
21 self.statusbar = self.CreateStatusBar()
22 self.Centre()
23
24 self.Bind(wx.EVT_TOOL, self.OnNew, id=1)
25 self.Bind(wx.EVT_TOOL, self.OnOpen, id=2)
26 self.Bind(wx.EVT_TOOL, self.OnSave, id=3)
27 self.Bind(wx.EVT_TOOL, self.OnExit, id=4)
28
29 def OnNew(self, event):
30 self.statusbar.SetStatusText('New Command')
31
32 def OnOpen(self, event):
33 self.statusbar.SetStatusText('Open Command')
34
35 def OnSave(self, event):
36 self.statusbar.SetStatusText('Save Command')
37
38 def OnExit(self, event):
39 self.Close()
40
41 class MyApp(wx.App):
42 def OnInit(self):
43 frame = MyToolBar(None, -1, 'toolbar.py')
44 frame.Show(True)
45 return True
46
47 app = MyApp(0)
48 app.MainLoop()
wx.BoxSizer will be explained later in layout section. Toolbar widget is created in three steps.
Firstly, we create a toolbar object.
toolbar = wx.ToolBar(self, -1, style=wx.TB_HORIZONTAL | wx.NO_BORDER)
Then we add some tools to the toolbar with the AddSimpleTool() method. You don't find this method in the reference book. It is a wxPython 'extension'. This is a curse and also a blessing. It makes python programming easier. But on the other hand, these extensions are undocumented. You have to look at the wrapper code, demo example or ask on the mailing list.
toolbar.AddSimpleTool(1, wx.Image('stock_new.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'New', '')In the end, we call the Realize() method. This method shows or renders the toolbar widget.
toolbar.Realize()
The toolbar widget has several event handlers. When you click on a toolbar icon a wx.EVT_COMMAND_TOOL_CLICKED event is generated. We bind this event to a specified method with the wx.EVT_TOOL handler.
In order to show some meaningful output to our events, we have set up a statusbar.
self.statusbar = self.CreateStatusBar()
This is yet another wxPython extension. So when we click on a toolbar button, a message is displayed on the statusbar. This is done with the SetStatusText() method.
Figure: toolbar.py
Layout Management
There are basically two methods for layout of our widgets. The first method is manual. We place widgets by specifying the position in the constructor of the widget.
1 #!/usr/bin/python
2
3 # layout.py
4
5 import wx
6
7 class MyFrame(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(250, 50))
10
11 panel = wx.Panel(self, -1)
12 wx.Button(panel, -1, "Button1", (0,0))
13 wx.Button(panel, -1, "Button2", (80,0))
14 wx.Button(panel, -1, "Button3", (160,0))
15
16 class MyApp(wx.App):
17 def OnInit(self):
18 frame = MyFrame(None, -1, 'layout.py')
19 frame.Show(True)
20 frame.Centre()
21 return True
22
23 app = MyApp(0)
24 app.MainLoop()
When the window is resized, the size and the position of buttons do not change. This is one of the main features of the manual positioning of the widgets.
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
wx.StaticBoxSizer
wx.GridSizer
wx.GridBagSizer
wx.BoxSizer
Let's make a program in which three buttons will occupy one row placed at the top of the window. These buttons will resize when the window is resized.
1 #!/usr/bin/python
2
3 # wxboxsizer.py
4
5 import wx
6
7 class MyFrame(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title, (-1, -1), wx.Size(250, 50))
10 panel = wx.Panel(self, -1)
11 box = wx.BoxSizer(wx.HORIZONTAL)
12 box.Add(wx.Button(panel, -1, 'Button1'), 1 )
13 box.Add(wx.Button(panel, -1, 'Button2'), 1 )
14 box.Add(wx.Button(panel, -1, 'Button3'), 1 )
15 panel.SetSizer(box)
16 self.Centre()
17
18 class MyApp(wx.App):
19 def OnInit(self):
20 frame = MyFrame(None, -1, 'wxboxsizer.py')
21 frame.Show(True)
22 return True
23
24 app = MyApp(0)
25 app.MainLoop()
We can place widgets vertically or horizontally.
box = wx.BoxSizer(integer orient)
where orientation can be wx.VERTICAL or wx.HORIZONTAL. Adding widgets into the wx.BoxSizer is done via the Add() method. In order to understand it, we need to look at its parameters.
Add(wx.Window window, integer proportion=0, integer flag = 0, integer border = 0)
Figure: wxboxsizer.py
The proportion parameter defines the share or ratio of available sizer space that the widget will occupy in the direction of the defined orientation. Let's assume we have three buttons with the proportions 0, 1, and 2. They are added into a horizontal wx.BoxSizer. The button with proportion 0 will not change at all when the sizer's width (horizontal size) changes (i.e. the button will always be the same width). The rest of the width of the sizer is split into 3 (2+1) shares. The button with proportion 2 will always occupy 2 of those 3 shares (its width will be 2/3 of the available width), and the button with proportion 1 will always occupy 1 of those shares.
With the flag parameter, you can further configure the behaviour of the widgets within a wx.BoxSizer. We can control the border (though "padding" would be a more accurate name than "border") between the widgets. We add some space between widgets in pixels. In order to apply border, we need to define which sides will use the border. We can choose between these flags:
- wx.LEFT
- wx.RIGHT
- wx.BOTTOM
- wx.TOP
- wx.ALL
We can combine them with the | operator. e.g wx.LEFT | wx.BOTTOM. If we use wx.EXPAND flag, our widget will use all the space that is available in the direction perpendicular to the sizer's orient direction. Lastly, we can also define the alignment of our widgets. We do it with the following flags :
- wx.ALIGN_LEFT
- wx.ALIGN_RIGHT
- wx.ALIGN_TOP
- wx.ALIGN_BOTTOM
- wx.ALIGN_CENTER_VERTICAL
- wx.ALIGN_CENTER_HORIZONTAL
- wx.ALIGN_CENTER
An example:
1 #!/usr/bin/python
2
3 # layout3.py
4
5 import wx
6
7 class MyFrame(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title, (-1, -1), wx.Size(450, 300))
10
11 panel = wx.Panel(self, -1)
12 box = wx.BoxSizer(wx.HORIZONTAL)
13 box.Add(wx.Button(panel, -1, 'Button1'), 1, wx.ALL, 5)
14 box.Add(wx.Button(panel, -1, 'Button2'), 0, wx.EXPAND)
15 box.Add(wx.Button(panel, -1, 'Button3'), 0, wx.ALIGN_CENTER)
16 panel.SetSizer(box)
17 self.Centre()
18
19 class MyApp(wx.App):
20 def OnInit(self):
21 frame = MyFrame(None, -1, 'layout3.py')
22 frame.Show(True)
23 return True
24
25 app = MyApp(0)
26 app.MainLoop()
In our example we again have three buttons. The first one has some border around all its sides. It is the only one that changes in the horizontal dimension when the main window is resized. The second one occupies all space alloted to it in the vertical direction. The third one is aligned in the centre.
We can combine various wx.BoxSizer-s. For example, we can put several horizontal wx.BoxSizer-s into a vertical wx.BoxSizer and vice versa. This way we can make complex layouts.
1 #!/usr/bin/python
2
3 # borders.py
4
5 import wx
6
7 class MyFrame(wx.Frame):
8 def __init__(self, parent, id, title):
9
10 wx.Frame.__init__(self, parent, id, title)
11 hbox = wx.BoxSizer(wx.HORIZONTAL)
12 pnl1 = wx.Panel(self, -1, style=wx.SIMPLE_BORDER)
13 pnl2 = wx.Panel(self, -1, style=wx.RAISED_BORDER)
14 pnl3 = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
15 pnl4 = wx.Panel(self, -1, style=wx.NO_BORDER)
16
17 hbox.Add(pnl1, 1, wx.EXPAND | wx.ALL, 3)
18 hbox.Add(pnl2, 1, wx.EXPAND | wx.ALL, 3)
19 hbox.Add(pnl3, 1, wx.EXPAND | wx.ALL, 3)
20 hbox.Add(pnl4, 1, wx.EXPAND | wx.ALL, 3)
21
22 self.SetSize((400, 120))
23 self.SetSizer(hbox)
24 self.Centre()
25
26 class MyApp(wx.App):
27 def OnInit(self):
28 frame = MyFrame(None, -1, 'borders.py')
29 frame.Show(True)
30 return True
31
32 app = MyApp(0)
33 app.MainLoop()
We show four various border styles available in wxPython. Border is a simple window decoration.
Available Borders:
- wx.SIMPLE_BORDER
- wx.RAISED_BORDER
- wx.SUNKEN_BORDER
- wx.NO_BORDER
wx.GridSizer
wx.GridSizer lays out its children in a two-dimensional table. The width of each field is the width of the widest child. The height of each field is the height of the tallest child.
wx.GridSizer(integer rows, integer cols, integer vgap, integer hgap)
In the constructor we provide the number of rows and the number of columns of our table and the horizontal and vertical gap between the children widgets. We insert our widgets into the table with the AddMany() method. Children are inserted from left to right, top to bottom.
1 #!/usr/bin/python
2
3 # calculator.py
4
5 import wx
6
7 class MyFrame(wx.Frame):
8 def __init__(self, parent, id, title):
9
10 wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(300, 250))
11 self.formula = False
12 menubar = wx.MenuBar()
13 file = wx.Menu()
14 file.Append(22, '&Quit', 'Exit Calculator')
15 menubar.Append(file, '&File')
16 self.SetMenuBar(menubar)
17 wx.EVT_MENU(self, 22, self.OnClose)
18 sizer = wx.BoxSizer(wx.VERTICAL)
19 self.display = wx.TextCtrl(self, -1, '', style=wx.TE_RIGHT)
20 sizer.Add(self.display, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 4)
21
22 gs = wx.GridSizer(4, 4, 3, 3)
23 gs.AddMany([(wx.Button(self, 20, 'Cls'), 0, wx.EXPAND),
24 (wx.Button(self, 21, 'Bck'), 0, wx.EXPAND),
25 (wx.StaticText(self, -1, ''), 0, wx.EXPAND),
26 (wx.Button(self, 22, 'Close'), 0, wx.EXPAND),
27 (wx.Button(self, 1, '7'), 0, wx.EXPAND),
28 (wx.Button(self, 2, '8'), 0, wx.EXPAND),
29 (wx.Button(self, 3, '9'), 0, wx.EXPAND),
30 (wx.Button(self, 4, '/'), 0, wx.EXPAND),
31 (wx.Button(self, 5, '4'), 0, wx.EXPAND),
32 (wx.Button(self, 6, '5'), 0, wx.EXPAND),
33 (wx.Button(self, 7, '6'), 0, wx.EXPAND),
34 (wx.Button(self, 8, '*'), 0, wx.EXPAND),
35 (wx.Button(self, 9, '1'), 0, wx.EXPAND),
36 (wx.Button(self, 10, '2'), 0, wx.EXPAND),
37 (wx.Button(self, 11, '3'), 0, wx.EXPAND),
38 (wx.Button(self, 12, '-'), 0, wx.EXPAND),
39 (wx.Button(self, 13, '0'), 0, wx.EXPAND),
40 (wx.Button(self, 14, '.'), 0, wx.EXPAND),
41 (wx.Button(self, 15, '='), 0, wx.EXPAND),
42 (wx.Button(self, 16, '+'), 0, wx.EXPAND) ])
43
44 sizer.Add(gs, 1, wx.EXPAND)
45
46 self.SetSizer(sizer)
47 self.Centre()
48
49 self.Bind(wx.EVT_BUTTON, self.OnClear, id=20)
50 self.Bind(wx.EVT_BUTTON, self.OnBackspace, id=21)
51 self.Bind(wx.EVT_BUTTON, self.OnClose, id=22)
52 self.Bind(wx.EVT_BUTTON, self.OnSeven, id=1)
53 self.Bind(wx.EVT_BUTTON, self.OnEight, id=2)
54 self.Bind(wx.EVT_BUTTON, self.OnNine, id=3)
55 self.Bind(wx.EVT_BUTTON, self.OnDivide, id=4)
56 self.Bind(wx.EVT_BUTTON, self.OnFour, id=5)
57 self.Bind(wx.EVT_BUTTON, self.OnFive, id=6)
58 self.Bind(wx.EVT_BUTTON, self.OnSix, id=7)
59 self.Bind(wx.EVT_BUTTON, self.OnMultiply, id=8)
60 self.Bind(wx.EVT_BUTTON, self.OnOne, id=9)
61 self.Bind(wx.EVT_BUTTON, self.OnTwo, id=10)
62 self.Bind(wx.EVT_BUTTON, self.OnThree, id=11)
63 self.Bind(wx.EVT_BUTTON, self.OnMinus, id=12)
64 self.Bind(wx.EVT_BUTTON, self.OnZero, id=13)
65 self.Bind(wx.EVT_BUTTON, self.OnDot, id=14)
66 self.Bind(wx.EVT_BUTTON, self.OnEqual, id=15)
67 self.Bind(wx.EVT_BUTTON, self.OnPlus, id=16)
68
69 def OnClear(self, event):
70 self.display.Clear()
71
72 def OnBackspace(self, event):
73 formula = self.display.GetValue()
74 self.display.Clear()
75 self.display.SetValue(formula[:-1])
76
77 def OnClose(self, event):
78 self.Close()
79
80 def OnDivide(self, event):
81 if self.formula:
82 return
83 self.display.AppendText('/')
84
85 def OnMultiply(self, event):
86 if self.formula:
87 return
88 self.display.AppendText('*')
89
90 def OnMinus(self, event):
91 if self.formula:
92 return
93 self.display.AppendText('-')
94
95 def OnPlus(self, event):
96 if self.formula:
97 return
98 self.display.AppendText('+')
99
100 def OnDot(self, event):
101 if self.formula:
102 return
103 self.display.AppendText('.')
104
105 def OnEqual(self, event):
106 if self.formula:
107 return
108 formula = self.display.GetValue()
109 self.formula = False
110 try:
111 self.display.Clear()
112 output = eval(formula)
113 self.display.AppendText(str(output))
114 except StandardError:
115 self.display.AppendText("Error")
116
117 def OnZero(self, event):
118 if self.formula:
119 self.display.Clear()
120 self.formula = False
121 self.display.AppendText('0')
122
123 def OnOne(self, event):
124 if self.formula:
125 self.display.Clear()
126 self.formula = False
127 self.display.AppendText('1')
128
129 def OnTwo(self, event):
130 if self.formula:
131 self.display.Clear()
132 self.formula = False
133 self.display.AppendText('2')
134
135 def OnThree(self, event):
136 if self.formula:
137 self.display.Clear()
138 self.formula = False
139 self.display.AppendText('3')
140
141 def OnFour(self, event):
142 if self.formula:
143 self.display.Clear()
144 self.formula = False
145 self.display.AppendText('4')
146
147 def OnFive(self, event):
148 if self.formula:
149 self.display.Clear()
150 self.formula = False
151 self.display.AppendText('5')
152
153 def OnSix(self, event):
154 if self.formula:
155 self.display.Clear()
156 self.formula = False
157 self.display.AppendText('6')
158
159 def OnSeven(self, event):
160 if self.formula:
161 self.display.Clear()
162 self.formula = False
163 self.display.AppendText('7')
164
165 def OnEight(self, event):
166 if self.formula:
167 self.display.Clear()
168 self.formula = False
169 self.display.AppendText('8')
170
171 def OnNine(self, event):
172 if self.formula:
173 self.display.Clear()
174 self.formula = False
175 self.display.AppendText('9')
176
177 class MyApp(wx.App):
178 def OnInit(self):
179 frame = MyFrame(None, -1, 'calculator.py')
180 frame.Show(True)
181 self.SetTopWindow(frame)
182 return True
183
184 app = MyApp(0)
185 app.MainLoop()
The formula we input is processed by the eval built-in python function.
output = eval(formula)
If we make an error in our formula, an error message is displayed. Notice how we managed to put a space between the Bck and Close buttons. We simply put an empty wx.StaticText there. Such tricks are quite common.
Figure: calculator.py
wx.GridBagSizer
The most complicated sizer in wxPython. It enables explicit positioning of the items. Items can also optionally span more than one row and/or column. wx.GridBagSizer has a simple constructor.
wx.GridBagSizer(integer vgap, integer hgap)
The vertical and the horizontal gap defines the space in pixels used between children. You add items to grid with the Add() method.
Add(self, item, tuple pos, tuple span=wx.DefaultSpan, integer flag=0, integer border=0, userData=None)
Item is a widget that you insert into the grid. pos specifies the position in the virtual grid. The topleft cell has pos of (0, 0). span is an optional spanning of the widget. e.g. span of (3, 2) spans a widget across 3 rows and 2 columns. flag and border were discussed earlier by wx.BoxSizer.
The items in the grid can change their size or keep the default size, when the window is resized. If you want your items to grow and shrink, you can use these two methods.
AddGrowableRow(integer row) AddGrowableCol(integer col)
1 #!/usr/bin/python
2
3 # wxgridbagsizer.py
4
5 import wx
6
7 class MyFrame(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition)
10
11 sizer = wx.GridBagSizer(9, 9)
12 sizer.Add(wx.Button(self,-1, "Button"), (0, 0), wx.DefaultSpan, wx.ALL, 5)
13 sizer.Add(wx.Button(self,-1, "Button"), (1, 1), (1,7), wx.EXPAND)
14 sizer.Add(wx.Button(self,-1, "Button"), (6, 6), (3,3), wx.EXPAND)
15 sizer.Add(wx.Button(self,-1, "Button"), (3, 0), (1,1), wx.ALIGN_CENTER)
16 sizer.Add(wx.Button(self,-1, "Button"), (4, 0), (1,1), wx.ALIGN_LEFT)
17 sizer.Add(wx.Button(self,-1, "Button"), (5, 0), (1,1), wx.ALIGN_RIGHT)
18 sizer.AddGrowableRow(6)
19 sizer.AddGrowableCol(6)
20
21 self.SetSizerAndFit(sizer)
22 self.Centre()
23
24 class MyApp(wx.App):
25 def OnInit(self):
26 frame = MyFrame(None, -1, "wxgridbagsizer.py")
27 frame.Show(True)
28 self.SetTopWindow(frame)
29 return True
30
31 app = MyApp(0)
32 app.MainLoop()
If you want your item to span more than one cell, you must provide wx.EXPAND flag.
self.SetSizerAndFit(sizer)
This method is same as SetSizer() except that it also sends size hints to the window. All buttons are displayed on the window.
Basic Objects
wxPython is a collection of various objects. We can divide them into two groups.
- Visual objects
- Non-visual objects
Examples of visual objects are: widgets, fonts, colours or cursors. Non-visual objects: sizers, timers or events.
Cursors
A cursor is a simple graphical object. It is used to indicate position on the monitor or any other display device. It usually dynamically changes itself. Typically, when you hover a mouse pointer over a hypertext, the cursor changes to a hand.
In the next code example, we create a grid of nine wx.Panels. Each panel shows a different cursor.
1 #!/usr/bin/python
2
3 # cursors.py
4
5 import wx
6
7 class Cursors(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title)
10
11 vbox = wx.BoxSizer(wx.VERTICAL)
12 sizer = wx.GridSizer(3, 3, 2, 2)
13
14 cursors = [ wx.CURSOR_ARROW, wx.CURSOR_HAND, wx.CURSOR_WATCH, wx.CURSOR_SPRAYCAN, wx.CURSOR_PENCIL,
15 wx.CURSOR_CROSS, wx.CURSOR_QUESTION_ARROW, wx.CURSOR_POINT_LEFT, wx.CURSOR_SIZING]
16
17 for i in cursors:
18 panel = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
19 panel.SetCursor(wx.StockCursor(i))
20 sizer.Add(panel, flag=wx.EXPAND)
21
22 vbox.Add(sizer, 1, wx.EXPAND | wx.TOP, 5)
23 self.SetSizer(vbox)
24
25 self.Centre()
26 self.Show()
27
28
29 app = wx.App(0)
30 Cursors(None, -1, 'Cursors.py')
31 app.MainLoop()
Various predefined cursors: Listed below.
Fonts
We create different kinds of fonts with the wx.Font object. It has the following constructor:
wx.Font(integer pointSize, wx.FontFamily family, integer style, integer weight,
bool underline = false, string faceName = '',
wx.FontEncoding encoding = wx.FONTENCODING_DEFAULT)The specified parameters can have the following options:
family:
- wx.DEFAULT
- wx.DECORATIVE
- wx.ROMAN
- wx.SWISS
- wx.SCRIPT
- wx.MODERN
style:
- wx.NORMAL
- wx.SLANT
- wx.ITALIC
weight:
- wx.NORMAL
- wx.LIGHT
- wx.BOLD
fonts.py script shows three different fonts.
1 #!/usr/bin/python
2
3 # fonts.py
4
5 import wx
6
7 class MyFrame(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(325, 320))
10
11 panel = wx.Panel(self, -1)
12
13 text1 = "Now listen to me mama\nMama mama\nYou're taking away my last chance\nDon't take it away"
14
15 text2 = '''You won't cry for my absence, I know -
16 You forgot me long ago.
17 Am I that unimportant...?
18 Am I so insignificant...?
19 Isn't something missing?
20 Isn't someone missing me?'''
21
22 text3 = '''But if I had one wish fulfilled tonight
23 I'd ask for the sun to never rise
24 If God passed a mic to me to speak
25 I'd say stay in bed, world
26 Sleep in peace'''
27
28 font1 = wx.Font(10, wx.NORMAL, wx.ITALIC, wx.NORMAL)
29 font2 = wx.Font(10, wx.ROMAN, wx.NORMAL, wx.NORMAL)
30 font3 = wx.Font(10, wx.MODERN, wx.NORMAL, wx.BOLD)
31 lyrics1 = wx.StaticText(panel, -1, text1,(30,15), style=wx.ALIGN_CENTRE)
32 lyrics1.SetFont(font1)
33 lyrics2 = wx.StaticText(panel, -1, text2,(30,100), style=wx.ALIGN_CENTRE)
34 lyrics2.SetFont(font2)
35 lyrics3 = wx.StaticText(panel, -1, text3,(5,220), style=wx.ALIGN_CENTRE)
36 lyrics3.SetFont(font3)
37 self.Center()
38
39 class MyApp(wx.App):
40 def OnInit(self):
41 frame = MyFrame(None, -1, 'fonts.py')
42 frame.Show(True)
43 self.SetTopWindow(frame)
44 return True
45
46 app = MyApp(0)
47 app.MainLoop()
Figure: fonts.py
Colours
A colour is an object representing a combination of Red, Green, and Blue (RGB) intensity values. Valid RGB values are in the range 0 to 255.
There are three ways for setting colours. We can create a wx.Colour object, use a predefined colour name or use hex value string.
SetBackgroundColour(wx.Colour(0,0,255))
SetBackgroundColour('BLUE')
SetBackgroundColour('#0000FF')Predefined colour names in wxPython:
- wx.BLACK
- wx.WHITE
- wx.RED
- wx.BLUE
- wx.GREEN
- wx.CYAN
- wx.LIGHT_GREY
Standard colour database:
colours.py script shows eight different colours. wxBlack is simple. sea green is poetic and #0000FF technical. It is up to the developer, what to choose.
1 #!/usr/bin/python
2
3 # colours.py
4
5 import wx
6
7
8 class Colours(wx.Dialog):
9 def __init__(self, parent, id, title):
10
11 wx.Dialog.__init__(self, parent, id, title, size=(300, 300))
12 vbox = wx.BoxSizer(wx.VERTICAL)
13 self.pnl1 = wx.Panel(self, -1)
14 self.pnl2 = wx.Panel(self, -1)
15 self.pnl3 = wx.Panel(self, -1)
16 self.pnl4 = wx.Panel(self, -1)
17 self.pnl5 = wx.Panel(self, -1)
18 self.pnl6 = wx.Panel(self, -1)
19 self.pnl7 = wx.Panel(self, -1)
20 self.pnl8 = wx.Panel(self, -1)
21
22 gs = wx.GridSizer(4,2,3,3)
23 gs.AddMany([ (self.pnl1, 0 ,wx.EXPAND),
24 (self.pnl2, 0, wx.EXPAND),
25 (self.pnl3, 0, wx.EXPAND),
26 (self.pnl4, 0, wx.EXPAND),
27 (self.pnl5, 0, wx.EXPAND),
28 (self.pnl6, 0, wx.EXPAND),
29 (self.pnl7, 0, wx.EXPAND),
30 (self.pnl8, 0, wx.EXPAND) ])
31
32 vbox.Add(gs, 1, wx.EXPAND | wx.TOP, 5)
33 self.SetSizer(vbox)
34 self.SetColors()
35 self.Centre()
36 self.ShowModal()
37 self.Destroy()
38
39 def SetColors(self):
40 self.pnl1.SetBackgroundColour(wx.BLACK)
41 self.pnl2.SetBackgroundColour(wx.Colour(139,105,20))
42 self.pnl3.SetBackgroundColour(wx.RED)
43 self.pnl4.SetBackgroundColour('#0000FF')
44 self.pnl5.SetBackgroundColour('sea green')
45 self.pnl6.SetBackgroundColour('midnight blue')
46 self.pnl7.SetBackgroundColour(wx.LIGHT_GREY)
47 self.pnl8.SetBackgroundColour('plum')
48
49 app = wx.App(0)
50 Colours(None, -1, 'colours.py')
51 app.MainLoop()
Figure: colours.py
The full database has currently about 630 different colour names. The list can be found in the colourdb.py file. It is also shown in the wxPython demo. In randomcolours.py script we have a single window. We change the background colour of the window to the randomly chosen colour.
1 #!/usr/bin/python
2
3 # randomcolours.py
4
5 import wx
6
7 from random import randrange
8 from wx.lib.colourdb import *
9
10 class MyFrame(wx.Frame):
11 def __init__(self, parent, id, title):
12 wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(400, 350))
13
14 self.panel = wx.Panel(self, -1)
15 self.colors = getColourList()
16 self.timer = wx.Timer(self)
17 self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
18 self.timer.Start(1500)
19 self.col_num = len(self.colors)
20 self.Centre()
21
22 def OnTimer(self, event):
23 self.panel.SetBackgroundColour(wx.RED)
24 position = randrange(0, self.col_num-1, 1)
25 self.panel.SetBackgroundColour(self.colors[position])
26 self.panel.Refresh()
27
28 class MyApp(wx.App):
29 def OnInit(self):
30 updateColourDB()
31 frame = MyFrame(None, -1, 'randomcolours.py')
32 frame.Show(True)
33 self.SetTopWindow(frame)
34 return True
35
36 app = MyApp(0)
37 app.MainLoop()
Bitmaps
There are two kinds of graphics. Vector and bitmap. With vector graphics, images are created with mathematical formulas that define all the shapes of the image. Geometric objects like curves and polygons are used. A bitmap or a bit map is a collection of bits that form an image. It is a grid of individual dots stored in memory or in a file. Each dot has it's 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:
- PNG
- JPEG
- GIF
- TIFF
- BMP
1 #!/usr/bin/python
2
3 # bitmap.py
4
5 import wx
6
7 class MyFrame(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title, size = (270, 270))
10
11 self.bitmap = wx.Bitmap('memento.jpg')
12 wx.EVT_PAINT(self, self.OnPaint)
13
14 self.Centre()
15
16 def OnPaint(self, event):
17 dc = wx.PaintDC(self)
18 dc.DrawBitmap(self.bitmap, 60, 20)
19
20
21 class MyApp(wx.App):
22 def OnInit(self):
23 frame = MyFrame(None, -1, 'Memento')
24 frame.Show(True)
25 self.SetTopWindow(frame)
26 return True
27
28 app = MyApp(0)
29 app.MainLoop()
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 it's 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:
- Identify the event name: wx.EVT_SIZE, wx.EVT_CLOSE etc
- Create an event handler. It is a method, that is called, when an event is generated
- Bind an event to an event handler.
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)
- event is one of EVT_* objects. It specifies the type of the event.
- handler is an object to be called. In other words, it is a method, that a programmer binds to an event.
- source parameter is used when we want to differentiate between the same event type from different widgets.
- id parameter is used, when we have multiple buttons, menu items etc. The id is used to differentiate among them.
- id2 is used when it is desirable to bind a handler to a range of ids, such as with EVT_MENU_RANGE.
Note that method Bind() is defined in class EvtHandler. It is the class, from which wx.Window inherits. wx.Window is a base class for most widgets in wxPython. There is also a reverse process. If we want to unbind a method from an event, we call the Unbind() method. It has the same paremeters as the above one.
Possible events |
|
wx.Event |
the event base class |
wx.ActivateEvent |
a window or application activation event |
wx.CloseEvent |
a close window or end session event |
wx.EraseEvent |
an erase background event |
wx.FocusEvent |
a window focus event |
wx.KeyEvent |
a keypress event |
wx.IdleEvent |
an idle event |
wx.InitDialogEvent |
a dialog initialisation event |
wx.JoystickEvent |
a joystick event |
wx.MenuEvent |
a menu event |
wx.MouseEvent |
a mouse event |
wx.MoveEvent |
a move event |
wx.PaintEvent |
a paint event |
wx.QueryLayoutInfoEvent |
used to query layout information |
wx.SetCursorEvent |
used for special cursor processing based on current mouse position |
wx.SizeEvent |
a size event |
wx.ScrollWinEvent |
a scroll event sent by a built-in Scrollbar |
wx.ScrollEvent |
a scroll event sent by a stand-alone scrollbar |
wx.SysColourChangedEvent |
a system colour change event |
Examples
The following code is an example of a wx.ScrollWinEvent. This event is generated, when we click on a built in Scrollbar. Built-in Scrollbar is activated with the SetScrollbar() method call. For stand-alone Scrollbars, there is another event type, namely wx.ScrollEvent.
1 #!/usr/bin/python
2
3 # myscrollwinevent.py
4
5 import wx
6
7 class MyScrollWinEvent(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title)
10 panel = wx.Panel(self, -1)
11 self.st = wx.StaticText(panel, -1, '0', (30,0))
12 panel.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
13 panel.SetScrollbar(wx.VERTICAL, 0, 6, 50);
14 self.Centre()
15
16 def OnScroll(self, evt):
17 y = evt.GetPosition()
18 self.st.SetLabel(str(y))
19
20 class MyApp(wx.App):
21 def OnInit(self):
22 msw = MyScrollWinEvent(None, -1, 'myscrollwinevent.py')
23 msw.Show(True)
24 return True
25
26 app = MyApp(0)
27 app.MainLoop()
The wx.SizeEvent is generated, when our window is resized. In our example, we show the size of the window in the titlebar.
1 #!/usr/bin/python
2
3 # sizeevent.py
4
5 import wx
6
7 class SizeEvent(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title)
10
11 self.Bind(wx.EVT_SIZE, self.OnSize)
12 self.Centre()
13
14 def OnSize(self, event):
15 self.SetTitle(str(event.GetSize()))
16
17 class MyApp(wx.App):
18 def OnInit(self):
19 se = SizeEvent(None, -1, 'sizeevent.py')
20 se.Show(True)
21 return True
22
23 app = MyApp(0)
24 app.MainLoop()
1 #!/usr/bin/python
2
3 # moveevent.py
4
5 import wx
6
7 class MoveEvent(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title)
10
11 wx.StaticText(self, -1, 'x:', (10,0))
12 wx.StaticText(self, -1, 'y:', (10,20))
13 self.st1 = wx.StaticText(self, -1, '', (30, 0))
14 self.st2 = wx.StaticText(self, -1, '', (30, 20))
15 self.Bind(wx.EVT_MOVE, self.OnMove)
16 self.Centre()
17
18 def OnMove(self, event):
19 x, y = event.GetPosition()
20 self.st1.SetLabel(str(x))
21 self.st2.SetLabel(str(y))
22
23 class MyApp(wx.App):
24 def OnInit(self):
25 me = MoveEvent(None, -1, 'moveevent.py')
26 me.Show(True)
27 return True
28
29 app = MyApp(0)
30 app.MainLoop()
A paint event is generated when a window is redrawn. This happens when we resize window, maximize it. A paint event can be generated programatically as well. For example, when we call SetLabel() method to change a wxStaticText widget. Note that when we minimize a window, no paint event is generated.
1 #!/usr/bin/python
2
3 # paintevent.py
4
5 import wx
6
7 class PaintEvent(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title)
10
11 self.Bind(wx.EVT_PAINT, self.OnPaint)
12 self.Centre()
13
14 def OnPaint(self, event):
15 wx.Bell()
16
17 class MyApp(wx.App):
18 def OnInit(self):
19 pe = PaintEvent(None, -1, 'paintevent.py')
20 pe.Show(True)
21 return True
22
23 app = MyApp(0)
24 app.MainLoop()
When we press a key on our keyboard, wx.KeyEvent is generated. There are three different key handlers:
- EVT_KEY_DOWN
- EVT_KEY_UP
- EVT_CHAR
A common request is to close application, when Esc key is pressed.
1 #!/usr/bin/python
2
3 # keyevent.py
4
5 import wx
6
7
8 class KeyEvent(wx.Frame):
9 def __init__(self, parent, id, title):
10 wx.Frame.__init__(self, parent, id, title)
11
12 panel = wx.Panel(self, -1)
13 panel.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
14 panel.SetFocus()
15
16 self.Centre()
17 self.Show(True)
18
19
20 def OnKeyDown(self, event):
21 keycode = event.GetKeyCode()
22 if keycode == wx.WXK_ESCAPE:
23 ret = wx.MessageBox('Are you sure to quit?', 'Question', wx.YES_NO | wx.CENTRE |
24 wx.NO_DEFAULT, self)
25 if ret == wx.YES:
26 self.Close()
27 event.Skip()
28
29
30 app = wx.App()
31 KeyEvent(None, -1, 'keyevent.py')
32 app.MainLoop()
We find out, which key was pressed by calling GetKeyCode() method. In our case, keycode is wx.WXK_ESCAPE.
keycode = event.GetKeyCode()
Other keycodes are listed below.
Dialogs
In wxPython you can use predefined dialogs or create your own dialogs. You can also create dialog based applications.
The example shows a skeleton of a dialog based application in wxPython.
1 #!/usr/bin/python
2 # simpledialog.py
3
4 import wx
5
6 class MyDialog(wx.Dialog):
7 def __init__(self, parent, id, title):
8 wx.Dialog.__init__(self, parent, id, title)
9
10 class MyApp(wx.App):
11 def OnInit(self):
12 dia = MyDialog(None, -1, "simpledialog.py")
13 dia.ShowModal()
14 dia.Destroy()
15 return True
16
17 app = MyApp(0)
18 app.MainLoop()
Notice that you cannot resize the dialog window. The Destroy() method is necessary. It deletes the dialog from the memory. Otherwise, the script would not exit properly.
There are two types of dialogs. Modal and modeless. Modal dialog does not allow a user to work with the rest of the application until it is destroyed. Modal dialogs are created with the ShowModal() method. Dialogs are modeless when called with Show().
Custom dialogs
There are two methods that simplify the creation of dialogs. Both return a specific sizer object.
CreateTextSizer(self, string message) CreateButtonSizer(self, long flags)
CreateTextSizer() method creates a text sizer. In the following example, we add some buttons into the sizer to demonstrate it.
1 #!/usr/bin/python
2
3 # customdialog1.py
4
5 import wx
6
7 class MyDialog(wx.Dialog):
8 def __init__(self, parent, id, title):
9 wx.Dialog.__init__(self, parent, id, title, size=(350,300))
10
11 sizer = self.CreateTextSizer('My Buttons')
12 sizer.Add(wx.Button(self, -1, 'Button'), 0, wx.ALL, 5)
13 sizer.Add(wx.Button(self, -1, 'Button'), 0, wx.ALL, 5)
14 sizer.Add(wx.Button(self, -1, 'Button'), 0, wx.ALL, 5)
15 sizer.Add(wx.Button(self, -1, 'Button'), 0, wx.ALL|wx.ALIGN_CENTER, 5)
16 sizer.Add(wx.Button(self, -1, 'Button'), 0, wx.ALL|wx.EXPAND, 5)
17 self.SetSizer(sizer)
18
19 class MyFrame(wx.Frame):
20 def __init__(self, parent, id, title):
21 wx.Frame.__init__(self, parent, id, title, size=(550,500))
22
23 panel = wx.Panel(self, -1)
24 wx.Button(panel, 1, 'Show Custom Dialog', (100,100))
25 self.Bind (wx.EVT_BUTTON, self.OnShowCustomDialog, id=1)
26
27 def OnShowCustomDialog(self, event):
28 dia = MyDialog(self, -1, 'buttons')
29 dia.ShowModal()
30 dia.Destroy()
31
32 class MyApp(wx.App):
33 def OnInit(self):
34 frame = MyFrame(None, -1, 'customdialog1.py')
35 frame.Show(True)
36 frame.Centre()
37 return True
38
39 app = MyApp(0)
40 app.MainLoop()
CreateButtonSizer() method creates a row of buttons. You can specify button types with different flags. CreateButtonSizer() method can take the following flags:
- wx.OK
- wx.CANCEL
- wx.YES
- wx.NO
- wx.HELP
- wx.NO_DEFAULT
1 #!/usr/bin/python
2
3 # customdialog2.py
4
5 import wx
6
7 class MyDialog(wx.Dialog):
8 def __init__(self, parent, id, title):
9 wx.Dialog.__init__(self, parent, id, title)
10
11 vbox = wx.BoxSizer(wx.VERTICAL)
12 stline = wx.StaticText(self, 11, 'Discipline ist Macht.')
13 vbox.Add(stline, 1, wx.ALIGN_CENTER|wx.TOP, 45)
14 sizer = self.CreateButtonSizer(wx.NO|wx.YES|wx.HELP)
15 vbox.Add(sizer, 0, wx.ALIGN_CENTER)
16 self.SetSizer(vbox)
17 self.Bind(wx.EVT_BUTTON, self.OnYes, id=wx.ID_YES)
18
19 def OnYes(self, event):
20 self.Close()
21
22 class MyFrame(wx.Frame):
23 def __init__(self, parent, id, title):
24 wx.Frame.__init__(self, parent, id, title)
25 panel = wx.Panel(self, -1)
26 wx.Button(panel, 1, 'Show custom Dialog', (50,50))
27 self.Bind(wx.EVT_BUTTON, self.OnShowCustomDialog, id=1)
28
29 def OnShowCustomDialog(self, event):
30 dia = MyDialog(self, -1, '')
31 val = dia.ShowModal()
32 dia.Destroy()
33
34 class MyApp(wx.App):
35 def OnInit(self):
36 frame = MyFrame(None, -1, 'customdialog2.py')
37 frame.Show(True)
38 frame.Centre()
39 return True
40
41 app = MyApp(0)
42 app.MainLoop()
Note that wxPython does not take the order of flags into account.
sizer = self.CreateButtonSizer(wxNO|wxYES|wxHELP)
The buttons will be created according to the standards.
Common Predefined Dialogs
wxPython provides several common dialogs. They save programmers a lot of work. They also help promote standards in applications. We will show these ones:
wx.MessageDialog
wx.ColourDialog
wx.FileDialog
wx.PageSetupDialog
wx.FontDialog
wx.DirDialog
wx.SingleChoiceDialog
wx.TextEntryDialog
1 #!/usr/bin/python
2
3 # commondialogs.py
4
5 import wx
6 import os, sys
7
8 class MyFrame(wx.Frame):
9 def __init__(self, parent, id, title):
10 wx.Frame.__init__(self, parent, id, title)
11
12 self.CreateStatusBar()
13 menuBar = wx.MenuBar()
14 menu = wx.Menu()
15 menu.Append(99, "&Message Dialog", "Shows a Message Dialog")
16 menu.Append(100, "&Color Dialog", "Shows a Color Dialog")
17 menu.Append(101, "&File Dialog", "Shows a File Dialog")
18 menu.Append(102, "&Page Setup Dialog", "Shows a Page Setup Dialog")
19 menu.Append(103, "&Font Dialog", "Shows a Font Dialog")
20 menu.Append(104, "&Directory Dialog", "Shows a Directory Dialog")
21 menu.Append(105, "&SingleChoice Dialog", "Shows a SingleChoice Dialog")
22 menu.Append(106, "&TextEntry Dialog", "Shows a TextEntry Dialog")
23 menuBar.Append(menu, "&Dialogs")
24 self.SetMenuBar(menuBar)
25
26 self.Bind(wx.EVT_MENU, self.message, id=99)
27 self.Bind(wx.EVT_MENU, self.choosecolor, id=100)
28 self.Bind(wx.EVT_MENU, self.openfile, id=101)
29 self.Bind(wx.EVT_MENU, self.pagesetup, id=102)
30 self.Bind(wx.EVT_MENU, self.choosefont, id=103)
31 self.Bind(wx.EVT_MENU, self.opendir, id=104)
32 self.Bind(wx.EVT_MENU, self.singlechoice, id=105)
33 self.Bind(wx.EVT_MENU, self.textentry, id=106)
34
35 def message(self, event):
36 dlg = wx.MessageDialog(self, 'To save one life is as if you have saved the world.', 'Talmud', wx.OK|wx.ICON_INFORMATION)
37 dlg.ShowModal()
38 dlg.Destroy()
39
40 def choosecolor(self, event):
41 dlg = wx.ColourDialog(self)
42 dlg.GetColourData().SetChooseFull(True)
43 if dlg.ShowModal() == wx.ID_OK:
44 data = dlg.GetColourData()
45 self.SetStatusText('You selected: %s\n' % str(data.GetColour().Get()))
46 dlg.Destroy()
47
48 def openfile(self, event):
49 dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.*", wx.OPEN)
50 if dlg.ShowModal() == wx.ID_OK:
51 path = dlg.GetPath()
52 mypath = os.path.basename(path)
53 self.SetStatusText("You selected: %s" % mypath)
54 dlg.Destroy()
55
56 def pagesetup(self, event):
57 dlg = wx.PageSetupDialog(self)
58 if dlg.ShowModal() == wx.ID_OK:
59 data = dlg.GetPageSetupData()
60 tl = data.GetMarginTopLeft()
61 br = data.GetMarginBottomRight()
62 self.SetStatusText('Margins are: %s %s' % (str(tl), str(br)))
63 dlg.Destroy()
64
65 def choosefont(self, event):
66 default_font = wx.Font(10, wx.SWISS , wx.NORMAL, wx.NORMAL, False, "Verdana")
67 data = wx.FontData()
68 if sys.platform == 'win32':
69 data.EnableEffects(True)
70 data.SetAllowSymbols(False)
71 data.SetInitialFont(default_font)
72 data.SetRange(10, 30)
73 dlg = wx.FontDialog(self, data)
74 if dlg.ShowModal() == wx.ID_OK:
75 data = dlg.GetFontData()
76 font = data.GetChosenFont()
77 color = data.GetColour()
78 text = 'Face: %s, Size: %d, Color: %s' % (font.GetFaceName(), font.GetPointSize(), color.Get())
79 self.SetStatusText(text)
80 dlg.Destroy()
81
82 def opendir(self, event):
83 dlg = wx.DirDialog(self, "Choose a directory:", style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
84 if dlg.ShowModal() == wx.ID_OK:
85 self.SetStatusText('You selected: %s\n' % dlg.GetPath())
86 dlg.Destroy()
87
88 def singlechoice(self, event):
89 sins = ['Greed', 'Lust', 'Gluttony', 'Pride', 'Sloth', 'Envy', 'Wrath']
90 dlg = wx.SingleChoiceDialog(self, 'Seven deadly sins', 'Which one?', sins, wx.CHOICEDLG_STYLE)
91 if dlg.ShowModal() == wx.ID_OK:
92 self.SetStatusText('You chose: %s\n' % dlg.GetStringSelection())
93 dlg.Destroy()
94
95 def textentry(self, event):
96 dlg = wx.TextEntryDialog(self, 'Enter some text','Text Entry')
97 dlg.SetValue("Default")
98 if dlg.ShowModal() == wx.ID_OK:
99 self.SetStatusText('You entered: %s\n' % dlg.GetValue())
100 dlg.Destroy()
101
102 class MyApp(wx.App):
103 def OnInit(self):
104 myframe = MyFrame(None, -1, "commondialogs.py")
105 myframe.CenterOnScreen()
106 myframe.Show(True)
107 return True
108
109 app = MyApp(0)
110 app.MainLoop()
The script shows eight different common dialogs. All the output is displayed on the statusbar.
Core Widgets
In this section, we will introduce basic widgets in wxPython. Each widget will have a small code example.
wx.Button
wx.Button is a simple widget. It contains a text string. It is used to trigger an action.
wx.Button styles
- wx.BU_LEFT
- wx.BU_TOP
- wx.BU_RIGHT
- wx.BU_BOTTOM
- wx.BU_EXACTFIT
- wx.NO_BORDER
wx.Button methods |
|
SetDefault() |
set the button to be the default item on a window |
wx.Size GetDefaultSize() |
get the default button size on a platform |
1 #!/usr/bin/python
2
3 # buttons.py
4
5 import wx
6 import random
7
8 APP_SIZE_X = 300
9 APP_SIZE_Y = 200
10
11 class MyButtons(wx.Dialog):
12 def __init__(self, parent, id, title):
13 wx.Dialog.__init__(self, parent, id, title, size=(APP_SIZE_X, APP_SIZE_Y))
14
15 wx.Button(self, 1, 'Close', (50, 130))
16 wx.Button(self, 2, 'Random Move', (150, 130), (110, -1))
17
18 self.Bind(wx.EVT_BUTTON, self.OnClose, id=1)
19 self.Bind(wx.EVT_BUTTON, self.OnRandomMove, id=2)
20
21 self.Centre()
22 self.ShowModal()
23 self.Destroy()
24
25 def OnClose(self, event):
26 self.Close(True)
27
28 def OnRandomMove(self, event):
29 screensize = wx.GetDisplaySize()
30 randx = random.randrange(0, screensize.x - APP_SIZE_X)
31 randy = random.randrange(0, screensize.y - APP_SIZE_Y)
32 self.Move((randx, randy))
33
34 app = wx.App(0)
35 MyButtons(None, -1, 'buttons.py')
36 app.MainLoop()
Figure: buttons.py
wx.ToggleButton
wx.ToggleButton is a button that has two states. Pressed and not pressed. You toggle between these two states by clicking on it. There are situations where this functionality fits well.
wx.ToggleButton methods |
|
SetValue(bool value) |
set a state of a toggle button |
bool GetValue() |
get a state of the toggle button |
SetLabel(string label) |
set a label for the button |
1 #!/usr/bin/python
2
3 # togglebuttons.py
4
5 import wx
6
7 class ToggleButtons(wx.Dialog):
8 def __init__(self, parent, id, title):
9 wx.Dialog.__init__(self, parent, id, title, size=(300, 200))
10
11 self.colour = wx.Colour(0, 0, 0)
12
13 wx.ToggleButton(self, 1, 'red', (20, 25))
14 wx.ToggleButton(self, 2, 'green', (20, 60))
15 wx.ToggleButton(self, 3, 'blue', (20, 100))
16
17 self.panel = wx.Panel(self, -1, (150, 20), (110, 110), style=wx.SUNKEN_BORDER)
18 self.panel.SetBackgroundColour(self.colour)
19
20 self.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleRed, id=1)
21 self.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleGreen, id=2)
22 self.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleBlue, id=3)
23
24 self.Centre()
25 self.ShowModal()
26 self.Destroy()
27
28 def ToggleRed(self, event):
29 green = self.colour.Green()
30 blue = self.colour.Blue()
31 if self.colour.Red():
32 self.colour.Set(0, green, blue)
33 else:
34 self.colour.Set(255, green, blue)
35 self.panel.SetBackgroundColour(self.colour)
36
37 def ToggleGreen(self, event):
38 red = self.colour.Red()
39 blue = self.colour.Blue()
40 if self.colour.Green():
41 self.colour.Set(red, 0, blue)
42 else:
43 self.colour.Set(red, 255, blue)
44 self.panel.SetBackgroundColour(self.colour)
45
46 def ToggleBlue(self, event):
47 red = self.colour.Red()
48 green = self.colour.Green()
49 if self.colour.Blue():
50 self.colour.Set(red, green, 0)
51 else:
52 self.colour.Set(red, green, 255)
53 self.panel.SetBackgroundColour(self.colour)
54
55
56 app = wx.App(0)
57 ToggleButtons(None, -1, 'togglebuttons.py')
58 app.MainLoop()
Figure: togglebuttons.py
wx.BitmapButton
A bitmap button is a button, that displays a bitmap. A bitmap button can have three other states. Selected, focused and displayed. We can set a specific bitmap for those states.
wx.BitmapButton methods |
|
wx.Bitmap GetBitmapLabel() |
return the label bitmap |
SetBitmapLabel(wx.Bitmap bitmap) |
set the bitmap label for the button |
wx.Bitmap GetBitmapFocus() |
return the bitmap for the focused state |
wx.Bitmap GetBitmapDisabled() |
return the bitmap for the disabled state |
wx.Bitmap GetBitmapSelected() |
return the bitmap for the selected state |
SetBitmapFocus(wx.Bitmap bitmap) |
set the bitmap for the focused state |
SetBitmapSelected(wx.Bitmap bitmap) |
set the bitmap for the selected state |
SetBitmapDisabled(wx.Bitmap bitmap) |
set the bitmap for the disabled state |
SetMargins(int x, int y) |
not implemented |
int GetMarginX() |
not implemented |
int GetMarginY() |
not implemented |
A video player is a nice example, where bitmap buttons are used. We can see play, pause, next, previous and volume bitmap buttons there. So we create a skeleton of a video player in our next example.
1 #!/usr/bin/python
2
3 # player.py
4
5 import wx
6
7 class MyFrame(wx.Frame):
8 def __init__(self, parent, id, title):
9 wx.Frame.__init__(self, parent, id, title, size=(350, 300))
10 panel = wx.Panel(self, -1)
11
12 pnl1 = wx.Panel(self, -1)
13 pnl1.SetBackgroundColour(wx.BLACK)
14 pnl2 = wx.Panel(self, -1 )
15
16 menubar = wx.MenuBar()
17 file = wx.Menu()
18 play = wx.Menu()
19 view = wx.Menu()
20 tools = wx.Menu()
21 favorites = wx.Menu()
22 help = wx.Menu()
23
24 file.Append(101, '&quit', 'Quit application')
25
26 menubar.Append(file, '&File')
27 menubar.Append(play, '&Play')
28 menubar.Append(view, '&View')
29 menubar.Append(tools, '&Tools')
30 menubar.Append(favorites, 'F&avorites')
31 menubar.Append(help, '&Help')
32
33 slider1 = wx.Slider(pnl2, -1, 0, 0, 1000)
34 pause = wx.BitmapButton(pnl2, -1, wx.Bitmap('icons/stock_media-pause.png'))
35 play = wx.BitmapButton(pnl2, -1, wx.Bitmap('icons/stock_media-play.png'))
36 next = wx.BitmapButton(pnl2, -1, wx.Bitmap('icons/stock_media-next.png'))
37 prev = wx.BitmapButton(pnl2, -1, wx.Bitmap('icons/stock_media-prev.png'))
38 volume = wx.BitmapButton(pnl2, -1, wx.Bitmap('icons/volume.png'))
39 slider2 = wx.Slider(pnl2, -1, 0, 0, 100, size=(120, -1))
40
41 vbox = wx.BoxSizer(wx.VERTICAL)
42 hbox1 = wx.BoxSizer(wx.HORIZONTAL)
43 hbox2 = wx.BoxSizer(wx.HORIZONTAL)
44
45 hbox1.Add(slider1, 1)
46 hbox2.Add(pause)
47 hbox2.Add(play, flag=wx.RIGHT, border=5)
48 hbox2.Add(next, flag=wx.LEFT, border=5)
49 hbox2.Add(prev)
50 hbox2.Add((150, -1), 1, flag=wx.EXPAND | wx.ALIGN_RIGHT)
51 hbox2.Add(volume, flag=wx.ALIGN_RIGHT)
52 hbox2.Add(slider2, flag=wx.ALIGN_RIGHT | wx.TOP | wx.LEFT, border=5)
53
54 vbox.Add(hbox1, 1, wx.EXPAND | wx.BOTTOM, 10)
55 vbox.Add(hbox2, 1, wx.EXPAND)
56 pnl2.SetSizer(vbox)
57
58 sizer = wx.BoxSizer(wx.VERTICAL)
59 sizer.Add(pnl1, 1, flag=wx.EXPAND)
60 sizer.Add(pnl2, flag=wx.EXPAND | wx.BOTTOM | wx.TOP, border=10)
61
62 self.SetMinSize((350, 300))
63 self.SetMenuBar(menubar)
64 self.CreateStatusBar()
65 self.SetSizer(sizer)
66 self.Centre()
67
68 class MyApp(wx.App):
69 def OnInit(self):
70 frame = MyFrame(None, -1, 'Player')
71 frame.Show(True)
72 self.SetTopWindow(frame)
73 return True
74
75 app = MyApp(0)
76 app.MainLoop()
Figure: player.py
wx.StaticLine
This widget displays a simple line on the window. It can be horizontal or vertical.
wx.StaticLine styles
- wx.LI_HORIZONTAL
- wx.LI_VERTICAL
wx.StaticLine methods |
|
bool IsVertical() |
check if the line is vertical |
integer GetDefaultSize() |
return the size of the line |
1 #!/usr/bin/python
2
3 # centraleurope.py
4
5 import wx
6
7 class MyDialog(wx.Dialog):
8 def __init__ (self, parent, ID, title):
9 wx.Dia
