| | |
Starting wxPython (GUI code)
![]() |
Well, here's the tutorial like you asked for. I've expanded on it quite a bit. Could people please read over it, in case I've made mistakes? There's a better version at my blog because it features screenshots. Link: http://fuse.baywords.com/wxpython-tutorial/
Tutorial: GUI programming with wxPython
Index
Introduction
Start
Sizers
Putting it in classes
Event handling
Binding the enter key
Creating dropdown menus
Message and file browsing dialogues
Appendix
Introduction
Hi there! So you've made a few scripts in Python. They work really well, you maybe know your way around file IO, string manipulation, and list splicing (and heck maybe even regular expressions and other cool stuff)... but you're thinking "Is that it? Surely there's more to Python." So this is it: GUI programming. Here I explain how to make the move from the command line to full-blown graphical programmes like you see in KDE, Mac or Windows.
GUI programming is fast, self-contained, and simple. So you can code your functions first, or your GUI first - it's up to you. GUI programming is like LEGO: you have a few core blocks and you build everything else from those blocks. There's not much you can't build, and once you understand how those few core blocks work, where they fit, GUI programming will become second nature. There are numerous GUI packages available to use with Python. Tkinter comes with Python, whilst Qt and GTK are used to programme KDE and Gnome for linux, respectively. Which do I suggest you use? wxPython. It's not used by Gnome, KDE, or Windows, but it is, in my opinion, the most 'Pythonic' GUI package. It's powerful and efficient. It also inherits the native look, so you don't need to worry about it not 'fitting in' or looking correct.
To start with, download wxPython here: http://www.wxpython.org/download.php
Windows, Linux/UNIX, and Mac are supported. You might like to note the words of Guido van Rossum, creator of Python:
"wxPython is the best and most mature cross-platform GUI toolkit, given a number of constraints. The only reason wxPython isn't the standard Python GUI toolkit is that Tkinter was there first."
Once you've installed that, load up IDLE or your IDE of choice, and create a new script:
Start
Moderator's note: Some programming editors don't like the extra long comment lines, so run the above code without them ...
This makes a window. It then puts a button on the window. Yes, the button takes up the whole window. Have a read, understand it, fiddle if you can, then go on. Please compile this code now to confirm it runs.
Note the bit 'redirect=False'. This is so that errors go to the interpreter instead of pop up in their own window. You may want to turn this back to True (or just delete it) later on, but for now you'll likely be encountering errors that crash the programme and require you to kill it to exit - so you want a record of the error. To kill the programme when you've made an error, right-click it in the taskbar, then select close, wait a bit, then press 'End Task' at the prompt. If this isn't working, press ctrl+alt+delete, go to processes and kill pythonw.exe (make sure it's the one that's about 23mb, not the 7mb ones, which are where you're typing your code). Onwards:
Compile and run this code first. Now look at the placement of widgets and what they are. Notice wx.TextCtrl is a text box. It's quite flexible - allowing multiline text with scroll bars, read only mode, write mode, etc. I've set it to read only for what I am about to do with it. You modify many features of a 'widget' by changing or adding things to the 'style' parameter, as above.
Also look at the position and size parameters. Fiddle with them a bit - they move and change the size of the widget. Compile and run, etc. Press the buttons. Note how they do nothing? That's because we need 'event handlers'.
Before I go on to event handlers, I'd like to make this GUI more flexible. For example, press the maximise button (top right). Notice how the programme doesn't expand to fit the screen? Let's fix that. It's fairly simple. Just add an extra 'node' in the tree; put a layer between the window frame and the widgets. It's called a 'sizer':
Tutorial: GUI programming with wxPython
Index
Introduction
Start
Sizers
Putting it in classes
Event handling
Binding the enter key
Creating dropdown menus
Message and file browsing dialogues
Appendix
Introduction
Hi there! So you've made a few scripts in Python. They work really well, you maybe know your way around file IO, string manipulation, and list splicing (and heck maybe even regular expressions and other cool stuff)... but you're thinking "Is that it? Surely there's more to Python." So this is it: GUI programming. Here I explain how to make the move from the command line to full-blown graphical programmes like you see in KDE, Mac or Windows.
GUI programming is fast, self-contained, and simple. So you can code your functions first, or your GUI first - it's up to you. GUI programming is like LEGO: you have a few core blocks and you build everything else from those blocks. There's not much you can't build, and once you understand how those few core blocks work, where they fit, GUI programming will become second nature. There are numerous GUI packages available to use with Python. Tkinter comes with Python, whilst Qt and GTK are used to programme KDE and Gnome for linux, respectively. Which do I suggest you use? wxPython. It's not used by Gnome, KDE, or Windows, but it is, in my opinion, the most 'Pythonic' GUI package. It's powerful and efficient. It also inherits the native look, so you don't need to worry about it not 'fitting in' or looking correct.
To start with, download wxPython here: http://www.wxpython.org/download.php
Windows, Linux/UNIX, and Mac are supported. You might like to note the words of Guido van Rossum, creator of Python:
"wxPython is the best and most mature cross-platform GUI toolkit, given a number of constraints. The only reason wxPython isn't the standard Python GUI toolkit is that Tkinter was there first."
Once you've installed that, load up IDLE or your IDE of choice, and create a new script:
Start
python Syntax (Toggle Plain Text)
import wx # always add this line, or your code won't compile - the wx module contains the GUI code you'll use """The start of our wxPython GUI tutorial""" app = wx.App(redirect=False) # create a wx application window = wx.Frame(None, title = 'Sample GUI App') # create a window btn = wx.Button(window) # create a button 'widget' on the window - note how the button receives the window. This creates a tree-like structure where the window is the parent node and anything else like buttons are child nodes. window.Show() # make the frame (and hence button) visible app.MainLoop() # keep things going
Moderator's note: Some programming editors don't like the extra long comment lines, so run the above code without them ...
python Syntax (Toggle Plain Text)
import wx """The start of our wxPython GUI tutorial""" app = wx.App(redirect=False) window = wx.Frame(None, title = 'Sample GUI App') btn = wx.Button(window) window.Show() app.MainLoop()
This makes a window. It then puts a button on the window. Yes, the button takes up the whole window. Have a read, understand it, fiddle if you can, then go on. Please compile this code now to confirm it runs.
Note the bit 'redirect=False'. This is so that errors go to the interpreter instead of pop up in their own window. You may want to turn this back to True (or just delete it) later on, but for now you'll likely be encountering errors that crash the programme and require you to kill it to exit - so you want a record of the error. To kill the programme when you've made an error, right-click it in the taskbar, then select close, wait a bit, then press 'End Task' at the prompt. If this isn't working, press ctrl+alt+delete, go to processes and kill pythonw.exe (make sure it's the one that's about 23mb, not the 7mb ones, which are where you're typing your code). Onwards:
python Syntax (Toggle Plain Text)
import wx """Example with custom sizes and positions for widgets and frames.""" app = wx.App(redirect=False) window = wx.Frame(None, title = 'Sample GUI App', pos = (100,100), size = (400,500)) helloBtn = wx.Button(window, label = 'Hello', pos = (400 - 60 - 15, 10), size = (60,25)) byeBtn = wx.Button(window, label = 'Bye', pos = (400 - 120 - 15, 10), size = (60,25)) printArea = wx.TextCtrl(window, pos = (10, 10), size = (400 - 120 - 15 - 10, 25), style = wx.TE_READONLY) window.Show() app.MainLoop()
Compile and run this code first. Now look at the placement of widgets and what they are. Notice wx.TextCtrl is a text box. It's quite flexible - allowing multiline text with scroll bars, read only mode, write mode, etc. I've set it to read only for what I am about to do with it. You modify many features of a 'widget' by changing or adding things to the 'style' parameter, as above.
Also look at the position and size parameters. Fiddle with them a bit - they move and change the size of the widget. Compile and run, etc. Press the buttons. Note how they do nothing? That's because we need 'event handlers'.
Before I go on to event handlers, I'd like to make this GUI more flexible. For example, press the maximise button (top right). Notice how the programme doesn't expand to fit the screen? Let's fix that. It's fairly simple. Just add an extra 'node' in the tree; put a layer between the window frame and the widgets. It's called a 'sizer':
Last edited by vegaseat; Jun 21st, 2009 at 9:54 am. Reason: added a note
Mir's Fuselage
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Sizers
Notice how the 'tree' is preserved? You can eventually trace the buttons and text box back to the parent window through the background widget.
Remember, anything starting with a lower case letter tends to be an arbitrary variable name which you can rename to whatever you want. It is the things with capitals at the start that are important. Conventionally (at least in C++, Java and Haskell), you name variables and functions: thisIsAVariable, and classes: ThisIsAClass. So you could rename 'window' and 'background' to anything you wanted. Same applies to the widget names.
Have a fiddle with that. Sizers are a bit complex at first but you should use them instead of statically defining the size and start co-ords. Note the hierarchy: horizontalBox is a child of verticalBox. Position in the programme depends on location in the code, as you'll see below.
Note I changed the button and textbox names and added a new textbox. Examine the 'style' parameters. One is for input, one is for output. The output box is also multiline - like word wrap. Because the output box isn't for typing in, it was given the read-only style. Note how to combine two styles we used something called "bit-wise OR"? You may know it as a single pipe: |
Resize the window and confirm it works.
Oh, and the wx.VERTICAL value indicates the sizer is vertical. Otherwise the default is horizontal. Proportion=0 means make the widget as small as possible. If a widget had proportion = 1 and another had proportion = 2, then the first widget would take up 1/3 of the space (1+2 = 3) whilst the second would take up 2/3, after the proportion = 0 widgets had been considered.
Now, I want you to try and add TWO more widgets to the application. Add two buttons above inputArea. These two buttons need to be side-by-side, like the load and transfer buttons.
Did it work? Or did you accidentally add them above each-other, like this:
Screenshot: http://img149.imageshack.us/img149/6...glayoutvq5.png
To fully describe the location of a widget in the frame, you need to have one vertical box sizer, and as many horizontal box sizers as you have rows with more than one widget. So if you're adding a row of widgets that are side-by-side, you'll need a new horizontal box sizer:
Screenshot: http://img237.imageshack.us/img237/2...tlayoutuq4.png
python Syntax (Toggle Plain Text)
import wx """Example with sizers for dynamic resizing.""" app = wx.App(redirect=False) window = wx.Frame(None, title = 'Sample GUI App', pos = (100,100), size = (400,500)) background = wx.Panel(window) loadBtn = wx.Button(background, label = 'Load') transferBtn = wx.Button(background, label = 'Transfer') inputArea = wx.TextCtrl(background) transferArea = wx.TextCtrl(background, style = wx.TE_READONLY | wx.TE_MULTILINE) horizontalBox = wx.BoxSizer() horizontalBox.Add(inputArea, proportion = 1, border = 0) horizontalBox.Add(transferBtn, proportion = 0, border = 0) horizontalBox.Add(loadBtn, proportion = 0, border = 0) verticalBox = wx.BoxSizer(wx.VERTICAL) verticalBox.Add(horizontalBox, proportion = 0, flag = wx.EXPAND, border = 0) verticalBox.Add(transferArea, proportion = 1, flag = wx.EXPAND, border = 0) background.SetSizer(verticalBox) window.Show() app.MainLoop()
Notice how the 'tree' is preserved? You can eventually trace the buttons and text box back to the parent window through the background widget.
Remember, anything starting with a lower case letter tends to be an arbitrary variable name which you can rename to whatever you want. It is the things with capitals at the start that are important. Conventionally (at least in C++, Java and Haskell), you name variables and functions: thisIsAVariable, and classes: ThisIsAClass. So you could rename 'window' and 'background' to anything you wanted. Same applies to the widget names.
Have a fiddle with that. Sizers are a bit complex at first but you should use them instead of statically defining the size and start co-ords. Note the hierarchy: horizontalBox is a child of verticalBox. Position in the programme depends on location in the code, as you'll see below.
Note I changed the button and textbox names and added a new textbox. Examine the 'style' parameters. One is for input, one is for output. The output box is also multiline - like word wrap. Because the output box isn't for typing in, it was given the read-only style. Note how to combine two styles we used something called "bit-wise OR"? You may know it as a single pipe: |
Resize the window and confirm it works.
Oh, and the wx.VERTICAL value indicates the sizer is vertical. Otherwise the default is horizontal. Proportion=0 means make the widget as small as possible. If a widget had proportion = 1 and another had proportion = 2, then the first widget would take up 1/3 of the space (1+2 = 3) whilst the second would take up 2/3, after the proportion = 0 widgets had been considered.
Now, I want you to try and add TWO more widgets to the application. Add two buttons above inputArea. These two buttons need to be side-by-side, like the load and transfer buttons.
Did it work? Or did you accidentally add them above each-other, like this:
Screenshot: http://img149.imageshack.us/img149/6...glayoutvq5.png
To fully describe the location of a widget in the frame, you need to have one vertical box sizer, and as many horizontal box sizers as you have rows with more than one widget. So if you're adding a row of widgets that are side-by-side, you'll need a new horizontal box sizer:
python Syntax (Toggle Plain Text)
import wx """Example with a 2D grid of sizers of sorts for more complex widget placement.""" app = wx.App(redirect=False) window = wx.Frame(None, title = 'Sample GUI App', pos = (100,100), size = (400,500)) background = wx.Panel(window) loadBtn = wx.Button(background, label = 'Load') transferBtn = wx.Button(background, label = 'Transfer') inputArea = wx.TextCtrl(background) transferArea = wx.TextCtrl(background, style = wx.TE_READONLY | wx.TE_MULTILINE) exOneBtn = wx.Button(background, label = 'example one') exTwoBtn = wx.Button(background, label = 'example two') horizontalBoxOne = wx.BoxSizer() horizontalBoxOne.Add(exOneBtn, proportion = 1, border = 0) horizontalBoxOne.Add(exTwoBtn, proportion = 1, border = 0) horizontalBoxTwo = wx.BoxSizer() horizontalBoxTwo.Add(inputArea, proportion = 1, border = 0) horizontalBoxTwo.Add(transferBtn, proportion = 0, border = 0) horizontalBoxTwo.Add(loadBtn, proportion = 0, border = 0) verticalBox = wx.BoxSizer(wx.VERTICAL) verticalBox.Add(horizontalBoxOne, proportion = 0, flag = wx.EXPAND, border = 0) verticalBox.Add(horizontalBoxTwo, proportion = 0, flag = wx.EXPAND, border = 0) verticalBox.Add(transferArea, proportion = 1, flag = wx.EXPAND, border = 0) background.SetSizer(verticalBox) window.Show() app.MainLoop()
Screenshot: http://img237.imageshack.us/img237/2...tlayoutuq4.png
Mir's Fuselage
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Putting it in classes
One more thing before event handling... let's make our code more shall we say 'standard':
I have thrown it all in a class. That's about it. Look over it, compile it, run it, understand it, etc. Note how you can always use classes to seperate parts of your programme. Generally whenever you have a new panel/frame/window, you should put it in its own class. Now on to the good stuff.
One more thing before event handling... let's make our code more shall we say 'standard':
python Syntax (Toggle Plain Text)
import wx # Declare constants in capitals and as global variables at the start of your code # This makes it much easier to change them later, especially if they are used often. # It also indicates they are values that won't change throughout the programme's execution. WINDOW_WIDTH = 400 WINDOW_HEIGHT = 500 class MainFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, title = 'Sample GUI App', pos = (200,75), size = (WINDOW_WIDTH, WINDOW_HEIGHT)) self.background = wx.Panel(self) self.loadBtn = wx.Button(self.background, label = 'Load') self.transferBtn = wx.Button(self.background, label = 'Transfer') self.inputArea = wx.TextCtrl(self.background) self.transferArea = wx.TextCtrl(self.background, style = wx.TE_READONLY | wx.TE_MULTILINE) self.horizontalBox = wx.BoxSizer() self.horizontalBox.Add(self.inputArea, proportion = 1, border = 0) self.horizontalBox.Add(self.transferBtn, proportion = 0, border = 0) self.horizontalBox.Add(self.loadBtn, proportion = 0, border = 0) self.verticalBox = wx.BoxSizer(wx.VERTICAL) self.verticalBox.Add(self.horizontalBox, proportion = 0, flag = wx.EXPAND, border = 0) self.verticalBox.Add(self.transferArea, proportion = 1, flag = wx.EXPAND, border = 0) self.background.SetSizer(self.verticalBox) self.Show() app = wx.App(redirect=False) window = MainFrame() app.MainLoop()
I have thrown it all in a class. That's about it. Look over it, compile it, run it, understand it, etc. Note how you can always use classes to seperate parts of your programme. Generally whenever you have a new panel/frame/window, you should put it in its own class. Now on to the good stuff.
Mir's Fuselage
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Event handling
I added two bind commands, one for each button, and two methods to the class. That's it. Have fun fiddling with your own methods!
(The programme transfer whatever you type into the small box, inputArea, into the larger box, transferArea once you press 'Transfer'. Alternatively, you can type a file location into the inputArea and press load and it will load that file into the transferArea.)
Binding the enter key
Maybe you'd like to know how to make a textbox call a certain method when somebody hits the enter key? Add the style wx.TE_PROCESS_ENTER to the textbox's style section, then add a new binding below the textbox: textboxWidget.Bind(wx.EVT_TEXT_ENTER, functionNameHere), like so:
Whenever you type something into the top textbox, then hit enter, it transfers it to the bottom one - just like if you'd hit the 'Transfer' button.
python Syntax (Toggle Plain Text)
import wx WINDOW_WIDTH = 400 WINDOW_HEIGHT = 500 class MainFrame(wx.Frame): """A working programme! Event handling, binding, methods, the lot.""" def __init__(self): wx.Frame.__init__(self, None, title = 'Sample GUI App', pos = (200,75), size = (WINDOW_WIDTH, WINDOW_HEIGHT)) self.background = wx.Panel(self) self.loadBtn = wx.Button(self.background, label = 'Load') self.loadBtn.Bind(wx.EVT_BUTTON, self.loadEvent) self.transferBtn = wx.Button(self.background, label = 'Transfer') self.transferBtn.Bind(wx.EVT_BUTTON, self.transferEvent) self.inputArea = wx.TextCtrl(self.background) self.transferArea = wx.TextCtrl(self.background, style = wx.TE_READONLY | wx.TE_MULTILINE) self.horizontalBox = wx.BoxSizer() self.horizontalBox.Add(self.inputArea, proportion = 1, border = 0) self.horizontalBox.Add(self.transferBtn, proportion = 0, border = 0) self.horizontalBox.Add(self.loadBtn, proportion = 0, border = 0) self.verticalBox = wx.BoxSizer(wx.VERTICAL) self.verticalBox.Add(self.horizontalBox, proportion = 0, flag = wx.EXPAND, border = 0) self.verticalBox.Add(self.transferArea, proportion = 1, flag = wx.EXPAND, border = 0) self.background.SetSizer(self.verticalBox) self.Show() def loadEvent(self, event): fileContents = open(self.inputArea.GetValue(), 'rb') self.transferArea.SetValue(fileContents.read()) fileContents.close() def transferEvent(self, event): self.transferArea.SetValue(self.inputArea.GetValue()) app = wx.App(redirect=False) window = MainFrame() app.MainLoop()
I added two bind commands, one for each button, and two methods to the class. That's it. Have fun fiddling with your own methods!
(The programme transfer whatever you type into the small box, inputArea, into the larger box, transferArea once you press 'Transfer'. Alternatively, you can type a file location into the inputArea and press load and it will load that file into the transferArea.)
Binding the enter key
Maybe you'd like to know how to make a textbox call a certain method when somebody hits the enter key? Add the style wx.TE_PROCESS_ENTER to the textbox's style section, then add a new binding below the textbox: textboxWidget.Bind(wx.EVT_TEXT_ENTER, functionNameHere), like so:
python Syntax (Toggle Plain Text)
import wx WINDOW_WIDTH = 400 WINDOW_HEIGHT = 500 class MainFrame(wx.Frame): """Now we've added an event handler for somebody hitting the enter key.""" def __init__(self): wx.Frame.__init__(self, None, title = 'Sample GUI App', pos = (200,75), size = (WINDOW_WIDTH, WINDOW_HEIGHT)) self.background = wx.Panel(self) self.loadBtn = wx.Button(self.background, label = 'Load') self.loadBtn.Bind(wx.EVT_BUTTON, self.loadEvent) self.transferBtn = wx.Button(self.background, label = 'Transfer') self.transferBtn.Bind(wx.EVT_BUTTON, self.transferEvent) self.inputArea = wx.TextCtrl(self.background, style=wx.TE_PROCESS_ENTER) self.inputArea.Bind(wx.EVT_TEXT_ENTER, self.transferEvent) self.transferArea = wx.TextCtrl(self.background, style = wx.TE_READONLY | wx.TE_MULTILINE) self.horizontalBox = wx.BoxSizer() self.horizontalBox.Add(self.inputArea, proportion = 1, border = 0) self.horizontalBox.Add(self.transferBtn, proportion = 0, border = 0) self.horizontalBox.Add(self.loadBtn, proportion = 0, border = 0) self.verticalBox = wx.BoxSizer(wx.VERTICAL) self.verticalBox.Add(self.horizontalBox, proportion = 0, flag = wx.EXPAND, border = 0) self.verticalBox.Add(self.transferArea, proportion = 1, flag = wx.EXPAND, border = 0) self.background.SetSizer(self.verticalBox) self.Show() def loadEvent(self, event): fileContents = open(self.inputArea.GetValue(), 'rb') self.transferArea.SetValue(fileContents.read()) fileContents.close() def transferEvent(self, event): self.transferArea.SetValue(self.inputArea.GetValue()) app = wx.App(redirect=False) window = MainFrame() app.MainLoop()
Whenever you type something into the top textbox, then hit enter, it transfers it to the bottom one - just like if you'd hit the 'Transfer' button.
Mir's Fuselage
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Creating dropdown menus
Now, the 'Load' button is gone, and I've added a menu bar with 2 menus each with 2 menu items. Each menu item does something, but it's only to print 'placeholder'. Let's give them a real purpose:
python Syntax (Toggle Plain Text)
import wx WINDOW_WIDTH = 400 WINDOW_HEIGHT = 500 class MainFrame(wx.Frame): """We've finally added menubars.""" def __init__(self): wx.Frame.__init__(self, None, title = 'Sample GUI App', pos = (200,75), size = (WINDOW_WIDTH, WINDOW_HEIGHT)) # Menubar. self.menuBar = wx.MenuBar() self.menuFile = wx.Menu() # Create a dropdown menu for 'File' self.menuInfo = wx.Menu() # Create a dropdown menu for 'Info' self.SetMenuBar(self.menuBar) # Tell the main frame what menubar to use. self.menuBar.Append(self.menuFile, '&File') # Add a menu. self.loadItem = self.menuFile.Append(-1, '&Load') # Add an item to the menu. self.Bind(wx.EVT_MENU, self.loadEvent, self.loadItem) # Bind an event to the menu item. Note how for menu items the Bind format is different? # I specify the widget to be bound as the last parameter, not just before 'Bind', as I usually do. # You can use this version for any binding you do, if you want. It's necessary for menus, though. self.exitItem = self.menuFile.Append(-1, 'E&xit') self.Bind(wx.EVT_MENU, self.exitEvent, self.exitItem) self.menuBar.Append(self.menuInfo, '&Info') # Add another menu. Note how their order on the menubar depends on the order they appear in your code? self.aboutItem = self.menuInfo.Append(-1, '&About') self.Bind(wx.EVT_MENU, self.aboutEvent, self.aboutItem) self.helpItem = self.menuInfo.Append(-1, '&Help') self.Bind(wx.EVT_MENU, self.helpEvent, self.helpItem) # Start of sizers and widgets contained within. self.background = wx.Panel(self) self.transferBtn = wx.Button(self.background, label = 'Transfer') self.transferBtn.Bind(wx.EVT_BUTTON, self.transferEvent) self.inputArea = wx.TextCtrl(self.background, style=wx.TE_PROCESS_ENTER) self.inputArea.Bind(wx.EVT_TEXT_ENTER, self.transferEvent) self.transferArea = wx.TextCtrl(self.background, style = wx.TE_READONLY | wx.TE_MULTILINE) self.horizontalBox = wx.BoxSizer() self.horizontalBox.Add(self.inputArea, proportion = 1, border = 0) self.horizontalBox.Add(self.transferBtn, proportion = 0, border = 0) self.verticalBox = wx.BoxSizer(wx.VERTICAL) self.verticalBox.Add(self.horizontalBox, proportion = 0, flag = wx.EXPAND, border = 0) self.verticalBox.Add(self.transferArea, proportion = 1, flag = wx.EXPAND, border = 0) self.background.SetSizer(self.verticalBox) self.Show() def loadEvent(self, event): print "placeholder" def exitEvent(self, event): print "placeholder" def helpEvent(self, event): print "placeholder" def aboutEvent(self, event): print "placeholder" def transferEvent(self, event): self.transferArea.SetValue(self.inputArea.GetValue()) app = wx.App(redirect=False) window = MainFrame() app.MainLoop()
Now, the 'Load' button is gone, and I've added a menu bar with 2 menus each with 2 menu items. Each menu item does something, but it's only to print 'placeholder'. Let's give them a real purpose:
Mir's Fuselage
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Message and file browsing dialogues
The widgets we need are wx.FileDialog and wx.MessageDialog. Now, because we only want it when we go to 'File' -> 'Load' on the menubar, we will actually create it in the 'Load' event method. Similarly, I will expand the event methods of 'Exit', 'Help', and 'About'.
Note how I imported the module os, aswell. Also notice how the 'Exit' item in the menubar now closes the programme (the X still closes it, too).
The end result: http://img291.imageshack.us/img291/7...althingwy1.png
The widgets we need are wx.FileDialog and wx.MessageDialog. Now, because we only want it when we go to 'File' -> 'Load' on the menubar, we will actually create it in the 'Load' event method. Similarly, I will expand the event methods of 'Exit', 'Help', and 'About'.
python Syntax (Toggle Plain Text)
import wx import os WINDOW_WIDTH = 400 WINDOW_HEIGHT = 500 class MainFrame(wx.Frame): """We've finally added menubars.""" def __init__(self): wx.Frame.__init__(self, None, title = 'Sample GUI App', pos = (200,75), size = (WINDOW_WIDTH, WINDOW_HEIGHT)) # Menubar. self.menuBar = wx.MenuBar() self.SetMenuBar(self.menuBar) self.menuFile = wx.Menu() self.menuInfo = wx.Menu() self.menuBar.Append(self.menuFile, '&File') self.loadItem = self.menuFile.Append(-1, '&Load') self.Bind(wx.EVT_MENU, self.loadEvent, self.loadItem) self.exitItem = self.menuFile.Append(-1, 'E&xit') self.Bind(wx.EVT_MENU, self.exitEvent, self.exitItem) self.menuBar.Append(self.menuInfo, '&Info') self.aboutItem = self.menuInfo.Append(-1, '&About') self.Bind(wx.EVT_MENU, self.aboutEvent, self.aboutItem) self.helpItem = self.menuInfo.Append(-1, '&Help') self.Bind(wx.EVT_MENU, self.helpEvent, self.helpItem) # Start of sizers and widgets contained within. self.background = wx.Panel(self) self.transferBtn = wx.Button(self.background, label = 'Transfer') self.transferBtn.Bind(wx.EVT_BUTTON, self.transferEvent) self.inputArea = wx.TextCtrl(self.background, style=wx.TE_PROCESS_ENTER) self.inputArea.Bind(wx.EVT_TEXT_ENTER, self.transferEvent) self.transferArea = wx.TextCtrl(self.background, style = wx.TE_READONLY | wx.TE_MULTILINE) self.horizontalBox = wx.BoxSizer() self.horizontalBox.Add(self.inputArea, proportion = 1, border = 0) self.horizontalBox.Add(self.transferBtn, proportion = 0, border = 0) self.verticalBox = wx.BoxSizer(wx.VERTICAL) self.verticalBox.Add(self.horizontalBox, proportion = 0, flag = wx.EXPAND, border = 0) self.verticalBox.Add(self.transferArea, proportion = 1, flag = wx.EXPAND, border = 0) self.background.SetSizer(self.verticalBox) self.Show() def loadEvent(self, event): """Create a load dialogue box, load a file onto transferArea, then destroy the load box.""" loadBox = wx.FileDialog(self, message="Open", defaultDir=os.getcwd(), defaultFile="", style=wx.OPEN) if loadBox.ShowModal() == wx.ID_OK: # When the user clicks 'Open', do this: fileName = loadBox.GetPath() target = open(fileName, 'rb') self.transferArea.SetValue(target.read()) target.close() loadBox.Destroy() # Note how I destroy the load box now so I can return to the programme. def exitEvent(self, event): """Exit programme.""" self.Destroy() def helpEvent(self, event): """Writes help information to the transferArea.""" self.transferArea.SetValue("""You could type a help file for the user here.""") def aboutEvent(self, event): """Pops up a little message box that tells you stuff.""" aboutInfo = """Hello there! I am a person, and I coded this programme.""" aboutBox = wx.MessageDialog(self, message=aboutInfo, caption='About', style = wx.ICON_INFORMATION | wx.STAY_ON_TOP | wx.OK) if aboutBox.ShowModal() == wx.ID_OK: # Until the user clicks OK, show the message aboutBox.Destroy() def transferEvent(self, event): """Moves data in inputArea onto transferArea.""" self.transferArea.SetValue(self.inputArea.GetValue()) app = wx.App(redirect=False) window = MainFrame() app.MainLoop()
Note how I imported the module os, aswell. Also notice how the 'Exit' item in the menubar now closes the programme (the X still closes it, too).
The end result: http://img291.imageshack.us/img291/7...althingwy1.png
Last edited by Fuse; Jun 9th, 2008 at 6:11 am.
Mir's Fuselage
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
The end result: http://img291.imageshack.us/img291/7...althingwy1.png
If you have any further inquiries, do a google search, read a few forum threads, and read the documentation for the method or class in question. Feel free to ask me (and others) here, too, but try and make it a last resort - you learn more through trial and error, generally.
Also, if you have any corrections, tips, or suggestions for this tutorial, please leave a comment in this thread, or on my blog: http://fuse.baywords.com/wxpython-tutorial/
Appendix
Widgets
wx.TextCtrl(parent, style=?)
Standard GUI textbox.
Styles:
wx.TE_PROCESS_ENTER
-- When the user hits the enter key, a method is called. The method called depends on the binding, as in Binding the enter key above.
wx.TE_MULTILINE
-- Textbox spans multiple lines, starting a new line when the sentence would otherwise go off-screen. Has a vertical scrollbar by default.
wx.HSCROLL
-- Create a horizontal scrollbar. I believe this works for some other widgets (hence no 'TE' prefix), but haven't tested it.
wx.TE_READONLY
-- Textbox cannot be typed in, but may still be written to by calling widgetName.SetValue(valueHere)
The are more. If you know what they are, please post them in this thread so that a moderator can update this section! I can't find them in the API... no surprise there.
wx.Button(parent, label='?')
Standard GUI button.
Styles: ?
wx.SearchCtrl(parent, style=?)
This class inherits wx.TextCtrl, so generally all the stuff (e.g. styles) you can do with that applies here.
This widget can have a dropdown menu added to it. There is a semi-decent explanation here: http://www.wxpython.org/docs/api/wx....trl-class.html
I personally use this class for the SetDescriptiveText("text here") method that allows you to display greyed out text which dissapears when the user clicks and types in the box. You can turn off the left-hand side search icon, too.
Extra styles: ?
wx.MenuBar and wx.Menu
The manu bar at the top left-hand side of almost all programmes in almost all OS'es
Sample use:
# Create menubar
self.menuBar = wx.MenuBar()
# Set it as the programme's menubar
self.SetMenuBar(self.menuBar)
# Create a menu
self.menuFile = wx.Menu()
# Add the menu to the menubar
self.menuBar.Append(self.menuFile, '&File')
# Create and add an item to the menu
self.loadItem = self.menuFile.Append(-1, '&Load')
# Give the item an event handler
self.Bind(wx.EVT_MENU, self.loadEvent, self.loadItem)
wx.MessageDialog
Used for errors, warnings, about, etc.
Styles here: http://www.wxpython.org/docs/api/wx....log-class.html
Events
wx.EVT_TEXT_ENTER
wx.EVT_BUTTON
wx.EVT_MENU
There are many more here: http://www.wxpython.org/docs/api/wx.Event-class.html
I believe they are all listed, but navigating to find the event you want is very much a guessing game.
References
Style guide: http://wiki.wxpython.org/wxPython%20Style%20Guide
-- It would be wise to read that style guide. It was written in 2006, so it is relatively up to date, and offers good advice on style to use for your more advanced GUI programming.
vegasat's binding example:
http://www.daniweb.com/forums/post335474-3.html
-- How to place widgets in a class, how to do event handling.
wxPython standard documentation:
http://www.wxpython.org/docs/api/wx-module.html
-- Don't rely on it for anything useful. There's a reason I wrote this guide.
General Python style guide:
http://www.python.org/dev/peps/pep-0008/
-- How you should probably format your code.
http://fuse.baywords.com/wxpython-tutorial/
If you have any further inquiries, do a google search, read a few forum threads, and read the documentation for the method or class in question. Feel free to ask me (and others) here, too, but try and make it a last resort - you learn more through trial and error, generally.
Also, if you have any corrections, tips, or suggestions for this tutorial, please leave a comment in this thread, or on my blog: http://fuse.baywords.com/wxpython-tutorial/
Appendix
Widgets
wx.TextCtrl(parent, style=?)
Standard GUI textbox.
Styles:
wx.TE_PROCESS_ENTER
-- When the user hits the enter key, a method is called. The method called depends on the binding, as in Binding the enter key above.
wx.TE_MULTILINE
-- Textbox spans multiple lines, starting a new line when the sentence would otherwise go off-screen. Has a vertical scrollbar by default.
wx.HSCROLL
-- Create a horizontal scrollbar. I believe this works for some other widgets (hence no 'TE' prefix), but haven't tested it.
wx.TE_READONLY
-- Textbox cannot be typed in, but may still be written to by calling widgetName.SetValue(valueHere)
The are more. If you know what they are, please post them in this thread so that a moderator can update this section! I can't find them in the API... no surprise there.
wx.Button(parent, label='?')
Standard GUI button.
Styles: ?
wx.SearchCtrl(parent, style=?)
This class inherits wx.TextCtrl, so generally all the stuff (e.g. styles) you can do with that applies here.
This widget can have a dropdown menu added to it. There is a semi-decent explanation here: http://www.wxpython.org/docs/api/wx....trl-class.html
I personally use this class for the SetDescriptiveText("text here") method that allows you to display greyed out text which dissapears when the user clicks and types in the box. You can turn off the left-hand side search icon, too.
Extra styles: ?
wx.MenuBar and wx.Menu
The manu bar at the top left-hand side of almost all programmes in almost all OS'es
Sample use:
# Create menubar
self.menuBar = wx.MenuBar()
# Set it as the programme's menubar
self.SetMenuBar(self.menuBar)
# Create a menu
self.menuFile = wx.Menu()
# Add the menu to the menubar
self.menuBar.Append(self.menuFile, '&File')
# Create and add an item to the menu
self.loadItem = self.menuFile.Append(-1, '&Load')
# Give the item an event handler
self.Bind(wx.EVT_MENU, self.loadEvent, self.loadItem)
wx.MessageDialog
Used for errors, warnings, about, etc.
Styles here: http://www.wxpython.org/docs/api/wx....log-class.html
Events
wx.EVT_TEXT_ENTER
wx.EVT_BUTTON
wx.EVT_MENU
There are many more here: http://www.wxpython.org/docs/api/wx.Event-class.html
I believe they are all listed, but navigating to find the event you want is very much a guessing game.
References
Style guide: http://wiki.wxpython.org/wxPython%20Style%20Guide
-- It would be wise to read that style guide. It was written in 2006, so it is relatively up to date, and offers good advice on style to use for your more advanced GUI programming.
vegasat's binding example:
http://www.daniweb.com/forums/post335474-3.html
-- How to place widgets in a class, how to do event handling.
wxPython standard documentation:
http://www.wxpython.org/docs/api/wx-module.html
-- Don't rely on it for anything useful. There's a reason I wrote this guide.

General Python style guide:
http://www.python.org/dev/peps/pep-0008/
-- How you should probably format your code.
http://fuse.baywords.com/wxpython-tutorial/
Last edited by Fuse; Jun 9th, 2008 at 6:11 am.
Mir's Fuselage
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Source code for final programme and hard copy of tutorial:
http://www.mediafire.com/?fwdynnltynj
Also, you might be interested in making an executable version of your programme in Windows. I suggest you use py2exe: http://www.py2exe.org/
The executable produced is minuscule in size, but the catch is that needs like half the Python libraries as well as the full wx library to run. So most programmes you make will be at about the 15mb mark. They can easily be compressed to less than half that size, however using 7-zip, rar or zip for example.
py2exe seems to produce application that have more of a Windows 95/98/ME feel rather than an XP feel, I've found. I'm not sure if I'm missing libraries or what, but at the end of the day, it runs perfectly and looks native enough, so. But if anybody knows how to keep the XP theme it has in IDLE, let me know.
http://www.mediafire.com/?fwdynnltynj
Also, you might be interested in making an executable version of your programme in Windows. I suggest you use py2exe: http://www.py2exe.org/
The executable produced is minuscule in size, but the catch is that needs like half the Python libraries as well as the full wx library to run. So most programmes you make will be at about the 15mb mark. They can easily be compressed to less than half that size, however using 7-zip, rar or zip for example.
py2exe seems to produce application that have more of a Windows 95/98/ME feel rather than an XP feel, I've found. I'm not sure if I'm missing libraries or what, but at the end of the day, it runs perfectly and looks native enough, so. But if anybody knows how to keep the XP theme it has in IDLE, let me know.
Mir's Fuselage
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Because what's not to like about being killed by a toilet seat from Mir's atmospheric re-entry?
Here is a templet file that allows you to package your wxPython program to an executable file with the py2exe module. It contains an XML manifest that gives the wxPython widgets a Windows XP appearance on XP machines:
Any questions? Ask in a separate thread in the Python forum please.
python Syntax (Toggle Plain Text)
# Py2Exe version 6.6 setup file for wxPython GUI programs. # Creates a single executable file. # # Simply change the filename entry to whatever you called your source file. # Optionally edit the version info and add the name of your icon file. # It's easiest to save the modified templet code for instance as # wx2exe.py to the same folder that containes your source file # and the optional iconfile like "icon.ico" # # Now run the customized wx2exe.py ... # # Two subfolders will be created called build and dist. # The dist folder contains your .exe file, MSVCR71.dll and w9xpopen.exe # w9xpopen.exe is needed for os.popen() only. # Your .exe file contains your byte code, all needed modules # and the Python interpreter. # The MSVCR71.dll can be distributed, but is often already in # the Windows system32 folder. # The build folder is for info only and can be deleted. from distutils.core import setup import py2exe import sys # Enter the filename of your wxPython source code file to compile. # Your distribution file will be this filename with a .exe extension. filename = "wxButton1.py" # this creates the filename of your .exe file in the dist folder if filename.endswith(".py"): distribution = filename[:-3] elif filename.endswith(".pyw"): distribution = filename[:-4] # if run without args, build executables in quiet mode if len(sys.argv) == 1: sys.argv.append("py2exe") sys.argv.append("-q") class Target: def __init__(self, **kw): self.__dict__.update(kw) # for the versioninfo resources, edit to your needs self.version = "0.6.6" self.company_name = "My Company" self.copyright = "no copyright" # fill this in with your own program description self.name = "WxPython Button Test" # start of manifest for custom icon and appearance ... # # This XML manifest will be inserted as resource into your .exe file # It gives the controls a Windows XP appearance (if run on XP) # manifest_template = ''' <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity version="5.0.0.0" processorArchitecture="x86" name="%(prog)s" type="win32" /> <description>%(prog)s Program</description> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly> </dependency> </assembly> ''' RT_MANIFEST = 24 # description is the versioninfo resource # script is the wxPython code file # manifest_template is the above XML code # distribution will be the exe filename # icon_resource is optional, remove any comment and give it # an iconfile you have, otherwise a default icon is used # dest_base will be the exe filename test_wx = Target( description = "A GUI app", script = filename, other_resources = [(RT_MANIFEST, 1, manifest_template % dict(prog=distribution))], #icon_resources = [(1, "icon.ico")], dest_base = distribution) # end of manifest setup( options = {"py2exe": {"compressed": 1, "optimize": 2, "ascii": 1, "bundle_files": 1}}, zipfile = None, windows = [test_wx] )
Last edited by Ene Uran; Jun 9th, 2008 at 12:22 pm.
drink her pretty
![]() |
Similar Threads
- Starting Python (Python)
- Autoupdater in wxPython (Python)
- what is python (Python)
Other Threads in the Python Forum
- Previous Thread: sudoku solver problem
- Next Thread: Password Generator
| Thread Tools | Search this Thread |
alarm anydbm app beginner cipher cmd conversion coordinates corners curves cx-freeze data decimals definedlines development dictionary directory dynamic error events excel feet file float format function generator getvalue halp handling homework http import input ip itunes keycontrol leftmouse line linux list lists loan loop maintain maze millimeter module mouse number numbers output parsing path prime programming push py2exe pygame pymailer python queue random rational raw_input recursion recursive schedule screensaverloopinactive script searchingfile slicenotation split sqlite ssh string strings sudokusolver text threading time tlapse tooltip tuple tutorial type ubuntu unicode url urllib urllib2 variable variables ventrilo vigenere web webservice wikipedia wxpython xlwt






