假设我们有三个 tkinter 小部件:一个标签、一个 TreeView 和一个选项菜单(下面简称为“菜单”)。一旦选择了一个选项,我就成功地使菜单执行一个功能。简短的函数如下所示:
def data_process():
# do something takes time
def print_data()
data_process() # do something takes time too
# print stuff to treeview
def refresh_table(self, args):
label['text'] = 'Executing' # change label text to Executing
print_data() # a func. which takes time to run
label['text'] = 'Done' # change text to Done
label = tk.Label(parent, text = 'ready')
label.pack()
menu = tk.OptionMenu(parent, var, *list, command = lambda _:refresh_table(self, args))
menu.pack()
table = tk.Treeview(parent)
table.pack()
函数print_data
将打印一些内容到 TreeView 小部件(表)。标签小部件就像一个状态栏,告诉用户现在发生了什么。所以我尝试做的工作流程是:
在菜单中选择一个选项并调用
refresh_table
。将标签文本更改为“正在执行”。
执行
print_data
并在 TreeView 中打印内容。当
print_data
完成后,将标签更改为“完成”。
事情是这样的。当我选择该选项时,程序会(如预期)执行操作。但是,标签一开始并没有更改为“正在执行”。相反,当 print_data
执行完毕(几乎同时)时,它会更改为“完成”。我怀疑一旦所有需求完成,命令 refresh_table
就会影响目标小部件。因为我确实看到标签闪现“正在执行”,但立即显示“完成”。对于这样的情况,大家有什么想法吗?任何建议表示赞赏。谢谢!
最佳答案
I suspect that the command refresh_table effects the target widgets once all demands are done.
确实如此。每次进入 mainloop
时,您的 GUI 都会更新。当您的函数 refresh_table
运行时,不会处理任何事件(例如更新标签),因为这些事件仅发生在空闲时间(因此它们的另一个名称为“空闲任务”)。但是,由于 UI 在执行 refresh_table
期间并不空闲,因此在函数中的所有代码完成(因为整个程序在同一线程上运行)和重绘事件之前,事件循环不可访问等待中。您可以通过调用 update_idletasks 来修复此问题,它将立即处理所有待处理事件。会像这样完成(假设您的主窗口被称为 parent
并且您位于类定义中,重要的是您在主窗口上直接调用 update_idletasks
更改标签的文本):
def refresh_table(self, args):
label['text'] = 'Executing'
self.parent.update_idletasks()
print_data()
label['text'] = 'Done'
还有update
,它不仅会调用待处理的空闲任务,而且会处理基本上所有需要处理的内容,这在您的情况下是不必要的。可以在 in the answer to this question 找到为什么 update_idletasks
比 update
更可取的很好的解释。 .
关于Python/tkinter : Change label twice with one command execution,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54106472/