0
votes

PROBLEM: tkinter's root.destroy() function freezes because of the last line in my program, code.interact(local=locals())

BACKGROUND: I am running my program from the terminal. It generates some text data, creates an instance of a class with said data, and drops me off in a python session where I can then run methods and test everything. This interpreter session is made possible with code.interact(local=locals())

From here, I run an "edit" method that allows the user to edit the generated text in a tkinter text box, and then push a button to save and quit. The button calls a function that saves a new txt file and then uses root.destroy() to end the tkinter mainloop. However, this freezes and does not actually close the window. Deleting the code.interact line at the end and calling the "edit" method from the program itself (ie NOT from the interpreter) fixes the problem.

In my troubleshooting, I have found that tkinter does not always play well when called from a python interpreter, which code.interact does.

QUESTION: Is there any way to preserve tkinter functionality from within a python interpreter? Is that actually my problem, or is it more to do with code.interact?

UPDATE: It seems that the issue relates to tkinter's mainloop running in tandem with code.interact's own loop. Two infinity loops competing with one another. Deleting either line seems to fix the issue. In this case, mainloop is not necessary for my GUI as it has no need to interactively update it's appearance.

Sample code below (then run project_instance.edit() from the python interpreter that follows)

import tkinter as tk
import code

class Project():
    def __init__(self, text):
        self.script = text

    def edit(self):
        root = tk.Tk()
        S = tk.Scrollbar(root)
        T = tk.Text(root, height=60, width=60, undo=True, insertbackground="white", wrap=tk.WORD, font=("Courier", 30), background="black", foreground="grey")
        S.pack(side=tk.RIGHT, fill=tk.Y)
        T.pack(side=tk.LEFT, fill=tk.Y)
        S.config(command=T.yview)
        T.config(yscrollcommand=S.set)
        quote = self.script
        T.insert(tk.END, quote)

        def file_save():
            text2save = str(T.get(1.0, tk.END)).strip() # starts from `1.0`, not `0.0`
            with open("script_text.txt", "w") as s:
                s.write(text2save)
                uri = s.name
            root.destroy()
        b = tk.Button(root, height=5, width=10, font=("Courier", 44), text="Save", command=file_save, background="black", foreground="grey").pack()
        root.mainloop()


sample_text = "This is sample text."
project_instance = Project(sample_text)
code.interact(local=locals())
1
code is a standard python package. docs.python.org/3/library/code.html put simply, after calling your python program from the terminal, it "drops you off" inside the python session. It is certainly part of the problem.dv151
OK…then please provide a runnable minimal reproducible example.martineau
@martineau just added some sample code, thanks for your helpdv151
Calling root.destroy() in the file_save() function destroys the root and all its children. You should be able to exit the mainloop() more gracefully using root.quit().martineau
@martineau actually I have wondered what's the difference between quit() and destroy() methods, sometimes quit() does not work properly, so I use destroy()Cool Cloud

1 Answers

0
votes

UPDATE: It seems that the issue relates to tkinter's mainloop running in tandem with code.interact's own loop. Two infinity loops competing with one another. Deleting either line seems to fix the issue. In this case, mainloop is not necessary for my GUI as it has no need to interactively update it's appearance.

Another fix is to destroy the root using the "after" method. root.after(1, root.destroy()) which seems to be able to "escape" the competing loops as well.

I imagine threading would also work to isolate the loops from each other. More good info in this article:

https://gordonlesti.com/use-tkinter-without-mainloop/#:~:text=The%20method%20mainloop%20has%20an,is%20waiting%20for%20some%20events.