2

This may seem a bit general, but the problem is quite simple actually. Is it possible to subscribe to a subset of topics using the pubsub module.

Let me briefly explain what I would like to accomplish. In a wxpython project I want to change the text in the status bar according to different events. So I would like to have one function (one listener) which would subscribe to a set of topics. In the listener I would have if statement and several elif statements where the topic's name would be checked. Then the status bar text would be changed accordingly.

Is it possible to do this, or is it a bad idea in the first place. How should I deal with that kind of situation. Thanks in advance

user3176500
  • 389
  • 2
  • 6
  • 15

3 Answers3

1

It is a bad idea in the first place. You should let pubsub do the work for you. One listener per topic. There is no cost to doing that, it segregates your code, makes it easier to maintain, separation of responsibilities.

That said, a listener can listen to base topic: pub.subscribe('a.b', listener) will get messages for topic a.b, a.b.c, a.b.d, a.b.c.e, etc. As described in the pubsub docs, you can tell pusub to give the topic object as part of message by using a keyword arg that has a default value of pub.AUTO_TOPIC. But if you use this strategy and you end up with a long list of if/elif/else its probably not the way to go.

Perhaps if you give more details about the topic hierarchy you intend to have, and the kind of if/else you had in mind, I can provide more useful feedback.

Oliver
  • 27,510
  • 9
  • 72
  • 103
0

There are a couple of approaches. First and perhaps simplest, you could have just one listener. When you publish to the listener, you just pass it different pieces of information. For example, from class A, you would pass one string. For class B, you might pass a different string, but to the same listener. Then in the listener, you just check for which string, as you mentioned.

The other approach would be to create multiple listeners that are all bound to the same function or method. Then you can publish to the different listeners and they will all call the same thing.

You should use the approach that you find easiest to understand and debug.

Here's an example:

import wx
from wx.lib.pubsub import pub 

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

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, wx.ID_ANY, "Secondary Frame")
        panel = wx.Panel(self)

        msg = "Enter a Message to send to the main frame"
        instructions = wx.StaticText(panel, label=msg)
        self.msgTxt = wx.TextCtrl(panel, value="")

        sendBtn = wx.Button(panel, label="Send Msg")
        sendBtn.Bind(wx.EVT_BUTTON, self.onSendMsg)

        closeBtn = wx.Button(panel, label="Send and Close")
        closeBtn.Bind(wx.EVT_BUTTON, self.onSendAndClose)

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

    #----------------------------------------------------------------------
    def onSendMsg(self, event):
        """"""
        msg = "Another message!"
        pub.sendMessage("anotherListener", 
                        message=msg,
                        listener="anotherListener")

    #----------------------------------------------------------------------
    def onSendAndClose(self, event):
        """
        Send a message and close frame
        """
        msg = self.msgTxt.GetValue()
        pub.sendMessage("panelListener", 
                        message=msg,
                        listener="panelListener")
        self.Close()

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

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        pub.subscribe(self.myListener, "panelListener")
        pub.subscribe(self.myListener, "anotherListener")

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

    #----------------------------------------------------------------------
    def myListener(self, message, listener=None):
        """
        Listener function
        """

        if listener == "panelListener":
            print "panel listener has sent the following: ",
            print message
        elif listener == "anotherListener":
            print "another listener sent the following: ", 
            print message

    #----------------------------------------------------------------------
    def onOpenFrame(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)
    frame = MyFrame()
    app.MainLoop()

This is a modified example from my tutorial: http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/

Mike Driscoll
  • 32,629
  • 8
  • 45
  • 88
  • Hi thanks for the answer. Do you have in mind any minimal example or at least a link where I could find one. I am using version 3 API by the way. – user3176500 Mar 17 '14 at 14:29
  • I added an example of sorts. – Mike Driscoll Mar 17 '14 at 14:48
  • Hi Mike. Thanks for the example. Still, I have some questions. In MyPanel you have two subscriptions. Can't write something like (writing more generally) pub.subscribe(self.myListener, ["topic1", "topic2"]). Also in order to keep tracks of the topics, I assume, you had to add the extra vaiable, that is listener, and wrote pub.sendMessage("topic",message=msg,listener="topic"), where I used "topic" instead of "anotherListener" for instance. Can't you get the topic implicitly through messaging. Would your example also work if I had listeners with various input arguments ? – user3176500 Mar 18 '14 at 07:04
  • I am not aware of a way to subscribe to multiple topics at once. However, it appears that can kind of do that by using the topic hierarchy method mentioned in the docs: http://pubsub.sourceforge.net/usage/usage_basic_tasks.html#topic-name. I can't find anything about the listener having the topic name itself, but you might find the following useful: http://pubsub.sourceforge.net/usage/usage_advanced_debug.html#using-the-pub-listener-class – Mike Driscoll Mar 18 '14 at 13:50
  • Thank you Mike for turning my attention to the first link. – user3176500 Mar 19 '14 at 07:15
0

Another approach is to call pub.subscribe, multiple times, for the same subscriber function but with different specific subtopics. For example say you have an email spam filter that classifies spam into 20 different specific categories that can be grouped into five groups. At this time you only want to handle two groups of those categories. Here is a way:

def delete_spam_subscriber(header, message):
   delete_message(header, message)

def junkmail_subscriber(header, message):
   move_to_junk(header, message)

pub.subscribe(delete_spam_subscriber, 'root.spam.profanity')
pub.subscribe(delete_spam_subscriber, 'root.spam.spyware')
pub.subscribe(delete_spam_subscriber, 'root.spam.indirect')

pub.subscribe(junkmail_subscriber, 'root.spam.unknown_sender')
pub.subscribe(junkmail_subscriber, 'root.spam.forum_replys')
pub.subscribe(junkmail_subscriber, 'root.spam.receive_but_limit')

This is of course if you don't want to create by grouping topics 'root.spam.deletables', 'root.spam.to_junk_folder'

DevPlayer
  • 5,393
  • 1
  • 25
  • 20