Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
250 views
in Technique[技术] by (71.8m points)

python - How to connect a progress bar to a function?

I'm trying to connect a progress bar to a function for my project.

This is what I have so far but im pretty sure it does nothing:

def main():
    pgBar.start()
    function1()
    function2()
    function3()
    function4()
    pgBar.stop()

Here is the code where I make my progress bar if that helps at all:

pgBar = ttk.Progressbar(window, orient = HORIZONTAL, length=300, mode = "determinate")
pgBar.place(x=45, y=130)

I have been doing some research and understand that the tkinter window freezes when running a function or something like that. Is there a way I could "unfreeze" the window at the end of each function that is called inside the main one?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Since tkinter is single threaded, you need another thread to execute your main function without freezing the GUI. One common approach is that the working thread puts the messages into a synchronized object (like a Queue), and the GUI part consumes this messages, updating the progress bar.

The following code is based on a full detailed example on ActiveState:

import tkinter as tk
from tkinter import ttk
import threading
import queue
import time


class App(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)
        self.queue = queue.Queue()
        self.listbox = tk.Listbox(self, width=20, height=5)
        self.progressbar = ttk.Progressbar(self, orient='horizontal',
                                           length=300, mode='determinate')
        self.button = tk.Button(self, text="Start", command=self.spawnthread)
        self.listbox.pack(padx=10, pady=10)
        self.progressbar.pack(padx=10, pady=10)
        self.button.pack(padx=10, pady=10)

    def spawnthread(self):
        self.button.config(state="disabled")
        self.thread = ThreadedClient(self.queue)
        self.thread.start()
        self.periodiccall()

    def periodiccall(self):
        self.checkqueue()
        if self.thread.is_alive():
            self.after(100, self.periodiccall)
        else:
            self.button.config(state="active")

    def checkqueue(self):
        while self.queue.qsize():
            try:
                msg = self.queue.get(0)
                self.listbox.insert('end', msg)
                self.progressbar.step(25)
            except Queue.Empty:
                pass


class ThreadedClient(threading.Thread):

    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        for x in range(1, 5):
            time.sleep(2)
            msg = "Function %s finished..." % x
            self.queue.put(msg)


if __name__ == "__main__":
    app = App()
    app.mainloop()

Since the original example on ActiveState is a bit messy IMO (the ThreadedClient is quite coupled with the GuiPart, and things like controlling the moment to spawn the thread from the GUI are not as straightforward as they could be), I have refactored it and also added a Button to start the new thread.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...