Use your business data to your advantage with the help of Syncfusion’s new data science offerings. Discover how a custom big data solution can provide your company with valuable predictions about key market trends.

Introduction

Programming is all about patterns. There are patterns at every level, from the programming language itself, to the toolkit, to the application. Being able to discern and choose the optimal approach to use to solve the problem at hand can at times be a difficult task. The more patterns you know, the bigger your toolbox, and the easier it will become to be able to choose the right tool for the job.

Different programming languages and toolkits often lend themselves to certain patterns and approaches to problem solving. The Python programming language and wxPython are no different, so let's jump in and take a look at how to apply some common design approaches and techniques to wxPython applications.

Creating Singletons

In object oriented programming, the Singleton pattern is a fairly simple concept of only allowing exactly one instance of a given object to exist at a given time. This means that it only allows for only one instance of the object to be in memory at any given time, so that all references to the object are shared throughout the application. Singletons are often used to maintain a global state in an application since all occurrences of one in an application reference the same exact instance of the object. Within the core wxPython library, there are a number of singleton objects, such as ArtProvider , ColourDatabase , and SystemSettings . This recipe shows how to make a singleton Dialog class, which can be useful for creating non-modal dialogs that should only have a single instance present at a given time, such as a settings dialog or a special tool window.

How to do it...

To get started, we will define a metaclass that can be reused on any class that needs to be turned into a singleton. We will get into more detail later in the How it works section. A metaclass is a class that creates a class. It is passed a class to it's __init__ and __call__ methods when someone tries to create an instance of the class.

def __call__(cls, *args, **kw): if not cls.instance: # Not created or has been Destroyed obj = super(Singleton, cls).__call__(*args, **kw) cls.instance = obj cls.instance.SetupWindow()

return cls.instance

Here we have an example of the use of our metaclass, which shows how easy it is to turn the following class into a singleton class by simply assigning the Singleton class as the __metaclass__ of SingletonDialog. The only other requirement is to define the SetupWindow method that the Singleton metaclass uses as an initialization hook to set up the window the first time an instance of the class is created.

Note that in Python 3+ the __metaclass__ attribute has been replaced with a metaclass keyword argument in the class definition.

How it works...

There are a number of ways to implement a Singleton in Python. In this recipe, we used a metaclass to accomplish the task. This is a nicely contained and easily reusable pattern to accomplish this task. The Singleton class that we defined can be used by any class that has a SetupWindow method defined for it. So now that we have done it, let's take a quick look at how a singleton works.

The Singleton metaclass dynamically creates and adds a class variable called instance to the passed in class. So just to get a picture of what is going on, the metaclass would generate the following code in our example:

class SingletonDialog(wx.Dialog): instance = None

Then the first time the metaclass's __call__ method is invoked, it will then assign the instance of the class object returned by the super class's __call__ method, which in this recipe is an instance of our SingletonDialog. So basically, it is the equivalent of the following:

SingletonDialog.instance = SingletonDialog(*args,**kwargs)

Any subsequent initializations will cause the previously-created one to be returned, instead of creating a new one since the class definition maintains the lifetime of the object and not an individual reference created in the user code.

Our SingletonDialog class is a very simple Dialog that has TextCtrl, CheckBox, and Ok Button objects on it. Instead of invoking initialization in the dialog's __init__ method, we instead defined an interface method called SetupWindow that will be called by the Singleton metaclass when the object is initially created. In this method, we just perform a simple layout of our controls in the dialog. If you run the sample application that accompanies this topic, you can see that no matter how many times the show dialog button is clicked, it will only cause the existing instance of the dialog to be brought to the front. Also, if you make changes in the dialog's TextCtrl or CheckBox, and then close and reopen the dialog, the changes will be retained since the same instance of the dialog will be re-shown instead of creating a new one.

Implementing an observer pattern

The observer pattern is a design approach where objects can subscribe as observers of events that other objects are publishing. The publisher(s) of the events then just broadcasts the events to all of the subscribers. This allows the creation of an extensible, loosely-coupled framework of notifications, since the publisher(s) don't require any specific knowledge of the observers. The pubsub module provided by the wx.lib package provides an easy-to-use implementation of the observer pattern through a publisher/subscriber approach. Any arbitrary number of objects can subscribe their own callback methods to messages that the publishers will send to make their notifications. This recipe shows how to use the pubsub module to send configuration notifications in an application.

How to do it...

Here, we will create our application configuration object that stores runtime configuration variables for an application and provides a notification mechanism for whenever a value is added or modified in the configuration, through an interface that uses the observer pattern:

Here, in the FontPicker's event handler, we get the newly-selected font and call SetValue on the Configuration object owned by the App object in order to change the configuration, which will then cause the ('config', 'font') message to be published:

Now, here, we define the application's main window that will subscribe it's OnConfigMsg method as an observer of all ('config',) messages, so that it will be called whenever the configuration is modified:

Here is the observer method that will be called when any message beginning with 'config' is sent by the pubsub Publisher. In this sample application, we just check for the ('config', 'font') message and update the font of the TextCtrl object to use the newly-configured one:

How it works...

This recipe shows a convenient way to manage an application's configuration by allowing the interested parts of an application to subscribe to updates when certain parts of the configuration are modified. Let's start with a quick walkthrough of how pubsub works.

Pubsub messages use a tree structure to organize the categories of different messages. A message type can be defined either as a tuple ('root', 'child1', 'grandchild1') or as a dot-separated string ('root.child1.grandchild1'). Subscribing a callback to ('root',) will cause your callback method to be called for all messages that start with ('root',). This means that if a component publishes ('root', 'child1', 'grandchild1') or ('root', 'child1'), then any method that is subscribed to ('root',) will also be called

Pubsub basically works by storing the mapping of message types to callbacks in static memory in the pubsub module. In Python, modules are only imported once any other part of your application that uses the pubsub module shares the same singleton Publisher object.

In our recipe, the Configuration object is a simple object for storing data about the configuration of our application. Its SetValue method is the important part to look at. This is the method that will be called whenever a configuration change is made in the application. In turn, when this is called, it will send a pubsub message of ('config',) + (key,) that will allow any observers to subscribe to either the root item or more specific topics determined by the exact configuration item.

Next, we have our simple ConfigDialog class. This is just a simple example that only has an option for configuring the application's font. When a change is made in the FontPickerCtrl in the ConfigPanel, the Configuration object will be retrieved from the App and will be updated to store the newly-selected Font. When this happens, the Configuration object will publish an update message to all subscribed observers.

Our ObserverFrame is an observer of all ('config',) messages by subscribing its OnConfigMsg method to MSG_CONFIG_ROOT. OnConfigMsg will be called any time the Configuration object's SetValue method is called. The msg parameter of the callback will contain a Message object that has a topic and data attribute. The topic attribute will contain the tuple that represents the message that triggered the callback and the data attribute will contain any data that was associated with the topic by the publisher of the message. In the case of a ('config', 'font') message, our handler will update the Font of the Frame and its TextCtrl.

Strategy pattern

The strategy pattern is an approach that allows for an application to choose the strategy or behavior that will be used at run time. It accomplishes this by encapsulating different algorithms and making them usable by the client regardless of what the underlying behavior of the algorithm is. This is probably one of the most fundamental design patterns in programming, and you're probably already using it in one form or another without even knowing it. This recipe shows how to create a reusable Dialog class that uses the strategy pattern to allow for the main content to vary depending on the strategy used.

How to do it...

First, we will start by defining a base interface with all of the strategies that our dialog class will use:

How it works...

Since all strategies that our dialog will use must be interchangeable, it is important to define an interface that they will implement. So, in our BaseDialogStrategy class, we defined a simple three-method interface that our StrategyDialog will delegate to.

The StrategyDialog is basically just a simple generic shell that delegates all decisions regarding its appearance and behavior to the strategy. When the dialog is initialized, it asks the strategy for a window object that will be used as the main content area of the dialog. The dialog then creates and adds some standard OK/Cancel buttons to the interface.

When a user clicks on one of these buttons, the StrategyDialog will then simply delegate to its strategy, to allow the strategy to handle the user action. This allows us to reuse this dialog class in many different ways, by simply implementing different strategies.

Model View Controller

Model View Controller (MVC) is a design pattern that creates a clear separation of concerns within a program's architecture. It breaks down into three layers: the Model, which is the application's data objects and business logic at the bottom, the View at the top, which typically consists of controls for displaying and editing data, and finally the Controller in the middle, which is responsible for mediating the flow of data from the Model to the View and vice versa:

M VC is really one big monster pattern made up of other, simpler patterns working together. The Model implements an observer pattern to keep interested parties updated on changes, which allows it to be implemented separately from the View and Controller. The View and the Controller, on the other hand, implement a strategy pattern where the Controller is a strategy that implements the behavior of the View.

In this recipe, we explore how to create a simple number generator application that implements this pattern in wxPython.

How to do it...

Since there are multiple components that need to work together, having defined interfaces is an important step in the process, so first let us define some base classes that define the interface for our number generator's model and controller.

Beginning with our Model's interface, we provide a class that simply requires its Generate method to be overridden in order to provide the implementation-specific behavior. We have also built in a simple observer pattern mechanism to allow the view to subscribe to update notifications in the model:

def NotifyObservers(self): """Notify all observers of current value""" for observer in self.observers: observer()

Next we have the base interface definition for the controllers of our framework's view to derive from. This just defines one simple DoGenerateNext method that must be overridden by the specific implementation:

Then our FibonacciController provides the controller specialization, which in this example just makes one update to the user interface, to disable the button while the model is calculating the next value:

Now that the model and controller have been defined, let's take a look at our view, which is composed of a Frame, a Panel that has a TextCtrl for displaying the current value stored in the model, and a Button for retrieving the next value in the sequence defined by the model:

How it works...

Using MVC to design an application's framework takes a fair amount of discipline. As can be seen in this simple example, there is quite a bit of extra "stuff" that needs to be done. As described before, MVC separates concerns into three main roles:

The Model

The View

The Controller

So let's take a look at how these roles came together in our sample recipe.

First the Model: This has the ability to store a value and to generate the next one in the sequence when its Generate method is called. In this recipe, we implemented a Model that calculates and stores Fibonacci numbers. The important part to take away from this is that the Model does not have any direct knowledge of the View or the Controller.

Next let's jump to the View, which just displays a TextCtrl field and a Button. It does not know any of the details of how the Controller or Model works. It only interacts with them through a defined interface. When the user clicks on the Button, it asks the Controller to decide what to do. In order to know when the Model has changed, it registers a callback function with the Model, as an observer of when the Model's SetValue method is called.

Now to the Controller which is the glue that holds the Model and View together. The Controller is primarily charged with implementing the View's behavior in regards to the Model's state. Our simple Controller for this recipe only has one interface method that is called in response to a Button click in the View. This action first disables the Button, and then tells the Model to generate the next number in the sequence.

There's more...

You may be wondering "what's the point?" of all that extra rigmarole to create such a simple application. Well since the Model is completely separate from the View, it can be more easily unit tested in an automated test suite. In addition to this, since the View is just simply a view and does not implement any behavior, it can easily be reused if, for example, we wanted to add a Prime Number generator model to our application.

Maintainability is also improved since all three parts are separated and can be worked on individually without interfering with the other components. Because of these benefits, many other toolkits, such as Django and web2py make use of this pattern.

Using mixin classes

A mixin class is a design approach that is similar to the strategy pattern, but directly uses inheritance in order to add extended/common functionality to a new class. This recipe shows how to create a mixin class that adds debug logging facilities to any class that uses it.

How to do it...

First, let's start by creating our LoggerMixin class, which will provide the logging functionality to classes that need to have logging. It simply provides a Log method that will write the passed in string to a file:

How it works...

The mixin class in this recipe is the LoggerMixin class. It will add a Log method to the classes that use it, which will take a simple string as an argument and write it to the specified log file with a timestamp and ID that shows where the message came from.

A mixin works by using multiple inheritance in order to add additional functionality to a class. The LoggerMixin mixin class can be used with any Python class, but it expects (but doesn't require) that the class it is being mixed into has a GetName method to use for getting the ID portion of the log message:

There's more

There are a number of handy mixin classes provided by the wx.lib.mixins package. Here is a quick rundown on some of the mixins that are available and what functionality they provide.

ListCtrl mixins

All of the following mixins classes are intended for use with a ListCtrl subclass and are provided by the wx.lib.mixins.listctrl module:

TreeCtrl mixins

All of the following mixin classes are for use with a TreeCtrl subclass, and are provided by the wx.lib.mixins.treectrl module:

Using decorators

Due to the window hierarchy, there are some architectural issues that can be presented to the programmer that lead to some tedious and unnecessary code duplication due to the need to have delegate accessor methods or properties at each level of the containment hierarchy. Typically, any Frame or Dialog in an application is structured as shown in the following diagram:

When needing to retrieve or modify the data that is shown in the window, it is the widgets and Controls that need to be accessed. These are contained by the Panel which is in turn contained by the Top Level Window. Since the Panel is responsible for its children, it will often have methods for modifying and accessing the data that is maintained by its children's controls. Because of this, the top-level window class often needs to have duplicate methods that simply delegate to the Panel's methods for getting and setting the window's data. These delegate methods are needed because the top-level window is the object that is instantiated at the application level and the application should not need to know the details of the top-level window's Panel in order to use it.

T his recipe shows how to create a simple decorator method that takes advantage of Python's dynamic nature in order to expose a select method of a custom Panel class to its top-level window container.

How to do it...

This decorator class takes the name of a class as an argument and will dynamically define the delegate method in the targeted child Panel of the top level window:

class expose(object): """Expose a panels method to a to a specified class The panel that is having its method exposed by this decorator must be a child of the class its exposing itself too. """ def __init__(self, cls): """@param cls: class to expose the method to""" super(expose, self).__init__() self.cls = cls

Here is where the magic occurs. We use setattr to dynamically add a function with the same name as the function being decorated to the targeted class. When called from the targeted class, the new method will walk through the window's children to find its Panel, and will delegate the call to the child class's method:

The example code that accompanies this article has a sample application that shows how to use this decorator.

How it works...

This recipe isn't so much a design pattern as it is a technique to help make writing new Dialog and Frame classes quicker and also to reduce code duplication. To do this, we created a decorator class for exposing methods from child Panel classes to their parent top-level window. Let's start with a look at the expose decorator to see how it works its magic.

The expose decorator takes a single argument, which is the class that the method should be exposed to. A reference to this is saved in the constructor for later use when the decorator is applied in its __call__ method. The __call__ method creates a method called delegate which will search for the first child panel that has a method with the same name as the one that is being exposed. If it finds an appropriate panel, then it simply calls the panel's method and returns its value. Next, it uses setattr to insert the newly-generated delegate method with an alias matching the Panel's method into the namespace of the class specified in the decorator's constructor. At this point, the method is available for use in the top-level window that expose was called with. Finally, we just return the unaltered original function to the Panel class it belongs to.

Just to be clear, this decorator, as it is defined in this recipe, can only be used by Panel subclasses that have a known relationship of being the only child of their parent window. This is typical of how most Frame and Dialog subclasses are constructed, as can be seen in the example CommentDialog class that is included in this article's sample code.

Summary

This article introduced you to a number of common programming patterns, and explained how to apply them to wxPython applications. The information in this article provided you with an understanding of some strong approaches and techniques to software design that will not only serve you in writing wxPython applications but can also be generally applied to other frameworks as well, to expand your programming toolbox.

Alerts & Offers

Series & Level

We understand your time is important. Uniquely amongst the major publishers, we seek to develop and publish the broadest range of learning and information products on each technology. Every Packt product delivers a specific learning pathway, broadly defined by the Series type. This structured approach enables you to select the pathway which best suits your knowledge level, learning style and task objectives.

Learning

As a new user, these step-by-step tutorial guides will give you all the practical skills necessary to become competent and efficient.

Beginner's Guide

Friendly, informal tutorials that provide a practical introduction using examples, activities, and challenges.

Essentials

Fast paced, concentrated introductions showing the quickest way to put the tool to work in the real world.

Cookbook

A collection of practical self-contained recipes that all users of the technology will find useful for building more powerful and reliable systems.

Blueprints

Guides you through the most common types of project you'll encounter, giving you end-to-end guidance on how to build your specific solution quickly and reliably.

Mastering

Take your skills to the next level with advanced tutorials that will give you confidence to master the tool's most powerful features.

Starting

Accessible to readers adopting the topic, these titles get you into the tool or technology so that you can become an effective user.

Progressing

Building on core skills you already have, these titles share solutions and expertise so you become a highly productive power user.