asyncio and tkinter (was: What should go to stdout/stderr and why Python logging write everything to stderr?)
Thomas Passin
list1 at tompassin.net
Thu Jan 5 22:44:40 EST 2023
On 1/5/2023 7:52 PM, Stefan Ram wrote:
> Thomas Passin <list1 at tompassin.net> writes:
>> On 1/5/2023 4:24 PM, Stefan Ram wrote:
>>> You often can replace threads in tkinter by coroutines using
>>> asyncio when you write a replacement for the mainloop of
>>> tkinter that uses asyncio. Now, try to read only the official
>>> documentation of asyncio and tkinter and figure out only from
>>> this how to get such a program to work!
>> Cool! Can we have a link, please?
>
> I do not post links, but tried to create a minimal example app.
Thanks! That's not exactly obvious, is it? Congratulations on getting
it working.
> import asyncio
> import logging
> import tkinter
>
> # This program was tested on Windows, it is experimental.
> # It should open a tkinter root window.
> # Ctrl-E should create a task. You should be able to create
> # a new task even while one task is already running.
> # Each task will end after about 10 seconds.
> # Ctrl-X should exit the process.
> # Or, use the menu "Action".
>
> # I (Stefan Ram) designed and wrote the program, but especially
> # the code in "app_async_mainloop" and "terminate_tasks" was
> # written following educational material from the World-Wide Web.
>
> class app_class( tkinter.Tk ):
> def __init__( self, *args, **kwargs ):
> self.is_running = 1_000_000
> super().__init__( *args, **kwargs )
> root = self
> root.protocol( "WM_DELETE_WINDOW", self.app_exit_macro )
> async def app_example_task( self, example_label ):
> try:
> for i in range( 10 ):
> example_label[ 'text' ]=str( i )
> await asyncio.sleep( 1 )
> except asyncio.CancelledError:
> pass
> example_label.destroy()
> def app_example_macro( self ):
> root = self
> example_label=tkinter.Label( root )
> example_label.pack()
> asyncio.get_running_loop().create_task\
> ( self.app_example_task( example_label ))
> root = self
> def terminate_tasks( self ):
> loop = asyncio.get_running_loop()
> pending = asyncio.all_tasks( loop=loop )
> label_tasks = []
> for task in pending:
> if 'app_example_task' in str( task ):
> label_tasks.append( task )
> task.cancel()
> group = asyncio.gather( *label_tasks, return_exceptions=True )
> try:
> loop.run_until_complete( group )
> except AssertionError:
> # ignoring an assertion error on Windows I do not understand
> pass
> def app_exit_macro( self ):
> self.terminate_tasks()
> self.is_running = 99
> root = self
> root.destroy()
> def app_add_basemenu( self, example=False ):
> root = self
> menubar = tkinter.Menu( root )
> menu = tkinter.Menu( menubar, tearoff=0 )
> name = "Actions"; menu = tkinter.Menu( menubar, tearoff=0 )
> if example:
> text = "Example";
> callback = self.app_example_macro; menu.add_command\
> ( label=text, underline=0, command=callback,
> accelerator="Control-e" );
> root.bind\
> ( "<Control-e>", lambda event, callback=callback:callback() )
> text = "Exit";
> callback = self.app_exit_macro
> menu.add_command\
> ( label=text, underline=1,
> command=callback, accelerator="Control-x" )
> root.bind\
> ( "<Control-x>", lambda event,callback=callback: callback() )
> menubar.add_cascade( label=name, underline=0, menu=menu )
> root.config( menu=menubar )
> async def app_async_mainloop( self ):
> root = self
> root.willdispatch()
> root.tk.dooneevent( tkinter._tkinter.DONT_WAIT )
> while self.is_running > 0:
> if self.is_running < 1_000_000: self.is_running -= 1
> try:
> await asyncio.sleep\
> ( tkinter._tkinter.getbusywaitinterval() / 10_000 )
> root.tk.dooneevent( tkinter._tkinter.DONT_WAIT )
> root.update()
> except tkinter.TclError:
> break
> async def app_async_main( self ):
> try:
> await self.app_async_mainloop()
> except asyncio.CancelledError:
> logging.debug( f'asyncio.CancelledError' )
> def app_async_run( self ):
> asyncio.run( self.app_async_main() )
>
> app = app_class()
> app.app_add_basemenu(example=True)
> app.app_async_run()
>
>
>
More information about the Python-list
mailing list