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