вторник, 17 июля 2012 г.

wxPython and PubSub: A Simple Tutorial (Перевод)


Я видел некоторое количество вопросов в списке рассылки wxPython и на IRC каналах про коммуникацию между фреймами. И практически всегда разработчикам требовался PubSub модуль. Модель Publisher / Subscriber - это способ послать сообщения одному или более получателю. Об этом Вы можете почитать здесь. Шаблон Observer (Наблюдатель) как раз и основывается на этой модели. В мире wxPython у нас есть модуль pubsub, который доступен посредством wx.lib.pubsub. Он включён в wxPython, но его можно загрузить и как отдельный модуль с Forge. В качестве альтернативы можно использовать и модуль PyDispatcher.
В любом случае, в этой статье мы не будем заниматься теорией. Вместо этого мы рассмотрим полу-практический пример в wxPython для того, чтобы показать, как можно использовать pubsub для обмена информацией между двумя фреймами. Если Вы всё ещё со мной - то давайте начнём!

Как передавать информацию между двумя фреймами

Я заметил, что иногда мне нужно открыть немодальный фрейм для получения информации от пользователя, а затем передать её обратно в главный фрейм моего приложения. Иногда же мне надо лишь сказать одному из моих фреймов, что другой фрейм закрылся. В этих двух случаях pubsub как раз является идеальным решением. Следующий пример как раз демонстрирует решение этих двух проблем:
import wx
from wx.lib.pubsub import Publisher
 
########################################################################
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="")
        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(closeBtn, 0, flags, 5)
        panel.SetSizer(sizer)
 
    #----------------------------------------------------------------------
    def onSendAndClose(self, event):
        """
        Посылаем сообщение и закрываем фрейм
        """
        msg = self.msgTxt.GetValue()
        Publisher().sendMessage(("show.mainframe"), msg)
        self.Close()
 
 
########################################################################
class MainPanel(wx.Panel):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent)
        self.frame = parent
 
        Publisher().subscribe(self.showFrame, ("show.mainframe"))
 
        self.pubsubText = wx.TextCtrl(self, value="")
        hideBtn = wx.Button(self, label="Hide")
        hideBtn.Bind(wx.EVT_BUTTON, self.hideFrame)
 
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.pubsubText, 0, wx.ALL|wx.CENTER, 5)
        sizer.Add(hideBtn, 0, wx.ALL|wx.CENTER, 5)
        self.SetSizer(sizer)
 
    #----------------------------------------------------------------------
    def hideFrame(self, event):
        """"""
        self.frame.Hide()
        new_frame = OtherFrame()
        new_frame.Show()
 
    #----------------------------------------------------------------------
    def showFrame(self, msg):
        """
        Показывает фрейм и сообщение, посланное ему
        """
        self.pubsubText.SetValue(msg.data)
        frame = self.GetParent()
        frame.Show()
 
########################################################################
class MainFrame(wx.Frame):
 
    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Pubsub Tutorial")
        panel = MainPanel(self)
 
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = MainFrame()
    frame.Show()
    app.MainLoop()
Нашей первой остановкой в экскурсии по этому коду будет класс MainPanel. Посмотрите на это:
Publisher().subscribe(self.showFrame, ("show.mainframe"))
Этот код создаёт Одиночку (singleton) (он же получатель), который подписывается на топик “show.mainframe”. Другие части могут публиковать сообщения в этот топик и получатель будет получать эти сообщения и передавать их методу “showFrame”. Для того, чтобы посмотреть на это в действии, давайте взглянем на метод “onSendAndClose” класса “OtherFrame”.
# OtherFrame класс
def onSendAndClose(self, event):
    """
    Посылает сообщение и закрывает фрейм
    """
    msg = self.msgTxt.GetValue()
    Publisher().sendMessage(("show.mainframe"), msg)
    self.Close()
Тут мы получаем значение из поля ввода и посылаем его обратно, в главный фрейм. Для этого мы вызываем метод sendMessage объекта Publisher и передаём ему строку с именем топика и само сообщение. Сообщение может быть списком объектов или одним объектом. В нашем случае - это просто строка. В ответ на её получение вызывается метод showFrame класса MainPanel:
# MainPanel класс
def showFrame(self, msg):
    """
    Показывает фрейм и полученное сообщение
    """
    self.pubsubText.SetValue(msg.data)
    frame = self.GetParent()
    frame.Show()
В этом методе мы извлекаем данные, посланные через pubsub при помощи свойства data. Если бы мы послали несколько объектов, то для получения нужного объекта надо было бы обратиться к нему по индексу - что-то вроде msg.data[0]. Новая версия pubsub немного отличается по API от того, что написано в их книге рецептов. Этот новый API доступен для wxPython 2.8.11.0. Обратите внимание: у меня были некоторые проблемы с созданием бинарника для нового pubsub, так как я использовал старый API. Смотрите здесь.
Теперь Вы знаете, как можно использовать pubsub для ваших проектов. Этот пример показывает, как обмениваться информацией между двумя фреймами. Have fun!

Домашнее чтение

Комментариев нет:

Отправить комментарий