1

I am popping up a custom dialog box using Tkinter. I am opening it from another Tkinter window.

root = Tk()
class ListDialog:
    def __init__(self, names, prompt):
        self.names = names
        self.sub_root = Tk()
        self.sub_root.title("Intovex")
        self.sub_root.iconbitmap("Icon.ico")
        self.myfont = Font(root=self.sub_root, family="Arial", size=8)
        self.sub_root.maxsize(320, 240)
        self.sub_root.wm_attributes("-topmost", True)
        self.sub_root.wm_attributes("-toolwindow", True)
        self.var = IntVar()
        label = Label(self.sub_root, text=prompt)
        label.pack(fill=X)
        c=1
        print(names)
        for i in names:
            print(i)
            r = Radiobutton(self.sub_root, text=i, variable=self.var, value=c, command=self.end)
            r.pack(anchor=W)
            c+=1
        self.var.set(1)
        button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0", fg="White", bd=0, width=12, pady=4, padx=4, height=1,font=self.myfont, highlightcolor="#A3C7F0")
        button.pack(side=BOTTOM)
        self.choice = names[0]

    def end(self):
        ch = self.var.get()
        print(str(ch))
        self.choice = self.names[ch - 1]

    def endit(self):
        self.sub_root.destroy()

    def ask(self):
        self.sub_root.mainloop()
var2 = StringVar()
def get_choice():
    list = ListDialog(["Test1", "Test2"], "Testing")
    list.ask()
    var2.set(str(list.choice))

label = Label(root, text="", textvariable=var2)
button = Button(root, text="Test", command=get_choice)
label.pack()
button.pack()
root.mainloop()

However, it works when it is run alone by directly instantiating the class and invoking ask() method. You may have seen that I have print statements everywhere in the code(it is for debugging) and I found where isn't it working

  • The print statement to print the names list parameter is printing the whole list correctly.
  • Inside the for-loop also it prints the elements in the names list correctly
  • When I click on a radio button it invokes the end() method. There it always prints the default value.
root = Tk()
class ListDialog:
    def __init__(self, names, prompt):
        self.names = names
        self.sub_root = Tk()
        self.sub_root.title("Intovex")
        self.sub_root.iconbitmap("Icon.ico")
        self.myfont = Font(root=self.sub_root, family="Arial", size=8)
        self.sub_root.maxsize(320, 240)
        self.sub_root.wm_attributes("-topmost", True)
        self.sub_root.wm_attributes("-toolwindow", True)
        self.var = IntVar()
        label = Label(self.sub_root, text=prompt)
        label.pack(fill=X)
        c=1
        print(names)
        for i in names:
            print(i)
            r = Radiobutton(self.sub_root, text=i, variable=self.var, value=c, command=self.end)
            r.pack(anchor=W)
            c+=1
        self.var.set(1)
        button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0", fg="White", bd=0, width=12, pady=4, padx=4, height=1,font=self.myfont, highlightcolor="#A3C7F0")
        button.pack(side=BOTTOM)
        self.choice = names[0]

    def end(self):
        ch = self.var.get()
        print(str(ch))
        self.choice = self.names[ch - 1]

    def endit(self):
        self.sub_root.destroy()

    def ask(self):
        self.sub_root.mainloop()
list = ListDialog(["Test1", "Test2"], "Testing")
list.ask()
print(list.choice)

But it works if I open it as a TopLevel widget. But then the main window doesn't wait till the popup windows returns the value(choice).

1 Answer 1

1

The problem with the code in the first snippet is because you're calling Tk() more that once within the tkinter application — it confuses the interface code and can cause a variety of problems, as you're finding out.

If you replace the call inside the __init__() method of the ListDialog class, with one to tk.Toplevel() instead, then your code will start working.

I also streamlined the for loop that creates the Radiobuttons by changing it so to use the built-in enumerate() function to automatically keep a count of the names. In conjunction with that, I made the initial value of the IntVar zero which is not one of the values that selecting one the radiobuttons will assign it. This is so when the ListDialog is first displayed, none of them will be selected. It also ensures that the end() callback function will get called whenever the user presses one of them, so your app will always be informed when that happens.

Note that, although I didn't change it, you shouldn't name a variable list because that hides the name of the built-in class by that name. In general you should avoid naming anything something that conflicts with an existing standard Python name.

from tkinter import *
from tkinter.font import Font

root = Tk()

class ListDialog:
    def __init__(self, names, prompt):
        self.names = names
#        self.sub_root = Tk()  # Wrong - don't call Tk() more than once.
        root.withdraw()  # Hide root window.
        self.sub_root = Toplevel()  # Create another top-level window.
        self.sub_root.title("Intovex")
#        self.sub_root.iconbitmap("Icon.ico")  # I don't have this file...
        self.myfont = Font(root=self.sub_root, family="Arial", size=8)
        self.sub_root.maxsize(320, 240)
        self.sub_root.wm_attributes("-topmost", True)
        self.sub_root.wm_attributes("-toolwindow", True)
        self.var = IntVar(value=0)  # Define and init value to one *not* produced by btns.
        label = Label(self.sub_root, text=prompt)
        label.pack(fill=X)
        print(names)
        for c, name in enumerate(names, start=1):
            print(c)
            r = Radiobutton(self.sub_root, text=c, variable=self.var, value=c,
                            command=self.end)
            r.pack(anchor=W)

        button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0",
                        fg="White", bd=0, width=12, pady=4, padx=4, height=1,
                        font=self.myfont, highlightcolor="#A3C7F0")
        button.pack(side=BOTTOM)
        self.choice = names[0]

    def end(self):
        ch = self.var.get()
        print(str(ch))
        self.choice = self.names[ch - 1]

    def endit(self):
        self.sub_root.destroy()
        root.deiconify()  # Reshow root window.

    def ask(self):
        self.sub_root.mainloop()

var2 = StringVar()

def get_choice():
    list = ListDialog(["Test1", "Test2"], "Testing")
    list.ask()
    var2.set(str(list.choice))

label = Label(root, text="", textvariable=var2)
button = Button(root, text="Test", command=get_choice)
label.pack()
button.pack()
root.mainloop()
Sign up to request clarification or add additional context in comments.

5 Comments

when I use TopLevel the main window doesn't wait for the toplevel to complete.
i used TopLevel but the main window does not wait till the TopLr=evel window completes
I heard you the first time. You can call root.withdraw() to hide the main window. Read the documentation for Toplevel() in my answer.
I modified my answer to show how to hide and re-show the main window.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.