Search code examples
pythontkintercomboboxgetvalue

Better understanding combox in tkinter-Python - How to get the selected value in tkinter


I'm trying to learn Python and I've been having a hard time trying to understand the following problem:

My goal is to collect the value selected by the user in a combobox. Just below, I put a snippet of the program that I'm putting together. However, a doubt arose: In the PageOne Class - selected function, why does the code "list_1.append (var.get ())" works and the code "list_1 = (var.get ())" doesn't work ???

Also, in this part of the code, any manipulation of the list_1 variable is also unavailable, pointing out the error: UnboundLocalError: local variable 'list_1' referenced before assignment.

Could you help me to better understand this problem?

from tkinter import *
from tkinter import ttk

list_1 = []

class Validation_Tool(Tk):

    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)
        container = Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)
        self.frames = {}

        for F in (PageOne, PageTwo):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame

            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame("PageOne")

    def show_frame(self, page_name):
        frame = self.frames[page_name]
        frame.tkraise()

    def quit(self):
        self.destroy()

class PageOne(Frame):

        # Combobox event handler. ADDED
    def selected(self, event, var):
        # list_1=(var.get())        # DOES NOT WORK !!!!!!!!!!!!!!!!!
        list_1.append(var.get())    # IT WORKS!!!!!!!!!!!!

    def button(self, list_1):
        print(list_1)

    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.controller = controller

        combobox_var = StringVar()  # ADDED.
        combobox = ttk.Combobox(self, values=list('abcde'),
                                textvar=combobox_var)  # For each Combobox. ADDED.
        combobox.grid(row=1, column=0)
        combobox.bind('<<ComboboxSelected>>',  # Bind event handler.           ADDED.
                        lambda event, var=combobox_var: self.selected(event, var))  # ADDED.

        quit_button = Button(self, text="Quit Program",
                            command=lambda: controller.quit())
        next_button = Button(self, text='Show options', command= lambda: self.button(list_1))

        quit_button.place(relx=0.98, rely=0.98, anchor=SE)
        next_button.place(relx=0.76, rely=0.98, anchor=SE)

class PageTwo(Frame):

    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.controller = controller


if __name__ == "__main__":
    root = Validation_Tool()
    root.geometry('400x300+430+250')
    root.title("Validation Tool")
    root.mainloop()

Solution

  • I think I fixed it:

    from tkinter import ttk
    import tkinter as tk
    
    
    list_1 = None
    
    class Validation_Tool(tk.Tk):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            container = tk.Frame(self)
            container.pack(side="top", fill="both", expand=True)
            container.grid_rowconfigure(0, weight=1)
            container.grid_columnconfigure(0, weight=1)
            self.frames = {}
    
            for FrameClass in (PageOne, PageTwo):
                page_name = FrameClass.__name__
                frame = FrameClass(parent=container, controller=self)
                self.frames.update({page_name: frame})
    
                frame.grid(row=0, column=0, sticky="news")
    
            self.show_frame("PageOne")
    
        def show_frame(self, page_name):
            frame = self.frames[page_name]
            frame.tkraise()
    
        def quit(self):
            self.destroy()
    
    
    class PageOne(tk.Frame):
        def __init__(self, parent, controller):
            super().__init__(parent)
            self.controller = controller
    
            self.combobox_var = tk.StringVar()
            self.combobox = ttk.Combobox(self, values=list("abcde"), textvar=self.combobox_var)
            self.combobox.grid(row=1, column=0)
            self.combobox.bind("<<ComboboxSelected>>", self.selected)
    
            quit_button = tk.Button(self, text="Quit Program", command=self.controller.quit)
            next_button = tk.Button(self, text="Show options", command=self.button)
    
            quit_button.place(relx=0.98, rely=0.98, anchor="se")
            next_button.place(relx=0.76, rely=0.98, anchor="se")
    
        def selected(self, event):
            # `list_1` need to be global as we are assigning a value to it
            global list_1
            list_1 = self.combobox_var.get()
    
        def button(self):
            print(list_1)
    
    
    class PageTwo(tk.Frame):
        def __init__(self, parent, controller):
            super().__init__(parent)
            self.controller = controller
    
    
    if __name__ == "__main__":
        root = Validation_Tool()
        root.geometry("400x300+430+250")
        root.title("Validation Tool")
        root.mainloop()
    

    All I had to do was change the binding to:

    self.combobox.bind("<<ComboboxSelected>>", self.selected)
    

    and the method to:

    def selected(self, event):
        # `list_1` need to be global as we are assigning a value to it
        global list_1
        list_1 = self.combobox_var.get()
    

    If you want to assign a value to a global object you need to tell python to treat the object as a global variable by using global list_1. I also made your code more pythonic.