wxPython’s Context Managers

The wxPython toolkit added context managers to its code base a few years ago, but for some reason you don’t see very many examples of their use. In this article, we’ll look at three examples of context managers in wxPython. A wxPython user was the first person to suggest using context managers in wxPython on the wxPython mailing list. We’ll start off by rolling our own context manager and then look at a couple of examples of built-in context managers in wxPython.


Creating Your Own wxPython Context Manager

Creating your own context manager in wxPython is pretty easy. We will use the wx.FileDialog for our example of a context manager.

import os
import wx


########################################################################
class ContextFileDialog(wx.FileDialog):
    """"""

    #----------------------------------------------------------------------
    def __enter__(self):
        """"""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.Destroy()
        
        
########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        
        btn = wx.Button(self, label='Open File')
        btn.Bind(wx.EVT_BUTTON, self.onOpenFile)
        
    #----------------------------------------------------------------------
    def onOpenFile(self, event):
        """"""
        wildcard = "Python source (*.py)|*.py|" \
            "All files (*.*)|*.*"
        kwargs = {'message':"Choose a file",
                  'defaultDir':os.path.dirname(os.path.abspath( __file__ )), 
                  'defaultFile':"",
                  'wildcard':wildcard,
                  'style':wx.FD_OPEN | wx.FD_MULTIPLE | wx.FD_CHANGE_DIR
                  }
        with ContextFileDialog(self, **kwargs) as dlg:
            if dlg.ShowModal() == wx.ID_OK:
                paths = dlg.GetPaths()
                print "You chose the following file(s):"
                for path in paths:
                    print path
        
        
########################################################################
class MyFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title='wxPython Contexts')
        panel = MyPanel(self)
        self.Show()
        
if __name__ == '__main__':
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

In this example, we subclass wx.FileDialog and all we do is override the __enter__ and __exit__ methods. This will turn our FileDialog instance into a context manager when we call it using Pythons with statement. You can see this in the onOpenFile event handler within the MyPanel class. Now let’s move on and look at some of wxPython’s builtin examples!


wxPython’s Context Managers

The wxPython package supports context managers in anything that subclasses wx.Dialog as well as the following widgets:

  • wx.BusyInfo
  • wx.BusyCursor
  • wx.WindowDisabler
  • wx.LogNull
  • wx.DCTextColourChanger
  • wx.DCPenChanger
  • wx.DCBrushChanger
  • wx.DCClipper
  • wx.Freeze / wx.Thaw

There are probably more widgets, but this was the only listing I could find at the time of writing. Let’s look at a couple examples:

import time
import wx

########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        self.frame = parent

        main_sizer = wx.BoxSizer(wx.VERTICAL)

        dlg_btn = wx.Button(self, label='Open ColorDialog')
        dlg_btn.Bind(wx.EVT_BUTTON, self.onOpenColorDialog)
        main_sizer.Add(dlg_btn, 0, wx.ALL|wx.CENTER)

        busy_btn = wx.Button(self, label='Open BusyInfo')
        busy_btn.Bind(wx.EVT_BUTTON, self.onOpenBusyInfo)
        main_sizer.Add(busy_btn,0, wx.ALL|wx.CENTER)

        self.SetSizer(main_sizer)


    #----------------------------------------------------------------------
    def onOpenColorDialog(self, event):
        """
        Creates and opens the wx.ColourDialog
        """
        with wx.ColourDialog(self) as dlg:
            if dlg.ShowModal() == wx.ID_OK:
                data = dlg.GetColourData()
                color = str(data.GetColour().Get())
                print 'You selected: %s\n' % color

    #----------------------------------------------------------------------
    def onOpenBusyInfo(self, event):
        """
        Creates and opens an instance of BusyInfo
        """
        msg = 'This app is busy right now!'
        self.frame.Hide()
        with wx.BusyInfo(msg) as busy:
            time.sleep(5)
        self.frame.Show()


########################################################################
class MyFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title='Context Managers')
        panel = MyPanel(self)

        self.Show()

#----------------------------------------------------------------------
if __name__ == '__main__':
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

In the above code, we have two examples of wxPython’s context managers. The first one is in the onOpenColorDialog event handler. Here we create an instance of wx.ColourDialog and then grab the selected color if the user presses the OK button. The second example is only a bit more complex in that it hides the frame before showing the BusyInfo instance. Frankly, I think this example could be improved a bit by putting the frame’s hiding and showing into the context manager itself, but ‘ll leave that as an exercise for the reader to try out.


Wrapping Up

wxPython’s context managers are quite handy and they’re fun to use. I hope you’ll find yourself using them in your own code sometime soon. Be sure and try out some of the other context managers in wxPython to see if they might suit your code base or just to make your code a little cleaner.