Search code examples
pythontkintertkinter-text

Should I use tkinter Toplevel for Text widget or not?


I am trying to use a tkinter Toplevel window with a Text box to post to a Text box in the main window, but I keep getting the following error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\someone\ouch\lib\tkinter\__init__.py", line 1705, in __call__
    return self.func(*args)
  File "<ipython-input-14-df46ff30dac4>", line 21, in GetFromDIALOG
    X = littletext.get("1.0","end")
NameError: name 'littletext' is not defined

In my script below, I call the Text widget in the Toplevel "littletext" and the Text widget in the main window "BIGTEXT". I also tried setting the Text boxes as StringVars, as most tutorials and scripts seem to suggest is right, but StringVars seem to work more on Entry (not Text) boxes.

I am wondering if I should use a custom class instead of a Toplevel, although I am not sure how I would handle that would work either.

My code is below.

import tkinter as tk

def openDIALOG(window):

    DIALOG = tk.Toplevel(window)
    DIALOG.title("DIALOG")
    DIALOG.geometry("400x300+100+100")

    littletext = tk.Text(DIALOG, height=15)
    littletext.pack()

    btn1 = tk.Button(DIALOG, text ="POST TO MAIN WINDOW", command=GetFromDIALOG)
    btn1.pack(side="left")

    btn2 = tk.Button(DIALOG, text ="EXIT", command = DIALOG.destroy)
    btn2.pack(side="left")


def GetFromDIALOG ():

    X = littletext.get("1.0","end")
    print(X)


window = tk.Tk()
window.title("main")

menubar = tk.Menu(window)
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label="RUN A DIALOG", command=lambda:openDIALOG(window))
filemenu.add_command(label="Exit", command=window.destroy)
menubar.add_cascade(label="FILE", menu=filemenu)
window.config(menu=menubar)

BIGTEXT = tk.Text(window)
BIGTEXT.pack()

BUTTON = tk.Button(window, text="Post", command=openDIALOG)
BUTTON.pack()

window.mainloop()

Solution

  • One way to fix the NameError, which is due it it being a local variable in the openDIALOG() function in your code, is to make it an attribute of instances of the class. This is better than using a global variable, which is another option, but global variables are bad.

    Stackoverflow is not intended to replace existing tutorials or documentation, so I'll describe only very briefly what the demo code below it doing. This topic would be covered much more thoroughly in most of the Python tutorials that are available online, if you took time to for them.

    So, here's how that might be done based on the code in your question. Notice how littletext is now self.littletext. This is because it has been turned into a class attribute of the class MY_DIALOG that's been defined, so it can be access through the self argument which will automatically be passed as the first argument of all the class' functions (which are know as class "methods").

    import tkinter as tk
    from tkinter.constants import *
    
    
    class MyDialog:
        def __init__(self, window):
            DIALOG = tk.Toplevel(window)
            DIALOG.title("DIALOG")
            DIALOG.geometry("400x300+100+100")
    
            self.littletext = tk.Text(DIALOG, height=15)
            self.littletext.pack()
    
            btn1 = tk.Button(DIALOG, text="POST TO MAIN WINDOW", command=self.GetFromDIALOG)
            btn1.pack(side="left")
    
            btn2 = tk.Button(DIALOG, text="EXIT", command=DIALOG.destroy)
            btn2.pack(side="left")
    
        def GetFromDIALOG(self):
            X = self.littletext.get("1.0", END)
            print(X)
    
    
    if __name__ == '__main__':
        window = tk.Tk()
        window.title("main")
    
        menubar = tk.Menu(window)
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(label="RUN A DIALOG", command=lambda: MyDialog(window))
        filemenu.add_command(label="Exit", command=window.destroy)
        menubar.add_cascade(label="FILE", menu=filemenu)
        window.config(menu=menubar)
    
        BIGTEXT = tk.Text(window)
        BIGTEXT.pack()
    
        BUTTON = tk.Button(window, text="Post", command=lambda: MyDialog(window))
        BUTTON.pack()
    
        window.mainloop()