Search code examples
pythonpython-3.xtkintercombobox

How do I assign bind functions when new fields are created after user clicks a button?


I have a form where a user creates a record of product attributes by clicking a button. Once clicked, four combo boxes, one label, and one entry field are loaded on the form that make up a record.

The dropdown list on the 4th combo box is dependent on the entries made in the first 3 combo boxes. The end user is likely to click the "Add" button bunch of times prior to making entries. How do I make sure that when a user goes back to the first record after clicking the Add button "N" times and makes changes to the combo boxes of the first record, the appropriate values are loaded in the 4th combo box?

Here is what I have

def addItemInEdit():
    global rowNum2
    global brandDropdownList2, typeDropdownList2, sizeDropdownList2, qtyTextList2, itemList2, 
     packedDateDropdownList2

####################### Loading label, dropdowns, and text fields

    itemList2.append(ttk.Label(frameEdit, text=str(rowNum2 + 1) + ". ").grid(row=rowNum2, column=0))
    brandDropdownList2.append(ttk.Combobox(frameEdit, state='readonly', values=listOfBrands, width=10))
    brandDropdownList2[-1].grid(row=rowNum2, column=1, pady=5, padx=5)
    brandDropdownList2[-1].current(0)

    typeDropdownList2.append(ttk.Combobox(frameEdit, state='readonly', values=listOftypes, width=5))
    typeDropdownList2[-1].grid(row=rowNum2, column=2)
    typeDropdownList2[-1].current(0)

    sizeDropdownList2.append(ttk.Combobox(frameEdit, state='readonly', values=listOfSizes, width=10))
    sizeDropdownList2[-1].grid(row=rowNum2, column=3, pady=5, padx=5)
    sizeDropdownList2[-1].current(0)

    packedDateDropdownList2.append(ttk.Combobox(frameEdit, state='disabled', width=7))
    packedDateDropdownList2[-1].grid(row=rowNum2, column=4, pady=5)

    qtyTextList2.append(ttk.Entry(frameEdit, width=7))
    qtyTextList2[-1].grid(row=rowNum2, column=5, padx=5)
    qtyTextList2[-1].insert(0, "Qty")


####################### Adding bind functions.

    for item in range(len(brandDropdownList2)):
        def getDates(event):
            listOfDates=[]

################################## Making sure the use has made actual selections
            if brandDropdownList2[len(brandDropdownList2)-1].get()!="Brand" and typeDropdownList2[len(brandDropdownList2)-1].get()!="Type" and sizeDropdownList2[len(brandDropdownList2)-1].get()!="Size":
                packedDateDropdownList2[len(brandDropdownList2) - 1].config(state='readonly')
                packedDateDropdownList[len(brandDropdownList) - 1].set('')
                getDatesQuery = "SELECT [PackedOn] FROM SalesData where [Brand]=? AND [Type] = ? AND [Size]=?"
                conForDates = pyodbc.connect(dbPath)
                curForDates = conForDates.cursor()
                dateListOutput = curForDates.execute(getDatesQuery,(brandDropdownList2[len(brandDropdownList2)-1].get(),typeDropdownList2[len(brandDropdownList2)-1].get(),sizeDropdownList2[len(brandDropdownList2)-1].get())).fetchall()

                for item in dateListOutput:
                    if item[0] not in listOfDates:
                        listOfDates.append(item[0])

                packedDateDropdownList2[len(brandDropdownList2)-1].config(values=listOfDates)
            else:
                packedDateDropdownList2[len(brandDropdownList2) - 1].set('')
                packedDateDropdownList2[len(brandDropdownList2) - 1].config(state='disabled')


        brandDropdownList2[len(brandDropdownList)-1].bind("<<ComboboxSelected>>",getDates)
        typeDropdownList2[len(brandDropdownList)-1].bind("<<ComboboxSelected>>",getDates)
        sizeDropdownList2[len(brandDropdownList)-1].bind("<<ComboboxSelected>>",getDates)

    rowNum2 = rowNum2 + 1


I know the "For" loop for binding is wrong, but I feel there is a feature of Python that I don't know.


Solution

    1. Your getDates() function always get the values from the last row of comboboxes which is not correct.
    2. You only need to bind the function to newly added comboboxes.
    3. Better to put getDates() out of addItemInEdit() function.

    Below is modified version of your code:

    def getDates(event):
        index = event.widget.row_id
    
        # get the dates from database
        # is SELECT DISTINCT supported???
        getDatesQuery = "SELECT [PackedOn] FROM SalesData where [Brand] = ? AND [Type] = ? AND [Size] = ?"
        conForDates = pyodbc.connect(dbPath)
        curForDates = conForDates.cursor()
        dateListOutput = curForDates.execute(getDatesQuery,(brandDropdownList2[index].get(), typeDropdownList2[index].get(), sizeDropdownList2[index].get())).fetchall()
        # should conForDates.close() be called???
    
        listOfDates = list(set(item[0] for item in dateListOutput))
    
        packedDateDropdownList2[index].config(state='readonly')
        packedDateDropdownList2[index].config(values=listOfDates)
        packedDateDropdownList2[index].set('')
    
    def addItemInEdit():
        global rowNum2
    
        itemList2.append(ttk.Label(frameEdit, text=str(rowNum2 + 1) + ". ").grid(row=rowNum2, column=0))
        brandDropdownList2.append(ttk.Combobox(frameEdit, state='readonly', values=listOfBrands, width=10))
        brandDropdownList2[-1].grid(row=rowNum2, column=1, pady=5, padx=5)
        brandDropdownList2[-1].current(0)
        brandDropdownList2[-1].row_id = rowNum2 # save the current row number
    
        typeDropdownList2.append(ttk.Combobox(frameEdit, state='readonly', values=listOftypes, width=5))
        typeDropdownList2[-1].grid(row=rowNum2, column=2)
        typeDropdownList2[-1].current(0)
        typeDropdownList2[-1].row_id = rowNum2 # save the current row number
    
        sizeDropdownList2.append(ttk.Combobox(frameEdit, state='readonly', values=listOfSizes, width=10))
        sizeDropdownList2[-1].grid(row=rowNum2, column=3, pady=5, padx=5)
        sizeDropdownList2[-1].current(0)
        sizeDropdownList2[-1].row_id = rowNum2 # save the current row number
    
        packedDateDropdownList2.append(ttk.Combobox(frameEdit, state='disabled', width=7))
        packedDateDropdownList2[-1].grid(row=rowNum2, column=4, pady=5)
        packedDateDropdownList2[-1].row_id = rowNum2 # save the current row number
    
        qtyTextList2.append(ttk.Entry(frameEdit, width=7))
        qtyTextList2[-1].grid(row=rowNum2, column=5, padx=5)
        qtyTextList2[-1].insert(0, "Qty")
        qtyTextList2[-1].row_id = rowNum2 # save the current row number
    
        # bind the required callback to newly added comboboxes
        brandDropdownList2[-1].bind("<<ComboboxSelected>>", getDates)
        typeDropdownList2[-1].bind("<<ComboboxSelected>>", getDates)
        sizeDropdownList2[-1].bind("<<ComboboxSelected>>", getDates)
    
        rowNum2 += 1