WxPython Interface

From TRCCompSci - AQA Computer Science
Jump to: navigation, search

Install

Manual Installation

if you have access to the command line, or have already installed Python 3 (ie not via Visual Studio etc) you can type the following command to install PyGame:

pip install -U wxPython

or

python -m install -U wxPython

Visual Studio 2017

If you added the Python option during the install process, Python3 should be installed along with the pip program. So on the tools tab, look for Python and then choose Python Environments.

Then change the drop down box to packages, a search box will appear so type in wxPython. It will provide you with an install link so just click it, it should say "pip install wxPython" from PyPi"

Hello, World

As is traditional, we are first going to write a Small "Hello, world" application. Here is the code:

#!python
#!/usr/bin/env python
import wx

app = wx.App(False)  # Create a new app, don't redirect stdout/stderr to a window.
frame = wx.Frame(None, wx.ID_ANY, "Hello World") # A Frame is a top-level window.
frame.Show(True)     # Show the frame.
app.MainLoop()

Every wxPython app is an instance of wx.App. For most simple applications you can use wx.App as is. A wx.Frame is a top-level window. The syntax is wx.Frame(Parent, Id, Title). Most of the constructors have this shape (a parent object, followed by an Id). frame.Show(True) will make a frame visible by "showing" it. Finally app.MainLoop(), this starts the application's MainLoop whose role is to handle the events.

Run the program and you should see a window like this one:

wxPython window

Building a simple text editor

In this tutorial we are going to build a simple text editor. In the process, we will explore several widgets, and learn about features such as events and callbacks.

First steps

The first step is to make a simple frame with an editable text box inside. A text box is made with the wx.TextCtrl widget. By default, a text box is a single-line field, but the wx.TE_MULTILINE parameter allows you to enter multiple lines of text.

#!python
#!/usr/bin/env python
import wx
class MyFrame(wx.Frame):
    """ We simply derive a new class of Frame. """
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(200,100))
        self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        self.Show(True)

app = wx.App(False)
frame = MyFrame(None, 'Small editor')
app.MainLoop()

In this example, we inherit from wx.Frame and overwrite its __init__ method. Here we declare a new wx.TextCtrl which is a simple text edit control. Note that since the MyFrame runs self.Show() inside its __init__ method, we no longer have to call frame.Show() explicitly.

Adding a menu bar

Every application should have a menu bar and a status bar. Let's add them to ours:

#!python
import wx

class MainWindow(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(200,100))
        self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        self.CreateStatusBar() # A Statusbar in the bottom of the window

        # Setting up the menu.
        filemenu= wx.Menu()

        # wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided by wxWidgets.
        filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
        filemenu.AppendSeparator()
        filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")

        # Creating the menubar.
        menuBar = wx.MenuBar()
        menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
        self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.
        self.Show(True)

app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()

TIP: 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 wxWidgets know how to display the widget in each platform to make it look more native.

Event handling

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, etc). Much of GUI programming consists of responding to events. You bind an object to an event using the Bind() method:

#!python
class MainWindow(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self,parent, title=title, size=(200,100))
        ...
        menuItem = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
        self.Bind(wx.EVT_MENU, self.OnAbout, menuItem)

This means that, from now on, when the user selects the "About" menu item, the method self.OnAbout will be executed. wx.EVT_MENU is the "select menu item" event. wxWidgets understands many other events [|see the full list]. The self.OnAbout method should be added to the MainWindow class:

#!python
def OnAbout(self, event):
        ...

The method is executed when the event occurs. By default, this method will handle the event and the event will stop after the callback finishes. However, you can "skip" an event with event.Skip(). This causes the event to go through the hierarchy of event handlers. For example:

#!python
def OnButtonClick(self, event):
    if (some_condition):
        do_something()
    else:
        event.Skip()

def OnEvent(self, event):
    ...

When a button-click event occurs, the method OnButtonClick gets called. If some_condition is true, we do_something() otherwise we let the event be handled by the more general event handler. Now let's have a look at our application:

#!python
import os
import wx


class MainWindow(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(200,100))
        self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        self.CreateStatusBar() # A StatusBar in the bottom of the window

        # Setting up the menu.
        filemenu= wx.Menu()

        # wx.ID_ABOUT and wx.ID_EXIT are standard ids provided by wxWidgets.
        menuAbout = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
        menuExit = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")

        # Creating the menubar.
        menuBar = wx.MenuBar()
        menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
        self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.

        # Set events.
        self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
        self.Bind(wx.EVT_MENU, self.OnExit, menuExit)

        self.Show(True)

    def OnAbout(self,e):
        # A message dialog box with an OK button. wx.OK is a standard ID in wxWidgets.
        dlg = wx.MessageDialog( self, "A small text editor", "About Sample Editor", wx.OK)
        dlg.ShowModal() # Show it
        dlg.Destroy() # finally destroy it when finished.

    def OnExit(self,e):
        self.Close(True)  # Close the frame.

app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()

Dialogs

Of course an editor is useless if it is not able to save or open documents. That's where Common dialogs come in. Common dialogs are those offered by the underlying platform so that your application will look exactly like a native application. Here is the implementation of the OnOpen method in MainWindow:

#!python
    def OnOpen(self,e):
        """ Open a file"""
        self.dirname = ''
        dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", wx.FD_OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            self.filename = dlg.GetFilename()
            self.dirname = dlg.GetDirectory()
            f = open(os.path.join(self.dirname, self.filename), 'r')
            self.control.SetValue(f.read())
            f.close()
        dlg.Destroy()

Explanation:

  • First, we create the dialog by calling the appropriate Constructor.
  • Then, we call ShowModal. That opens the dialog - "Modal" means that the user cannot do anything on the application until he clicks OK or Cancel.
  • The return value of ShowModal is the Id of the button pressed. If the user pressed OK we read the file.

You should now be able to add the corresponding entry into the menu and connect it to the OnOpen method. If you have trouble, scroll down to the appendix for the full source code.