insolor / async-tkinter-loop

Asynchronous mainloop implementation for tkinter. Makes it possible to use async functions as event handlers and widget commands.
https://insolor.github.io/async-tkinter-loop/
MIT License
70 stars 3 forks source link

Dialogs (such as messagebox) block async mainloop #39

Open insolor opened 1 year ago

insolor commented 1 year ago

In the following code (pure tkinter) counter continues to work while messagebox dialog is shown:

import tkinter as tk
from tkinter import messagebox

root = tk.Tk()

def loop():
    counter.set(counter.get() + 1)
    root.after(1000, loop)

def show_message():
    messagebox.showinfo("Info", "Some info")

counter = tk.IntVar()
tk.Label(root, textvariable=counter).pack()

tk.Button(root, text="Start counter", command=loop).pack()
tk.Button(root, text="Show message", command=show_message).pack()

root.mainloop()

But with the corresponding code with async_tkinter_loop counter is paused when messagebox is shown:

import asyncio
import tkinter as tk
from tkinter import messagebox
from async_tkinter_loop import async_mainloop, async_handler

root = tk.Tk()

@async_handler
async def loop():
    while True:
        counter.set(counter.get() + 1)
        await asyncio.sleep(1.0)

def show_message():
    messagebox.showinfo("Info", "Some info")

counter = tk.IntVar()
tk.Label(root, textvariable=counter).pack()

tk.Button(root, text="Start counter", command=loop).pack()
tk.Button(root, text="Show message", command=show_message).pack()

async_mainloop(root)
ra1u commented 8 months ago

Thank you for this libraray: I got same issue.

My solution was to turn things around. Instead of running tkinter on top of async, it runs async loop inside tkinter.

We start tkinter and register callback 1ms later. In callback from tkinter we do blocking call

  1. register async task 5ms later to stop async loop
  2. we run loop "forewer"
  3. when we unblock 5ms later, we register same tkinter callback 1ms later

Proof of concept: https://gist.github.com/ra1u/8aa66c09e985dd2a9fe297bcd222ddf7

insolor commented 8 months ago

@ra1u interesting, I'll take a look later

ghmanoj commented 2 months ago

@insolor Any updates on this? Most probably the issue I am facing is also related to this. When I let a long running async function start and then drag the tkinter window the async function pauses and continues again only after I leave the window from dragging.

insolor commented 2 months ago

@ghmanoj this is probably a similar problem, but not the same one. In your case, if you need a non-stopping async function try to run the function in a separate thread, it should help (it won't help with dialogs though).

ra1u commented 2 months ago

If i recall correctly there is "bug" in MS Windows tkinter, that when you drag window around holding top menu bar, there is 100% cpu utilisation, because Windows is sending lot notifications on tkinter. Workaround is, that you replace default top bar, with tkinter widget including custom bar and custom X button on right. I am not sure that what is presented in current issue will help with this 100% cpu issue.