wxPython is a Python wrapper for wxWidgets (which is written in C++), a popular cross-platform GUI toolkit. Developed by Robin Dunn along with Harri Pasanen, wxPython is implemented as a Python extension module.
Just like wxWidgets, wxPython is also a free software. It can be downloaded from the official website http://wxpython.org. Binaries and source code for many operating system platforms are available for download on this site.
Principal modules in wxPython API include a core module. It consists of wxObject class, which is the base for all classes in the API. Control module contains all the widgets used in GUI application development. For example, wx.Button, wx.StaticText (analogous to a label), wx.TextCtrl (editable text control), etc.
wxPython API has GDI (Graphics Device Interface) module. It is a set of classes used for drawing on widgets. Classes like font, color, brush, etc. are a part of it. All the container window classes are defined in Windows module.
Official website of wxPython also hosts Project Phoenix – a new implementation of wxPython for Python 3.*. It focuses on improving speed, maintainability, and extensibility. The project began in 2012 and is still in beta stage.
Prebuilt binaries for Windows OS (both 32 bit and 64 bit) are available on http://www.wxpython.org/download.php page. Latest versions of installers available are − wxPython3.0-win32-3.0.2.0-py27.exe for 32-bit Python 2.7 wxPython3.0-win64-3.0.2.0-py27.exe for 64-bit Python 2.7
wxPython demo, samples and wxWidgets documentation is also available for download on the same page.
wxPython3.0-win32-docs-demos.exe
wxPython binaries for many Linux distros can be found in their respective repositories. Corresponding package managers will have to be used to download and install. For instance on Debian Linux, following command should be able to install wxPython.
sudo apt-get install python-wxgtk3.0
Prebuilt binaries for MacOS in the form of disk images are available on the download page of the official website.
A simple GUI application displaying Hello World message is built using the following steps −
Import wx module.
Define an object of Application class.
Create a top level window as object of wx.Frame class. Caption and size parameters are given in constructor.
Although other controls can be added in Frame object, their layout cannot be managed. Hence, put a Panel object into the Frame.
Add a StaticText object to display ‘Hello World’ at a desired position inside the window.
Activate the frame window by show() method.
Enter the main event loop of Application object.
import wx app = wx.App() window = wx.Frame(None, title = "wxPython Frame", size = (300,200)) panel = wx.Panel(window) label = wx.StaticText(panel, label = "Hello World", pos = (100,50)) window.Show(True) app.MainLoop()
The above code produces the following output −
wxFrame object is the most commonly employed top level window. It is derived from wxWindow class. A frame is a window whose size and position can be changed by the user. It has a title bar and control buttons. If required, other components like menu bar, toolbar and status bar can be enabled. A wxFrame window can contain any frame that is not a dialog or another frame.
Creating a good looking GUI by manual coding can be tedious. A visual GUI designer tool is always handy. Many GUI development IDEs targeted at wxPython are available. Following are some of them −
wxFormBuilder is an open source, cross-platform WYSIWYG GUI builder that can translate the wxWidget GUI design into C++, Python, PHP or XML format. A brief introduction to usage of wxFormBuilder is given here.
First of all the latest version of wxFormBuilder needs to be downloaded and installed from http://sourceforge.net/projects/wxformbuilder/. On opening the application, a new project with blank grey area at the center appears.
Give a suitable name to the project and choose Python as code generation language. This is done in the Object properties window as shown in the following image −
Then from ‘Forms’ tab of components palette, choose Frame.
Add a vertical wxBoxSizer from ‘Layouts’ tab.
Add necessary controls in the Box with suitable captions. Here, a StaticText (label), two TextCtrl objects (text boxes) and a wxButton object are added. The frame looks like the following image −
Enable Expand and Stretch on these three controls. In the object properties for wxButton object, assign a function findsquare() to OnButtonClick event.
Save the project and press F8 to generate Python code for developed GUI. Let the generated file be named as Demo.py
In the executable Python script, import demo.py and define FindSquare() function. Declare Application object and start a main event loop. Following is the executable code −
import wx #import the newly created GUI file import demo class CalcFrame(demo.MyFrame1): def __init__(self,parent): demo.MyFrame1.__init__(self,parent) def FindSquare(self,event): num = int(self.m_textCtrl1.GetValue()) self.m_textCtrl2.SetValue (str(num*num)) app = wx.App(False) frame = CalcFrame(None) frame.Show(True) #start the applications app.MainLoop()
The above code produces the following output −
Original wxWidgets (written in C++) is a huge class library. GUI classes from this library are ported to Python with wxPython module, which tries to mirror the original wxWidgets library as close as possible. So, wx.Frame class in wxPython acts much in the same way as wxFrame class in its C++ version.
wxObject is the base for most of the classes. An object of wxApp (wx.App in wxPython) represents the application itself. After generating the GUI, application enters in an event loop by MainLoop() method. Following diagrams depict the class hierarchy of most commonly used GUI classes included in wxPython.
S.N. | Classes & Description |
---|---|
1 | wx.Frame
wx.Frame Class has a default constructor with no arguments. |
2 | wx.Panel
wx.Panel class is usually put inside a wxFrame object. This class is also inherited from wxWindow class. |
3 | wx.StaticText
wx.StaticText class object presents a control holding such read-only text. It can be termed as a passive control since it doesn’t produce any event. |
4 | TextCtrl
In wxPython, an object of wx.TextCtrl class serves this purpose. It is a control in which the text can be displayed and edited. |
5 | RadioButton & RadioBox
Each button, an object of wx.RadioButton class carries a text label next to a round button. wxPython API also consists of wx.RadioBox class. Its object offers a border and label to the group. |
6 | wx.CheckBox
A checkbox displays a small labeled rectangular box. When clicked, a checkmark appears inside the rectangle to indicate that a choice is made. |
7 | ComboBox & Choice Class
A wx.ComboBox object presents a list of items to select from. It can be configured to be a dropdown list or with permanent display. wxPython API contains a wx.Choice class, whose object is also a dropdown list, which is permanently read-only. |
8 | Wx.Gauge
Wx.Gauge class object shows a vertical or horizontal bar, which graphically shows incrementing quantity. |
9 | wx.Slider
wxPython API contains wx.Slider class. It offers same functionality as that of Scrollbar. Slider offers a convenient way to handle dragging the handle by slider specific wx.EVT_SLIDER event binder. |
10 | wx.MenuBar
A horizontal bar just below the title bar of a top level window is reserved to display a series of menus. It is an object of wx.MenuBar class in wxPython API. |
11 | wx.Toolbar
If the style parameter of wx.Toolbar object is set to wx.TB_DOCKABLE, it becomes dockable. A floating toolbar can also be constructed using wxPython’s AUIToolBar class. |
12 | Wx.Dialog
Although a Dialog class object appears like a Frame, it is normally used as a pop-up window on top of a parent frame. The objective of a Dialog is to collect some data from the user and send it to the parent frame. |
13 | wx.Notebook
wx.Notebook widget presents a tabbed control. One Notebook object in a frame has one or more tabs (called Pages), each of them having a panel showing the layout of controls. |
14 | wx.SplitterWindow
Object of this class is a layout manager, which holds two subwindows whose size can be changed dynamically by dragging the boundaries between them. The Splitter control gives a handle that can be dragged to resize the controls. |
15 | HTMLWindow
wxHTML library contains classes for parsing and displaying HTML content. Although this is not intended to be a full-featured browser, wx.HtmlWindow object is a generic HTML viewer. |
16 | ListBox & ListCtrl
A wx.ListBox widget presents a vertically scrollable list of strings. By default, a single item in the list is selectable. ListCtrl widget is a highly enhanced list display and selection tool. List of more than one column can be displayed in Report view, List view or Icon view. |
Unlike a console mode application, which is executed in a sequential manner, a GUI based application is event driven. Functions or methods are executed in response to user’s actions like clicking a button, selecting an item from collection or mouse click, etc., called events.
Data pertaining to an event which takes place during the application’s runtime is stored as object of a subclass derived from wx.Event. A display control (such as Button) is the source of event of a particular type and produces an object of Event class associated to it. For instance, click of a button emits a wx.CommandEvent. This event data is dispatched to event handler method in the program. wxPython has many predefined event binders. An Event binder encapsulates relationship between a specific widget (control), its associated event type and the event handler method.
For example, to call OnClick() method of the program on a button’s click event, the following statement is required −
self.b1.Bind(EVT_BUTTON, OnClick)
Bind() method is inherited by all display objects from wx.EvtHandler class. EVT_.BUTTON here is the binder, which associates button click event to OnClick() method.
In the following example, the MoveEvent, caused by dragging the top level window – a wx.Frame object in this case – is connected to OnMove() method using wx.EVT_MOVE binder. The code displays a window. If it is moved using mouse, its instantaneous coordinates are displayed on the console.
import wx class Example(wx.Frame): def __init__(self, *args, **kw): super(Example, self).__init__(*args, **kw) self.InitUI() def InitUI(self): self.Bind(wx.EVT_MOVE, self.OnMove) self.SetSize((250, 180)) self.SetTitle('Move event') self.Centre() self.Show(True) def OnMove(self, e): x, y = e.GetPosition() print "current window position x = ",x," y= ",y ex = wx.App() Example(None) ex.MainLoop()
The above code produces the following output −
current window position x = 562 y = 309
current window position x = 562 y = 309
current window position x = 326 y = 304
current window position x = 384 y = 240
current window position x = 173 y = 408
current window position x = 226 y = 30
current window position x = 481 y = 80
Some of the subclasses inherited from wx.Event are listed in the following table −
S.N. | Events & Description |
---|---|
1 | wxKeyEvent Occurs when a key is presses or released |
2 | wxPaintEvent Is generated whenever contents of the window needs to be redrawn |
3 | wxMouseEvent Contains data about any event due to mouse activity like mouse button pressed or dragged |
4 | wxScrollEvent Associated with scrollable controls like wxScrollbar and wxSlider |
5 | wxCommandEvent Contains event data originating from many widgets such as button, dialogs, clipboard, etc. |
6 | wxMenuEvent Different menu-related events excluding menu command button click |
7 | wxColourPickerEvent wxColourPickerCtrl generated events |
8 | wxDirFilePickerEvent Events generated by FileDialog and DirDialog |
Events in wxPython are of two types. Basic events and Command events. A basic event stays local to the window in which it originates. Most of the wxWidgets generate command events. A command event can be propagated to window or windows, which are above the source window in class hierarchy.
Following is a simple example of event propagation. The complete code is −
import wx class MyPanel(wx.Panel): def __init__(self, parent): super(MyPanel, self).__init__(parent) b = wx.Button(self, label = 'Btn', pos = (100,100)) b.Bind(wx.EVT_BUTTON, self.btnclk) self.Bind(wx.EVT_BUTTON, self.OnButtonClicked) def OnButtonClicked(self, e): print 'Panel received click event. propagated to Frame class' e.Skip() def btnclk(self,e): print "Button received click event. propagated to Panel class" e.Skip() class Example(wx.Frame): def __init__(self,parent): super(Example, self).__init__(parent) self.InitUI() def InitUI(self): mpnl = MyPanel(self) self.Bind(wx.EVT_BUTTON, self.OnButtonClicked) self.SetTitle('Event propagation demo') self.Centre() self.Show(True) def OnButtonClicked(self, e): print 'click event received by frame class' e.Skip() ex = wx.App() Example(None) ex.MainLoop()
In the above code, there are two classes. MyPanel, a wx.Panel subclass and Example, a wx.Frame subclass which is the top level window for the program. A button is placed in the panel.
This Button object is bound to an event handler btnclk() which propagates it to parent class (MyPanel in this case). Button click generates a CommandEvent which can be propagated to its parent by Skip() method.
MyPanel class object also binds the received event to another handler OnButtonClicked(). This function in turn transmits to its parent, the Example class. The above code produces the following output −
Button received click event. Propagated to Panel class. Panel received click event. Propagated to Frame class. Click event received by frame class.
A GUI widget can be placed inside the container window by specifying its absolute coordinates measured in pixels. The coordinates are relative to the dimensions of the window defined by size argument of its constructor. Position of the widget inside the window is defined by pos argument of its constructor.
import wx app = wx.App() window = wx.Frame(None, title = "wxPython Frame", size = (300,200)) panel = wx.Panel(window) label = wx.StaticText(panel, label = "Hello World", pos = (100,50)) window.Show(True) app.MainLoop()
This Absolute Positioning however is not suitable because of the following reasons −
The position of the widget does not change even if the window is resized.
The appearance may not be uniform on different display devices with different resolutions.
Modification in the layout is difficult as it may need redesigning the entire form.
wxPython API provides Layout classes for more elegant management of positioning of widgets inside the container. The advantages of Layout managers over absolute positioning are −
Layout manager is called Sizer in wxPython. Wx.Sizer is the base class for all sizer subclasses. Let us discuss some of the important sizers such as wx.BoxSizer, wx.StaticBoxSizer, wx.GridSizer, wx.FlexGridSizer, and wx.GridBagSizer.
S.N. | Sizers & Description |
---|---|
1 | BoxSizer
This sizer allows the controls to be arranged in row-wise or column-wise manner. BoxSizer’s layout is determined by its orientation argument (either wxVERTICAL or wxHORIZONTAL). |
2 | GridSizer
As the name suggests, a GridSizer object presents a two dimensional grid. Controls are added in the grid slot in the left-to-right and top-to-bottom order. |
3 | FlexiGridSizer
This sizer also has a two dimensional grid. However, it provides little more flexibility in laying out the controls in the cells. |
4 | GridBagSizer
GridBagSizer is a versatile sizer. It offers more enhancements than FlexiGridSizer. Child widget can be added to a specific cell within the grid. |
5 | StaticBoxSizer
A StaticBoxSizer puts a box sizer into a static box. It provides a border around the box along with a label at the top. |
Button widget is most widely used in any GUI interface. It captures the click event generated by the user. Its most obvious use is to trigger a handler function bound to it.
wxPython class library provides different types of buttons. There is a simple, traditional button, wx.Button class object, which carries some text as its caption. A two-state button is also available, which is named as wx.ToggleButton. Its pressed or depressed state can be identified by eventhandler function.
Another type of button, wx.BitmapButton displays a bitmap (image) as icon on its face.
Constructor for wx.Button class and wx.ToggleButton class takes the following arguments −
Wx.Button(parent, id, label, pos, size, style)
These are some important methods of wx.Button class −
S.N. | Methods & Description |
---|---|
1 | SetLabel() Sets the button’s caption programmatically |
2 | GetLabel() Returns the button’s caption |
3 | SetDefault() Button is set to default for the top level window. Emulates the click event on pressing Enter key |
Two important methods of wx.ToggleButton class are −
S.N. | Methods & Description |
---|---|
1 | GetValue() Returns the state of toggle button (on/off) |
2 | SetValue() Sets the state of button programmatically |
In order to create a bitmap button, firstly, a bitmap object needs to be constructed out of an image file.
The following variation of wx.Bitmap class constructor is most commonly used −
Wx.Bitmap(fiiename, wx.BITMAP_TYPE)
Some of the predefined bitmap type constants are −
wx.BITMAP_TYPE_BMP |
wx.BITMAP_TYPE_ICO |
wx.BITMAP_TYPE_CUR |
wx.BITMAP_TYPE_TIFF |
wx.BITMAP_TYPE_TIF |
wx.BITMAP_TYPE_GIF |
wx.BITMAP_TYPE_PNG |
wx.BITMAP_TYPE_JPEG |
wx.BITMAP_TYPE_PCX |
wx.BITMAP_TYPE_ICON |
wx.BITMAP_TYPE_ANY |
This bitmap object is used as one of the parameters for wx.BitmapButton class constructor.
Wx.BitmapButton(parent, id, bitmap, pos, size, style)
On some OS platforms, the bitmap button can display both bitmap and label. SetLabel() methods assign the caption. On other platforms, it serves as an internal label.
The normal button as well bitmap button emits a wx.CommandEvent. EVT_BUTTON binder associates a handler function to it.
The toggle button on the other hand uses wx.TOGGLEBUTTON binder for event handling.
In the following example, buttons of all three types are placed in a vertical box sizer of a panel.
Simple button object is created using the statement −
self.btn = wx.Button(panel, -1, "click Me")
Toggle button is constructed by following statement −
self.tbtn = wx.ToggleButton(panel , -1, "click to on")
These buttons are added into vertical sizer using the following statements −
vbox.Add(self.btn,0,wx.ALIGN_CENTER) vbox.Add(self.tbtn,0,wx.EXPAND|wx.ALIGN_CENTER)
Note − Because of wx.EXPAND flag, the toggle button occupies the entire width of the frame.
Using EVT_BUTTON and EVT_TOGGLEBUTTON binders they are associated with the respective handlers.
self.btn.Bind(wx.EVT_BUTTON,self.OnClicked) self.tbtn.Bind(wx.EVT_TOGGLEBUTTON,self.OnToggle)
Three bitmap buttons are added into a horizontal box sizer. These buttons display an image as icon as their caption.
bmp = wx.Bitmap("NEW.BMP", wx.BITMAP_TYPE_BMP) self.bmpbtn = wx.BitmapButton(panel, id = wx.ID_ANY, bitmap = bmp, size = (bmp.GetWidth()+10, bmp.GetHeight()+10)) bmp1 = wx.Bitmap("OPEN.BMP", wx.BITMAP_TYPE_BMP) self.bmpbtn1 = wx.BitmapButton(panel, id = wx.ID_ANY, bitmap = bmp1, size = (bmp.GetWidth()+10, bmp.GetHeight()+10)) bmp2 = wx.Bitmap("SAVE.BMP", wx.BITMAP_TYPE_BMP) self.bmpbtn2 = wx.BitmapButton(panel, id = wx.ID_ANY, bitmap = bmp2, size = (bmp.GetWidth()+10, bmp.GetHeight()+10))
Click event of these three buttons is directed to OnClicked() method.
self.bmpbtn.Bind(wx.EVT_BUTTON, self.OnClicked) self.bmpbtn1.Bind(wx.EVT_BUTTON, self.OnClicked) self.bmpbtn2.Bind(wx.EVT_BUTTON, self.OnClicked)
Internal labels of these buttons are set to NEW, OPEN and SAVE respectively.
OnClicked() event handler function retrieves the label of source button, which caused the click event. That label is printed on the console.
def OnClicked(self, event): btn = event.GetEventObject().GetLabel() print "Label of pressed button = ",btn
OnToggle() event handler is triggered when the toggle button is clicked. Its state is read by GetValue() method and accordingly, the button’s caption is set.
def OnToggle(self,event): state = event.GetEventObject().GetValue() if state == True: print "off" event.GetEventObject().SetLabel("click to off") else: print "on" event.GetEventObject().SetLabel("click to on")
The complete code listing is as follows −
import wx class Mywin(wx.Frame): def __init__(self, parent, title): super(Mywin, self).__init__(parent, title = title,size = (200,150)) panel = wx.Panel(self) vbox = wx.BoxSizer(wx.VERTICAL) self.btn = wx.Button(panel,-1,"click Me") vbox.Add(self.btn,0,wx.ALIGN_CENTER) self.btn.Bind(wx.EVT_BUTTON,self.OnClicked) self.tbtn = wx.ToggleButton(panel , -1, "click to on") vbox.Add(self.tbtn,0,wx.EXPAND|wx.ALIGN_CENTER) self.tbtn.Bind(wx.EVT_TOGGLEBUTTON,self.OnToggle) hbox = wx.BoxSizer(wx.HORIZONTAL) bmp = wx.Bitmap("NEW.BMP", wx.BITMAP_TYPE_BMP) self.bmpbtn = wx.BitmapButton(panel, id = wx.ID_ANY, bitmap = bmp, size = (bmp.GetWidth()+10, bmp.GetHeight()+10)) hbox.Add(self.bmpbtn,0,wx.ALIGN_CENTER) self.bmpbtn.Bind(wx.EVT_BUTTON,self.OnClicked) self.bmpbtn.SetLabel("NEW") bmp1 = wx.Bitmap("OPEN.BMP", wx.BITMAP_TYPE_BMP) self.bmpbtn1 = wx.BitmapButton(panel, id = wx.ID_ANY, bitmap = bmp1, size = (bmp.GetWidth()+10, bmp.GetHeight()+10)) hbox.Add(self.bmpbtn1,0,wx.ALIGN_CENTER) self.bmpbtn1.Bind(wx.EVT_BUTTON,self.OnClicked) self.bmpbtn1.SetLabel("OPEN") bmp2 = wx.Bitmap("SAVE.BMP", wx.BITMAP_TYPE_BMP) self.bmpbtn2 = wx.BitmapButton(panel, id = wx.ID_ANY, bitmap = bmp2, size = (bmp.GetWidth()+10, bmp.GetHeight()+10)) hbox.Add(self.bmpbtn2,0,wx.ALIGN_CENTER) self.bmpbtn2.Bind(wx.EVT_BUTTON,self.OnClicked) self.bmpbtn2.SetLabel("SAVE") vbox.Add(hbox,1,wx.ALIGN_CENTER) panel.SetSizer(vbox) self.Centre() self.Show() self.Fit() def OnClicked(self, event): btn = event.GetEventObject().GetLabel() print "Label of pressed button = ",btn def OnToggle(self,event): state = event.GetEventObject().GetValue() if state == True: print "Toggle button state off" event.GetEventObject().SetLabel("click to off") else: print " Toggle button state on" event.GetEventObject().SetLabel("click to on") app = wx.App() Mywin(None, 'Button demo') app.MainLoop()
The above code produces the following output −
Label of pressed button = click Me
Toggle button state off
Toggle button state on
Label of pressed button = NEW
Label of pressed button = OPEN
Label of pressed button = SAVE
wxAui is an Advanced User Interface library incorporated in wxWidgets API. Wx.aui.AuiManager the central class in AUI framework.
AuiManager manages the panes associated with a particular frame using each panel’s information in wx.aui.AuiPanelInfo object. Let us learn about various properties of PanelInfo object control docking and floating behavior.
Putting dockable windows in the top level frame involves the following steps −
First, create an AuiManager object.
self.mgr = wx.aui.AuiManager(self)
Then, a panel with required controls is designed.
pnl = wx.Panel(self) pbox = wx.BoxSizer(wx.HORIZONTAL) text1 = wx.TextCtrl(pnl, -1, "Dockable", style = wx.NO_BORDER | wx.TE_MULTILINE) pbox.Add(text1, 1, flag = wx.EXPAND) pnl.SetSizer(pbox)
The following parameters of AuiPanelInfo are set.
Direction − Top, Bottom, Left, Right, or Center
Position − More than one pane can be placed inside a dockable region. Each is given a position number.
Row − More than one pane appears in one row. Just like more than one toolbar appearing in the same row.
Layer − Panes can be placed in layers.
Using this PanelInfo, the designed panel is added into the manager object.
info1 = wx.aui.AuiPaneInfo().Bottom() self.mgr.AddPane(pnl,info1)
Rest of the top level window may have other controls as usual.
The complete code is as follows −
import wx import wx.aui class Mywin(wx.Frame): def __init__(self, parent, title): super(Mywin, self).__init__(parent, title = title, size = (300,300)) self.mgr = wx.aui.AuiManager(self) pnl = wx.Panel(self) pbox = wx.BoxSizer(wx.HORIZONTAL) text1 = wx.TextCtrl(pnl, -1, "Dockable", style = wx.NO_BORDER | wx.TE_MULTILINE) pbox.Add(text1, 1, flag = wx.EXPAND) pnl.SetSizer(pbox) info1 = wx.aui.AuiPaneInfo().Bottom() self.mgr.AddPane(pnl, info1) panel = wx.Panel(self) text2 = wx.TextCtrl(panel, size = (300,200), style = wx.NO_BORDER | wx.TE_MULTILINE) box = wx.BoxSizer(wx.HORIZONTAL) box.Add(text2, 1, flag = wx.EXPAND) panel.SetSizerAndFit(box) self.mgr.Update() self.Bind(wx.EVT_CLOSE, self.OnClose) self.Centre() self.Show(True) def OnClose(self, event): self.mgr.UnInit() self.Destroy() app = wx.App() Mywin(None,"Dock Demo") app.MainLoop()
The above code produces the following output −
A typical GUI application may have multiple windows. Tabbed and stacked widgets allow to activate one such window at a time. However, many a times this approach may not be useful as view of other windows is hidden.
One way to display multiple windows simultaneously is to create them as independent windows. This is called as SDI (Single Document Interface). This requires more memory resources as each window may have its own menu system, toolbar, etc.
MDI framework in wxPython provides a wx.MDIParentFrame class. Its object acts as a container for multiple child windows, each an object of wx.MDIChildFrame class.
Child windows reside in the MDIClientWindow area of the parent frame. As soon as a child frame is added, the menu bar of the parent frame shows a Window menu containing buttons to arrange the children in a cascaded or tiled manner.
The following example illustrates the uses of MDIParentFrame as top level window. A Menu button called NewWindow adds a child window in the client area. Multiple windows can be added and then arranged in a cascaded or tiled order.
The complete code is as follows −
import wx class MDIFrame(wx.MDIParentFrame): def __init__(self): wx.MDIParentFrame.__init__(self, None, -1, "MDI Parent", size = (600,400)) menu = wx.Menu() menu.Append(5000, "&New Window") menu.Append(5001, "&Exit") menubar = wx.MenuBar() menubar.Append(menu, "&File") self.SetMenuBar(menubar) self.Bind(wx.EVT_MENU, self.OnNewWindow, id = 5000) self.Bind(wx.EVT_MENU, self.OnExit, id = 5001) def OnExit(self, evt): self.Close(True) def OnNewWindow(self, evt): win = wx.MDIChildFrame(self, -1, "Child Window") win.Show(True) app = wx.App() frame = MDIFrame() frame.Show() app.MainLoop()
The above code produces the following output −
GDI+ (Graphics Drawing Interface), CoreGraphics and Cairo libraries form the framework of drawing API in wxPython. wx.GraphicsContext is the primary drawable object, using which various Device Context objects are created.
wx.DC is an abstract class. Its derived classes are used to render graphics and text on different devices. The Device Context classes are −
wx.ScreenDC − Use this to paint on the screen, as opposed to an individual window.
wx.ClientDC − Use this to paint on the client area of the window (the part without borders and other decorations), but do not use it from within an wxPaintEvent.
wx.PaintDC − Use this to paint on the client area of the window, but only from within a wxPaintEvent.
wx.WindowDC − Use this to paint on the whole area of the window, including decorations. This may not be available on non-Windows platforms.
Drawing API of wxPython offers different functions for drawing shape, text and image. Objects required for drawing purpose, like Colour, Pen, Brush and Font can also be constructed using GDI classes.
Colour object represents combination of RGB (RED, Green and Blue) intensity values, each on the scale of 0-255. There are a few predefined colour objects like −
Color with custom combination of RGB values is formed as wx.Colour object.
wx.Colour(r,g,b)
Pen object determines the colour, width and style of the shape of graphics like line, rectangle, circle etc.
Predefined Pen objects are −
wxBLACK_DASHED_PEN |
wxBLACK_PEN |
wxBLUE_PEN |
wxCYAN_PEN |
wxGREEN_PEN |
wxYELLOW_PEN |
wxGREY_PEN |
wxLIGHT_GREY_PEN |
wxMEDIUM_GREY_PEN |
wxRED_PEN |
wxTRANSPARENT_PEN |
wxWHITE_PEN |
Predefined Pen styles are −
wx.SOLID |
wx.DOT |
wx.LONG_DASH |
wx.SHORT_DASH |
wx.DOT_DASH |
wx.TRANSPARENT |
Brush is another elementary graphics object required to fill the backgrounds of shapes such as rectangle, ellipse, circle etc.
A custom Brush object requires wx.Colour and Brush style parameters. The following is a list of predefined brush styles −
wx.SOLID |
wx.STIPPLE |
wx.BDIAGONAL_HATCH |
wx.CROSSDIAG_HATCH |
wx.FDIAGONAL_HATCH |
wx.CROSS_HATCH |
wx.HORIZONTAL_HATCH |
wx.VERTICAL_HATCH |
wx.TRANSPARENT |
wxPython has a number of functions that facilitate drawing different shapes, text and image.
S.N. | Functions & Description |
---|---|
1 | DrawRectangle() Draws a rectangle of given dimensions |
2 | DrawCircle() Draws a circle at the given point as center and radius |
3 | DrawEllipse() Draws an ellipse with the given x and y radius |
4 | DrawLine() Draws a line beween two wx.Point objects |
5 | DrawBitmap() Draw an image at the given position |
6 | DrawText() Displays the given text at the specified position |
The above functions are implemented in the following example, making use of Pen, Brush, Colour and Font objects.
The complete code is as follows −
import wx class Mywin(wx.Frame): def __init__(self, parent, title): super(Mywin, self).__init__(parent, title = title,size = (500,300)) self.InitUI() def InitUI(self): self.Bind(wx.EVT_PAINT, self.OnPaint) self.Centre() self.Show(True) def OnPaint(self, e): dc = wx.PaintDC(self) brush = wx.Brush("white") dc.SetBackground(brush) dc.Clear() dc.DrawBitmap(wx.Bitmap("python.jpg"),10,10,True) color = wx.Colour(255,0,0) b = wx.Brush(color) dc.SetBrush(b) dc.DrawCircle(300,125,50) dc.SetBrush(wx.Brush(wx.Colour(255,255,255))) dc.DrawCircle(300,125,30) font = wx.Font(18, wx.ROMAN, wx.ITALIC, wx.NORMAL) dc.SetFont(font) dc.DrawText("Hello wxPython",200,10) pen = wx.Pen(wx.Colour(0,0,255)) dc.SetPen(pen) dc.DrawLine(200,50,350,50) dc.SetBrush(wx.Brush(wx.Colour(0,255,0), wx.CROSS_HATCH)) dc.DrawRectangle(380, 15, 90, 60) ex = wx.App() Mywin(None,'Drawing demo') ex.MainLoop()
The above code produces the following output −
Provision of drag and drop is very intuitive for the user. It is found in many desktop applications where the user can copy or move objects from one window to another just by dragging it with the mouse and dropping on another window.
Drag and drop operation involves the following steps −
In wxPython, there are two predefined drop targets −
Many wxPython widgets support drag and drop activity. Source control must have dragging enabled, whereas target control must be in a position to accept (or reject) drag.
Source Data that the user is dragging is placed on the the target object. OnDropText() of target object consumes the data. If so desired, data from the source object can be deleted.
In the following example, two ListCrl objects are placed horizontally in a Box Sizer. List on the left is populated with a languages[] data. It is designated as the source of drag. One on the right is the target.
languages = ['C', 'C++', 'Java', 'Python', 'Perl', 'JavaScript', 'PHP', 'VB.NET','C#'] self.lst1 = wx.ListCtrl(panel, -1, style = wx.LC_LIST) self.lst2 = wx.ListCtrl(panel, -1, style = wx.LC_LIST) for lang in languages: self.lst1.InsertStringItem(0,lang)
The second list control is empty and is an argument for object of TextDropTarget class.
class MyTextDropTarget(wx.TextDropTarget): def __init__(self, object): wx.TextDropTarget.__init__(self) self.object = object def OnDropText(self, x, y, data): self.object.InsertStringItem(0, data)
OnDropText() method adds source data in the target list control.
Drag operation is initialized by the event binder.
wx.EVT_LIST_BEGIN_DRAG(self, self.lst1.GetId(), self.OnDragInit)
OnDragInit() function puts drag data on the target and deletes from the source.
def OnDragInit(self, event): text = self.lst1.GetItemText(event.GetIndex()) tobj = wx.PyTextDataObject(text) src = wx.DropSource(self.lst1) src.SetData(tobj) src.DoDragDrop(True) self.lst1.DeleteItem(event.GetIndex())
The complete code is as follows −
import wx class MyTarget(wx.TextDropTarget): def __init__(self, object): wx.TextDropTarget.__init__(self) self.object = object def OnDropText(self, x, y, data): self.object.InsertStringItem(0, data) class Mywin(wx.Frame): def __init__(self, parent, title): super(Mywin, self).__init__(parent, title = title,size = (-1,300)) panel = wx.Panel(self) box = wx.BoxSizer(wx.HORIZONTAL) languages = ['C', 'C++', 'Java', 'Python', 'Perl', 'JavaScript', 'PHP', 'VB.NET','C#'] self.lst1 = wx.ListCtrl(panel, -1, style = wx.LC_LIST) self.lst2 = wx.ListCtrl(panel, -1, style = wx.LC_LIST) for lang in languages: self.lst1.InsertStringItem(0,lang) dt = MyTarget(self.lst2) self.lst2.SetDropTarget(dt) wx.EVT_LIST_BEGIN_DRAG(self, self.lst1.GetId(), self.OnDragInit) box.Add(self.lst1,0,wx.EXPAND) box.Add(self.lst2, 1, wx.EXPAND) panel.SetSizer(box) panel.Fit() self.Centre() self.Show(True) def OnDragInit(self, event): text = self.lst1.GetItemText(event.GetIndex()) tobj = wx.PyTextDataObject(text) src = wx.DropSource(self.lst1) src.SetData(tobj) src.DoDragDrop(True) self.lst1.DeleteItem(event.GetIndex()) ex = wx.App() Mywin(None,'Drag&Drop Demo') ex.MainLoop()
The above code produces the following output −