按钮按下时出现TTK不确定进度条

2022-04-17 00:00:00 python tkinter python-multithreading ttk

问题描述

我正在尝试创建一个进度条,该进度条在我的函数运行期间运行,以向用户显示正在发生的事情,而不仅仅是冻结。我的函数(GENERATE_REPORTS)查询数据库并写入CSV文件。以下是我的代码的抽象版本:

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()

使用此代码时,进度条直到GENERATE_REPORTS线程完成并且不动时才会弹出。如果我删除联接,一切正常,但它永远不会停止加载。如何使加载栏仅在GENERATE_REPORTS线程期间运行?


解决方案

欢迎来到事件驱动编程的有趣世界:)。您不能在这里使用join,该函数的要点是阻塞,直到线程完成,而使用线程的全部要点是避免阻塞主循环。您有两个选择:要么设置图形用户界面不断轮询线程以查看它是否仍在运行,要么设置线程在完成后向图形用户界面发回一条消息。后一种选择可能是最干净的,通常使用tkinter的事件机制来完成。

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()

相关文章