pythonpython-3.xtkinterpython-classcustomtkinter

Programatically create multiple instances of CustomTkinter Combobox Python


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


Solution

  • 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.