0

[Edited] I am trying to destroy a Toplevel object from inside its widget's callback, but it seems that it cannot be destroyed until the callback function is done running. Below is the essence of my script:

from tkinter import *
from time import sleep
import gnupg
w = Tk()
t = Toplevel()
lbl = Label(t, text="blah blah")
lbl.grid(row=0, column=0)
lbl.bind("<Button-1>", func_a)

def func_a(event):
   event.widget.master.destroy()
   gpg = gnupg.GPG()
   plaindata = b'Some data'
   encrdata = plaindata
   for i in range(20):
      encrdata = gpg.encrypt(encrdata,
         symmetric=True,
         passphrase='something', 
         recipients=None).data
   print("func_a is done")
w.mainloop()

As you can see, I am using a gpg symmetric encryption 20 times, which takes ~20 seconds. What I expected to see when clicking on Label (object "lbl") is the entire Toplevel window (object "t") immediately disappearing and then 20 seconds later message "func_a is gone" being printed in the terminal. Instead, Toplevel window became unresponsive for 20 seconds (I could still move it but all of its widgets kinda froze) before finally disappearing the same time the aforementioned message was printed.

Could you please explain why the parent Toplevel did not get destroyed immediately? Does it have to do with the function being called as a widget's callback? And how can I force the parent window to be killed before other content in the callback function is completed?

Proto Ukr
  • 492
  • 4
  • 13
  • Why don't just `sleep(20)` instead of repeating `sleep(1)` 20 times? – CoderCharmander Dec 14 '19 at 08:00
  • The repeated calls to `sleep()` are preventing `mainloop()` from executing. Call `event.widget.master.update_idletasks()` instead. Also, if you want to delay in a tkinter application, use the universal widget method [`after()`](https://web.archive.org/web/20190222214221id_/http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/universal.html) instead. – martineau Dec 14 '19 at 08:31
  • ***"parent `window.destroy()` before ... callback ... is completed? "***: You can't, as a `callback` is part of the `tkinter.mainloop()` which have to do the **cleanup** on `.destroy()`. You have to *unbind* from the `mainloop` using `Thread`. – stovfl Dec 14 '19 at 08:54
  • @martineau Perhaps I didn't explain myself clearly. This is just a simple testcase to replicate the problem. In my real script, I am utilizing a few loops that don't have sleep() in them but still take time to complete. Let's say I'm hashing stuff many times, and it takes 10-15 seconds to complete. Until it is done, the parent window can't be destroyed or even modified — it just sits there frozen for 10-15 seconds. That's what the question is about. – Proto Ukr Dec 14 '19 at 10:58
  • @stovfl, that sounds interesting. Unfortunately, my Python skills are not up to the task, so could you please provide a very simple example to demonstrate how it works? – Proto Ukr Dec 14 '19 at 10:59
  • 1
    None of that changes anything I said — _everything_ has to occur while `mainloop()` is running in a tkinter app or it will freeze. See my answer to the quesiton [Freezing/Hanging tkinter Gui in waiting for the thread to complete](https://stackoverflow.com/questions/53696888/freezing-hanging-tkinter-gui-in-waiting-for-the-thread-to-complete). – martineau Dec 14 '19 at 11:03

1 Answers1

1

OK, thanks to @martineau's pointers, I found a solution -- a very easy one at that. I just invoked method update() from the Toplevel object whenever I wanted any changes to this object to take effect. This worked even if the preceding change was destroying this very object. Thanks to all who provided their comments and feedback.

Proto Ukr
  • 492
  • 4
  • 13