0

I know this question has been asked numerous times before, but they all use people's code and classes/various functions, etc, so its been a bit difficult to try and follow/understand what's going on. I wanted a more simpler setup to understand what's happening here and how it all works.

When you thread a function, once the function is complete, the thread is closed:

import threading
def fun():
    x=0
    while x<1000:
        x+=1
threading.Thread(target=fun).start()

So I decided to take this idea one step further with Tkinter.

import tkinter as tk
from tkinter import *
from tkinter import messagebox as mb
import threading

def fun():
    x=0
    while x<10000900:
        x+=1
        if x == 50:
            print(x)


def main():
    while True:
        fun()
        if mb.askquestion('Replay', 'Would you like to play another round?') != 'yes':
            root.destroy()
            break

root = tk.Tk()
root.geometry('600x600')
threading.Thread(target=main).start()
root.mainloop()

The idea being, that when the main function was broken (based on the user response), the thread would also close automatically. Additionally, so would the Tkinter window. However, when the above script is run, the Tkinter window does close, but the terminal still indicates something, which I assume is the thread. What I don't understand is why in the first case where I use threading, the program ends properly, whereas the 2nd one doesn't.

samman
  • 559
  • 10
  • 29
  • i dont know if this is related, but as soon as i executed your second code, my internet adapter stopped responding and IDE got stuck as well – Delrius Euphoria Jul 26 '20 at 20:27
  • 1
    @CoolCloud I haven't experienced any of that, just the program not ending (i.e. its like its stuck in an infinite loop) – samman Jul 26 '20 at 20:59
  • See this [solution](https://stackoverflow.com/a/65607405) using sub process rather than threads – Nde Kong Sep 05 '22 at 22:55

1 Answers1

2

When you execute root.destroy() you kill the main thread (mainloop) as well as the extra thread running your main funtion. That way the break statement never gets executed - the thread that would execute that statement is abruptly ended.

If you replace root.destroy() with root.after(10, root.destroy) the program acts as expected. This is because you're delaying the call to root.destroy() by some time (10 ms). This delay allows the break statement to be executed since the thread is still alive for 10 ms.

Wojciech
  • 332
  • 1
  • 7
  • Bit confused how this works. So does this mean after the function is broken it executes root.destroy()? Also, I found out root.quit() also works (in addition to your your answer (although I don't quite understand why). – samman Jul 26 '20 at 23:58
  • @samman yes, root.destroy() is executed only after the loop is broken. This is done by delaying the call to root.destroy using root.after(). I've edited my answer to make it clearer. As to why root.quit() also works - I'm not entirely sure here but it seems root.quit() doesn't kill the mainloop thread (and all the other threads with it) and because of this the thread that runs 'main' is still around to break the loop (the break statement gets executed). I found this helpful: https://stackoverflow.com/a/15577605/7520085 – Wojciech Jul 27 '20 at 01:42
  • 1
    Why don't just move the `root.destroy()` outside the while loop, i.e. the end of `main()` and make the thread a daemon thread. – acw1668 Jul 27 '20 at 01:55
  • @Wojciech that link was great, thank you! And yeah, seems like root.quit() does exactly what I wanted (to only close the Tkinter window, but keep the rest of the script going) – samman Jul 27 '20 at 02:34
  • @acw1668 because I hadn't even heard of a deamon thread until your comment lol. – samman Jul 27 '20 at 16:42