Can't get ttk.Progressbar to start correctly

后端 未结 2 1969
隐瞒了意图╮
隐瞒了意图╮ 2021-01-28 04:57

I am not able to get the ttk.Progressbar widget to work. May I know what is the issue and how I can fix it?

I know the Progressbar widget is functional; whe

2条回答
  •  -上瘾入骨i
    2021-01-28 05:43

    The tkinter .after() method cannot be used to implement an indeterminate ttk.Progressbar() widget concurrently with another ongoing process. This is because the on-going process, simulated by the time.sleep(5) method, is stalling the tkinter application from issuing another process. During the stall, not even the .after() method can run despite it having a very much shorter wait interval.

    As mentioned by @Lukas comments and the references he had shared, an approach to implement an indeterminate ttk.Progressbar() running concurrently with another application process is to use a thread.daemon from python's threading module to manage the concurrency.

    Alternatively, python's asyncio infrastructure can be used to implement an indeterminate ttk.Progressbar() running concurrently with another application process. I recently explored this possibility. A caveat to this approach is that the "stalling process", and the activation and termination of the ttk.Progressbar must be written up in separate coroutines.

    Below is my script showing how to implement asyncio with tkinter 8.6 and its ttk.Progressbar() widget in Python 3.6.

    import tkinter as tk
    import tkinter.ttk as ttk
    import tkinter.messagebox as tkMessageBox
    
    import asyncio
    
    INTERVAL = 0.05 #seconds
    
    class App(ttk.Frame):
    
    
        def __init__( self, master, loop, interval=0.05, *args, **kw ):
            super().__init__( master,style='App.TFrame')
            self.master = master
            self.loop = loop
            self._set_style()
            self._create_widgets()
    
    
        def _set_style( self ):
            self.style = ttk.Style()
            self.style.configure( 'App.TFrame',  background='pink')
            self.style.configure( 'sp.TFrame',  background='light green')
    
    
        def _create_widgets( self ):
            self.sp_frame = ttk.Frame( self, style='sp.TFrame' )
            self.sp_frame.grid(row=0, column=0)
    
            #sp_frame widgets
            self.sp_label1 = ttk.Label( self.sp_frame, text='SP(s):')
            self.sp_combox = ttk.Combobox(
                self.sp_frame, state="readonly", values=['a','b','c']  )
            self.sp_combox.bind('<>', self._connect_esp)
            self.sp_pbar = ttk.Progressbar( self.sp_frame, length=200,
                                            mode='indeterminate',
                                            orient=tk.HORIZONTAL, )
            self.sp_label1.grid( row=0, column=0 )
            self.sp_combox.grid( row=0, column=1, padx=[10,0] )
            self.sp_pbar.grid(   row=1, column=0, columnspan=2, sticky='ew' )
    
    
        def _connect_esp( self, event):
    
            async def dojob( loop, start_time, duration=1 ):
                print( '\nasync def dojob( loop, end_time):' )
                while True:
                    duration = 3 #seconds
                    t = loop.time()
                    delta = t - start_time
                    print( 'wait time = {}'.format( delta ) )
                    if delta >= duration:
                        break
                    await asyncio.sleep( 1 )
    
            async def trackjob( loop ):
                print( '\nasync def trackjob( loop ):' )
                start_time = loop.time()
                self.sp_pbar.start( 50 )
                self.sp_pbar.update_idletasks()
                print( 'Job: STARTED' ) 
                result = await dojob( loop, start_time )
                print( 'result = ', result, type(result) )
                print( 'Job: ENDED' ) 
                self.sp_pbar.stop()
                self.sp_pbar.update_idletasks()
    
            try:
                task = self.loop.create_task( trackjob( self.loop ) )
                print( 'task = ', task, type(task))
            except Exception:
                raise
    
    
    async def tk_update( root, interval=INTERVAL ):
        print( '\nasync def tk_update( interval ):' )
        try:
            while True:
                root.update() #tk update 
                await asyncio.sleep( interval )
        except tk.TclError as err:
            if "application has been destroyed" not in err.args[0]:
                raise
    
    
    def ask_quit( root, loop, interval=INTERVAL ):
        '''Confirmation to quit application.'''
        if tkMessageBox.askokcancel( "Quit","Quit?" ):
            root.update_task.cancel() #Cancel asyncio task to update Tk()
            root.destroy() #Destroy the Tk Window instance.
            loop.stop() # Stop asyncio loop. This is needed before a run_forever type loop can be closed.
    
    
    def main():
        loop = asyncio.get_event_loop()
    
        root = tk.Tk()
        root.geometry('300x100+0+24')
        root.rowconfigure(0, weight=1)
        root.columnconfigure(0, weight=1)
        root.update_task = loop.create_task( tk_update( root ) ) 
    
        app = App( root, loop )
        app.grid(row=0, column=0, sticky='nsew')
        #root.mainloop() #DO NOT IMPLEMENT; this is replaced by running
                         # tk's update() method in a asyncio loop called loop.
                         # See tk_update() method and root.update_task.
    
        #Tell Tk window instance what to do before it is destroyed.
        root.protocol("WM_DELETE_WINDOW",
                      lambda :ask_quit( root, loop ) ) 
    
        try:
            print('start loop.run_forever()')
            loop.run_forever()
        finally:
            loop.run_until_complete( loop.shutdown_asyncgens() )
            loop.close()
    
    
    if __name__ == '__main__':
        main()
    

    Taking a macro view, it does seem that implementing tkinter within Python's asyncio event loop can facilitate the development of better concurrent GUI applications. I am discovering this myself and hope this attached script can help fellow tkinter users in learning so.

提交回复
热议问题