Python 将asyncio与tkinter及其';s ttk.progressbar小部件:如何强制异步IO任务结束?

Python 将asyncio与tkinter及其';s ttk.progressbar小部件:如何强制异步IO任务结束?,python,tkinter,progress-bar,python-asyncio,Python,Tkinter,Progress Bar,Python Asyncio,下面是我使用asyncio操作tkinter及其ttk.Progressbar()小部件的脚本。我在参考了这些引用(,)之后得出了它。我让ProgesBar小部件开始工作。但我似乎无法取消(停止)负责更新tkinter.Tk()的asyncio任务对象,该对象是我创建的,用于替换通常的Tk()事件mainloop()。因此,在Tk()窗口被销毁后,我看不到命令提示符或>提示符如何或应该终止任务对象root.update\u task?我正在使用python 3.6。我希望loop.shutdow

下面是我使用
asyncio
操作
tkinter
及其
ttk.Progressbar()
小部件的脚本。我在参考了这些引用(,)之后得出了它。我让ProgesBar小部件开始工作。但我似乎无法取消(停止)负责更新
tkinter.Tk()
asyncio
任务对象,该对象是我创建的,用于替换通常的
Tk()
事件
mainloop()
。因此,在Tk()窗口被销毁后,我看不到命令提示符或
>
提示符如何或应该终止任务对象
root.update\u task
我正在使用python 3.6。我希望
loop.shutdown\u asyncgens()
可以结束取消的任务,但事实并非如此为什么不起作用?谢谢

import tkinter as tk
import tkinter.ttk as ttk
import tkinter.messagebox as tkMessageBox

import time
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 ):
        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)

        #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):
        print( '\ndef connect_esp( self, event ):' )

        async def dojob( loop, start_time, duration=1 ):
            print( '\nasync def dojob( loop, end_time):' )
            while True:
                duration = 5 #seconds
                t = loop.time()
                delta = t - start_time
                print( 'wait time = {}'.format( delta ) )
                if delta >= duration:
                    break
                await asyncio.sleep( 1 )
            return True

        async def trackjob( loop ):
            print( '\nasync def trackjob( loop ):' )
            print( 'Job: STARTED' ) 
            start_time = loop.time()
            self.sp_pbar.start( 50 )
            self.sp_pbar.update_idletasks()

            result = await dojob( loop, start_time )
            print( 'result = ', result, type(result) )

            if result:
                self.sp_pbar.stop()
                self.sp_pbar.update_idletasks()
                print( 'Job: ENDED' ) 
                return True
            return False

        try:
            future = self.loop.create_task( trackjob( self.loop ) )
            print( 'future = ', future, type(future))
        except syncio.CancelledError as err:
            print( '_connect_esp(): future is cancelled.' )
            raise
        except asyncio.InvalidStateError as err:
            print( '_connect_esp(): The operation is not allowed in this state..' )
            raise
        except asyncio.TimeoutError as err:
            print( '_connect_esp(): The operation exceeded the given deadline..' )
            raise
        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:
        print( '\nasync def tk_update( self, interval ):' )
        if "application has been destroyed" not in err.args[0]:
            raise
    except asyncio.CancelledError as err:
        print( '\nasync def tk_update( self, interval ):' )
        print('Request to cancel tk_update_task received but may not be done.')
        await asyncio.sleep( interval )
    print( '\nasync def tk_update( interval ):' )
    print( '0 Cancelled = ', root.update_task.cancelled() )
    print('END of def tk_update')


def ask_quit( root, interval=INTERVAL ):
    '''Confirmation to quit application.'''
    print( '\ndef ask_quit( self ):' )
    task=root.update_task
    if tkMessageBox.askokcancel( "Quit","Quit?" ):
        task.cancel()
        print( '1 Cancelled = ', task.cancelled() )
        root.destroy() #Destroy the Tk Window instance.
    print( '2 Cancelled = ', task.cancelled() )


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 ) ) 

    try:
        loop.run_forever()
        print('after loop.run_forever()')
    finally:
        loop.run_until_complete( loop.shutdown_asyncgens() )
        loop.close()
    print( 'Is Loop closed = ', loop.isclosed() )


if __name__ == '__main__':
    main()
将tkinter作为tk导入
将tkinter.ttk导入为ttk
将tkinter.messagebox作为tkMessageBox导入
导入时间
导入异步
间隔=0.05秒
类应用程序(ttk.Frame):
定义初始值(自、主、循环,间隔=0.05,*args,**kw):
super()
self.master=master
self.loop=循环
self.\u set\u style()
self.\u创建\u小部件()
定义设置样式(自):
打印(“\ndef\u set\u style(self):”)
self.style=ttk.style()
self.style.configure('App.TFrame',background='pink')
self.style.configure('sp.TFrame',background='浅绿色〕
def_创建_小部件(自):
打印('\ndef\u创建\u小部件(自身):')
self.sp_frame=ttk.frame(self,style='sp.TFrame')
self.sp_frame.grid(行=0,列=0)
#sp_框架小部件
self.sp_label1=ttk.Label(self.sp_frame,text='sp(s):'))
self.sp_combox=ttk.Combobox(
self.sp_frame,state=“readonly”,value=['a','b','c'])
self.sp\u combox.bind(“”,self.\u connect\u esp)
self.sp_pbar=ttk.Progressbar(self.sp_帧,长度=200,
模式='不确定',
方向=tk.水平,)
self.sp_label1.grid(行=0,列=0)
self.sp_combox.grid(行=0,列=1,padx=[10,0])
self.sp_pbar.grid(行=1,列=0,列span=2,sticky='ew')
定义连接esp(自身、事件):
打印(“\ndef connect\u esp(自,事件):”)
异步def dojob(循环,开始时间,持续时间=1):
打印('\n同步定义dojob(循环,结束时间):')
尽管如此:
持续时间=5秒
t=循环时间()
delta=t-开始时间
打印('wait time={}'。格式(增量))
如果增量>=持续时间:
打破
等待asyncio.sleep(1)
返回真值
异步def跟踪作业(循环):
打印('\n同步定义跟踪作业(循环):')
打印('作业:已启动')
开始时间=循环时间()
自启动(50)
self.sp_pbar.update_idletasks()
结果=等待dojob(循环、开始时间)
打印('result=',result,type(result))
如果结果为:
self.sp_pbar.stop()
self.sp_pbar.update_idletasks()
打印('作业:已结束')
返回真值
返回错误
尝试:
future=self.loop.create_任务(trackjob(self.loop))
打印('future=',future,type(future))
除syncio.CancelleError作为错误外:
打印(“\u connect\u esp():未来已取消”。)
提升
除asyncio.InvalidStateError作为错误外:
打印(“\u connect\u esp():此状态下不允许该操作…”
提升
除asyncio.TimeoutError作为错误外:
打印(“\u connect\u esp():操作超过了给定的截止日期…”)
提升
除例外情况外:
提升
异步def tk_更新(根,间隔=间隔):
打印('\n同步定义tk_更新(间隔):')
尝试:
尽管如此:
root.update()#tk update
等待异步睡眠(间隔)
除tk.TclError作为错误外:
打印('\n同步定义tk_更新(自身,间隔):')
如果错误参数[0]中没有“应用程序已销毁”:
提升
除asyncio.CancelleError作为错误外:
打印('\n同步定义tk_更新(自身,间隔):')
打印('已收到取消tk_更新_任务的请求,但可能未完成')
等待异步睡眠(间隔)
打印('\n同步定义tk_更新(间隔):')
打印('0 Cancelled=',root.update\u task.Cancelled())
打印(“def tk_更新结束”)
def ask_quit(根,间隔=间隔):
“确认退出申请”
打印(“\n退出请求(自我):”)
task=root.update\u任务
如果tkMessageBox.askokcancel(“退出”、“退出?”):
task.cancel()
打印('1已取消=',task.Cancelled())
root.destroy()#销毁Tk窗口实例。
打印('2已取消=',task.Cancelled())
def main():
loop=asyncio.get\u event\u loop()
root=tk.tk()
根几何体('300x100+0+24')
rowconfigure(0,权重=1)
root.columnconfigure(0,权重=1)
root.update\u task=loop.create\u task(tk\u update(root))
app=app(根,循环)
app.grid(行=0,列=0,sticky='nsew')
#root.mainloop()#不实现;这被跑步所取代
#tk的update()方法在名为loop的异步IO循环中。
#请参阅tk_update()方法和root.update_任务。
#在Tk窗口实例被销毁之前,告诉它要做什么。
协议(“WM_删除_窗口”,
lambda:请求退出(根)
尝试:
loop.run_forever()
打印('after loop.run_forever()'))
最后:
loop.run_直到_完成(loop.shutdown_asyncgens()
    loop.run_until_complete(root.update_task)
    #loop.run_forever()
loop.run_forever()
    Run the event loop until stop() is called.