Hans Lado
Hans Lado

Reputation: 1

Why does my Tkinter GUI freeze when the user presses a button (only when I use threading)?

Ok, continuation of my previous question. I'm making a GUI in Tkinter where, when you press a button, another GUI is generated, where you put in some text and you press a button there and it generates some text inside a Word document. I need the user to fill out the text inputs before defining my variables, though, so that the variables aren't blank. So I'm using the threading module to do that. But, when I use the threading module, or any other way to make Python wait, the first Tkinter GUI freezes after you press a button.

import threading
import tkinter as tk

ra = threading.Event()

def function2():
    import docx
    from docx import Document
    doc = Document("template.docx")
    ra.set()
    para_1 = doc.add_paragraph(variable)
    para_1.add_run(" foo.")
    para_1.add_run(variable2)
    para_1.add_run(" beep boop.")
    doc.save("example.docx")

def function1():
    master = tk.Tk()
    e1 = tk.Entry(master)
    e1.grid(row=0, column=1)
    e2 = tk.Entry(master)
    e2.grid(row=1, column=1)
    tk.Button(master, text="Generate", width=15, command=function2).grid(row=1)
    ra.wait()
    global variable
    variable = (e1.get())
    global variable2
    variable2 = (e2.get())

r = tk.Tk()
b1 = tk.Button(r, text='example', width=25, command=function1)
b1.pack(padx=5, pady=15) 
r.mainloop()

So I expected that this would run as normal, giving me a document with (not empty) variables and pre-defined strings. Problem is, when you press example, the Tkinter GUI freezes and doesn't give you another GUI like I expect.

Upvotes: 0

Views: 192

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 386332

Everything is running in a single thread because you haven't explicitly run any of your code in a second thread. Your threading.Event object will never be set, so your program will wait forever at the point where you call ra.wait.

I need the user to fill out the text inputs before defining my variables, though, so that the variables aren't blank.

The way to wait for a dialog to be filled out is to use the tkinter method wait_window which will wait for a window to be destroyed before returning. Your button needs to call a function that gets the value from the dialog and then destroy the dialog, which will cause wait_window to return and allow your code to continue.

Also, you need to use Toplevel, not Tk to create additional windows.

Here is one way to use wait_window, based on the code in your question:

import tkinter as tk

def function2():
    print('variable:', variable)
    print('variable2:', variable2)

def function1():
    def close_dialog():
        global variable, variable2
        variable = e1.get()
        variable2 = e2.get()
        master.destroy()

    master = tk.Toplevel()
    e1 = tk.Entry(master)
    e2 = tk.Entry(master)
    b = tk.Button(master, text="Generate", width=15, command=close_dialog)

    e1.grid(row=0, column=1)
    e2.grid(row=1, column=1)
    b.grid(row=1)

    master.wait_window(master)
    function2()

r = tk.Tk()
b1 = tk.Button(r, text='example', width=25, command=function1)
b1.pack(padx=5, pady=15)
r.mainloop()

Upvotes: 2

Related Questions