我正在尝试让tkinter的event_generate
与update
一起使用进行原子单元测试。
以下代码无法按预期工作。未打印"已生成退格事件"。我的理解是,event_generate
将事件放在tkinter的事件队列中,然后update
应该清除并执行队列中的所有事件。
class UpdWin(tk.Tk):
def __init__(self):
super().__init__()
self.bind('<BackSpace>',
lambda event: print(event.keysym, 'event generated.'))
app = UpdWin()
app.event_generate('<BackSpace>')
app.update() # Update doesn't work if placed here
但是,以下代码确实打印"生成的退格事件"。事件队列中的事件将在 __init__
期间清除并执行。在此之后,主代码将事件放入 tkinter 的队列中。
class UpdWin(tk.Tk):
def __init__(self):
super().__init__()
self.bind('<BackSpace>',
lambda event: print(event.keysym, 'event generated.'))
self.update() # Update works if placed here
app = UpdWin()
app.event_generate('<BackSpace>')
我的第一个想法是,我对update
命令的理解一定是错误的。Lundh、Shipman 和 TclCmd 手册页都有不同的update
条目。
处理所有挂起的事件,调用事件回调,完成任何挂起的几何管理,根据需要重绘小部件,并调用所有挂起的空闲任务。伦德 (1999(
此方法强制更新显示。希普曼(新墨西哥理工学院(
此命令用于通过重复进入事件循环来使应用程序"最新",直到处理完所有挂起的事件(包括空闲回调(。TclCmd 手册页
由此,我怀疑存在未记录的计时问题,并尝试了更新命令的第三个位置。以下代码也有效。
class UpdWin(tk.Tk):
def __init__(self):
super().__init__()
self.bind('<BackSpace>',
lambda event: print(event.keysym, 'event generated.'))
app = UpdWin()
app.update() # Update works if placed here
app.event_generate('<BackSpace>')
为什么 tkinter 的update
在event_generate
之前调用时不起作用,但在之后没有?
编辑
如果app.update
位于位置 1.
,则以下代码将打印"生成的退格事件"。它将打印"FocusIn 事件生成",如果app.update
位于位置 2.
注意:布莱恩·奥克利(Bryan Oakley(的回应表明,这种效应可能取决于机器。
import tkinter as tk
app = tk.Tk()
# app.update() # Position 1
app.bind('<FocusIn>', lambda event: print('FocusIn event generated.'))
app.bind('<BackSpace>', lambda event: print(event.keysym, 'event generated.'))
app.event_generate('<BackSpace>')
app.update() # Position 2
以下使用 when='tail'
的代码从不打印"生成的退格事件",无论app.update
位于位置 1 还是位置 2。
import tkinter as tk
app = tk.Tk()
app.update() # Position 1
app.bind('<FocusIn>', lambda event: print('<FocusIn> event generated.'))
app.bind('<BackSpace>', lambda event: print(event.keysym, 'event generated.'))
app.event_generate('<BackSpace>', when="tail")
# app.update() # Position 2
我无法重复您的观察,所以我无法确定发生了什么。显然缺少一些代码,这也可能会受到您运行的平台以及您启动程序的方式的影响。
但是,我认为问题归结为三个因素:
- 默认情况下,
event_generate
立即处理事件的任何处理程序(即:update
和mainloop
本身不需要( - 如果在调用
event_generate
之前不调用update()
,则不会绘制窗口,并且当未绘制窗口时,操作系统或窗口管理器不会为其提供键盘焦点。 - 如果应用没有键盘焦点,Tkinter 可能会忽略键盘事件。
这可能并不完全是这样 - 因为您告诉它哪个窗口将接收事件,因此重点可能不是一个考虑因素。但是,窗口的可见性可能仍然起作用。可能是tkinter逻辑说要忽略不可见窗口上的事件。也有可能您的窗口管理器/操作系统可能没有在前面运行该程序 - 键盘焦点可能位于其他应用程序上,直到您手动单击窗口(例如,这似乎是OSX上的行为(。
您可以尝试使用 when
参数来event_generate
。这允许您将处理程序的处理推迟到处理完任何其他排队事件之后。这样做,然后调用update()
,也许你的代码会起作用。例如:
app.event_generate('<Backspace>`, when="tail")
app.update()
在尝试生成事件之前,还可以尝试通过调用 app.focus_force()
将应用强制置于前台。
键盘事件时,您可能还希望先生成<FocusIn>
事件。当您开始生成事件时,您需要确保您生成的事件的行为尽可能类似于用户生成的事件,这意味着您需要了解可见性、键盘焦点和鼠标焦点。许多年前,当我走这条路时,我记得在处理按钮时必须生成<Enter>
和<Leave>
事件,例如,因为内置绑定有时依赖于这些将按钮的状态设置为"活动"。
event_generate
命令的权威文档是 Tcl/Tk 手册页。