【发布时间】:2015-10-03 02:41:28
【问题描述】:
[根据 cmets 对原始帖子进行了相当大的编辑,并使上下文 - 2 个模块 - 更清晰,并总结我认为的关键潜在问题。代码也更新了。我有一个工作版本,但完全不确定它是否以正确的方式完成。] (免责声明......我正在学习 Tkinter!)
我试图在应用程序运行时显示进度条(例如,遍历音乐库文件夹树,但这在这里无关紧要)。
我想将它作为一个与主应用程序分开的模块中的类来实现,这样我就可以在其他地方使用它(应用程序本身实际上也是在 2 个模块中)。
出于这个原因,也因为我不想打乱应用程序的主窗口设计,我希望进度条出现在单独的窗口中。
我已经尝试了这两种方法......我自己使用文本小部件粗略绘制的进度条,并且 - 一旦我发现它 - ttk.Progressbar。我现在专注于使用 ttk.Progressbar。
但是请注意,这两种方法都存在基本相同的问题,即在不阻止控制恢复到调用模块的情况下正确显示进度窗口的内容。
我的这个 (ProgressBar) 类具有启动、更新和停止进度条的方法。据我了解,有三种方法可以强制刷新类方法中的状态窗口。这三个似乎都有缺点。
- root.master.mainloop() 将控制权保留在进度窗口中,并且应用程序停止执行。这基本上达不到目的。
- root.master.update_idletasks() 将控制权交还给调用应用程序,但状态窗口显示为空白。出于不同的原因,也违背了目的。
- root.master.update() 似乎工作正常,状态窗口更新为可见内容,控制权返回到调用应用程序。但是我在几个地方读到过这是一种危险的使用方法。
所以基本问题是 - 强制窗口更新的正确方法是什么(例如 Set 方法);以及为什么 update_idletasks() 会清空进度窗口。
我相信以下代码反映了所提出的建议,但我已对其进行了调整以反映预期的导入类。
# dummy application importing the StatusBar class.
# this reflects app is itslef using tkinter
from ProgressBar12 import ProgressBar
import tkinter as Tk
import time
import os
def RunAppProcess():
print('App running')
Bar = ProgressBar(tkroot) # '' arg to have ProgressBar create its tkroot
Bar.start('Progress...', 0) # >0 determinate (works) / 0 for indeterminate (doesnt!)
print('starting process')
# this simulates some process, (eg for root, dirs, files = os.walk(lib))
for k in range(10):
Bar.step(5) # (should be) optional for indeterminate
time.sleep(.2)
Bar.stop('done') # '' => kill the window; or 'message' to display in window
def EndAppProcess():
tkroot.withdraw()
tkroot.destroy()
# Application init code, the application is using tkinter
# (should probably be in an init procedure etc, but this will serve)
tkroot = Tk.Tk()
tkroot.title("An Application")
tkroot.geometry("100x100")
tkroot.configure(bg='khaki1')
# a 2 button mini window: [Start] and [Quit]
Tk.Button(tkroot, text='Start', bg='orange', command=RunAppProcess).grid(sticky=Tk.W)
Tk.Button(tkroot, text="Quit", bg="orange", command=EndAppProcess).grid(sticky=Tk.W)
tkroot.mainloop()
进度条模块
# determinate mode
import tkinter as Tk
import tkinter.font as TkF
from tkinter import ttk
import time
# print statements are for tracing execution
# changes from the sample code previsouly given reflect:
# - suggestions made in the answer and in comments
# - to reflect the actual usage with the class imported into a calling module rather than single module solution
# - consistent terminology (progress not status)
# - having the class handle either determinate or indeterminate progress bar
class ProgressBar():
def __init__(self, root):
print('progress bar instance init')
if root == '':
root = tkInit()
self.master=Tk.Toplevel(root)
# Tk.Button(root, text="Quit all", bg="orange", command=root.quit).grid() A bit rude to mod the callers window
self.customFont2 = TkF.Font(family="Calibri", size=12, weight='bold')
self.customFont5 = TkF.Font(family="Cambria", size=16, weight='bold')
self.master.config(background='ivory2')
self.create_widgets()
self.N = 0
self.maxN = 100 # default for %
def create_widgets(self):
self.msg = Tk.Label(self.master, text='None', bg='ivory2', fg='blue4') #, font=self.customFont2)
self.msg.grid(row=0, column=0, sticky=Tk.W)
self.bar = ttk.Progressbar(self.master, length=300, mode='indeterminate')
self.bar.grid(row=1, column=0, sticky=Tk.W)
#self.btn_abort = Tk.Button(self.master, text=' Abort ', command=self.abort, font=self.customFont2, fg='maroon')
#self.btn_abort.grid(row=2,column=0, sticky=Tk.W)
#self.master.rowconfigure(2, pad=3)
print('progress bar widgets done')
def start(self, msg, maxN):
if maxN <= 0:
#indeterminate
self.msg.configure(text=msg)
self.bar.configure(mode='indeterminate')
self.maxN = 0
self.bar.start()
self.master.update()
else: # determinate
self.msg.configure(text=msg)
self.bar.configure(mode='determinate')
self.maxN = maxN
self.N = 0
self.bar['maximum'] = maxN
self.bar['value'] = 0
def step(self, K):
#if self.maxN == 0: return # or raise error?
self.N = min(self.maxN, K+self.N)
self.bar['value'] = self.N
self.master.update() # see set(..)
def set(self, K):
#if self.maxN == 0: return
self.N = min(self.maxN, K)
self.bar['value'] = self.N
#self.master.mainloop() # <<< calling module does not regain control. Pointless.
#self.master.update_idletasks # <<< works, EXCEPT statusbar window is blank! Also pointless. But calling module regains control
self.master.update() # <<< works in all regards, BUT I've read this is dangerous.
def stop(self, msg):
print('progress bar stopping')
self.msg.configure(text=msg)
if self.maxN <= 0:
self.bar.stop()
else:
self.bar['value'] = self.maxN
#self.bar.stop()
if msg == '':
self.master.destroy()
else: self.master.update()
def abort(self):
# eventually will raise an error to the calling routine to stop the process
self.master.destroy()
def tkInit():
print('progress bar tk init')
tkroot = Tk.Tk()
tkroot.title("Progress Bar")
tkroot.geometry("250x50")
tkroot.configure(bg='grey77')
tkroot.withdraw()
return tkroot
if (__name__ == '__main__'):
print('start progress bar')
tkroot = tkInit()
tkroot.configure(bg='ivory2')
Bar = ProgressBar(tkroot)
Bar.start('Demo', 10)
for k in range(11):
Bar.set(k)
time.sleep(.2)
Bar.stop('done, you can close me')
else:
# called from another module
print('progress bar module init. (nothing) done.')
这是基于答案中的第一个解决方案;作为替代方案,我将尝试使用 after() ...。我首先必须确切了解它的作用。
【问题讨论】:
标签: python-3.x tkinter