wxPython中EVT_KILL_FOCUS事件不触发 事件绑定逻辑异常问题咨询
问题描述
我写了一段wxPython的演示程序,用来复现事件触发异常的问题,代码已经尽量精简,毕竟这类GUI应用的最小复现很难再压缩长度:程序里放了两个文本输入框,给每个输入框都绑定了两个事件处理器:
- 捕获
EVT_TEXT事件的on_text_changed() - 捕获
EVT_KILL_FOCUS事件的on_focus_lost()
实际运行时的现象:
- 在任意输入框键入内容时,
on_text_changed()能正常被调用,输出的提示和预期一致 - 但按照逻辑,不管是用Tab在两个输入框之间切换焦点,还是用鼠标点击选中另一个输入框,当前失去焦点的输入框都应该触发
on_focus_lost(),可实际运行时这个处理器一次都没有被调用过。
我现在不确定是自己对EVT_KILL_FOCUS的适用场景理解有偏差,还是事件绑定的写法本身有问题,附上完整复现代码,想找一下问题的根本原因。
复现代码
import wx class DemoFrame(wx.Frame): def __init__(self, *args, **kwds): kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) self.SetSize((400, 300)) self.SetTitle("Demo") self.panel_1 = wx.Panel(self, wx.ID_ANY) sizer_1 = wx.BoxSizer(wx.HORIZONTAL) self.text_ctrl_1 = wx.TextCtrl(self.panel_1, wx.ID_ANY, "") sizer_1.Add(self.text_ctrl_1, 0, 0, 0) self.text_ctrl_2 = wx.TextCtrl(self.panel_1, wx.ID_ANY, "") sizer_1.Add(self.text_ctrl_2, 0, 0, 0) self.panel_1.SetSizer(sizer_1) self.Layout() self.Bind(wx.EVT_KILL_FOCUS, self.on_focus_lost, self.text_ctrl_1) self.Bind(wx.EVT_TEXT, self.on_text_changed, self.text_ctrl_1) self.Bind(wx.EVT_KILL_FOCUS, self.on_focus_lost, self.text_ctrl_2) self.Bind(wx.EVT_TEXT, self.on_text_changed, self.text_ctrl_2) def on_focus_lost(self, event): print("触发EVT_KILL_FOCUS事件") event.Skip() def on_text_changed(self, event): print("触发EVT_TEXT事件,输入内容为:", event.String) event.Skip() class DemoApp(wx.App): def OnInit(self): self.frame = DemoFrame(None, wx.ID_ANY, "") self.SetTopWindow(self.frame) self.frame.Show() return True if __name__ == "__main__": demo = DemoApp(0) demo.MainLoop()
问题根因与解决方案
这个问题的核心是wxPython不同类型事件的路由机制存在本质差异:
EVT_TEXT属于wxCommandEvent(命令事件),这类事件支持在父窗口上通过Bind(事件, 处理器, 事件源)的写法绑定,系统会根据事件源自动匹配对应处理器。你原来的写法对这类事件是完全生效的,所以键入内容时能正常触发回调。EVT_KILL_FOCUS属于普通wxEvent(非命令事件),这类事件会直接发送给目标控件本身,不会走命令事件的源路由逻辑。你在父窗口上用Bind搭配事件源参数的写法,根本捕获不到这类事件——除非事件在目标控件被处理后调用Skip()主动向上冒泡,否则永远不会走到父窗口的事件绑定表。
修改方法
对于焦点这类非命令事件,必须直接在控件实例上调用Bind方法绑定处理器,不能依托上层窗口做带源参数的绑定。
将原来的焦点事件绑定代码:
self.Bind(wx.EVT_KILL_FOCUS, self.on_focus_lost, self.text_ctrl_1) self.Bind(wx.EVT_KILL_FOCUS, self.on_focus_lost, self.text_ctrl_2)
替换为以下写法:
self.text_ctrl_1.Bind(wx.EVT_KILL_FOCUS, self.on_focus_lost) self.text_ctrl_2.Bind(wx.EVT_KILL_FOCUS, self.on_focus_lost)
修改完成后,不管是按Tab切换焦点还是用鼠标点击切换,失去焦点的输入框都会正常触发on_focus_lost()处理器。
内容的提问来源于stack exchange,提问作者BoarGules
相关产品推荐
相关产品推荐

