Updated for latest pubsub release v3.3.0rc1

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@75492 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Oliver Schoenborn
2013-12-29 20:57:01 +00:00
parent 1690888828
commit db399def0e
34 changed files with 1313 additions and 88 deletions

View File

@@ -1,21 +1,9 @@
PyPubSub provides a publish - subscribe API that facilitates the development of
event-based (also known as message-based) applications. PyPubSub supports sending and
receiving messages between objects of an application, as well as a variety of
advanced features that facilitate debugging and maintaining topics and messages
in larger applications.
Release Notes
=============
See the PyPubSub website (http://pubsub.sourceforge.net/) for
further details, and to download.
In order for easy_install to find the correct download links, they are listed here:
* `Windows Installer <http://downloads.sf.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0.win32.exe>`_
* `Egg for Python 2.7 <https://downloads.sourceforge.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0-py2.7.egg>`_
* `Egg for Python 2.6 <https://downloads.sourceforge.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0-py2.6.egg>`_
* `Egg for Python 2.5 <https://downloads.sourceforge.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0-py2.5.egg>`_
* `Egg for Python 2.4 <https://downloads.sourceforge.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0-py2.4.egg>`_
* `Zip (source) <http://downloads.sf.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0.zip>`_
Note: "easy_install pypubsub" will install the egg, which is a zip file, whereas
"easy_install -Z pypubsub" will extract the egg contents to a folder; this option will
make importing pubsub much faster.
* cleanup low-level API: exception classes, moved some out of pub module that did not
belong there (clutter), move couple modules,
* completed the reference documentation
* support installation via pip
* follow some guidelines in some PEPs such as PEP 396 and PEP 8
* support Python 2.6, 2.7, and 3.2 to 3.4a4 but drop support for Python <= 2.5

View File

@@ -0,0 +1,22 @@
# this file gets copied to wx/lib/pubsub folder when release to wxPython
For wxPython users:
The code in this wx/lib/pubsub folder is taken verbatim from the PyPubSub
project on SourceForge.net. Pubsub originated as a wxPython lib, but it is now
a standalone project on SourceForge. It is included as part of wxPython
distribution for convenience to wxPython users, but pubsub can also be installed
standalone (see installation notes at http://pypubsub.sourceforge.net), or you
can also overwrite the version in this folder with the standalone version or
put an SVN checkout of pubsub in this folder, etc.
Note that the source distribution on SF.net tends to be updated more often than
the copy in wx/lib/pubsub. If you wish to install pubsub standalone, there are
instructions on the Install page of http://pypubsub.sourceforge.net.
There is extensive documentation for pubsub at http://pypubsub.sourceforge.net,
and some examples are in wx/lib/pubsub/examples. The WxPython wiki also discusses
usage of pubsub in wxPython.
Oliver Schoenborn
December 2013

View File

@@ -5,15 +5,15 @@ Pubsub package initialization.
:license: BSD, see LICENSE_BSD_Simple.txt for details.
Last change info:
- $Date: 2013-11-17 13:09:24 -0500 (Sun, 17 Nov 2013) $
- $Revision: 340 $
- $Date: 2013-12-22 11:36:16 -0500 (Sun, 22 Dec 2013) $
- $Revision: 344 $
"""
LAST_RELEASE_DATE = "2013-09-15"
LAST_RELEASE_VER = "3.2.1b"
__version__ = "3.3.0dev1"
__version__ = "3.3.0rc1"
__all__ = [
'pub',

View File

@@ -260,7 +260,13 @@ class Topic(PublisherMixin):
return bool(self.__listeners)
def getListeners(self):
"""Get an iterator of listeners subscribed to this topic."""
"""Get a copy of list of listeners subscribed to this topic. Safe to iterate over while listeners
get un/subscribed from this topics (such as while sending a message)."""
return py2and3.keys(self.__listeners)
def getListenersIter(self):
"""Get an iterator over listeners subscribed to this topic. Do not use if listeners can be
un/subscribed while iterating. """
return py2and3.iterkeys(self.__listeners)
def validate(self, listener):
@@ -382,7 +388,9 @@ class Topic(PublisherMixin):
self._treeConfig.notificationMgr.notifySend('post', self)
def __sendMessage(self, data, topicObj, iterState):
# now send message data to each listener for current topic:
# now send message data to each listener for current topic;
# use list of listeners rather than iterator, so that if listeners added/removed during
# send loop, no runtime exception:
for listener in topicObj.getListeners():
try:
self._treeConfig.notificationMgr.notifySend('in', topicObj, pubListener=listener)

View File

@@ -0,0 +1,15 @@
These two examples demonstrate a more advanced use of pubsub. One of the
examples uses the *kwargs* messaging protocol, the other uses the *arg1*
messaging protocol. There are two examples that can be run from this folder:
**main_kwargs.py**: advanced example that shows other capabilities of
pubsub such as pubsub notification and listener exception
handling, in the 'kwargs' messaging protocol. All modules that
start with 'kwargs\_' are used, as well as some modules that are
independent of protocol and are shared with the arg1_main
example.
**main_arg1.py**: same as kwargs_main but using the 'arg1' protocol.
All modules that start with 'kwargs\_' are used, as well as some
modules that are independent of protocol and are shared with the
kwargs_main example.

View File

@@ -0,0 +1,39 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
from pubsub.py2and3 import print_
# ------------ create some listeners --------------
class Listener:
def onTopic11(self, msg):
data = msg.data
print_('Method Listener.onTopic11 received: ', repr(data))
def onTopic1(self, msg, topic=pub.AUTO_TOPIC):
info = 'Method Listener.onTopic1 received "%s" message: %s'
print_(info % (topic.getName(), repr(msg.data)))
def __call__(self, msg):
print_('Listener instance received: ', msg.data)
listenerObj = Listener()
def listenerFn(msg):
data = msg.data
print_('Function listenerFn received: ', repr(data))
# ------------ subscribe listeners ------------------
pub.subscribe(listenerObj, pub.ALL_TOPICS) # via its __call__
pub.subscribe(listenerFn, 'topic_1.subtopic_11')
pub.subscribe(listenerObj.onTopic11, 'topic_1.subtopic_11')
pub.subscribe(listenerObj.onTopic1, 'topic_1')

View File

@@ -0,0 +1,19 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
def doSomething1():
pub.sendMessage('topic_1.subtopic_11', ('message for subtopic 11', 'other message', 123))
def doSomething2():
pub.sendMessage('topic_1', 'message for topic 1')
pub.sendMessage('topic_2.subtopic_21', 'message for subtopic 2')

View File

@@ -0,0 +1,32 @@
# Automatically generated by TopicTreeSpecPrinter(**kwargs).
# The kwargs were:
# - fileObj: file
# - width: 70
# - treeDoc: None
# - indentStep: 4
# - footer: '# End of topic tree definition. Note that application may l...'
class topic_1:
"""
Explain when topic_1 should be used
"""
class subtopic_11:
"""
Explain when subtopic_11 should be used
"""
class topic_2:
"""
Some something useful about topic2
"""
class subtopic_21:
"""
description for subtopic 21
"""
# End of topic tree definition. Note that application may load
# more than one definitions provider.

View File

@@ -0,0 +1,32 @@
# Automatically generated by TopicTreeSpecPrinter(**kwargs).
# The kwargs were:
# - fileObj: file
# - footer: '# End of topic tree definition. Note that application may l...'
# - indentStep: 4
# - treeDoc: None
# - width: 70
class topic_1:
"""
Explain when topic_1 should be used
"""
class subtopic_11:
"""
Explain when subtopic_11 should be used
"""
class topic_2:
"""
Some something useful about topic2
"""
class subtopic_21:
"""
description for subtopic 21
"""
# End of topic tree definition. Note that application may load
# more than one definitions provider.

View File

@@ -0,0 +1,21 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
from pubsub.py2and3 import print_
# create one special notification handler that ignores all except
# one type of notification
class MyPubsubExcHandler(pub.IListenerExcHandler):
def __call__(self, listenerID):
print_('Exception raised in listener %s during sendMessage()' % listenerID)
print_(TracebackInfo())
pub.setListenerExcHandler( MyPubsubExcHandler() )

View File

@@ -0,0 +1,37 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
from pubsub.py2and3 import print_
# ------------ create some listeners --------------
class Listener:
def onTopic11(self, msg, msg2, extra=None):
print_('Method Listener.onTopic11 received: ', repr(msg), repr(msg2), repr(extra))
def onTopic1(self, msg, topic=pub.AUTO_TOPIC):
info = 'Method Listener.onTopic1 received "%s" message: %s'
print_(info % (topic.getName(), repr(msg)))
def __call__(self, **kwargs):
print_('Listener instance received: ', kwargs)
listenerObj = Listener()
def listenerFn(msg, msg2, extra=None):
print_('Function listenerFn received: ', repr(msg), repr(msg2), repr(extra))
# ------------ subscribe listeners ------------------
pub.subscribe(listenerObj, pub.ALL_TOPICS) # via its __call__
pub.subscribe(listenerFn, 'topic_1.subtopic_11')
pub.subscribe(listenerObj.onTopic11, 'topic_1.subtopic_11')
pub.subscribe(listenerObj.onTopic1, 'topic_1')

View File

@@ -0,0 +1,19 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
def doSomething1():
pub.sendMessage('topic_1.subtopic_11',
msg='message for subtopic 11', msg2='other message', extra=123)
def doSomething2():
pub.sendMessage('topic_1', msg='message for topic 1')
pub.sendMessage('topic_2.subtopic_21', msg='message for subtopic 2')

View File

@@ -0,0 +1,53 @@
# Automatically generated by TopicTreeSpecPrinter(**kwargs).
# The kwargs were:
# - fileObj: file
# - width: 70
# - treeDoc: None
# - indentStep: 4
# - footer: '# End of topic tree definition. Note that application may l...'
class topic_1:
"""
Explain when topic_1 should be used
"""
def msgDataSpec(msg):
"""
- msg: a text string message for recipient
"""
class subtopic_11:
"""
Explain when subtopic_11 should be used
"""
def msgDataSpec(msg, msg2, extra=None):
"""
- extra: something optional
- msg2: a text string message #2 for recipient
"""
class topic_2:
"""
Some something useful about topic2
"""
def msgDataSpec(msg=None):
"""
- msg: a text string
"""
class subtopic_21:
"""
description for subtopic 21
"""
def msgDataSpec(msg, arg1=None):
"""
- arg1: UNDOCUMENTED
"""
# End of topic tree definition. Note that application may load
# more than one definitions provider.

View File

@@ -0,0 +1,53 @@
# Automatically generated by TopicTreeSpecPrinter(**kwargs).
# The kwargs were:
# - fileObj: file
# - footer: '# End of topic tree definition. Note that application may l...'
# - indentStep: 4
# - treeDoc: None
# - width: 70
class topic_1:
"""
Explain when topic_1 should be used
"""
def msgDataSpec(msg):
"""
- msg: a text string message for recipient
"""
class subtopic_11:
"""
Explain when subtopic_11 should be used
"""
def msgDataSpec(msg, msg2, extra=None):
"""
- extra: something optional
- msg2: a text string message #2 for recipient
"""
class topic_2:
"""
Some something useful about topic2
"""
def msgDataSpec(msg=None):
"""
- msg: a text string
"""
class subtopic_21:
"""
description for subtopic 21
"""
def msgDataSpec(msg, arg1=None):
"""
- arg1: UNDOCUMENTED
"""
# End of topic tree definition. Note that application may load
# more than one definitions provider.

View File

@@ -0,0 +1,51 @@
"""
Uses topic definition provider for arg1 messaging protocol. Compare with
main_kwargs.py which shows example using kwargs messaging protocol:
kwargs protocol provides for message data self-documentation and more
robustness (pubsub can determine if message data missing or unknown due
to type, etc).
Experiment by changing arg1_topics.py and looking at the output tree
in arg1_topics_out.py.
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import setuparg1
from pubsub import pub
from pubsub.py2and3 import print_
import notifhandle
import exchandle
import arg1_topics
#***** actual application **********
print_('Using "arg1" messaging protocol of pubsub v3')
try:
print_('------- init ----------')
pub.addTopicDefnProvider( arg1_topics, pub.TOPIC_TREE_FROM_CLASS )
pub.setTopicUnspecifiedFatal()
import arg1_listeners
import arg1_senders as senders
print_('-----------------------')
senders.doSomething1()
senders.doSomething2()
print_('------- done ----------')
print_('Exporting topic tree to', arg1_topics.__name__)
pub.exportTopicTreeSpec('arg1_topics_out')
except Exception:
import traceback
traceback.print_exc()
print_(pub.exportTopicTreeSpec())
print_('------ exiting --------')

View File

@@ -0,0 +1,50 @@
"""
Uses topic definition provider for kwargs messaging protocol. Compare with
main_arg1.py which shows example using arg1 messaging protocol:
kwargs protocol provides for message data self-documentation and more
robustness (pubsub can determine if message data missing or unknown due
to type, etc).
Experiment by changing arg1_topics.py and looking at the output tree
in kwargs_topics_out.py.
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
from pubsub.py2and3 import print_
import notifhandle
import exchandle
import kwargs_topics
#***** actual application **********
print_('Using "kwargs" messaging protocol of pubsub v3')
try:
print_('------- init ----------')
pub.addTopicDefnProvider( kwargs_topics, pub.TOPIC_TREE_FROM_CLASS )
pub.setTopicUnspecifiedFatal()
import kwargs_listeners
import kwargs_senders as senders
print_('-----------------------')
senders.doSomething1()
senders.doSomething2()
print_('------- done ----------')
print_('Exporting topic tree to', kwargs_topics.__name__)
pub.exportTopicTreeSpec('kwargs_topics_out')
except Exception:
import traceback
traceback.print_exc()
print_(pub.exportTopicTreeSpec())
print_('------ exiting --------')

View File

@@ -0,0 +1,30 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
from pubsub.py2and3 import print_
from pubsub.utils.notification import useNotifyByWriteFile, IgnoreNotificationsMixin
# create one special notification handler that ignores all except
# one type of notification
class MyPubsubNotifHandler(IgnoreNotificationsMixin):
def notifySubscribe(self, pubListener, topicObj, newSub):
newSubMsg = ''
if not newSub:
newSubMsg = ' was already'
msg = 'MyPubsubNotifHandler: listener %s%s subscribed to %s'
print_(msg % (pubListener.name(), newSubMsg, topicObj.getName()))
pub.addNotificationHandler( MyPubsubNotifHandler() )
# print_(all notifications to stdout)
import sys
useNotifyByWriteFile(sys.stdout, prefix='NotifyByWriteFile:')

View File

@@ -0,0 +1,17 @@
These two examples demonstrate a simple use of pubsub with the *arg1*
messaging protocol. There are two examples that can be run from this folder:
**console_main.py**: basic console based, uses the console_senders.py and
console_listeners.py modules, giving basic example of the *arg1*
messaging protocol.
**wx.py**: basic wxPython GUI application that uses the *arg1* messaging
protocol. Note that it imports pubsub from
wxPython's wx.lib package. Therefore you must have (a copy of)
pubsub installed there for this example to work (pubsub can be
installed in multiple places independently and they will all work
without interfering with each other).
Note that this example is copied almost
verbatim from the wxPython wiki site and is probably not a
good model of how an application should be structured.

View File

@@ -0,0 +1,41 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
from pubsub.py2and3 import print_
# ------------ create some listeners --------------
class Listener:
def onTopic11(self, msg):
msg, extra = msg.data
print_('Method Listener.onTopic11 received: ', repr(msg), repr(extra))
def onTopic1(self, msg, topic=pub.AUTO_TOPIC):
msg = msg.data[0]
info = 'Method Listener.onTopic1 received "%s" message: %s'
print_(info % (topic.getName(), repr(msg)))
def __call__(self, msg):
print_('Listener instance received: ', repr(msg))
listenerObj = Listener()
def listenerFn(msg):
msg, extra = msg.data
print_('Function listenerFn received: ', repr(msg), repr(extra))
# ------------ subscribe listeners ------------------
pub.subscribe(listenerObj, pub.ALL_TOPICS) # via its __call__
pub.subscribe(listenerFn, 'topic1.subtopic11')
pub.subscribe(listenerObj.onTopic11, 'topic1.subtopic11')
pub.subscribe(listenerObj.onTopic1, 'topic1')

View File

@@ -0,0 +1,23 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import setuparg1
from pubsub.py2and3 import print_
import console_senders as senders
import console_listeners
def run():
print_('Using "arg1" messaging protocol of pubsub v3')
senders.doSomething1()
senders.doSomething2()
if __name__ == '__main__':
run()

View File

@@ -0,0 +1,22 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
from pubsub.py2and3 import print_
def doSomething1():
print_('--- SENDING topic1.subtopic11 message ---')
pub.sendMessage('topic1.subtopic11', ('message for 11', 123))
print_('---- SENT topic1.subtopic11 message ----')
def doSomething2():
print_('--- SENDING topic1 message ---')
pub.sendMessage('topic1', ('message for 1',) )
print_('---- SENT topic1 message ----')

View File

@@ -0,0 +1,107 @@
"""
Taken from wxPython wiki at http://wiki.wxpython.org/ModelViewController/.
Used to verify that the wx.lib.pubsub can be replaced by pubsub v3 if message
protocol set to "arg1" (as long as only main API functions are used such as
sendMessage and subscribe... a few peripheral ones have changed or been replaced
with more powerful features).
Notes:
- the imports assume that pubsub package has been
copied to wx.lib, otherwise change them.
- this code is probably not a good example to follow, see instead
basic_kwargs_wx_*.py.
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
import wx
from wx.lib.pubsub import setuparg1
from wx.lib.pubsub import pub
class Model:
def __init__(self):
self.myMoney = 0
def addMoney(self, value):
self.myMoney += value
#now tell anyone who cares that the value has been changed
pub.sendMessage("MONEY CHANGED", self.myMoney)
def removeMoney(self, value):
self.myMoney -= value
#now tell anyone who cares that the value has been changed
pub.sendMessage("MONEY CHANGED", self.myMoney)
class View(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "Main View")
sizer = wx.BoxSizer(wx.VERTICAL)
text = wx.StaticText(self, -1, "My Money")
ctrl = wx.TextCtrl(self, -1, "")
sizer.Add(text, 0, wx.EXPAND|wx.ALL)
sizer.Add(ctrl, 0, wx.EXPAND|wx.ALL)
self.moneyCtrl = ctrl
ctrl.SetEditable(False)
self.SetSizer(sizer)
def SetMoney(self, money):
self.moneyCtrl.SetValue(str(money))
class ChangerWidget(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "Main View")
sizer = wx.BoxSizer(wx.VERTICAL)
self.add = wx.Button(self, -1, "Add Money")
self.remove = wx.Button(self, -1, "Remove Money")
sizer.Add(self.add, 0, wx.EXPAND|wx.ALL)
sizer.Add(self.remove, 0, wx.EXPAND|wx.ALL)
self.SetSizer(sizer)
class Controller:
def __init__(self, app):
self.model = Model()
#set up the first frame which displays the current Model value
self.view1 = View(None)
self.view1.SetMoney(self.model.myMoney)
#set up the second frame which allows the user to modify the Model's value
self.view2 = ChangerWidget(self.view1)
self.view2.add.Bind(wx.EVT_BUTTON, self.AddMoney)
self.view2.remove.Bind(wx.EVT_BUTTON, self.RemoveMoney)
#subscribe to all "MONEY CHANGED" messages from the Model
#to subscribe to ALL messages (topics), omit the second argument below
pub.subscribe(self.MoneyChanged, "MONEY CHANGED")
self.view1.Show()
self.view2.Show()
def AddMoney(self, evt):
self.model.addMoney(10)
def RemoveMoney(self, evt):
self.model.removeMoney(10)
def MoneyChanged(self, message):
"""
This method is the handler for "MONEY CHANGED" messages,
which pubsub will call as messages are sent from the model.
We already know the topic is "MONEY CHANGED", but if we
didn't, message.topic would tell us.
"""
self.view1.SetMoney(message.data)
if __name__ == "__main__":
app = wx.App()
Controller(app)
app.MainLoop()

View File

@@ -0,0 +1,13 @@
These two examples demonstrate a simple use of pubsub with the *kwargs*
messaging protocol (default). There are two examples that can be run from
this folder:
**console_main.py**: basic console based, uses the console_senders.py and
console_listeners.py modules, giving example of the *kwargs*
messaging protocol.
**wx_main.py**: wxPython GUI application with two windows (win1 and win2)
that exchange data without any reference to the other. This example
looks for pubsub on your system path so default install ok.

View File

@@ -0,0 +1,38 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
from pubsub.py2and3 import print_
# ------------ create some listeners --------------
class Listener:
def onTopic11(self, msg, extra=None):
print_('Method Listener.onTopic11 received: ', repr(msg), repr(extra))
def onTopic1(self, msg, topic=pub.AUTO_TOPIC):
info = 'Method Listener.onTopic1 received "%s" message: %s'
print_(info % (topic.getName(), repr(msg)))
def __call__(self, **kwargs):
print_('Listener instance received: ', kwargs)
listenerObj = Listener()
def listenerFn(msg, extra=None):
print_('Function listenerFn received: ', repr(msg), repr(extra))
# ------------ subscribe listeners ------------------
pub.subscribe(listenerObj, pub.ALL_TOPICS) # via its __call__
pub.subscribe(listenerFn, 'topic1.subtopic11')
pub.subscribe(listenerObj.onTopic11, 'topic1.subtopic11')
pub.subscribe(listenerObj.onTopic1, 'topic1')

View File

@@ -0,0 +1,22 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
import console_listeners
import console_senders as senders
from pubsub.py2and3 import print_
def run():
print_('Using "kwargs" messaging protocol of pubsub v3')
senders.doSomething1()
senders.doSomething2()
if __name__ == '__main__':
run()

View File

@@ -0,0 +1,22 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
from pubsub import pub
from pubsub.py2and3 import print_
def doSomething1():
print_('--- SENDING topic1.subtopic11 message ---')
pub.sendMessage('topic1.subtopic11', msg='message for 11', extra=123)
print_('---- SENT topic1.subtopic11 message ----')
def doSomething2():
print_('--- SENDING topic1 message ---')
pub.sendMessage('topic1', msg='message for 1')
print_('---- SENT topic1 message ----')

View File

@@ -0,0 +1,74 @@
"""
Adapted from wxPython website at http://wiki.wxpython.org/ModelViewController/.
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
import wx
from pubsub import pub
from pubsub.py2and3 import print_
print_('pubsub API version', pub.VERSION_API)
# notification
from pubsub.utils.notification import useNotifyByWriteFile
import sys
useNotifyByWriteFile(sys.stdout)
# the following two modules don't know about each other yet will
# exchange data via pubsub:
from wx_win1 import View
from wx_win2 import ChangerWidget
class Model:
def __init__(self):
self.myMoney = 0
def addMoney(self, value):
self.myMoney += value
#now tell anyone who cares that the value has been changed
pub.sendMessage("money_changed", money=self.myMoney)
def removeMoney(self, value):
self.myMoney -= value
#now tell anyone who cares that the value has been changed
pub.sendMessage("money_changed", money=self.myMoney)
class Controller:
def __init__(self):
self.model = Model()
#set up the first frame which displays the current Model value
self.view1 = View()
self.view1.setMoney(self.model.myMoney)
#set up the second frame which allows the user to modify the Model's value
self.view2 = ChangerWidget()
self.view1.Show()
self.view2.Show()
pub.subscribe(self.changeMoney, 'money_changing')
def changeMoney(self, amount):
if amount >= 0:
self.model.addMoney(amount)
else:
self.model.removeMoney(-amount)
if __name__ == "__main__":
app = wx.App()
c = Controller()
sys.stdout = sys.__stdout__
print_('---- Starting main event loop ----')
app.MainLoop()
print_('---- Exited main event loop ----')

View File

@@ -0,0 +1,33 @@
"""
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
import wx
from pubsub import pub
class View(wx.Frame):
def __init__(self, parent=None):
wx.Frame.__init__(self, parent, -1, "Main View")
sizer = wx.BoxSizer(wx.VERTICAL)
text = wx.StaticText(self, -1, "My Money")
ctrl = wx.TextCtrl(self, -1, "")
sizer.Add(text, 0, wx.EXPAND|wx.ALL)
sizer.Add(ctrl, 0, wx.EXPAND|wx.ALL)
self.moneyCtrl = ctrl
ctrl.SetEditable(False)
self.SetSizer(sizer)
#subscribe to all "MONEY CHANGED" messages from the Model
#to subscribe to ALL messages (topics), omit the second argument below
pub.subscribe(self.setMoney, "money_changed")
def setMoney(self, money):
self.moneyCtrl.SetValue(str(money))

View File

@@ -0,0 +1,38 @@
"""
Widget from which money can be added or removed from account.
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
import wx
from pubsub import pub
from pubsub.py2and3 import print_
class ChangerWidget(wx.Frame):
CHANGE = 10 # by how much money changes every time click
def __init__(self, parent=None):
wx.Frame.__init__(self, parent, -1, "Changer View")
sizer = wx.BoxSizer(wx.VERTICAL)
self.add = wx.Button(self, -1, "Add Money")
self.remove = wx.Button(self, -1, "Remove Money")
sizer.Add(self.add, 0, wx.EXPAND|wx.ALL)
sizer.Add(self.remove, 0, wx.EXPAND|wx.ALL)
self.SetSizer(sizer)
self.add.Bind(wx.EVT_BUTTON, self.onAdd)
self.remove.Bind(wx.EVT_BUTTON, self.onRemove)
def onAdd(self, evt):
print_('-----')
pub.sendMessage("money_changing", amount = self.CHANGE)
def onRemove(self, evt):
print_('-----')
pub.sendMessage("money_changing", amount = - self.CHANGE)

View File

@@ -0,0 +1,130 @@
"""
This test gives an example of how some computation results from an
auxiliary thread could be 'published' via pubsub in a thread-safe
manner, in a 'gui'-like application, ie an application where the
main thread is in an infinite event loop and supports the callback
of user-defined functions when the gui is idle.
The worker thread 'work' is to increment a counter
as fast as interpreter can handle. Every so often (every resultStep counts),
the thread stores the count in a synchronized queue, for later retrieval
by the main thread. In parallel to this, the main thread loops forever (or
until user interrupts via keyboard), doing some hypothetical work
(represented by the sleep(1) call) and calling all registered 'idle'
callbacks. The transfer is done by extracting items from the queue and
publishing them via pubsub.
Oliver Schoenborn
May 2009
:copyright: Copyright 2008-2009 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE.txt for details.
"""
__author__="schoenb"
__date__ ="$31-May-2009 9:11:41 PM$"
from Queue import Queue
import time
import threading
from pubsub import pub
from pubsub.py2and3 import print_
resultStep = 1000000 # how many counts for thread "result" to be available
def threadObserver(transfers, threadObj, count):
"""Listener that listens for data from testTopic. This function
doesn't know where the data comes from (or in what thread it was
generated... but threadObj is the thread in which this
threadObserver is called and should indicate Main thread)."""
print_(transfers, threadObj, count / resultStep)
pub.subscribe(threadObserver, 'testTopic')
def onIdle():
"""This should be registered with 'gui' to be called when gui is idle
so we get a chance to transfer data from aux thread without blocking
the gui. Ie this function must spend as little time as possible so
'gui' remains reponsive."""
thread.transferData()
class ParaFunction(threading.Thread):
"""
Represent a function running in a parallel thread. The thread
just increments a counter and puts the counter value on a synchronized
queue every resultStep counts. The content of the queue can be published by
calling transferData().
"""
def __init__(self):
threading.Thread.__init__(self)
self.running = False # set to True when thread should stop
self.count = 0 # our workload: keep counting!
self.queue = Queue() # to transfer data to main thread
self.transfer = 0 # count how many transfers occurred
def run(self):
print_('aux thread started')
self.running = True
while self.running:
self.count += 1
if self.count % resultStep == 0:
self.queue.put(self.count)
print_('aux thread done')
def stop(self):
self.running = False
def transferData(self):
"""Send data from aux thread to main thread. The data was put in
self.queue by the aux thread, and this queue is a Queue.Queue which
is a synchronized queue for inter-thread communication.
Note: This method must be called from main thread."""
self.transfer += 1
while not self.queue.empty():
pub.sendMessage('testTopic',
transfers = self.transfer,
threadObj = threading.currentThread(),
count = self.queue.get())
thread = ParaFunction()
def main():
idleFns = [] # list of functions to call when 'gui' idle
idleFns.append( onIdle )
try:
thread.start()
print_('starting event loop')
eventLoop = True
while eventLoop:
time.sleep(1) # pretend that main thread does other stuff
for idleFn in idleFns:
idleFn()
except KeyboardInterrupt:
print_('Main interrupted, stopping aux thread')
thread.stop()
except Exception, exc:
from pubsub import py2and3
exc = py2and3.getexcobj()
print_(exc)
print_('Exception, stopping aux thread')
thread.stop()
main()

View File

@@ -0,0 +1,86 @@
echo off
rem This script runs all examples. This should be mostly for using the
rem examples as regression tests (after all tests have passed in tests
rem folder).
rem One command line argument is required, the python version number to
rem use, no dots: 24 for 2.4, 30 for 3.0, etc.
rem
rem (C) Oliver Schoenborn 2009
set PY_VER=%1
IF "%1" EQU "" (
SET PY_VER=26
echo Will use Python 2.6. To use other, put version ID as command line arg
echo Example: for Python 2.7 put 27, for 3.0 put 30, etc.
)
set PYTHON_EXE=python
echo python exe is %PYTHON_EXE%
echo.
echo.
echo ######################## basic - kwargs - console #########################
echo.
pushd basic_kwargs
%PYTHON_EXE% console_main.py
popd
pause
echo.
echo.
echo ######################## basic - kwargs - wx #########################
echo.
pushd basic_kwargs
%PYTHON_EXE% wx_main.py
popd
pause
echo.
echo.
echo ######################## basic - arg1 - console #########################
echo.
pushd basic_arg1
%PYTHON_EXE% console_main.py
popd
pause
echo.
echo.
echo ######################## basic - arg1 - wx #########################
echo.
pushd basic_arg1
%PYTHON_EXE% wx_main.py
popd
pause
echo.
echo.
echo ######################## advanced - kwargs - console #########################
echo.
pushd advanced
%PYTHON_EXE% main_kwargs.py
popd
pause
echo.
echo.
echo ######################## advanced - arg1 - console #########################
echo.
pushd advanced
%PYTHON_EXE% main_arg1.py
popd
pause

View File

@@ -0,0 +1,152 @@
Will use Python 2.6. To use other, put version ID as command line arg
Example: for Python 2.7 put 27, for 3.0 put 30, etc.
python exe is python
######################## basic - kwargs - console #########################
Using "kwargs" messaging protocol of pubsub v3
--- SENDING topic1.subtopic11 message ---
Method Listener.onTopic11 received: 'message for 11' 123
Function listenerFn received: 'message for 11' 123
Method Listener.onTopic1 received "topic1.subtopic11" message: 'message for 11'
Listener instance received: {'msg': 'message for 11', 'extra': 123}
---- SENT topic1.subtopic11 message ----
--- SENDING topic1 message ---
Method Listener.onTopic1 received "topic1" message: 'message for 1'
Listener instance received: {'msg': 'message for 1'}
---- SENT topic1 message ----
Press any key to continue . . .
######################## basic - kwargs - wx #########################
pubsub version 3.1.2.201112.r243
PUBSUB: New topic "money_changed" created
PUBSUB: Subscribed listener "View.setMoney" to topic "money_changed"
PUBSUB: New topic "money_changing" created
PUBSUB: Subscribed listener "Controller.changeMoney" to topic "money_changing"
---- Starting main event loop ----
-----
PUBSUB: Start sending message of topic "money_changing"
PUBSUB: Sending message of topic "money_changing" to listener Controller.changeMoney
PUBSUB: Start sending message of topic "money_changed"
PUBSUB: Sending message of topic "money_changed" to listener View.setMoney
PUBSUB: Done sending message of topic "money_changed"
PUBSUB: Done sending message of topic "money_changing"
-----
PUBSUB: Start sending message of topic "money_changing"
PUBSUB: Sending message of topic "money_changing" to listener Controller.changeMoney
PUBSUB: Start sending message of topic "money_changed"
PUBSUB: Sending message of topic "money_changed" to listener View.setMoney
PUBSUB: Done sending message of topic "money_changed"
PUBSUB: Done sending message of topic "money_changing"
---- Exited main event loop ----
Press any key to continue . . .
######################## basic - arg1 - console #########################
Using "arg1" messaging protocol of pubsub v3
--- SENDING topic1.subtopic11 message ---
Function listenerFn received: 'message for 11' 123
Method Listener.onTopic11 received: 'message for 11' 123
Method Listener.onTopic1 received "topic1.subtopic11" message: 'message for 11'
Listener instance received: <pubsub.core.listenerimpl.Message instance at 0x018B7AD0>
---- SENT topic1.subtopic11 message ----
--- SENDING topic1 message ---
Method Listener.onTopic1 received "topic1" message: 'message for 1'
Listener instance received: <pubsub.core.listenerimpl.Message instance at 0x018B7AD0>
---- SENT topic1 message ----
Press any key to continue . . .
######################## basic - arg1 - wx #########################
Press any key to continue . . .
######################## advanced - kwargs - console #########################
Using "kwargs" messaging protocol of pubsub v3
------- init ----------
NotifyByWriteFile: New topic "topic_2" created
NotifyByWriteFile: New topic "topic_2.subtopic_21" created
NotifyByWriteFile: New topic "topic_1" created
NotifyByWriteFile: New topic "topic_1.subtopic_11" created
MyPubsubNotifHandler: listener Listener_8304 subscribed to ALL_TOPICS
NotifyByWriteFile: Subscribed listener "Listener" to topic "ALL_TOPICS"
MyPubsubNotifHandler: listener listenerFn_2464 subscribed to topic_1.subtopic_11
NotifyByWriteFile: Subscribed listener "listenerFn" to topic "topic_1.subtopic_11"
MyPubsubNotifHandler: listener Listener.onTopic11_8736 subscribed to topic_1.subtopic_11
NotifyByWriteFile: Subscribed listener "Listener.onTopic11" to topic "topic_1.subtopic_11"
MyPubsubNotifHandler: listener Listener.onTopic1_8736 subscribed to topic_1
NotifyByWriteFile: Subscribed listener "Listener.onTopic1" to topic "topic_1"
-----------------------
NotifyByWriteFile: Start sending message of topic "topic_1.subtopic_11"
NotifyByWriteFile: Sending message of topic "topic_1.subtopic_11" to listener Listener.onTopic11
Method Listener.onTopic11 received: 'message for subtopic 11' 'other message' 123
NotifyByWriteFile: Sending message of topic "topic_1.subtopic_11" to listener listenerFn
Function listenerFn received: 'message for subtopic 11' 'other message' 123
NotifyByWriteFile: Sending message of topic "topic_1" to listener Listener.onTopic1
Method Listener.onTopic1 received "topic_1.subtopic_11" message: 'message for subtopic 11'
NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener
Listener instance received: {'msg': 'message for subtopic 11', 'extra': 123, 'msg2': 'other message'}
NotifyByWriteFile: Done sending message of topic "topic_1.subtopic_11"
NotifyByWriteFile: Start sending message of topic "topic_1"
NotifyByWriteFile: Sending message of topic "topic_1" to listener Listener.onTopic1
Method Listener.onTopic1 received "topic_1" message: 'message for topic 1'
NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener
Listener instance received: {'msg': 'message for topic 1'}
NotifyByWriteFile: Done sending message of topic "topic_1"
NotifyByWriteFile: Start sending message of topic "topic_2.subtopic_21"
NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener
Listener instance received: {'msg': 'message for subtopic 2'}
NotifyByWriteFile: Done sending message of topic "topic_2.subtopic_21"
------- done ----------
Exporting topic tree to kwargs_topics
------ exiting --------
Press any key to continue . . .
######################## advanced - arg1 - console #########################
Using "arg1" messaging protocol of pubsub v3
------- init ----------
NotifyByWriteFile: New topic "topic_2" created
NotifyByWriteFile: New topic "topic_2.subtopic_21" created
NotifyByWriteFile: New topic "topic_1" created
NotifyByWriteFile: New topic "topic_1.subtopic_11" created
MyPubsubNotifHandler: listener Listener_2608 subscribed to ALL_TOPICS
NotifyByWriteFile: Subscribed listener "Listener" to topic "ALL_TOPICS"
MyPubsubNotifHandler: listener listenerFn_1024 subscribed to topic_1.subtopic_11
NotifyByWriteFile: Subscribed listener "listenerFn" to topic "topic_1.subtopic_11"
MyPubsubNotifHandler: listener Listener.onTopic11_8808 subscribed to topic_1.subtopic_11
NotifyByWriteFile: Subscribed listener "Listener.onTopic11" to topic "topic_1.subtopic_11"
MyPubsubNotifHandler: listener Listener.onTopic1_8808 subscribed to topic_1
NotifyByWriteFile: Subscribed listener "Listener.onTopic1" to topic "topic_1"
-----------------------
NotifyByWriteFile: Start sending message of topic "topic_1.subtopic_11"
NotifyByWriteFile: Sending message of topic "topic_1.subtopic_11" to listener Listener.onTopic11
Method Listener.onTopic11 received: ('message for subtopic 11', 'other message', 123)
NotifyByWriteFile: Sending message of topic "topic_1.subtopic_11" to listener listenerFn
Function listenerFn received: ('message for subtopic 11', 'other message', 123)
NotifyByWriteFile: Sending message of topic "topic_1" to listener Listener.onTopic1
Method Listener.onTopic1 received "topic_1.subtopic_11" message: ('message for subtopic 11', 'other message', 123)
NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener
Listener instance received: ('message for subtopic 11', 'other message', 123)
NotifyByWriteFile: Done sending message of topic "topic_1.subtopic_11"
NotifyByWriteFile: Start sending message of topic "topic_1"
NotifyByWriteFile: Sending message of topic "topic_1" to listener Listener.onTopic1
Method Listener.onTopic1 received "topic_1" message: 'message for topic 1'
NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener
Listener instance received: message for topic 1
NotifyByWriteFile: Done sending message of topic "topic_1"
NotifyByWriteFile: Start sending message of topic "topic_2.subtopic_21"
NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener
Listener instance received: message for subtopic 2
NotifyByWriteFile: Done sending message of topic "topic_2.subtopic_21"
------- done ----------
Exporting topic tree to arg1_topics
------ exiting --------
Press any key to continue . . .

View File

@@ -1,61 +0,0 @@
'''
This is used internally by pubsub to allow modules of the pubsub.utils
subpackage to import modules of a sibbling subpackage (such as
pubsub.core).
Value of "up" parameter: For intraImport() the defaut up is 1 because
it is always used from within an __init__ file ie most likely scenario
is up=1. The parentImport() function mostly used from within modules
of a package so up defaults to 2.
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
:license: BSD, see LICENSE_BSD_Simple.txt for details.
'''
# allow utils modules to find sibblings in core
def intraImport(pathList, up=1):
'''Enable modules of a subpackage to import modules of a sibbling
subpackage, or parent modules, ie A.B.C could import A.D.E by doing
'import D.E' (this is default behavior). If up > 1, can import from
higher up the import tree. For instance with up=2, ie A.B.C.D could
import A.E.F.G by doing 'import E.F.G'.
WARNING: it looks like Python
loads a separate copy of sibbling modules found via the modified
module search path; this is a problem if the sibbling modulres
contain globals (singletons) since more than one instance of a module
could exist in an application. Use parentImport() in such case.'''
import os
newPath = pathList[0]
for _ in range(0, up):
newPath = os.path.join(newPath, '..')
# py2exe requires abspath to find new path
pathList.append( os.path.abspath( newPath ) )
_importCache = {}
def parentImport(modName, up=2):
'''Import a module modName from parent of current package. Default
assumes used within a non-init module of pubsub.utils, ie up=2.'''
# see if in cache
modObj = _importCache.get( (modName, up), None )
if modObj is not None:
return modObj
# not in cache, assume parent already imported
module = globals()['__name__'].split('.')
parentModName = '.'.join( module[:-up] )
import sys
parentModObj = sys.modules[parentModName]
try:
modObj = getattr(parentModObj, modName)
_importCache[ (modName, up) ] = modObj
return modObj
except AttributeError:
msg = 'no module named %s in %s' % (modName, parentModName)
raise ImportError(msg)

View File

@@ -152,11 +152,10 @@ class TopicTreePrinter(ITopicTreeVisitor):
def __printTopicListeners(self, indent, topicObj):
if topicObj.hasListeners():
listeners = topicObj.getListeners()
item = '%s Listeners:' % self.__topicItemsBullet
self.__output.append( self.__formatDefn(indent, item) )
tmpIndent = indent + self.__indentStep
for listener in listeners:
for listener in topicObj.getListenersIter():
item = '%s %s (from %s)' % (self.__topicArgsBullet, listener.name(), listener.module())
self.__output.append( self.__formatDefn(tmpIndent, item) )