Why ttk Progressbar appears after process in Tkinter

后端 未结 2 1414
暖寄归人
暖寄归人 2020-12-17 04:21

I want to create a large text upon Tkinter menu command and provide visual support by a progress bar. Although the progress bar is meant to start before the sub

相关标签:
2条回答
  • 2020-12-17 05:03

    Here's another considerably simpler solution that doesn't require mixing Tkinter and multi-threading. To use it requires the ability to call the progressbar widget's update_idletasks() method multiple times during the time-consuming function.

    from Tkinter import *
    import ttk
    
    import time
    
    def foo(progressbar):
        progressbar.start()
        for _ in range(50):
            time.sleep(.1) # simulate some work
            progressbar.step(10)
            progressbar.update_idletasks()
        progressbar.stop()
    
    root = Tk()
    
    mainframe = ttk.Frame(root, padding="3 3 12 12")
    mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
    mainframe.columnconfigure(0, weight=1)
    mainframe.rowconfigure(0, weight=1)
    progressbar = ttk.Progressbar(mainframe, mode='indeterminate')
    progressbar.grid(column=1, row=100, sticky=W)
    
    ttk.Button(mainframe, text="Check",
               command=lambda:foo(progressbar)).grid(column=1, row=200, sticky=E)
    
    for child in mainframe.winfo_children():
        child.grid_configure(padx=5, pady=5)
    root.bind('<Return>', lambda event:foo(progressbar))
    
    root.mainloop()
    
    0 讨论(0)
  • 2020-12-17 05:10

    I think the problem is that the time-consuming loop is preventing the tkinter event loop, mainloop(), from running. In other words, when your work intensive function runs in the same thread as the GUI, it interferes with it by hogging the interpreter.

    To prevent this you can use a secondary Thread to run your function and run the GUI and its progressbar in the main thread. To give you an idea of how to do this, here's a simple example I derived from code in another (unrelated) progressbar question to show how easily something like that can be done. Note: It's generally recommended that secondary threads not be given direct access to the main thread's tkinter objects.

    from Tkinter import *
    import ttk
    
    import time
    import threading
    
    def foo():
        time.sleep(5) # simulate some work
    
    def start_foo_thread(event):
        global foo_thread
        foo_thread = threading.Thread(target=foo)
        foo_thread.daemon = True
        progressbar.start()
        foo_thread.start()
        root.after(20, check_foo_thread)
    
    def check_foo_thread():
        if foo_thread.is_alive():
            root.after(20, check_foo_thread)
        else:
            progressbar.stop()
    
    root = Tk()
    mainframe = ttk.Frame(root, padding="3 3 12 12")
    mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
    mainframe.columnconfigure(0, weight=1)
    mainframe.rowconfigure(0, weight=1)
    progressbar = ttk.Progressbar(mainframe, mode='indeterminate')
    progressbar.grid(column=1, row=100, sticky=W)
    
    ttk.Button(mainframe, text="Check",
               command=lambda:start_foo_thread(None)).grid(column=1, row=200,
                                                           sticky=E)
    
    for child in mainframe.winfo_children():
        child.grid_configure(padx=5, pady=5)
    root.bind('<Return>', start_foo_thread)
    
    root.mainloop()
    
    0 讨论(0)
提交回复
热议问题