现在的位置: 首页 > 综合 > 正文

wxPython学习]使用PubSub机制来更新视图

2012年08月19日 ⁄ 综合 ⁄ 共 2919字 ⁄ 字号 评论关闭

                                                    wxPython学习]使用PubSub机制来更新视图

最近忙于作一个生成测试用的XML报文的工具。在这个工具中我有几个面板,它们是同时存在的。其中一个面板是用来管理数据字典的,它可以进行编辑,在其它的面板上有这些数据字典的一个显示,不能进行编辑,它们反映的是同一个数据源。同时,这个工具还可以根据交易报文导入交易表中,同时会把每个交易字段加入到数据字典中(如果这个字段在数据字典中不存在的话),这样,数据字典就还有可能发生变化。面板之间可以相互切换。那么我面临的一个问题就是如何处理当数据字典变化时,不同的窗口(或称为视图)进行更新。

下面给出两个可能的情景。面板A用于数据字典的维护。面板B一方面可以显示数据字典,同时还可以导入交易信息,从而也会修改数据字典。面板C也可以显示数据字典。

情景一是当面板A维护了数据字典后,它要通知面板B和面板C中的相应的UI组件执行刷新过程。

 

情景二是当面板B导入交易信息后,修改了数据字典,它一方面要通知自身的显示数据字典的组件,另外还要通知面板A和面板C的相应的UI组件来更新。

 

以前我可以想到的方法就是:

1. 直接调用法

当面板A的数据字典发生变化,我主动调用面板B和面板C的相应组件的刷新处理方法。当面板B改动了数据字典后,由面板B来调用面板A和面板C的相应组件的刷新方法。但这种方法需要知道有哪些组件要刷新,并且可以得到需要执行刷新动作的对象方法。当一个应用UI元素很多,得到目标对象并不容易。而且代码看上去也很难看。

2. 通过发送事件

可以考虑使用事件机制,利用GUI的事件循环,直接传递到指定窗口,这也已经与第一种差不多了。

正好我看到了在 wxPython 中谈到了 pubsub 功能,可以非常好的解决我的问题。

3. 发布/订阅机制(pubsub)

pubsub 就是发布和订阅机制,可能大家有所耳闻。首先我有一个公共的发布渠着,谁都可以通过它发布某个主题的信息。这样我可以制定一些主题,用来描述某些状态的变化。可以由产生变化的一方发起。然后有一些会对这些变化进行处理的订阅者,它们可以收到自已感兴趣的主题,当有信息出现时,它们就可以进行各自的处理。这里只是给大家讲一下大概的原理。不同的环境实现起来差别挺大。比如说可以用在一个网络环境,而这里我感兴趣的是一个应用程序之内如何做到。

wxPython 提供了这种机制,源码在 wx.lib.pubsub.py 中,有使用说明,有完整的测试用例和代码。那么它的思想其实说白了也还是直接的调用,但它封装得很好,让你感受不到。那么简单来讲,它实现了以下东西:

主题发布

通过调用 Publisher()可以得到一个Singleton的对象(只有一个实例的对象)。然后调用这个对象的SendMessage(topic, message=None)即可发布主题。还可以顺便带一个消息。可以没有。

信息订阅

通过调用 Publisher() 得到发布者对象,然后调用这个对象的subscribe(callable, topic)方法来订阅某个主题。topic可以是分级结构,如果是这种情况,要使用tuple来存放主题,如('subject1', 'subject2')。一个订阅者可以同时订阅多个主题。

这样,当某个事件发生了,由想要发布这种变化的处理首先调用SendMessage()发布相关的主题。然后订阅者会收到这样的事件被调用,从而进行相应的处理。

这样的好处是发布者不用关心哪些视图要进行更新,它只是进行通知而已。而订阅者也不用关心是谁发布的,只要有调用就进行处理。

仔细看pubsub的实现,其实就是通过 Python 的特性,把pubsub模块导入后,这个模块的信息就会成为全局信息。通过Publisher()可以得到对应的唯一全局对象。然后,每个订阅处理会把一个可供调用的函数,类方法或可以调用的实例放到这个全局对象中。订阅者将根据主题被组织成树状结构。(而且结点好象是使用的弱引用,从而节省一些资源,没太懂。)当执行SendMessage()时,会根据发布的主题,在这个对象的主题树中查找,找到匹配的主题,由取出它的订阅方法,然后一个个的执行。这么看来,这种pubsub并不是一种异步的调用,只是直接调用。只不过给你的感觉有些象。其实就是在SendMessage()中进行调用。因此,这种方法要求订阅时要提供一个可供执行的对象方法、函数或可执行的对象。并且至少要有一个参数用来接收消息。因此它对订阅者函数的参数个数是有要求的。

为什么上面说主题是树呢?因为主题是可以分层次的,如:('subject',) ('subject', 'sport') 这定义了两个主题。如果一个订阅者两个都订阅了,那么当发送主题为('subject', 'sport')是,它会被调用两次。因为('subject', 'sport')还匹配('subject',)。

下面给出一个最简单的代码片:

import wx.lib.pubsub as pubsub  #导入模块
... 略
class A:
    def __init__():
        publisher = pubsub.Publisher()
        publisher.subscribe(self.OnSubscribe, 'metadata_update')
    def OnSubscribe(self, message):
        print 'A message'
        ...

class B:
    def __init__():
        publisher = pubsub.Publisher()
        publisher.subscribe(self.OnSubscribe, 'metadata_update')
    def OnSubscribe(self, message):
        print 'B Message'
        ...

上面A和B分别定阅了同一个主题。

publisher = pubsub.Publisher()
publisher.sendMessage('metadata_update')

这样就发布了一个主题。这样,A和B对象的方法都会被调用。如果有很多对象,会依次进行调用。但这些处理全是自动的,你感觉不到。

pubsub可以非常方便地简单化程序之间的关系。

更详细的内容建议还是看一看源码后的示例,有些比较复杂的调用。

如果你觉得不好,可以利用全局对象自已实现一个这样的pubsub功能。不过,最后提醒一点,还有别的pubsub处理方式,特别是在网络通讯中,完全可能是异步,而不象这个根本就是直接调用(因为是在一个程序内实现的)。其实利用事件循环机制也可以实现,如发布者维护订阅者的句柄。当发布主题时,找到所有的订阅者句柄,然后每一个发一个消息。只要想通了道理,实现方法其实是挺多的。只不过我介绍的这个相对简单,容易实现。不过它不是 Python 自带的模块,而是wxPython中提供的,要注意。有兴趣可以改造下或写个简单的用在其它的项目中。

抱歉!评论已关闭.