Python tkinter update_idletasks is blanking the window -
[this has been edited in light of comments original post, , make context - 2 modules - clearer , summarise think key underlying issue. code updated. have working version not @ sure done right way.] (disclaimer ... im learning tkinter go along!)
im attempting display progress bar while app running (eg walking music library folder tree doesnt matter here).
i'd implement class in separate module main app can use elsewhere (also app in 2 modules).
for reason, , because don't want upset app's main window design id progress bar appear in separate window.
i've tried 2 ways ... own crudely drawn progress bar using text widget, , - once discovered - ttk.progressbar. im focusing on using ttk.progressbar.
note had same problem either approach, getting contents of progress window display without preventing control reverting calling module.
my class (progressbar) has methods start, update, , stop progress bar. understand there 3 ways force refreshing of status window in class methods. 3 seem have drawbacks.
- root.master.mainloop() keeps control in progress window , app stops executing. defeats purpose.
- root.master.update_idletasks() gives control calling app status window blanked out. defeats purpose, different reason.
- root.master.update() seems work perfectly, status window updated visible contents , control goes calling app. ive read in several places dangerous method use.
so basic questions - correct way force window update (eg set method); , why update_idletasks() blanking progress window.
i believe following code reflects suggestions made have adapted reflect intended import class.
# dummy application importing statusbar class. # reflects app itslef using tkinter progressbar12 import progressbar import tkinter tk import time import os def runappprocess(): print('app running') bar = progressbar(tkroot) # '' arg have progressbar create tkroot bar.start('progress...', 0) # >0 determinate (works) / 0 indeterminate (doesnt!) print('starting process') # simulates process, (eg root, dirs, files = os.walk(lib)) k in range(10): bar.step(5) # (should be) optional indeterminate time.sleep(.2) bar.stop('done') # '' => kill window; or 'message' display in window def endappprocess(): tkroot.withdraw() tkroot.destroy() # application init code, application using tkinter # (should in init procedure etc, serve) tkroot = tk.tk() tkroot.title("an application") tkroot.geometry("100x100") tkroot.configure(bg='khaki1') # 2 button mini window: [start] , [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()
progressbar module
# determinate mode import tkinter tk import tkinter.font tkf tkinter import ttk import time # print statements tracing execution # changes sample code previsouly given reflect: # - suggestions made in answer , in comments # - reflect actual usage class imported calling module rather single module solution # - consistent terminology (progress not status) # - having 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() bit rude mod 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 % 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 not regain control. pointless. #self.master.update_idletasks # <<< works, except statusbar window blank! pointless. calling module regains control self.master.update() # <<< works in regards, i've read 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): # raise error calling routine stop 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) k in range(11): bar.set(k) time.sleep(.2) bar.stop('done, can close me') else: # called module print('progress bar module init. (nothing) done.')
this based on first of solutions in answer; alternative try second using after() .... first have understand does.
first, don't call mainloop() anywhere. following code displays moving progress bar until hit abort button. for() loop in code above serves no purpose nothing except stop program execuption 0.3*20 seconds. if want update progress bar yourself, see 2nd example , how uses "after" call update function until progress bar completes. , note associated contained in class, 1 of reasons use class. call update function outside class, update function still in same class created progress bar.
import tkinter tk import tkfont tkf import ttk import time class statusbar(): def __init__(self, root): self.master=tk.toplevel(root) tk.button(root, text="quit all", bg="orange", command=root.quit).grid() 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.ctr=0 self.create_widgets() def create_widgets(self): self.msg = tk.label(self.master, text='none', bg='ivory2', fg='blue4', font=self.customfont2, width=5) 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('widgets done') def start(self, msg): self.msg.configure(text=msg) self.bar.start() def stop(self, msg): self.msg.configure(text=msg) self.bar.stop() def abort(self): # raise error calling routine stop process self.master.destroy() if (__name__ == '__main__'): print('start') tkroot = tk.tk() tkroot.title("status bar") tkroot.geometry("500x75") tkroot.configure(bg='ivory2') bar = statusbar(tkroot) bar.start('demo') tkroot.mainloop()
uses after() update progress bar
try: import tkinter tk ## python 2.x except importerror: import tkinter tk ## python 3.x import ttk class testprogress(): def __init__(self): self.root = tk.tk() self.root.title('ttk.progressbar') self.increment = 0 self.pbar = ttk.progressbar(self.root, length=300) self.pbar.pack(padx=5, pady=5) self.root.after(100, self.advance) self.root.mainloop() def advance(self): # can float self.pbar.step(5) self.increment += 5 if self.increment < 100: self.root.after(500, self.advance) else: self.root.quit() tp=testprogress()
Comments
Post a Comment