wxPython 4 and PubSub

The Publish-Subscribe pattern is pretty common in computer science and very useful too. The wxPython GUI toolkit has had an implementation of it for a very long time in wx.lib.pubsub. This implementation is based on the PyPubSub package. While you could always download PyPubSub and use it directly instead, it was nice to be able to just run wxPython without an additional dependency.

However, as of wxPython 4.0.4, wx.lib.pubsub is now deprecated and will be removed in a future version of wxPython. So now you will need to download PyPubSub or PyDispatcher if you want to use the Publish-Subscribe pattern easily in wxPython.


Installing PyPubSub

You can install PyPubSub using pip.

Here’s how to do it:

pip install pypubsub

PyPubSub should install quite quickly. Once it’s done, let’s find out how to use it!


Using PyPubSub

Let’s take an example from my previous article on this topic and update it for using PyPubSub instead:

import wx
from pubsub import pub


class OtherFrame(wx.Frame):
    """"""

    def __init__(self):
        """Constructor"""
        super().__init__(None, title="Secondary Frame")
        panel = wx.Panel(self)

        msg = "Enter a Message to send to the main frame"
        instructions = wx.StaticText(panel, label=msg)
        self.msg_txt = wx.TextCtrl(panel, value="")
        close_btn = wx.Button(panel, label="Send and Close")
        close_btn.Bind(wx.EVT_BUTTON, self.on_send_and_slose)

        sizer = wx.BoxSizer(wx.VERTICAL)
        flags = wx.ALL|wx.CENTER
        sizer.Add(instructions, 0, flags, 5)
        sizer.Add(self.msg_txt, 0, flags, 5)
        sizer.Add(close_btn, 0, flags, 5)
        panel.SetSizer(sizer)

    def on_send_and_slose(self, event):
        """
        Send a message and close frame
        """
        msg = self.msg_txt.GetValue()
        pub.sendMessage("panel_listener", message=msg)
        pub.sendMessage("panel_listener", message="test2",
                        arg2="2nd argument!")
        self.Close()


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

    def __init__(self, parent):
        """Constructor"""
        super().__init__(parent)
        pub.subscribe(self.my_listener, "panel_listener")

        btn = wx.Button(self, label="Open Frame")
        btn.Bind(wx.EVT_BUTTON, self.on_open_frame)

    def my_listener(self, message, arg2=None):
        """
        Listener function
        """
        print(f"Received the following message: {message}")
        if arg2:
            print(f"Received another arguments: {arg2}")

    def on_open_frame(self, event):
        """
        Opens secondary frame
        """
        frame = OtherFrame()
        frame.Show()


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

    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None,
                          title="New PubSub API Tutorial")
        panel = MyPanel(self)
        self.Show()


if __name__ == "__main__":
    app = wx.App(False)

The main difference here between using the built-in PubSub is the import.

All you need to do is replace this:

from wx.lib.pubsub import pub 

with this:

from pubsub import pub

As long as you are using wxPython 2.9 or greater that is. If you were stuck using wxPython 2.8, then you will probably want to check out one of my previous articles on this topic to see how the PubSub API changed.

If you are using wxPython 2.9 or greater, then the change is super easy and almost painless.

As usual, you subscribe to a topic:

pub.subscribe(self.myListener, "panelListener")

And then you publish to that topic:

pub.sendMessage("panelListener", message=msg)

Give it a try and see how easy it is to add to your own code!


Wrapping Up

I personally really liked using wx.lib.pubsub, so I will probably keep using it with PyPubSub. However if you’ve ever wanted to try another package, like PyDispatcher, this would be as good a time as any to do so.


Related Reading