如何在另一个任务旁边运行 tkinter 的 after event?

Posted

技术标签:

【中文标题】如何在另一个任务旁边运行 tkinter 的 after event?【英文标题】:How to run tkinter's after event alongside another task? 【发布时间】:2019-10-22 20:35:10 【问题描述】:

我正在尝试为执行某些任务的脚本创建一个 tkinter GUI。该任务是通过单击开始按钮触发的,我想添加一个动态标签,通过显示: “在职的。” → “工作中……” → “工作中……”

我参考了this 的帖子并编写了以下脚本。在这里,我使用进度条来表示我的“任务”,并期望状态标签在进度条更新时发生变化(如上所述)。

import tkinter as tk

class UI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title('Hello World')

        self.prog_Label = tk.Label(self.root, text='Progress')
        self.prog_Label.grid(row=0, column=0, sticky=tk.W, padx=20, pady=(10, 0))

        self.prog_Bar = tk.ttk.Progressbar(self.root)
        self.prog_Bar.configure(value=0, mode='determinate', orient='horizontal')

        self.prog_Bar.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=20, pady=5)

        self.exe_Btn = tk.Button(self.root, text='Start', padx=15, command=self.run, relief='groove')
        self.exe_Btn.grid(row=2, column=0, padx=80, pady=(40, 20), sticky=tk.E)

        self.prog_Label = tk.Label(self.root, text='Status:-')
        self.prog_Label.grid(row=3, column=0, sticky=tk.W, padx=20, pady=10)

        self.root.mainloop()

    def run(self):
        self.update_status('Working')
        n = 0
        self.prog_Bar.configure(value=n, maximum=100000, mode='determinate', orient='horizontal')
        for i in range(100000):
            n += 1
            self.prog_Bar.configure(value=n)
            self.prog_Bar.update_idletasks()

    def update_status(self, status=None):
        if status is not None:
            current_status = 'Status: ' + status
        else:
            current_status = self.prog_Label['text']
            if current_status.endswith('...'):
                current_status = current_status.replace('...', '')
            else:
                current_status += '.'

        self.prog_Label['text'] = current_status
        self.prog_Label.update_idletasks()
        self._status = self.root.after(1000, self.update_status)

if __name__ == '__main__':
    ui = UI()

但是,程序的行为方式是,当单击开始按钮时,虽然状态标签立即从“-”变为“工作中”,但它只是在进度条到达末尾后才开始添加点。

有没有办法修改它以达到我的目的?

【问题讨论】:

【参考方案1】:

我稍微改变了你的结构,所以你的任务现在在它自己的类中,而不是睡觉你会在那里执行任务。我为任务添加了一个线程,因为我认为它需要自己的进程,这会阻止应用程序冻结,因为它会阻塞主 UI 循环。

import threading
import time

import tkinter as tk
import tkinter.ttk as ttk

class Task:
    def __init__(self):
        self.percent_done = 0

        threading.Thread(target = self.run).start()

    def run(self):
        while self.percent_done < 1:
            self.percent_done += 0.1

            # Do your task here

            time.sleep(0.5)

        self.percent_done = 1


class Application():
    def __init__(self):
        self.root = tk.Tk()

        self.root.title("Window Title")

        self.task = None
        self.label_dots = 0

        self.prog_bar = tk.ttk.Progressbar(self.root)
        self.prog_bar.configure(value=0, maximum=100, mode='determinate', orient='horizontal')

        self.prog_bar.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=20, pady=5)

        self.run_btn = tk.Button(self.root, text='Start', padx=15, command=self.start_task, relief='groove')
        self.run_btn.grid(row=2, column=0, padx=80, pady=(40, 20), sticky=tk.E)

        self.prog_label = tk.Label(self.root, text='Status: -')
        self.prog_label.grid(row=3, column=0, sticky=tk.W, padx=20, pady=10)

    def start_task(self):       
        self.task = Task()

        self.update_ui()


    def update_ui(self):
        # Percent is between 0 and 1 
        if 0 < self.task.percent_done < 1:
            status = "Working"

            self.label_dots += 1
            self.label_dots = self.label_dots % 4
        else:
            status = "Finished"

            self.label_dots = 0

        self.prog_bar.configure(value=self.task.percent_done * 100)

        label_text = "Status: - " + status + ("." * self.label_dots)

        self.prog_label.config(text = label_text)

        if status != "Finished":
            self.root.after(1000, self.update_ui)           


Application().root.mainloop()

【讨论】:

我刚刚尝试了您的解决方案,它完全符合我的要求。非常感谢!

以上是关于如何在另一个任务旁边运行 tkinter 的 after event?的主要内容,如果未能解决你的问题,请参考以下文章

尝试在 tkinter mainloop 旁边运行一个 while true 循环

如何在另一个python文件中的列表中附加一些东西?

在 tkinter python 中的输入框旁边打包标签

如何通过按下按钮从 tkinter 输入字段返回变量以在另一个函数中使用?

Tkinter 和 USB 设备检测

如何同时打开 Pygame 窗口和 Tkinter 窗口?