Я видел некоторое количество вопросов в списке рассылки 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!
Комментариев нет:
Отправить комментарий