Programatically create multiple instances of CustomTkinter Combobox Python

112 views Asked by At

New to python and trying to create an app with customtkinter. The app lets users pick values from different drop-downs and generates codes based on the chosen value.

Updated per comment from Mike-SMT:

import customtkinter as ctk
ctk.set_appearance_mode('light')

# define fonts
general_labels = button_text = ('Segoe UI', 18)
frame_heading = ('Segoe UI Semibold', 20)
small_label = ('Segoe UI', 12)

class FrameSpcl(ctk.CTkFrame): 
    def __init__(self, parent, rownum, colnum, frame_text, attributes_dict):
        super().__init__(parent)

        ctk.CTkLabel(master=self, text=frame_text, font = frame_heading).pack(padx=5, pady=5)
        self.configure(width=200, height=500, corner_radius=10, 
                       bg_color='transparent', fg_color='#d8ebca')
        
        # Create an empty list to store all ComboBox instances 
        combobox_list = []

        for attribute in list(attributes_dict.keys()):
            ctk.CTkLabel(master=self, text='Select '+attribute.replace('_', ' ').lower(), font=small_label, justify='left').pack(padx=10,pady=10)
            combobox_list.append(ctk.CTkComboBox(master=self, values=list(attributes_dict[attribute].keys())).pack(padx=20, pady=(0,20)))
        
        ctk.CTkButton(master=self, text='Generate Code', font=general_labels).pack(padx=20, pady=20) #, command=classFunc)
        code_lab = ctk.CTkLabel(master=self, text='Click to generate Code', font = frame_heading).pack(padx=5, pady=5)

        code_val = ''

        def classFunc():
            attributes = list(attributes_dict.keys())
            for i in range(0,len(combobox_list)):
                code_val += ' '+str(attributes_dict[attributes[i]].get(combobox_list[i].get()))
            print(code_val)
            return(code_val)
        
        # code_lab.configure(text='PN: '+classFunc())

        self.grid(row=rownum, column=colnum, padx=(20,20), pady=(20,20), sticky = 'n')

app = ctk.CTk()
app.geometry('950x700')
app.title('Car Code Generator')

car = dict(
    model = dict(zip(
        ['S', '3', 'X', 'Y'], 
        list(range(0,4))
    )), 
    trim = dict(zip(
        ['Standard', 'Long Range', 'All Wheel Drive', 'Sports'],
        ['RWD', 'RWDLR', 'AWD', 'SPRTS']
    ))
)

sale_terms = dict(
    lease = dict(zip(
        ['5 year', '6 year', '7 year', 'None'],
        ['5YL', '6YL', '7YL', '000']
    )), 
    insurance = dict(zip(
        ['base', '3 year enhanced', '5 year enhanced', 'None'], 
        list(range(0,4))
    )), 
    rewards = dict(zip(
        ['None', 'Bronze', 'Silver', 'Gold', 'Platinum', 'Diamond'], 
        list(range(0,6))
    ))
)

FrameSpcl(parent=app, rownum=0, colnum=0, frame_text='Car Options', attributes_dict=car)

FrameSpcl(parent=app, rownum=0, colnum=1, frame_text='Sale options', attributes_dict=sale_terms)

app.mainloop()

I'm not sure how to update the value for the label when the button is pressed. I tried using configure(text=pn) but it didn't work (commented out in the above code).

The rest of the app appears exactly how I want it to:

enter image description here

1

There are 1 answers

2
acw1668 On BEST ANSWER

You have appended results of .pack() to combobox_list which are all None. Also it is better to use instance method instead of nested function.

Below is the modified FrameSpcl class:

class FrameSpcl(ctk.CTkFrame):
    def __init__(self, parent, rownum, colnum, frame_text, attributes_dict):
        super().__init__(parent)
        # save attributs_dict for later use
        self.attributes_dict = attributes_dict

        ctk.CTkLabel(master=self, text=frame_text, font = frame_heading).pack(padx=5, pady=5)
        self.configure(width=200, height=500, corner_radius=10,
                       bg_color='transparent', fg_color='#d8ebca')

        # Create an empty list to store all ComboBox instances
        self.combobox_list = []  ### changed to instance variable

        for attribute in list(attributes_dict.keys()):
            ctk.CTkLabel(master=self, text='Select '+attribute.replace('_', ' ').lower(), font=small_label, justify='left').pack(padx=10,pady=10)
            ### split to two lines
            cb = ctk.CTkComboBox(master=self, values=list(attributes_dict[attribute].keys()))
            cb.pack(padx=20, pady=(0,20))
            # store the combobox to list
            self.combobox_list.append(cb)

        # added command=self.classFunc
        btn = ctk.CTkButton(master=self, text='Generate Code', font=general_labels, command=self.classFunc)
        btn.pack(padx=20, pady=20)
        # changed to self.code_lab
        self.code_lab = ctk.CTkLabel(master=self, text='Click to generate Code', font = frame_heading)
        self.code_lab.pack(padx=5, pady=5)

        self.grid(row=rownum, column=colnum, padx=(20,20), pady=(20,20), sticky = 'n')

    # changed to class method
    def classFunc(self):
        attributes = list(self.attributes_dict.keys())
        code_val = ''
        for i in range(0,len(self.combobox_list)):
            code_val += ' '+str(self.attributes_dict[attributes[i]].get(self.combobox_list[i].get()))
        print(code_val)
        ### update label
        self.code_lab.configure(text=code_val)
        return(code_val)

Updated: added updating label.