This article explains how to create GUI interface with Python. Python is very handy and a useful programming language. Python is quite easy to learn, yet it is highly capable of creating complex commercial quality applications. Since a GUI tool kit is not included in its standard module, developers require extra work to write GUI applications in Python. wxPython, a tool kit provide a solution for this issue. This article provides an introduction of wxPython and shows how this tool kit works.
The targeted audience of this article is expected to have working experience with Python. Readers required the basic knowledge of Python. This article will not provide neither the basic programming of Python nor the explanations of how to create the development environment for Python. This article assumes that Python and its development environment are already installed in their computers.
wxPython is a GUI tool kit, which can work with Python. The library of this GUI tool kit is written in C++, and the library is rapped as a Python module. Since wxPython is a cross platform library, it works with Windows and other major OSes. This article mainly explains for Windows environment. However, the subjects are basically applicable to other platforms. The OS specific parts are the installation process of Python and wxPython, and development environments.
Let's start writing an application, which simply displays a windows.
Its source code is shown below.
- sample_2_1.py
import wx class SampleApp(wx.App): def OnInit(self): self.init_frame() return True def init_frame(self): self.frm_main = wx.Frame(None) self.frm_main.SetTitle("Hello, wxPython!") self.frm_main.SetSize((400, 200)) self.frm_main.Show() if __name__ == "__main__": app = SampleApp(False) app.MainLoop()
sample_2_1.py is the basic and standard form of wxPython code. This can be considered as a mold for wxPython applications. First, “SampleApp” class, which inherits “wx.App,” is defined. “wxApp” is an application class, and it is the absolute requirement for creating any wxPython program. At “SampleApp,” this overrides “OnOnit()” method. This method crates and displays a window, and it returns “true.” “wxFrame” sets up a standard window. To execute this script, the object of “SampleApp” is created, and “MainLoop()” method is called. At “MainLoop()” method of “wxApp,” “OnInit()” is called, internally. After “OnInit()” method generates a window, the program starts the loop, which processes the window event. Until the window is closed, “MainLoop()” interrupts other methods.
Let's put several Controls on the window.
The source code is show below.
- sample_2_2.py
import wx class SampleApp(wx.App): def OnInit(self): self.init_frame() return True def init_frame(self): self.frm_main = wx.Frame(None) self.sizer = wx.BoxSizer() self.frm_main.SetSizer(self.sizer) self.txt_title = wx.TextCtrl(self.frm_main) self.sizer.Add(self.txt_title, 1, wx.ALIGN_CENTER_VERTICAL) self.btn_submit = wx.Button(self.frm_main) self.btn_submit.SetLabel("Submit") self.sizer.Add(self.btn_submit, 0, wx.ALIGN_CENTER_VERTICAL) self.frm_main.SetTitle("sample_2_2") self.frm_main.SetSize((400, 200)) self.frm_main.Show() if __name__ == "__main__": app = SampleApp(False) app.MainLoop()
“wx.TextCtrl” and “wx.Button” are placed in on the window. wxPython does not allow directly putting a Control on “wx.Frame.” “wx.Sizer” must be used. Consider A “wx.Sizer” as container for Controls, and it also works as the layout manager. A “wx.Sizer” is set on “wx.Frame,” and the Controls are placed on the “wx.Sizer.” The image of how these are put is shown below. On sample_2_2.py, “wx.BoxSizer” forces to put the Controls in one line.
Let's see how events are processed as controls are used. At the sample application of sample_2_3, as inputting text in the text Control and pushing the button, the window title is changed to the text, which has written in the text Control.
The source code of sample_2_3 is shown below
- sample_2_3.py
import wx class SampleApp(wx.App): def OnInit(self): self.init_frame() return True def init_frame(self): self.frm_main = wx.Frame(None) self.sizer = wx.BoxSizer() self.frm_main.SetSizer(self.sizer) self.txt_title = wx.TextCtrl(self.frm_main) self.sizer.Add(self.txt_title, 1, wx.ALIGN_CENTER_VERTICAL) self.btn_submit = wx.Button(self.frm_main) self.btn_submit.SetLabel("Change Title") self.btn_submit.Bind(wx.EVT_BUTTON, self.on_submit) self.sizer.Add(self.btn_submit, 0, wx.ALIGN_CENTER_VERTICAL) self.frm_main.SetTitle("sample_2_3") self.frm_main.SetSize((400, 200)) self.frm_main.Show() def on_submit(self, event): self.frm_main.SetTitle(self.txt_title.GetValue()) self.txt_title.SetValue("") if __name__ == "__main__": app = SampleApp(False) app.MainLoop()
The basic structure of this program is very similar to sample_2_2's. There are not many differences between this program and the previous one. “on_submit()” method is added to SampleApp, and the method bunds to “wx.Button” at the line of 39. As the button is pushed, “wx.EVT_BUTTON” event is generated and “on_submit()” is called.
In previous sections, xwPython enables to GUI programming with a text editor. From now on, xwPython employs wxGlade, a VisualBasic-like from designer. Developers can create a GUI application with a WYSWYG type editing system. To use wxGlade, the placement of GUI controls is easier and more intuitive than text editor. This article uses the version 0.6.3 of wxGlade.
wxGlade employs a simple drag-and-drop method, and GUI Controls are placed by the actions of mouse. GUI location data can be output in Python source code format, but XRC (XML) format is highly recommended.
When GUI location data is output as a part of Python source code, developers may attempt to modify the source code, such as adding event handlers. In fact, this is not a smart move. Each time location of a GUI part is changed, event handlers may removed from Python source code, which is output from wxGlade. To avoid this happening, the GUI location data and event handlers are stored separately in different formats. The GUI location data, which is generated from wxGlade, is kept in XRC format and separated from the invent handlers, which are treated as a part of source code. The GUI location data and event handlers are developed separately as well as independently.
wxGlade is consists of three windows. As booting wxGlade, these three windows appears.
In Main window, GUI parts, such as Frame and Control, are listed in the window. These parts are capable of drag-and-drop. Tree windows displays the hierarchical relations between GUI parts. Property windows manages the configurations of parameters of selected Controls.
The details of how these windows work will be explained in the following sections.
At the beginning, let's displays a simple window.
To execute this application, its appearance is almost identical to sample_2_2. Look at the steps of how this application is created. To use wxGlade, first, create at the location information of the GUI part.
To boot wxGlade, the basic information of configuration (listed below) is modified.
- The output format is specified to the XRC format.
- At Encoding section, UTF-8 is selected.
- The output file is named.
As finishing the modification of the basic information, “wx.Frame” is added. To push “Add a Frame” button, a dialog box, which asks to select a frame class, appears. “wxFrame” is selected and “OK” is pushed.
Then, “frame_1” is added to Tree windows, and a new design image is appeared.
To select “frame_1” at Tree window, it allows editing Frame properties of “frame_1.” At this point, Name Attribute is modified, and “frame_main” is inputed.
This is an image as Design displays a window, which wxGlade determines.
The window title and other specific configurations are remained in the source code side. The GUI location data is output in XRC format. Go to menu of Main window, select “Generate Code,” and output an XRC file. The location of the output file is set at the properties.
The output is shown below.
- sample_3_3.xrc
<?xml version="1.0" encoding="UTF-8"?> <!-- generated by wxGlade 0.6.3 on Wed Nov 16 22:19:37 2011 --> <resource version="2.3.0.1"> <object class="wxFrame" name="frame_main"> <style>wxDEFAULT_FRAME_STYLE</style> <title>frame_main</title> <object class="wxBoxSizer"> <orient>wxVERTICAL</orient> </object> </object> </resource>
The source code, which enables sample_3.3 to create a window, is shown below.
- sample_3.3.py
import wx from wx import xrc XRC_FILE = "sample_3_3.xrc" class SampleApp(wx.App): def OnInit(self): self.res = xrc.XmlResource(XRC_FILE) self.init_frame() return True def init_frame(self): self.frm_main = self.res.LoadFrame(None, "frame_main") self.frm_main.SetTitle("sample_3_3") self.frm_main.SetSize((400, 200)) self.frm_main.Show() if __name__ == "__main__": app = SampleApp(False) app.MainLoop()
At the line of 8, “xrc.XmlResource(XRC_FILE)” reads the XRC file (sample_3.3.xrc) and makes the XRC file an object. At the line of 13, “LoadFrame()” method loads the information of “wx.Frame” from the resource. To load the information, the required parameter is the Name attribute of Frame. To create GUI with wxGlade, the names of GUI parts such as the names of controls should be descriptive.
Next, this shows how to place Controls. Put “wx.TextCtrl” and “wx.Button” in the window, which is created at sample_3_3.
As explained in the previous sections, Controls need “wx.Sizer” for its placement. “free_main” (the names of main window) has already be equipped with a “wx.Sizer.” However, this “wx.Sizer” is actually a “wx.BoxSizer” and puts Controls only vertically. This is not suitable for this situation. To use Tree window, this “wx.BoxSizer” is removed. Then, select “Add a BoxSizer” from Main window, and add sizer to “frame_main” design image. At this time, “Horizontal” is selected at Orientation, and the number of slots is 2.
“Add a TextCtrl” is selected from Main window, and “wx.TextCtrl” is placed at the left side of the window. Apply to the same manner, “wx.button is added to the right side of the window.
To configure the name attribute of Control, output the source file as sample_3_4.xrc.
- sample_3_4.xrc
<?xml version="1.0" encoding="UTF-8"?> <!-- generated by wxGlade 0.6.3 on Wed Nov 16 22:15:53 2011 --> <resource version="2.3.0.1"> <object class="wxFrame" name="frame_main"> <style>wxDEFAULT_FRAME_STYLE</style> <size>400, 300</size> <title>main_frame</title> <object class="wxBoxSizer"> <orient>wxHORIZONTAL</orient> <object class="sizeritem"> <option>1</option> <flag>wxALIGN_CENTER_VERTICAL</flag> <object class="wxTextCtrl" name="text_input"> </object> </object> <object class="sizeritem"> <flag>wxALIGN_CENTER_VERTICAL</flag> <object class="wxButton" name="button_submit"> <label>Submit</label> </object> </object> </object> </object> </resource>
The source code of displaying a window defines two tasks: reading the resource file and displaying it. Therefore, this is not much different from the source code of sample_3_3.
- sample_3_4.py
import wx from wx import xrc XRC_FILE = "sample_3_4.xrc" class SampleApp(wx.App): def OnInit(self): self.res = xrc.XmlResource(XRC_FILE) self.init_frame() return True def init_frame(self): self.frm_main = self.res.LoadFrame(None, "frame_main") self.txt_input = xrc.XRCCTRL(self.frm_main, "text_input") self.btn_submit = xrc.XRCCTRL(self.frm_main, "button_submit") self.frm_main.SetTitle("sample_3_4") self.frm_main.SetSize((400, 200)) self.frm_main.Show() if __name__ == "__main__": app = SampleApp(False) app.MainLoop()
As the source code of sample_3_4 shows, an upside of using wxGlade is that the addition of new functions requires only minor alternations in the source code.
Event processing is added to sample_3_4. The basic function of sample_3_4 is the same as the sample_2_3. As text is input to TextCtrl and Button is pushed, Frame title is changed.
Since there is no change in the locations of GUI parts, XRC file of sample_3_5 is almost identical to sample_3_4's. The difference is only the label of button.
- sample_3_5.xrc
<?xml version="1.0" encoding="UTF-8"?> <!-- generated by wxGlade 0.6.3 on Wed Nov 16 22:16:28 2011 --> <resource version="2.3.0.1"> <object class="wxFrame" name="frame_main"> <style>wxDEFAULT_FRAME_STYLE</style> <size>400, 300</size> <title>main_frame</title> <object class="wxBoxSizer"> <orient>wxHORIZONTAL</orient> <object class="sizeritem"> <option>1</option> <flag>wxALIGN_CENTER_VERTICAL</flag> <object class="wxTextCtrl" name="text_title"> </object> </object> <object class="sizeritem"> <flag>wxALIGN_CENTER_VERTICAL</flag> <object class="wxButton" name="button_submit"> <label>ChangeTitle</label> </object> </object> </object> </object> </resource>
The source code, sample_3_5.py, is shown below.
- sample_3_5.py
import wx from wx import xrc XRC_FILE = "sample_3_5.xrc" class SampleApp(wx.App): def OnInit(self): self.res = xrc.XmlResource(XRC_FILE) self.init_frame() return True def init_frame(self): self.frm_main = self.res.LoadFrame(None, "frame_main") self.txt_title = xrc.XRCCTRL(self.frm_main, "text_title") self.btn_submit = xrc.XRCCTRL(self.frm_main, "button_submit") self.btn_submit.Bind(wx.EVT_BUTTON, self.on_submit) self.frm_main.SetTitle("sample_3_5") self.frm_main.SetSize((400, 200)) self.frm_main.Show() def on_submit(self, event): self.frm_main.SetTitle(self.txt_title.GetValue()) self.txt_title.SetValue("") if __name__ == "__main__": app = SampleApp(False) app.MainLoop()
The style of event processing is very similar to the sample_2_3's. The event processing is defined with The “Bind()” method and an event handler. The difference is that the Control is read form the resource of XRC. To read the Control, “xrc.XRCCTRL()” is used. The required parameter is the name attribute of its parent control.
To use wxGade, the location of GUI parts is easily changed. To modify only a XRC file, the location can be change while its source file remains the same, For example, to modify sample_3_5, the settings of the two controls are changed from the horizontal to the vertical.
At “wx.BoxSizer” the orientation is changed from HORIZONTAL to VERTICAL, and these Controls are placed in parallel, yet there is no gap between these Cntrols, and it reduces the legibility and functionality of the application. To avoid this happening, a border is put between these controls.
The location data (XRC file) of sample_3_6 is below.
- sample_3_6.xrc
<?xml version="1.0" encoding="UTF-8"?> <!-- generated by wxGlade 0.6.3 on Fri Nov 25 13:03:14 2011 --> <resource version="2.3.0.1"> <object class="wxFrame" name="frame_main"> <style>wxDEFAULT_FRAME_STYLE</style> <size>400, 300</size> <title>main_frame</title> <object class="wxBoxSizer"> <orient>wxVERTICAL</orient> <object class="sizeritem"> <flag>wxALL|wxALIGN_CENTER_VERTICAL</flag> <border>10</border> <object class="wxTextCtrl" name="text_title"> <size>200, 22</size> </object> </object> <object class="sizeritem"> <flag>wxALL|wxALIGN_CENTER_VERTICAL</flag> <border>10</border> <object class="wxButton" name="button_submit"> <label>ChangeTitle</label> </object> </object> </object> </object> </resource>
The source code of sample_3_6.py is show below.
- sample_3_6.py
import wx from wx import xrc XRC_FILE = "sample_3_6.xrc" class SampleApp(wx.App): def OnInit(self): self.res = xrc.XmlResource(XRC_FILE) self.init_frame() return True def init_frame(self): self.frm_main = self.res.LoadFrame(None, "frame_main") self.txt_title = xrc.XRCCTRL(self.frm_main, "text_title") self.btn_submit = xrc.XRCCTRL(self.frm_main, "button_submit") self.btn_submit.Bind(wx.EVT_BUTTON, self.on_submit) self.frm_main.SetTitle("sample_3_6") self.frm_main.SetSize((400, 200)) self.frm_main.Show() def on_submit(self, event): self.frm_main.SetTitle(self.txt_title.GetValue()) self.txt_title.SetValue("") if __name__ == "__main__": app = SampleApp(False) app.MainLoop()
The names of the inputed XRC file and the initial title of window are different from the source code of sample_3_5.py.
To put apart the location information of GUI parts from the source code of its program, and to store the location information in a XRC resource file independently, the design of window and processing of events can be easily developed. To employ wxGlade of WYSWYG design function, the progress of the GUI development can be visually and easily recognized.
Up this section, this article has discussed how to create a windows GUI application. In this section, it shows how to create an executable file (.exe) from the Python and other files. To use a tool called “py2exe”, a Python application is converted to Windows executable file. To convert to an executable file, the application can be distributed easily. This means that the program can be installed to any PC regardless of the installation of Python, and the installation of Python is not required.
Let's convert sample_3_5 to the executable file. Create a short Python program for setting up py2exe. This program includes necessary information to generate an executable file on py2exe.
- build.py
from distutils.core import setup import py2exe py2exe_options = { "compressed": 1, "optimize": 2, "bundle_files": 2} setup( options = {"py2exe": py2exe_options}, windows = [ {"script" : "sample_3_5.py"}], zipfile = None)
To execute py2exe, py2exe is treated as a parameter, and build.py is executed. But this method is little complex. The creation of a patch file, which handles all needed execution procedures, is highly recommended.
- build.bat
build.py py2exe copy .\msvcp90.dll .\dist\ copy .\sample_3_5.xrc .\dist\ move .\dist .\sample_3_5-bin rmdir /s /q .\build
The source code file and XRC resource file of sample_3_5 and a DLL file, which is called “msvcp90.dll” are put into the same file as build.bat. Executing build.bat file, an executable file is created under the sample_3_5-bin folder. To distribute the application, the folder is handed out. The target PCs do not require Python and wxPython.
- 2012/4/02 The article is initially uploaded. sizeritem ?xml version=