如果在程序的其他地方调用 after_idle,则单击时 tkinter optionmenu 会挂起
tkinter optionmenu hangs when clicked if after_idle is called elsewhere in the program
我有一个 GUI,当消息到达并被多个 websocket 排队时,它会连续处理消息。此函数使用 after_idle 设置对自身的回调以延续该过程,如下所示:
def process_queue(self, flush=False):
'''
Check for new messages in the queue periodically.
Sets up callback to itself to perpetuate the process.
'''
if flush:
while not self.queue.empty():
self.get_msg()
else:
self.get_msg()
self.master.after_idle(self.process_queue)
我在 GUI 上有一个 OptionMenu
小部件,它在单击时挂起并导致程序崩溃:
self.plotind = tk.StringVar()
self.plotind.set('MACD')
options = ['MACD']
self.indopts = tk.OptionMenu(self.analysis_frame, self.plotind, *[option for option in options])
self.indopts.grid(row=1, column=1)
如果我将 after_idle()
更改为 after()
,它可以正常工作。
我认为这是因为单击 OptionMenu
实际上会设置它自己的 after_idle()
调用来打开菜单,然后它与我在 process_queue()
中的菜单竞争。
如果需要,我当然可以在我的函数中使用 after()
- 它在处理队列时可能不是最快速的,但这不是世界末日。但是有没有更优雅的方式来处理这个问题?当 OptionMenu
存在时,大多数 GUI 应该能够处理在某处调用 after_idle()
?
一般来说,您不应从通过 after_idle
调用的函数中调用 after_idle
。
原因如下:
一旦 tkinter 开始处理空闲队列,它不会停止,直到队列为空。如果队列中有一项,tkinter 会将该项目拉出并调用该函数。如果该函数向队列中添加了一些内容,则队列不再为空,因此 tkinter 会处理新项目。如果这个新项目将某些东西放在空闲队列中,那么 tkinter 将处理它,依此类推。队列永远不会变空,并且除了服务这个队列之外,tkinter 永远不会有机会做任何其他事情。
一个常见的解决方案是使用after
两次,让队列有机会变空,从而让tkinter处理其他非空闲事件。
例如,而不是这个:
self.master.after_idle(self.process_queue)
...这样做:
self.master.after_idle(self.master.after, 1, self.process_queue)
这会为队列创建一个微小的 window 以使其变为空,从而允许 tkinter 处理其他 "non-idle" 事件,例如在再次调用 self.process_queue
之前重绘屏幕的请求。
我有一个 GUI,当消息到达并被多个 websocket 排队时,它会连续处理消息。此函数使用 after_idle 设置对自身的回调以延续该过程,如下所示:
def process_queue(self, flush=False):
'''
Check for new messages in the queue periodically.
Sets up callback to itself to perpetuate the process.
'''
if flush:
while not self.queue.empty():
self.get_msg()
else:
self.get_msg()
self.master.after_idle(self.process_queue)
我在 GUI 上有一个 OptionMenu
小部件,它在单击时挂起并导致程序崩溃:
self.plotind = tk.StringVar()
self.plotind.set('MACD')
options = ['MACD']
self.indopts = tk.OptionMenu(self.analysis_frame, self.plotind, *[option for option in options])
self.indopts.grid(row=1, column=1)
如果我将 after_idle()
更改为 after()
,它可以正常工作。
我认为这是因为单击 OptionMenu
实际上会设置它自己的 after_idle()
调用来打开菜单,然后它与我在 process_queue()
中的菜单竞争。
如果需要,我当然可以在我的函数中使用 after()
- 它在处理队列时可能不是最快速的,但这不是世界末日。但是有没有更优雅的方式来处理这个问题?当 OptionMenu
存在时,大多数 GUI 应该能够处理在某处调用 after_idle()
?
一般来说,您不应从通过 after_idle
调用的函数中调用 after_idle
。
原因如下:
一旦 tkinter 开始处理空闲队列,它不会停止,直到队列为空。如果队列中有一项,tkinter 会将该项目拉出并调用该函数。如果该函数向队列中添加了一些内容,则队列不再为空,因此 tkinter 会处理新项目。如果这个新项目将某些东西放在空闲队列中,那么 tkinter 将处理它,依此类推。队列永远不会变空,并且除了服务这个队列之外,tkinter 永远不会有机会做任何其他事情。
一个常见的解决方案是使用after
两次,让队列有机会变空,从而让tkinter处理其他非空闲事件。
例如,而不是这个:
self.master.after_idle(self.process_queue)
...这样做:
self.master.after_idle(self.master.after, 1, self.process_queue)
这会为队列创建一个微小的 window 以使其变为空,从而允许 tkinter 处理其他 "non-idle" 事件,例如在再次调用 self.process_queue
之前重绘屏幕的请求。