wxpython - 在外部文件中绑定事件

3 投票
3 回答
1146 浏览
提问于 2025-04-16 17:29

我正在尝试将一个图形界面(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")

我只想让它改变图形界面的标签,但使用一个外部文件。

这样做主要是为了保持代码的独立性,同时也是为了学习。

提前谢谢大家!

3 个回答

0

这个

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,是因为它们是绑定的方法。每个绑定的方法(简单来说,就是在类中定义的函数)在调用时会隐式地传递第一个参数,这个参数是对类实例的引用。所以,由于你不能像在“外部”函数中那样传统地访问这个实例,你必须通过事件对象来获取它。

还有一点,你这样做并没有真正将图形界面(gui)和逻辑分开。这是因为逻辑(在这个例子中是label_changer)需要了解图形界面,并直接对其进行操作。有一些方法可以实现更强的分离(st2053提到过其中一种),但对于一个相对较小的程序,如果你现在不想麻烦的话,简单地把代码分成多个文件,专注于完成任务就可以了。架构方面的事情可以稍后再考虑。

2

一种解决方案是修改 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 上的内容。

1

一种常见的方法是使用 MVC模式 和发布-订阅(pubsub)模式。可以参考这个 示例

撰写回答