3

Let's say that I have this tkinter script:

import tkinter as tk

def callback():
   new_root = tk.Tk()
   new_root.mainloop()
   print("Done")

root = tk.Tk()

button = tk.Button(root, text="Click me", command=callback)
button.pack()

root.mainloop() 

From my understanding of tkinter, when I press the button, a new window and tcl interpreter should be created. While running callback, the main window (root) shouldn't be updated so it should be unresponsive. new_root.mainloop() is a while True loop that runs until the second window is closed. Therefore, when I press the button it should create a new window, call .mainloop() on it and that should make the main window unresponsive. The problem is that that doesn't happen. The main window is responsive even though code execution is stuck inside new_root.mainloop().

Also closing the second window doesn't print "Done" until the rest of the tkinter windows are closed. Why does that happen?

I looked at the source code for tkinter and _tkinter but I couldn't find anything useful. I have Python 3.7.9, tcl 8.6

TheLizzard
  • 7,248
  • 2
  • 11
  • 31
  • Hmmmm, maybe each `Tk` have its own thread? Check the comment on [this](https://stackoverflow.com/a/36141286/13382000) answer – Delrius Euphoria Apr 08 '21 at 15:46
  • @CoolCloud I saw that there were a lot of comments suggesting that each window creates their own threads but that still wouldn't explain the second problem: *"closing the second window doesn't print "Done" until the rest of the tkinter windows are closed"* – TheLizzard Apr 08 '21 at 15:53
  • Yea I see that. Did you notice, when you remove `new_root.mainloop()`, it still pops up the window? Because just one `mainloop()` is needed for an entire code, I suppose. But that does not explain why it does not print 'Done'. Maybe because even when you close second window, the `mainloop()` is still going on there? – Delrius Euphoria Apr 08 '21 at 15:58
  • 1
    @CoolCloud That raises even more questions :D. Don't all `Tk()` instances have their own tcl interpreter? Shouldn't that mean that if I don't call `new_root.mainloop()` or `new_root.update()` the new window shouldn't be displayed. Why is it responsive when I remove the `new_root.mainloop()`? – TheLizzard Apr 08 '21 at 16:04
  • put `print("Done") between `new_root` and `new_root,mainloop()` – bangKok Apr 08 '21 at 16:07
  • 1
    `mainloop()` has an argument which default to 0 which means that `mainloop()` will exit if there is no instance of `Tk()`. Therefore when you close one of the instance, there is still another instance running, so the `mainloop()` will not exit until you close the remaining instance. Use `new_root.mainloop(1)` instead, then you will see "Done" when you close one of the instance. – acw1668 Apr 08 '21 at 16:07

1 Answers1

4

Though you're running a subsidiary event loop (really don't do that!) it still shares the same registry of event handlers as the outer loop, so events coming in are handled in the inner loop just as in the outer one. (There's a common piece of low-level event handling code that reaches deep into the OS to do the event processing efficiently. That code, the notifier, is stuff that very few people should ever touch; it's tricky because it merges some really weird and disparate event sources while also working around a bunch of strange bugs on some platforms.) The event_loop method returns once all windows are deleted. It literally calls the low level event processing engine (the API call is Tcl_DoOneEvent()) with appropriate flags, and does that in a while loop (until the number of existing windows drops below 1; that's exactly what it is waiting for). This is why you probably shouldn't count on it terminating and absolutely shouldn't nest it in a GUI callback.

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • I think there are other API calls that might work better for subsidiary event loops, but the usual rule is “try to not write those at all”. – Donal Fellows Apr 08 '21 at 16:05
  • I never would have guess that having 2 `Tk()` windows would have that effect. – TheLizzard Apr 08 '21 at 16:24
  • 3
    @TheLizzard: This is one of the reasons why you should avoid having more than one instance of `Tk`: the behavior isn't obvious or intuitive and there can be unexpected side effects. – Bryan Oakley Apr 08 '21 at 18:58
  • I _think_ you can have multiple instances of Tk as long as they're in different threads _and stay in those threads._ – Donal Fellows Apr 09 '21 at 08:13