首页 > 解决方案 > Python/tkinter:一次命令执行两次更改标签

问题描述

假设我们有三个 tkinter 小部件:一个标签、一个树视图和一个选项菜单(以下简称为“菜单”)。一旦选择了一个选项,我就成功地使菜单执行了一个功能。简要函数如下所示:

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会将某些内容打印到树视图小部件(表格)。标签小部件就像一个状态栏,告诉用户现在发生了什么。所以我尝试做的工作流程是:

  1. 在菜单中选择一个选项并调用refresh_table

  2. 将标签文本更改为“正在执行”。

  3. 在树视图中执行print_data和打印内容。

  4. 完成print_data后,将标签更改为“完成”。

事情就是这样。当我选择该选项时,程序会(如预期的那样)做一些事情。但是,标签在开始时并未更改为“正在执行”。print_data相反,它在执行完成时(几乎同时)更改为“完成” 。我怀疑refresh_table一旦完成所有要求,该命令就会影响目标小部件。因为我确实看到标签闪烁“正在执行”,但立即显示“完成”。对这种情况有什么想法吗?任何建议表示赞赏。谢谢!

标签: pythonpython-3.xooptkinter

解决方案


我怀疑命令 refresh_table 一旦完成所有需求就会影响目标小部件。

确实如此。每次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, 它不仅会调用挂起的空闲任务,而且基本上会处理所有要处理的事情,这在您的情况下是不必要的。在这个问题的答案中可以找到一个很好的解释为什么update_idletasks最可取。update


推荐阅读