wxpython — события привязки во внешних файлах

#python #wxpython

#python #wxpython

Вопрос:

Я пытаюсь привязать события из файла GUI для использования кода из другого файла (фактически «внешнего интерфейса» и «серверной части»). Я могу заставить серверную часть и интерфейс работать в одном файле, но когда я пытаюсь переместить их в отдельные файлы, у меня возникают проблемы с тем, чтобы серверная часть видела части (метки, кнопки и т.д.) интерфейса.

Т.е. мне нужен внутренний код для изменения меток, выполнения математических операций и тому подобного, и это должно повлиять на графический интерфейс.

Я предоставил простую версию своей программы. Все работает, за исключением ошибки, которую я получаю, когда пытаюсь заставить серверную часть видеть части графического интерфейса.

mainfile.py

 #!/usr/bin/python
# -*- coding: utf-8 -*-

import wx

import label_changer

class foopanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, id=wx.ID_ANY)

        box = wx.BoxSizer()
        btn = wx.Button(self,1,"Press")
        btn.Bind(wx.EVT_BUTTON,label_changer.change_label(self))
        box.Add(btn)

        self.lbl = wx.StaticText(self,1,"Foobar")
        box.Add(self.lbl)

        self.SetSizerAndFit(box)

class main_frame(wx.Frame):
    """Main Frame holding the main panel."""
    def __init__(self,*args,**kwargs):
        wx.Frame.__init__(self,*args,**kwargs)

        sizer = wx.BoxSizer()

        self.p = foopanel(self)

        sizer.Add(self.p,1)

        self.Show()

if __name__ == "__main__":
    app = wx.App(False)
    frame = main_frame(None,-1,)
    app.MainLoop()
  

label_changer.py

 def change_label(self):
    self.p.lbl.SetLabel("barfoo")
  

Все, что я хочу, это изменить метку графического интерфейса, но использовать внешний файл.

Я делаю это в основном для того, чтобы отделить свой код и просто для обучения.

Заранее спасибо!

Ответ №1:

Одним из решений является изменение change_label , чтобы принять аргумент, который идентифицирует метку для изменения. Например:

 def change_label(event, label):
    label.SetLabel("barfoo")
  

Затем используйте lambda для создания обратного вызова, который передает этот аргумент в:

 btn.Bind(wx.EVT_BUTTON, label_changer, 
    lambda event, label=self.p.lbl: label_changer.change_label(event, label))
  

Убедитесь, что вы определили self.lbl , прежде чем выполнять привязку.

Подробнее о передаче аргументов обратным вызовам см. в разделе Передача аргументов обратным вызовам в WxPyWiki

Ответ №2:

Распространенным способом сделать это является шаблон MVC и pubsub. Смотрите этот пример.

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

1. Я думаю, что это будет способ сделать это… Я сейчас читаю об этом… Спасибо за ответ!

Ответ №3:

Это

 btn.Bind(wx.EVT_BUTTON,label_changer.change_label(self))
  

должно быть

 btn.Bind(wx.EVT_BUTTON,label_changer.change_label)
  

и это

 def change_label(self):
    self.p.lbl.SetLabel("barfoo")
  

должно быть

 def change_label(event):
    panel = event.GetEventObject().GetParent()
    panel.lbl.SetLabel("barfoo")
  

Для пояснения вам нужно передать ссылку на функцию Bind , которая должна вызываться при возникновении события. wx всегда будет передавать этим функциям один аргумент — событие. self То, что вы обычно видите в обратных вызовах, является побочным продуктом того, что они являются связанными методами. Каждому привязанному методу (упрощая, функции, определенной в классе) при вызове неявно передается первый аргумент, который является ссылкой на экземпляр класса. Итак, поскольку вы не можете получить доступ к этому экземпляру традиционным способом с помощью «внешней» функции, вы должны получить доступ к нему через объект event.

Еще одна вещь, вы на самом деле не отделяете графический интерфейс от логики таким образом. Это потому, что логика (label_changer в данном случае) должна знать о графическом интерфейсе и напрямую управлять им. Есть способы добиться гораздо более строгого разделения (st2053 намекнул на один из них), но для относительно небольшой программы вам не нужно беспокоиться, если вы не хотите делать это прямо сейчас, достаточно просто разделить код на несколько файлов и сосредоточиться на выполнении задачи. Вы можете беспокоиться об архитектуре позже.

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

1. Хотя это будет работать, это не особенно хорошее решение, IMO. Для этого требуется, чтобы change_label было что-то известно о другом модуле — в частности, о том, что метка называется «lbl» и находится в родительском объекте объекта event. При изменении основного файла может потребоваться изменение label_changer.py файл тоже, что увеличивает ваши расходы на обслуживание.