Python Forum
Can't get tkinter database aware cascading comboboxes to update properly
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Can't get tkinter database aware cascading comboboxes to update properly
I'm running into a tkinter situation that I cannot find a resolution for as of yet. I've got three database linked comboboxes that I would like to cascade. I've got them working somewhat, as in they populate correctly on bootup with the default selections, and the next combobox down the line is filtered down to what is appropriate from the preceding combobox selection. However, when I manually go and change a combo box, it does not cascade/update the other boxes, and always shows the choices from the top level selection.

All the examples I can find online are trivial and do not talk to databases.

I am not using the "trace" feature to track changes to fields, as it didn't give me enough control, and was also painting me into a corner. I'm relying on the "bind" function to try to make things cascade and to track field changes.

Here is the code where I define the first combobox:


mydict, data = getCboList()

frm = tk.Frame(taskframe, bg="cyan").place(x=col2-5, y=y1-5, width=width1+10, height=hgt1+10)

cboTask = ttk.Combobox(taskframe,  textvariable=keyMainTask, values=data), y=y1, width = width1, height=hgt1)
cboTask.bind('<<ComboboxSelected>>', updateMainTasks)
Here is the code where I'm trying to make the cascading happen. First function is for the toplevel combobox, next function is for the next lower down combobox, which is trying to read the first combobox to know how to filter. It does filter, but I'm still only getting choices from the default value of the top level box:

def getCboList():  
    cnxn= sqlite3.connect(sqlFileLoc)
    c.execute('Select Headline, TaskID  from MainTasks')
    data = list(mydict.keys())

    return mydict, data
def getCboListTaskSteps(): 
    global mydict
    cnxn2= sqlite3.connect(sqlFileLoc)
        c2.execute('Select StepDescription, TaskStepID from TaskSteps where TaskID =' + str(mydict[cboTask.get()]))
        c2.execute('Select StepDescription, TaskStepID from TaskSteps ' )

    data2 = list(mydict2.keys())
    return mydict2, data2
And here is the beginning/subset of the code that is called by the "bind" function on the toplevel combobox. This code is where I try to update the fields on the form with data from the current combobox selection.

def updateMainTasks(self):
    cnxn= sqlite3.connect(sqlFileLoc)
    #try is to catch that mydict-key.get value exception when it is null
        sql='Select TaskID, Headline, Requester, Headline, Explanation,RequestDate, \
            Complete, Acknowledged,DueDate from MainTasks \
            where TaskID =' + str(mydict[cboTask.get()]) 
        df=pd.read_sql_query(sql, cnxn)

I have looked and looked online and in the forums, and as of yet have not hit the jackpot. Any help appreciated. Hopefully I've supplied enough code. The last assistance I received here helped immensely to get me out of a corner I was painted into. Thanks.
I'm trying this right now, based on something I saw in another discussion on this group, but don't know the syntax for that last line. It doesn't like just "row" in the append. The fetchall is returning a tuple.

sql2="Select StepDescription, TaskStepID from TaskSteps where TaskID = " +str(mydict[cboTask.get()])
    for row in thiscur.fetchall():
Are you trying to do something like this? C and E will have different menu. The rest is a default.

#! /usr/bin/env python3

import tkinter as tk
from tkinter import ttk

root = tk.Tk()
root['padx'] = 8
root['pady'] = 4

def update(*args):
    # label['text'] = f'Key -> {key.get()} | Value -> {mydict[key.get()]}'
    if mydict[key.get()] == 1:
        mydict2 = {'G': 9, 'H': 7, 'I': 6, 'J': 8}
    elif mydict[key.get()] == 4:
        mydict2 = {'K': 10, 'L': 15, 'M': 13, 'N': 11, 'O': 12, 'P': 14}
    else :
        mydict2 = {'Q': 'Other'}
    combobox2['values'] = list(mydict2)

mydict = {'A':5, 'B': 2, 'C': 1, 'D': 3, 'E': 4, 'F': 0}
data = list(mydict.keys())

key = tk.StringVar()
combobox = ttk.Combobox(root, values=data, textvariable=key)
combobox.bind('<<ComboboxSelected>>', update)

spacer = tk.Label(root).pack()

key2 = tk.StringVar()
combobox2 = ttk.Combobox(root, values='', textvariable=key2)

I welcome all feedback.
The only dumb question, is one that doesn't get asked.

My Scripts
CookBook - Shmup - PyQt5 Music Player

I'm not sure that I understand your question. This is my attempt to answer this question: "I have two combo boxes. When I change the value in one of the combo boxes I want to change the choices displayed in a second combo box." If that is the wrong question please clarify.

Where is the code where you update the fields in the combo box? Each time you want a combo box to list different values you need to do something like this:
cboTask["values"] = data
Did you leave this part out of your post, or is this what you are missing? In this example changing the abox selection loads a different set of values in bbox.
import tkinter as tk
from tkinter import ttk

"""Mapping of bbox choices based on abox selection"""
choices = {
    "A":["1", "2", "3"],
    "B":["4", "6", "7"],
    "C":["8", "8", "9"],

def aselected(box, var, values):
    """I get called when the abox selection changes"""
    box["values"] = values

root = tk.Tk()
key = list(choices.keys())[0]
value = choices[key]
avar = tk.StringVar(value=key)
bvar = tk.StringVar(value=value[0])
abox = ttk.Combobox(root, textvariable=avar, values=list(choices.keys()))
bbox = ttk.Combobox(root, textvariable=bvar, values=value)
abox.bind('<<ComboboxSelected>>', lambda event: aselected(bbox, bvar, choices[avar.get()]))
bbox.bind('<<ComboboxSelected>>', lambda event: print(bvar.get()))
Sorry for any confusion I caused. I thought cascading combo boxes was a common phrase to describe the situation. Your interpretation of the question was correct. Thanks for your help.
Looks like you may also be having a problem getting your database info in the right format for adding to the combo box. Your last post contains this:
for row in thiscur.fetchall():
What is returned by thiscur.fetchall()? A tuple is not the correct answer. What is in the tuple and what do you want to appear in cboTaskStep?
When I hear "cascading" and database I think of something that has hierarchy, like a TreeView displaying data which also has hierarchy. You can click on something in the tree view and if the corresponding data object is a collection the tree expands to show the components of the thing. You can make a treeview-like view using multiple combo boxes. Is that what you are trying to do? If so, why not use a TreeView? I would think a TreeView as being a much better way to view a database.

Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] TKINTER quiz using sqlite3 database hezza_23 45 16,225 Nov-29-2021, 09:42 PM
Last Post: Hilal
  [Tkinter] Update variable using tkinter entry methon drSlump 6 2,878 Oct-15-2021, 08:01 AM
Last Post: drSlump
  tkinter - update/refresh treeview snakes 4 11,838 May-13-2021, 07:10 AM
Last Post: snakes
  Linking Comboboxes MrP 24 4,434 Feb-03-2021, 10:59 PM
Last Post: MrP
  Print Values from a Sequence of Entries / ComboBoxes MC2020 4 1,795 Mar-28-2020, 10:05 PM
Last Post: MC2020
  Want to dynamically update numbers using tkinter in pygame script k0gane 0 1,376 Feb-09-2020, 09:01 AM
Last Post: k0gane
  Unable to update or refresh label text in tkinter jenkins43 3 5,088 Jul-24-2019, 02:09 PM
Last Post: Friend
  [PyQt] making dependant comboBoxes Hitsugaya 3 3,938 May-23-2019, 06:05 PM
Last Post: Alfalfa
  Tkinter - Make changes to graph and update it adriancovaci 0 5,829 Apr-08-2019, 09:02 AM
Last Post: adriancovaci

Forum Jump:

User Panel Messages

Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020