如果在程序中的其他位置调用after_idle,则单击tkinter optionmenu会挂起

问题描述 投票:0回答:1

我有一个GUI,当它们到达时串行处理消息并由几个websockets排队。此函数使用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() - 它在处理队列时可能没有最佳速度,但它不是世界末日。但是有更优雅的方式来处理这个问题吗?当after_idle()存在时,大多数GUI应该能够处理OptionMenu称为某处?

python-2.7 tkinter callback optionmenu idle-processing
1个回答
1
投票

一般来说,你不应该通过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)

这为队列创建了一个很小的窗口,允许tkinter处理其他“非空闲”事件,例如在再次调用self.process_queue之前重绘屏幕的请求。

© www.soinside.com 2019 - 2024. All rights reserved.