代码之家  ›  专栏  ›  技术社区  ›  Sun Bear

无法使ttk.progressbar正确启动

  •  0
  • Sun Bear  · 技术社区  · 7 年前

    我拿不到 ttk.Progressbar 要工作的小部件。我能知道是什么问题吗?我该怎么解决?

    我知道progressbar小部件是有用的;当我注释掉时 self.sp_pbar.stop() progressbar运行,但这发生在 time.sleep(5) 完成不是所需的行为。

    import tkinter as tk
    import tkinter.ttk as ttk
    import time
    
    class App(ttk.Frame):
    
    
        def __init__( self, master=None, *args, **kw ):
    
            super().__init__( master,style='App.TFrame')
    
            self.master = master
            self.espconnecting = False
    
            self._set_style()
            self._create_widgets()
    
    
        def _set_style( self ):
            print( '\ndef _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 ):
            print( '\ndef _create_widgets( self ):' )
            self.sp_frame = ttk.Frame( self, style='sp.TFrame' )
            self.sp_frame.grid(row=0, column=0)
    
            #self.sp_frame widgets
            self.sp_label1 = ttk.Label( self.sp_frame, text='SP(s):')
            self.sp_label2 = ttk.Label( self.sp_frame, text='ESP(s):')
            self.sp_label3 = ttk.Label( self.sp_frame, )
    
            self.sp_combox = ttk.Combobox( self.sp_frame, state="readonly",
                                           values=['a','b','c']  )
            self.sp_combox.bind('<<ComboboxSelected>>', 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' )
            self.sp_label2.grid( row=2, column=0)
            self.sp_label3.grid( row=2, column=1)
    
    
        def _connect_esp( self, event=None):
            print( '\ndef connect_esp( self, event=None ):' )
            self._show_conn_progress()
            print("START Connection")
            time.sleep(5) # The code is running a function here which can take some time.  
            print("END Connection")
            self.espconnecting = False
    
    
        def _show_conn_progress( self ):
            print( '\ndef _show_conn_progress( self ):' )
            self.espconnecting = True
            self.sp_label3['text']='Connecting.....'
            self.sp_label3.update_idletasks()
            self.sp_pbar.start()
            self._update_conn_progress()
    
    
        def _update_conn_progress( self ):
            print( '\ndef _update_conn_progress( self ):' )
            if not self.espconnecting:
                print('connected')
                self.sp_pbar.stop()
                self.sp_label3['text']='Connected'
            else:
                print('connecting')
                self.sp_pbar.update_idletasks()
                self.after(500, self._update_conn_progress) # Call this method after 500 ms.
    
    
    def main():
        root = tk.Tk()
        root.geometry('300x100+0+24')
        root.rowconfigure(0, weight=1)
        root.columnconfigure(0, weight=1)
    
        app = App( root )
        app.grid(row=0, column=0, sticky='nsew')
    
        root.mainloop()
    
    if __name__ == '__main__':
        main()
    
    1 回复  |  直到 7 年前
        1
  •  0
  •   Samuel Kazeem Aishwarya Shukla    7 年前

    这是您当前代码中的内容:

    你设置 self.espconnecting = False

    你打电话 _connect_esp()

    哪些调用 _show_conn_progress()

    哪套 self.espconnecting = True 启动progressbar self.sp_pbar.start()

    然后打电话 _update_conn_progress()

    它检查 self.espconnecting . 如果 self.esp连接 True (现在是)连接继续,进度条按预期滚动。 如果 self.esp连接 False 进度条已停止 self.sp_pbar.stop()

    以前 .after() 可以在500毫秒内回调,控件被传递回 _connect_esp 哪套 self.espconnecting=错误 . 然后 之后() 电话 _更新连接进度() 这是为了让酒吧继续营业,

    但是(这是你的问题):什么是最后的价值 self.esp连接 ? =False 因此,控制分支到 自行停止() ,它将停止progrss栏。这就是为什么当你注释那一行代码时,你的代码会像预期的那样工作,因为即使控制分支在那里,也不会有任何东西阻止进度条工作。

    解决方案

    不设置 self.espconnecting=错误 在里面 _连接ESP() 因为以前 之后() 使其在500毫秒内回调,控件将被传递回 _连接ESP() 哪套 self.espconnecting=错误 这会阻止你的进度条工作。

    这意味着您必须找到另一种方法来“结束连接”,一旦它开始。

    N. B: 我真的不认为有必要 time.sleep(5) 在代码中。

    下面是一种可能的解决方法:

    ...
    def __init__( self, master=None, *args, **kw ):
    
        super().__init__( master,style='App.TFrame')
    
        self.master = master
        self.espconnecting = False
        self.count=0
    
        self._set_style()
        self._create_widgets()
    
    
    def _set_style( self ):
        print( '\ndef _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 ):
        print( '\ndef _create_widgets( self ):' )
        self.sp_frame = ttk.Frame( self, style='sp.TFrame' )
        self.sp_frame.grid(row=0, column=0)
    
        #self.sp_frame widgets
        self.sp_label1 = ttk.Label( self.sp_frame, text='SP(s):')
        self.sp_label2 = ttk.Label( self.sp_frame, text='ESP(s):')
        self.sp_label3 = ttk.Label( self.sp_frame, )
    
        self.sp_combox = ttk.Combobox( self.sp_frame, state="readonly",
                                       values=['a','b','c']  )
        self.sp_combox.bind('<<ComboboxSelected>>', 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' )
        self.sp_label2.grid( row=2, column=0)
        self.sp_label3.grid( row=2, column=1)
    
    
    def _connect_esp( self, event=None):
        print( '\ndef connect_esp( self, event=None ):' )
        self._show_conn_progress()
        print("START Connection")
        time.sleep(5)
    
    def end_connection(self):
        print("END Connection")
        self.espconnecting = False
    
    
    def _show_conn_progress( self ):
        print( '\ndef _show_conn_progress( self ):' )
        self.espconnecting = True
        self.sp_label3['text']='Connecting.....'
        self.sp_label3.update_idletasks()
        self.sp_pbar.start()
        self._update_conn_progress()
    
    
    def _update_conn_progress( self ):
        print( '\ndef _update_conn_progress( self ):' )
        if not self.espconnecting:
            print('connected')
            self.sp_pbar.stop()
            self.sp_label3['text']='Connected'
        else:
            print('connecting')
            #self.sp_pbar.update_idletasks()
            self.after(500, self._update_conn_progress) # Call this method after 500 ms.
            self.count=self.count + 1
            if self.count==10:
                self.end_connection()
    
    
    def main():
        root = tk.Tk()
        root.geometry('300x100+0+24')
        root.rowconfigure(0, weight=1)
        root.columnconfigure(0, weight=1)
    
    app = App( root )
    app.grid(row=0, column=0, sticky='nsew')
    
    root.mainloop()
    
    if __name__ == '__main__':
        main()
    
        2
  •  0
  •   Sun Bear    7 年前

    特金特 .after() 方法不能用于实现不确定 ttk.Progressbar() 与另一个正在进行的进程并发的小部件。这是因为由time.sleep(5)方法模拟的正在进行的进程正在阻止Tkinter应用程序发出另一个进程。在摊位上,连 之后() 方法可以运行,尽管它有非常短的等待间隔。

    正如@lukas注释和他共享的引用所提到的,一种实现不确定 进度条() 与另一个应用程序进程同时运行是使用 thread.daemon 来自python的 threading 管理并发性的模块。

    或者,python的 阿辛乔 基础结构可用于实现不确定的 进度条() 与另一个应用程序进程同时运行。我最近探索 this possibility . 这种方法的一个警告是“暂停过程”,以及 ttk.Progressbar 必须分开写 coroutines .

    下面是我的脚本,演示如何实现 阿辛乔 具有 TKTITER 8.6 及其 进度条() Python3.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('<<ComboboxSelected>>', 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()
    

    从宏的角度来看,在python的异步事件循环中实现tkinter似乎有助于开发更好的并发gui应用程序。我自己也发现了这一点,希望这个附加的脚本可以帮助其他Tkinter用户学习。