Search code examples
pythontkintertkinter-menu

Python Single Choice Questions


def ask_choice_question(prompt, options):
    func = tkinter.Tk()
    v = tkinter.IntVar()
    v.set(-1)
    if(prompt):
        tkinter.Label(func, text=prompt).pack()
    for i, option in enumerate(options):
        tkinter.Radiobutton(func, text=option, variable=v, value=i).pack(anchor="w")
    tkinter.Button(func, text="Submit", command=func.destroy).pack()
    func.mainloop()
    print(v.get())
    if(v.get()== -1):
        return None
    print(options[v.get()])
    return options[v.get()]

By throughly looking through this website I have found something like the example above, and modified it slightly.

However, when calling func.destroy it seems the func doesn't return anything, neither do "print()"-s do anything. What do?

EDIT: Actually, something I might add, that this question is asked inside of another Tkinter window, and it actually outputs what needed when that window is closed.

EDIT2: It also may be useful to say, that the menu I'm getting looks something like this: http://prntscr.com/kg516z


Solution

  • The issue here is that there are several running Tk instances (i.e. several tcl interpreters) and the IntVar in the function does not belong to func but to the Tk instance created first. As a consequence, its value is not modified by the radiobuttons.

    I advise you to use only a single Tk instance, replacing func = tkinter.Tk() by func = tkinter.Toplevel() and using wait_window(func) (wait until func is destroyed) instead of mainloop():

    import tkinter
    
    def ask_choice_question(prompt, options):
        func = tkinter.Toplevel(root)
        v = tkinter.IntVar(root, -1)
        if(prompt):
            tkinter.Label(func, text=prompt).pack()
        for i, option in enumerate(options):
            tkinter.Radiobutton(func, text=option, variable=v, value=i).pack(anchor="w")
        tkinter.Button(func, text="Submit", command=func.destroy).pack()
        func.wait_window(func)
        print(v.get())
        if(v.get()== -1):
            return None
        print(options[v.get()])
        return options[v.get()]
    
    
    root = tkinter.Tk()
    tkinter.Button(root, text='Question',
                   command=lambda: print('returns ', ask_choice_question('Question?', ['Answer %i' % i for i in range(1, 5)]))).pack()
    root.mainloop()
    

    If you really want to keep the multiple Tk instances, then you need to specify the master of your IntVar and also to replace mainloop() by wait_window(func) (though I don't know why):

    import tkinter
    
    def ask_choice_question(prompt, options):
        func = tkinter.Tk()
        v = tkinter.IntVar(func, -1)
        if(prompt):
            tkinter.Label(func, text=prompt).pack()
        for i, option in enumerate(options):
            tkinter.Radiobutton(func, text=option, variable=v, value=i).pack(anchor="w")
        tkinter.Button(func, text="Submit", command=func.destroy).pack()
        func.wait_window(func)
        print(v.get())
        if(v.get()== -1):
            return None
        print(options[v.get()])
        return options[v.get()]
    
    root = tkinter.Tk()
    tkinter.Button(root, text='Question',
                   command=lambda: print('returns ', ask_choice_question('Question?', ['Answer %i' % i for i in range(1, 5)]))).pack()
    root.mainloop()