按下按钮时 Ttk 不确定进度条
Ttk Indeterminate progress bar on button press
我正在尝试创建一个 运行 的进度条,只要我的功能是 运行 向用户显示事情正在发生,而不仅仅是冻结。我的函数 (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
,该函数的要点是阻塞直到线程完成,而使用线程的全部要点是避免阻塞主循环。您有 2 个选择:要么将 GUI 设置为不断轮询线程以查看它是否仍然 运行,要么将线程设置为在完成时将消息发送回 GUI。后一个选项可能是最干净的,并且通常使用 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()
我看到你已经接受了上面的答案。但是,我会 post 我的答案,因为它不是基于线程的,我认为线程更简单,可能更合适:
from tkinter import *
from tkinter.ttk import *
import os
root = Tk()
# I set the length and maximum as shown to demonstrate the process in the
# proceeding function
progress = Progressbar(root, orient = HORIZONTAL,
length = 200/5, maximum=200/5, mode = 'determinate')
# Function
def my_func():
t=0
r= 1/5
for i in range(200):
print(i)
t=t+r
progress['value'] = t
root.update_idletasks()
progress.pack()
# Button
Button(root, text = 'Start', command = bar).pack(pady = 10)
mainloop()
我正在尝试创建一个 运行 的进度条,只要我的功能是 运行 向用户显示事情正在发生,而不仅仅是冻结。我的函数 (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
,该函数的要点是阻塞直到线程完成,而使用线程的全部要点是避免阻塞主循环。您有 2 个选择:要么将 GUI 设置为不断轮询线程以查看它是否仍然 运行,要么将线程设置为在完成时将消息发送回 GUI。后一个选项可能是最干净的,并且通常使用 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()
我看到你已经接受了上面的答案。但是,我会 post 我的答案,因为它不是基于线程的,我认为线程更简单,可能更合适:
from tkinter import *
from tkinter.ttk import *
import os
root = Tk()
# I set the length and maximum as shown to demonstrate the process in the
# proceeding function
progress = Progressbar(root, orient = HORIZONTAL,
length = 200/5, maximum=200/5, mode = 'determinate')
# Function
def my_func():
t=0
r= 1/5
for i in range(200):
print(i)
t=t+r
progress['value'] = t
root.update_idletasks()
progress.pack()
# Button
Button(root, text = 'Start', command = bar).pack(pady = 10)
mainloop()