如何在 Tkinter 中实现不确定的进度条?

How to implement indeterminate Progressbar in Tkinter?

我的应用程序偶尔会有一些耗时的后台进程 运行。我想使用简单的 indeterminate ttk.Progressbar 来证明该应用程序正在运行。 但是,我的实现只显示静态进度条。

无论如何,这是我实现它的方式。

class MyApp(ttk.Frame):
    def __init__(self, master):
        ttk.Frame.__init__(self, master)
        fname = 'test.txt'
        self.build_ui()
        self.process_file(fname)

    def process_file(self, fname):
        self.show_progress(True)
        fdata = self.read_file(fname)
        fdata = self.spellcheck(fdata)
        self.show_progress(False)
        return fdata

    def show_progress(self, start)
        if start:
            self.prog_win = tk.Toplevel()
            self.prog_win.title('Working...')
            self.prog_win.resizable(0, 0)
            self.progress_bar = ttk.Progressbar(self.prog_win,
                                                orient=tk.HORIZONTAL,
                                                mode='indeterminate',
                                                takefocus=True)
            self.progress_bar.grid()
            self.progress_bar.start()
        else:
            self.progress_bar.stop()
            self.prog_win.destroy()


root = tk.Tk()
root.update()
gui = MyApp(root)
gui.mainloop()

上面的代码没有按预期工作。 Progressbar 看起来是静止的,它不会移动,永远挂在那里。我尝试使用线程,但如果我在单独的 Thread 中启动 show_progress,它总是在处理完成后执行。

我做错了什么?

问题是 self.read_file()self.spellcheck() 正在阻塞,这会阻止 Tkinter 在其主循环中更新进度条。解决这个问题的一个简单方法是在单独的线程中进行处理,并定期检查线程是否完成了它的工作。

import threading

class MyApp(ttk.Frame):
    def __init__(self, master):
        ttk.Frame.__init__(self, master)
        fname = 'test.txt'
        self.build_ui()
        self.process_file(fname)

    def process_file(self, fname):
        self.show_progress(True)

        # Start thread to process file.
        self.thread = threading.Thread(target=self.process_file_worker, args=(fname,))
        self.thread.daemon = True # Allow the program to terminate without waiting for the thread to finish.
        self.thread.start()

        # Start checking the thread.
        self.process_file_check()

    def process_file_check(self):
        if self.thread.is_alive():
            # Thread is still running, check thread again in 10 milliseconds.
            self.after(10, self.process_file_check)

        else:
            # Thread finished, handle processed results.
            # Do something with `self.fdata`.
            self.show_progress(False)

    def process_file_worker(self, fname):
        # This is run inside the thread.
        fdata = self.read_file(fname)
        fdata = self.spellcheck(fdata)
        self.fdata = fdata

    def show_progress(self, start):
        if start:
            self.prog_win = tk.Toplevel()
            self.prog_win.title('Working...')
            self.prog_win.resizable(0, 0)
            self.progress_bar = ttk.Progressbar(self.prog_win,
                                                orient=tk.HORIZONTAL,
                                                mode='indeterminate',
                                                takefocus=True)
            self.progress_bar.grid()
            self.progress_bar.start()
        else:
            self.progress_bar.stop()
            self.prog_win.destroy()


root = tk.Tk()
root.update()
gui = MyApp(root)
gui.mainloop()