I am a Senior Reservoir Engineer at Maersk Oil in Copenhagen,
Denmark, where I mix a 10-years reservoir engineering knowledge with the power of Python and
its libraries to solve many everyday problems arising during the numerical studies of oil
and gas fields.

I am the author and the maintainer of the AGW
(Advanced Generic Widgets) library for wxPython, a large collection of owner-drawn and
custom widgets shipped officially with every new release of wxPython.

wxPython is one of the most famous frameworks used to build graphical user interfaces
(GUIs) in Python. It provides native look and feel widgets on all supported platforms
(Windows, Linux/Unix, Mac) and it has a vast repository of owner-drawn controls.
In addition, the wxPython demo is the place where to start looking for examples
and source code snippets.

If you plan to run the various scripts available in this tutorial directly from your
preferred editor, you should check that it does not interfere with the wxPython
event loop. Eclipse, Wingware IDE, Editra, Ulipad, Dr. Python and newest versions
of IDLE (and many other editors) support this functionality. If your preferred editor
does not - you can easily find out by running the Hello World
sample and see if it hangs - you can still run the samples via the command line:

Choose an installer that matches the version of Python you will be using. If you are using a 64-bit
version of Python then make sure you also get a 64-bit wxPython, otherwise choose a 32-bit installer
even if you are on a 64-bit version of Windows. There is no longer a separate ansi and Unicode build,
it’s all Unicode now.

The wxPython binaries for OSX are mountable disk images. Simply double click to mount the image and then
run the installer application in the image. Be sure to download the image that matches the version of Python
that you want to use it with. The files with “carbon” in the name use the Carbon API for implementing the
GUI, are compatible with PPC and i386 machines are will work on OSX 10.4 and onwards. The file with “cocoa”
in the name use the Cocoa API for implementing the GUI, requires at least OSX 10.5, and supports either
32-bit or 64-bit architectures.

To get prebuilt binaries for Linux or other platforms, please search in your distro’s package repository,
or any 3rd party repositories that may be available to you. Ubuntu users can get information about the the
wx APT repository here. If all else fails you can build wxPython yourself from the source code,
see the build instructions.

In this section, we are going to build step by step a skeleton of a wxPython application,
enriching it incrementally. Every sub-section contains one or more exercises for you to
familiarize yourself with the wxPython framework.

As in (almost) all every other language and library, this is the simplest “Hello World”
application you can write in wxPython:

# In every wxPython application, we must import the wx libraryimportwx# Create an application class instanceapp=wx.App()# Create a frame (i.e., a floating top-level window)frame=wx.Frame(None,-1,'Hello world')# Show the frame on screenframe.Show()# Enter the application main loopapp.MainLoop()

The last line enters what wxPython defines as “MainLoop”. A “MainLoop” is an endless
cycle that catches up all events coming up to your application. It is an integral part
of any windows GUI application.

Although the code is very simple, you can do a lot of things with your window. You can
maximize it, minimize it, move it, resize it. All these things have been already done
for you by the framework.

Almost all the applications sport a menu bar and a status bar in their main window.

A menu bar is a very powerful tool to let the user interact with your GUI as it displays
(various levels of) cascading menus with multiple options.

A status bar is a narrow window that can be placed along the bottom of a frame and it is
mostly used to give small amounts of status information.

importwxclassMainWindow(wx.Frame):def__init__(self,parent,title):wx.Frame.__init__(self,parent,title=title)# A Statusbar in the bottom of the windowself.CreateStatusBar()# Setting up the menufile_menu=wx.Menu()# wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided# by wxWidgets.file_menu.Append(wx.ID_ABOUT,'&About','Information about this application')file_menu.AppendSeparator()file_menu.Append(wx.ID_EXIT,'E&xit','Exit the application')# Creating the menubarmenu_bar=wx.MenuBar()# Adding the 'file_menu' to the menu barmenu_bar.Append(file_menu,'&File')# Adding the menu bar to the frame contentself.SetMenuBar(menu_bar)self.Show()app=wx.App(False)frame=MainWindow(None,'Sample application')app.MainLoop()

Notice the wx.ID_ABOUT and wx.ID_EXIT ids. These are standard ids provided by wxWidgets
(see a full list here).
It is a good habit to use the standard ID if there is one available. This helps wxPython know
how to display the widget in each platform to make it look more native.

Reacting to events in wxPython is called event handling. An event is when “something” happens
on your application (a button click, text input, mouse movement, a timer expires, etc...).
Much of GUI programming consists of responding to events.

You link a wxPython object to an event using the Bind() method:

classMainWindow(wx.Frame):def__init__(self,parent,title):wx.Frame.__init__(self,parent,title=title)# Other stuff...menu_item=file_menu.Append(wx.ID_EXIT,'E&xit','Exit the application')self.Bind(wx.EVT_MENU,self.OnExit,menu_item)

This means that, from now on, when the user selects the “Exit” menu item, the method OnExit
will be executed. wx.EVT_MENU is the “select menu item” event. wxPython understands many
other events (everything that starts with EVT_ in the wx namespace).

The OnExit method has the general declaration:

defOnExit(self,event):# Close the frame, cannot be vetoed if force=Trueself.Close(force=True)

Here event is an instance of a subclass of wx.Event. For example, a button-click event -
wx.EVT_BUTTON - is a subclass of wx.Event.

Working with events is straightforward in wxPython. There are three steps:

Identify the event binder name: wx.EVT_BUTTON, wx.EVT_CLOSE, etc...

Create an event handler. It is a method called when an event is generated

Bind an event to an event handler

Sometimes we need to stop processing an event: for example, think about a user closing your main
application window while the GUI still contains unsaved data. To do this, we call the method
Veto() on an event, inside an event handler:

classMainWindow(wx.Frame):def__init__(self,parent,title):wx.Frame.__init__(self,parent,title=title)# Other stuff...self.Bind(wx.EVT_CLOSE,self.OnClose)defOnClose(self,event):# This displays a message box asking the user to confirm# she wants to quit the applicationdlg=wx.MessageDialog(self,'Are you sure you want to quit?','Question',wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION)ifdlg.ShowModal()==wx.ID_YES:self.Destroy()else:event.Veto()

When a frame frame has exactly one child window, not counting the status and toolbar,
this child is resized to take the entire frame client area.

We can create a link between the wx.TextCtrl behaviour and the menu selected by the user
by binding menu events (like “Cut”, “Copy” and “Paste” menu selections) to the main frame and
process the results in an event handler. For example:

classMainWindow(wx.Frame):def__init__(self,parent,title):wx.Frame.__init__(self,parent,title=title)self.control=wx.TextCtrl(self,style=wx.TE_MULTILINE)# Other stuff, menubar creation, etc...# Bind the "copy menu event" to the OnCopy methodself.Bind(wx.EVT_MENU,self.OnCopy,id=wx.ID_COPY)defOnCopy(self,event):# See if we can copy from the text box...ifself.control.CanCopy():# Actually copy the wx.TextCtrl content# into the clipboardself.control.Copy()

In the OnCopy event handler we simply check if we can copy text from the text box (i.e., if
something is selected and can be copied to the clipboard) and we actually copy what is selected
into the clipboard.

Sizers, as represented by the wx.Sizer
class and its descendants in the wxPython class hierarchy, have become the method of choice to
define the layout of controls in parent windows because of their ability to create visually appealing
frames independent of the platform, taking into account the differences in size and style of the
individual controls.

The layout algorithm used by sizers in wxPython is closely related to layout systems in other GUI
toolkits, such as Java’s AWT, the GTK toolkit or the Qt toolkit. It is based upon the idea of individual
subwindows reporting their minimal required size and their ability to get stretched if the size of the
parent window has changed. This will most often mean that the programmer does not set the start-up size
of a window, the window will rather be assigned a sizer and this sizer will be queried about the recommended
size.

This sizer in turn will query its children (which can be normal windows, empty space or other sizers) so
that a hierarchy of sizers can be constructed.

Note

wx.Sizer does not derive from
wx.Window and thus does not interfere with tab
ordering and requires very few resources compared to a real window on screen.

What makes sizers so well fitted for use in wxPython is the fact that every control reports its own
minimal size and the algorithm can handle differences in font sizes or different window sizes on different
platforms without problems.

For example, if the standard font as well as the overall design of Linux/GTK widgets requires more space
than on Windows, the initial window size will automatically be bigger on Linux/GTK than on Windows.

There are currently 6 different kinds of sizers available in wxPython:

Note that only some controls can calculate their size (such as a wx.CheckBox)
whereas others (such as a wx.ListBox) don’t
have any natural width or height and thus require an explicit size. Some controls can calculate their height,
but not their width (e.g. a single line text control).

All sizers are containers, i.e. they are used to lay out one window item (or several window items),
which they contain. Such items are sometimes referred to as the children of the sizer. Independent of how
the individual sizers lay out their children, all children have certain features in common:

A minimal size: This minimal size is usually identical to the initial size of the controls and may either
be set explicitly in the wx.Size field of the control constructor or may be calculated by wxPython, typically
by setting the height and/or the width of the item to -1.

A border: The border is just empty space and is used to separate widgets in a parent window. This border can
either be all around, or at any combination of sides such as only above and below the control. The thickness of
this border must be set explicitly, typically 5 points. The following samples show frames with only one item
(a button) and a border of 0, 5, and 10 pixels around the button:

An alignment: Often, a widget is given more space than its minimal size plus its border. Depending on what
flags are used for the respective widget, this widget can be made to fill out the available space entirely, i.e.
it will grow to a size larger than the minimal size, or it will be moved to either the centre of the available
space or to either side of the space. The following sample shows a listbox and three buttons in a horizontal
box sizer; one button is centred, one is aligned at the top, one is aligned at the bottom:

A stretch factor: If a sizer contains more than one child and it is offered more space than its children and
their borders need, the question arises how to distribute the surplus space among the children. For this purpose,
a stretch factor (proportion) may be assigned to each child, where the default value of 0 indicates that the
child will not get more space than its requested minimum size.

A value of more than zero is interpreted in relation to the sum of all stretch factors in the children of the
respective sizer, i.e. if two children get a stretch factor of 1, they will get half the extra space each
independent of whether one control has a minimal sizer inferior to the other or not. The following sample shows
a frame with three buttons, the first one has a stretch factor of 1 and thus gets stretched, whereas the other
two buttons have a stretch factor of zero and keep their initial width:

window – a window, a spacer or another sizer to be added to the sizer. Its initial size
(either set explicitly by the user or calculated internally) is interpreted as the minimal and
in many cases also the initial size.

proportion (int) – this parameter is used in wx.BoxSizer to indicate if a child of a sizer
can change its size in the main orientation of the wx.BoxSizer - where 0 stands for not changeable
and a value of more than zero is interpreted relative to the value of other children of the same
wx.BoxSizer. For example, you might have a horizontal wx.BoxSizer with three children, two
of which are supposed to change their size with the sizer. Then the two stretchable windows would
get a value of 1 each to make them grow and shrink equally with the sizer’s horizontal dimension.

flag (int) – OR-combination of flags affecting sizer’s behaviour.

border (int) – determines the border width, if the flag parameter is set to include any border flag.

userData (object) – allows an extra object to be attached to the sizer item, for use in derived
classes when sizing information is more complex than the proportion and flag will allow for.

The first parameter to wx.BoxSizer.Add
is obviously the wx.Window or wx.Sizer that you are adding. The second one (the proportion)
defines how large the sizer’s children are in relation to each other. In a vertical sizer, this changes
the height; in a horizontal sizer, this changes the width. Here are some examples:

Code

Resulting Image

sizer=wx.BoxSizer(wx.VERTICAL)# Second button is three times as tall as first buttonsizer.Add(wx.Button(self,-1,'An extremely long button text'),1,0,0)sizer.Add(wx.Button(self,-1,'Small button'),3,0,0)sizer.SetSizeHints(self)self.SetSizer(sizer)

Same code as above, with window resized. Notice that the bottom button is still three times as tall as the top button.

sizer=wx.BoxSizer(wx.VERTICAL)# First button is 3/2 the height of the second buttonsizer.Add(wx.Button(self,-1,'An extremely long button text'),3,0,0)sizer.Add(wx.Button(self,-1,'Small button'),2,0,0)sizer.SetSizeHints(self)self.SetSizer(sizer)

If one of the proportion parameters is 0, that wx.Window will be the minimum size, and the others
will resize proportionally:

Code

Resulting Image

sizer=wx.BoxSizer(wx.VERTICAL)# Third button is twice the size of the second buttonsizer.Add(wx.Button(self,-1,'An extremely long button text'),0,0,0)sizer.Add(wx.Button(self,-1,'Small button'),1,0,0)sizer.Add(wx.Button(self,-1,'Another button'),2,0,0)sizer.SetSizeHints(self)self.SetSizer(sizer)

Same code as above, with window resized. The top button (proportion 0) is still the minimum height,
and the third button is still twice the height of the second.

This is especially useful when you want, for example, a button at the bottom which is only
as big as necessary, and some other control that occupies the rest of the frame. To do so,
give the button proportion 0 and the other control a number greater than 0. Mac users in
particular will appreciate you for not creating huge aqua-styled buttons.

The flag argument accepted by wx.Sizer.Add
is a OR-combination of the following flags. Two main behaviours are defined using these flags. One is
the border around a window: the border parameter determines the border width whereas the flags given here
determine which side(s) of the item that the border will be added. The other flags determine how the sizer
item behaves when the space allotted to the sizer changes, and is somewhat dependent on the specific kind
of sizer used.

Sizer Flag

Description

wx.TOP

These flags are used to specify which side(s) of the sizer
item the border width will apply to.

wx.BOTTOM

wx.LEFT

wx.RIGHT

wx.ALL

wx.EXPAND

The item will be expanded to fill the space assigned to
the item.

wx.SHAPED

The item will be expanded as much as possible while also
maintaining its aspect ratio

wx.FIXED_MINSIZE

If you would rather have a window item stay the size it started with then
use FIXED_MINSIZE

wx.RESERVE_SPACE_EVEN_IF_HIDDEN

Normally Sizers don’t allocate space for hidden windows or other items.
This flag overrides this behavior so that sufficient space is allocated for
the window even if it isn’t visible. This makes it possible to dynamically
show and hide controls without resizing parent dialog, for example.

wx.ALIGN_CENTERorwx.ALIGN_CENTRE

The ALIGN* flags allow you to specify the alignment of the item
within the space allotted to it by the sizer, adjusted for the border if
any.

wx.ALIGN_LEFT

wx.ALIGN_RIGHT

wx.ALIGN_TOP

wx.ALIGN_BOTTOM

wx.ALIGN_CENTER_VERTICALorwx.ALIGN_CENTRE_VERTICAL

wx.ALIGN_CENTER_HORIZONTALorwx.ALIGN_CENTRE_HORIZONTAL

Let’s start with the simplest case: the alignment flags. These are pretty self-explanatory.

Code

Resulting Image

sizer=wx.BoxSizer(wx.VERTICAL)# Second button is right alignedsizer.Add(wx.Button(self,-1,"An extremely long button text"),0,0,0)sizer.Add(wx.Button(self,-1,"Small Button"),0,wx.ALIGN_RIGHT,0)sizer.SetSizeHints(self)self.SetSizer(sizer)

sizer=wx.BoxSizer(wx.VERTICAL)# Second button is center-alignedsizer.Add(wx.Button(self,-1,"An extremely long button text"),0,0,0)sizer.Add(wx.Button(self,-1,"Small Button"),0,wx.ALIGN_CENTER,0)sizer.SetSizeHints(self)self.SetSizer(sizer)

You can see that the first button takes its minimum size, and the second one grows to match it. This affects
controls in the opposite manner of the second parameter; wx.EXPAND in a vertical sizer causes horizontal
expansion, and in a horizontal sizer it causes vertical expansion.

Next is wx.SHAPED. This flag ensures that the width and height of the object stay proportional to each other.
It doesn’t make much sense for buttons, but can be excellent for bitmaps, which would be contorted or clipped
if not scaled proportionally.

Same code as above, with window resized. The width grew dramatically with the height. In fact, it didn’t
quite grow vertically the whole way because it couldn’t maintain the correct ratio while doing so.

Finally, we have the border flags. These only make sense when the border parameter is greater than 0, and describe
the sides of the control on which the border should appear. In order to demonstrate this most clearly, we’ll keep
the wx.EXPAND flag.

A font is an object which determines the appearance of text, primarily when drawing text to a window or
device context. A wx.Font is determined by
the following parameters (not all of them have to be specified, of course):

An optional string specifying the actual typeface to be used. If None, a
default typeface will chosen based on the family.

Encoding

The font encoding

As an example, you can create a font object and assign it to a widget like this:

static_text=wx.StaticText(parent,-1,'Some text here')font1=wx.Font(10,wx.SWISS,wx.ITALIC,wx.NORMAL)static_text.SetFont(font1)text_ctrl=wx.TextCtrl(parent,-1,'Some other text')# A font can be retrieved from the OS default font and modifiedfont2=wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)font2.SetPointSize(12)text_ctrl.SetFont(font2)

As an alternative, you can also use the handy colour database provided in the
wx.lib.colourdb module
and comprising more than 600 named colours:

importwximportwx.lib.colourdbimportrandomclassMyFrame(wx.Frame):def__init__(self,parent):wx.Frame.__init__(self,parent,title='Colour database')# Show the selected colour in this panelpanel=wx.Panel(self)wx.lib.colourdb.updateColourDB()# Create a colour list from the colourdb databasecolour_list=wx.lib.colourdb.getColourList()# Get one random colour between those 600 availablerandom_colour=random.choice(colour_list)panel.SetBackgroundColour(random_colour)

A bitmap is a collection of bits that form an image. It is a grid of individual dots stored in memory
or in a file. Each dot has it’s own colour. When the image is displayed, the computer transfers a bit
map into pixels on monitors or ink dots on printers. The quality of a bitmap is determined by the
resolution and the color depth of the image. The resolution is the total number of pixels in
the image. The color depth is the amount of information in each pixel.

An example construction to create a bitmap in wxPython starting from an existing image:

bmp=wx.Bitmap('wxpython.png',wx.BITMAP_TYPE_PNG)

The same can be done with other file formats, such as GIF, JPG, BMP, TIFF, etc... The full list of
supported formats is available in the BitmapType
documentation.

When you need to use standard (i.e., platform-provided) bitmaps, you should take a look at
wx.ArtProvider, which is
a class used to customize the look of wxPython application.

When wxPython needs to display an icon or a bitmap (e.g. in the standard file dialog), it does not
use a hard-coded resource but asks wx.ArtProvider for it instead. A number of standard bitmaps are
readily available, and in particular the following:

ART_ERROR

ART_GOTO_LAST

ART_FILE_SAVE_AS

ART_QUESTION

ART_PRINT

ART_DELETE

ART_WARNING

ART_HELP

ART_COPY

ART_INFORMATION

ART_TIP

ART_CUT

ART_ADD_BOOKMARK

ART_REPORT_VIEW

ART_PASTE

ART_DEL_BOOKMARK

ART_LIST_VIEW

ART_UNDO

ART_HELP_SIDE_PANEL

ART_NEW_DIR

ART_REDO

ART_HELP_SETTINGS

ART_FOLDER

ART_PLUS

ART_HELP_BOOK

ART_FOLDER_OPEN

ART_MINUS

ART_HELP_FOLDER

ART_GO_DIR_UP

ART_CLOSE

ART_HELP_PAGE

ART_EXECUTABLE_FILE

ART_QUIT

ART_GO_BACK

ART_NORMAL_FILE

ART_FIND

ART_GO_FORWARD

ART_TICK_MARK

ART_FIND_AND_REPLACE

ART_GO_UP

ART_CROSS_MARK

ART_HARDDISK

ART_GO_DOWN

ART_MISSING_IMAGE

ART_FLOPPY

ART_GO_TO_PARENT

ART_NEW

ART_CDROM

ART_GO_HOME

ART_FILE_OPEN

ART_GOTO_FIRST

ART_FILE_SAVE

In order to use wx.ArtProvider and retrieve a standard bitmap, you may do something like:

We will now explore a few of the most frequently used core controls in wxPython. The actual number of
core classes (100) is way too big to explore them all in this tutorial, but this introductory overview
should get you started and whet your appetite for more...

Common dialog classes and functions encapsulate commonly-needed dialog box requirements. They are all modal,
grabbing the flow of control until the user dismisses the dialog, to make them easy to use within an application.

Some dialogs have both platform-dependent and platform-independent implementations, so that if underlying
windowing systems do not provide the required functionality, the generic classes and functions can stand in.
For example, under Windows, ColourDialog
uses the standard colour selector. There is also an equivalent called GenericColourDialog for other platforms.

The path (defaultDir) and filename (defaultFile) are distinct elements of a full file pathname.

If defaultDir is “”, the current directory will be used. If defaultFile is “”, no default filename
will be supplied. The wildcard parameter determines what files are displayed in the file selector, and file
extension supplies a type extension for the required filename.

The most common style bits used are wx.FD_OPEN (for a “file open” dialog) and wx.FD_SAVE
(for a “file save” dialog).

wx.FileDialog implements a wildcard filter. Typing a filename containing wildcards (*, ?) in
the filename text item, and clicking on Ok, will result in only those files matching the pattern
being displayed.

The wildcard may be a specification for multiple types of file with a description for each, such as:

wildcard="BMP files (*.bmp)|*.bmp|GIF files (*.gif)|*.gif"

As an example, to give the user the possibility to choose one Python file in a folder:

# This is how you pre-establish a file filter so that the dialog# only shows the extension(s) you want it to.wildcard='Python source (*.py)|*.py'dlg=wx.FileDialog(None,message="Choose a Python file",defaultDir=os.getcwd(),defaultFile="",wildcard=wildcard,style=wx.FD_OPEN)# Show the dialog and retrieve the user response. If it is the OK response,# process the data.ifdlg.ShowModal()==wx.ID_OK:# This returns the file that was selectedpath=dlg.GetPath()print(path)# Destroy the dialog. Don't do this until you are done with it!# BAD things can happen otherwise!dlg.Destroy()

This dialog shows a single or multi-line message, plus buttons that can be chosen from OK, Cancel, Yes,
and No. Under Windows, an optional icon can be shown, such as an exclamation mark or question mark.

It is mostly used as informational message window. As an example, if you want to inform the user
that some process in your GUI has finished running, you may do the following:

As an example, this is how you may add a tabbed page to your notebook control:

notebook=wx.Notebook(parent)# Create the page windows as children of the notebookpage=wx.Panel(notebook)# Add the pages to the notebook with the label to show# on the tabnotebook.AddPage(page,'Page 1')

This widget enables to split the main area of an application into parts. The user can dynamically resize those
parts with the mouse pointer. Such a solution can be seen in mail clients or in burning software. You can split
an area vertically or horizontally.

A tree control presents information as a hierarchy, with items that may be expanded to show further items.
It is implemented natively on Windows and it uses a generic implementation on GTK and Mac OS, although
newer versions of wxPython (the 2.9 ones) provide a native implementation in
DataViewTreeCtrl.

There is also a pure-Python implementation (with many more functionalities) in wx.lib, termed
CustomTreeCtrl.

In this section, we are going to look at few generalities about custom controls and how to draw
custom objects on some wxPython windows. Unfortunately the entire “owner-draw” subject is way too
big to be covered during a short tutorial, but this section should at least get you started and
whet your appetite for more.

(If you want to hear more details, please feel free to contact me at any time during the PythonBrasil8
conference).

Some device contexts are created temporarily in order to draw on a window. This is true for some of
the device contexts available for wxPython:

ScreenDC: Use this to paint on
the screen, as opposed to an individual window.

ClientDC: Use this to paint on
the client area of window (the part without borders and other decorations), but do not use it
from within an PaintEvent.

PaintDC: Use this to paint on the
client area of a window, but only from within a PaintEvent.

WindowDC: Use this to paint on
the whole area of a window, including decorations. This may not be available on non-Windows platforms.

Let’s focus on the PaintDC, which is
one of the most commonly used. To use this device context, we want to bind a paint event for a window
to an event handler, which will be responsible for drawing (almost) anything we want onto our window:

classMainWindow(wx.Frame):def__init__(self,parent,title):wx.Frame.__init__(self,parent,title=title)# Bind a "paint" event for the frame to the# "OnPaint" methodself.Bind(wx.EVT_PAINT,self.OnPaint)self.Show()defOnPaint(self,event):dc=wx.PaintDC(self)# Set a red brush to draw a rectangledc.SetBrush(wx.RED_BRUSH)dc.DrawRectangle(10,10,50,50)

The PaintEvent is triggered every time the window is redrawn, so we can be sure that our red rectangle will
always be drawn when the operating system wants to “refresh” the content of our window.

Similar things can be done using other graphical primitives, like DrawPoint:

defOnPaint(self,event):dc=wx.PaintDC(self)# Use a red pen to draw the pointsdc.SetPen(wx.Pen('RED'))# Get the size of the area inside the main windoww,h=self.GetClientSize()# Draw a sequence of points along the mid lineforxinrange(1,w,3):dc.DrawPoint(x,h/2)

defOnPaint(self,event):dc=wx.PaintDC(self)# Use a big font for the text...font=wx.Font(20,wx.SWISS,wx.NORMAL,wx.BOLD)# Inform the DC we want to use that fontdc.SetFont(font)# Draw our text onto the DCdc.DrawText('Hello World',10,10)