Ttk Indeterminate progress bar on button press

孤街醉人 提交于 2021-01-07 02:39:46

问题


I am trying to create a progress bar that runs as long as my function is running to show the user that things are happening and not just frozen. My function (generate_reports) makes queries to the database and writes to CSV files. Here is an abstract version of my code:

from tkinter import *
from tkinter import ttk
from billing import generate_reports

class app:
  def __init__(self, root):
    self.mainframe = ttk.Frame(root, padding = '4 4 12 12')
    self.mainframe.grid(column = 0, row = 0, sticky = (N, W, E, S))
    ttk.Button(self.mainframe, text = "Generate Billing Reports", command = self.do_reports).grid(column = 2, row = 3, sticky = (W, E))
  
  def do_reports(self, *args):
     pbar = ttk.Progressbar(self.mainframe, orient = HORIZONTAL, mode = 'indeterminate')
     pbar.grid(row = 4, column = 3, sticky = (W, E))
     t1 = threading.Thread(target = generate_reports, args = [start, end])
     t1.start()
     pbar.start()
     t1.join()
     pbar.stop()
     return

root = Tk()
BillingApp(root)
root.mainloop()

With this code, the progress bar doesn't pop up until after the generate_reports thread is completed and it is unmoving. If I remove the join, everything works fine but it never stops loading. How can I make the loading bar run for only the duration of the generate_reports thread?


回答1:


Heh welcome to the fun world of event driven programming :). You can't use join here, the point of that function is to block until the thread is done and the whole point of using a thread is to avoid blocking the mainloop. You have 2 choices: either set up the GUI to constantly poll the thread to see if it's still running, or set up the thread to send a message back to the GUI when it's done. This latter option is probably the cleanest, and it's often done using tkinter's event mechanism.

from tkinter import *
from tkinter import ttk
import threading
import time

def generate_reports(start, end):
    print("provide a mcve next time!")
    time.sleep(5)

def run_report(root, *args):
    generate_reports(*args)
    root.event_generate("<<PhishDoneEvent>>") # yes, this is using tkinter in a thread, but some tkinter methods are ok to use in threads

class BillingApp:
  def __init__(self, root):
    self.mainframe = ttk.Frame(root, padding = '4 4 12 12')
    self.mainframe.grid(column = 0, row = 0, sticky = (N, W, E, S))
    ttk.Button(self.mainframe, text = "Generate Billing Reports", command = self.do_reports).grid(column = 2, row = 3, sticky = (W, E))
    root.bind("<<PhishDoneEvent>>", self.report_done)

  def do_reports(self, *args):
    # note this makes a new widget with every click ... this is bad. Refactor to reuse the widget. 
    self.pbar = ttk.Progressbar(self.mainframe, orient = HORIZONTAL, mode = 'indeterminate')
    self.pbar.grid(row = 4, column = 3, sticky = (W, E))
    start, end = 4,5
    t1 = threading.Thread(target = run_report, args = [root, start, end])
    t1.start()
    self.pbar.start()

  def report_done(self, event=None):
    self.pbar.stop()
    Label(self.mainframe, text="report done").grid(row = 4, column = 3)

root = Tk()
BillingApp(root)
root.mainloop()


来源:https://stackoverflow.com/questions/65084804/ttk-indeterminate-progress-bar-on-button-press

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!