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

Popular posts from this blog

resizing Telegram inline keyboard -

command line - How can a Python program background itself? -

php - "cURL error 28: Resolving timed out" on Wordpress on Azure App Service on Linux -