Search code examples
pythontkinterpyodbc

Using lists in Tkinter combobox widget


I want to use a combox widget to display a list of vehicles which are populated by querying an access DB. Heres relevant code

from O365 import *
import tkinter as tk 
from tkinter import ttk
from tkcalendar import *
import pyodbc 

root = tk.Tk()
root.title('Loan Car Manager')
root.geometry('800x1200')
style = ttk.Style(root)

load_loan_vehicle_list_button = tk.Button(root, command = get_active_loan_cars, text = "Load / Refresh")
load_loan_vehicle_list_button.place(x=50 , y=50)

conn = pyodbc.connect(r'DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};UID=admin;UserCommitSync=Yes;Threads=3;SafeTransactions=0;PageTimeout=5;MaxScanRows=8;MaxBufferSize=2048;{FIL=MS Access};DriverId=25;DefaultDir=C:\Users\James\Documents;DBQ=C:\Users\James\Documents\Database1.accdb;')
cursor = conn.cursor()



def get_active_loan_cars():
    global loan_list
    cursor.execute("SELECT Loan_make , Loan_model , Loan_rego  FROM Loan_vehicle_data WHERE is_active =  True")
    for row in cursor.fetchall():
        loan_list = []
        loan_list.append(row)
        loan_car_drop_down = ttk.Combobox(root)
        loan_car_drop_down.place(x=50 , y=70)
        loan_car_drop_down['values'] = loan_list
        print(loan_list)



root.mainloop()

When I run this, it queries the DB as expected and returns the vehicles into a list loan_list.. When I insert the list into to combobox using loan_car_drop_down['values'] = loan_list it displays the list but the records are displayed on 1 line.. Eg:

enter image description here

When I print(loan_list) it returns:

[('Hyundai', 'Elantra', 'TEST123')] [('Hyundai', 'I30', 'ABC123')]

My question is how can I get these records to display on seperate lines of the combobox widget. Heres a picture referancing my goal.. Ignoring the months displayed in this example I would like to replace those with :

Hyundai', 'Elantra', 'TEST123' 'Hyundai', 'I30', 'ABC123'

enter image description here


Solution

  • to load records on a combobox I use a dictionary.

    I coupling the index of the combo with the primary key of the records to load in a dict.

    Below a full example, commented.

    The example loads a recordset, I have add some items and an integer primary key, when you select an item from the combo the says it the pk value retrieved from the dict.

    At the opening it is also positioned on a records you choose using pk.

    I hope you’ll find it useful

    #!/usr/bin/python3
    import tkinter as tk
    from tkinter import ttk
    from tkinter import messagebox
    
    
    class Main(ttk.Frame):
        def __init__(self, parent):
            super().__init__()
    
            self.parent = parent
            
            self.init_ui()
              
        def init_ui(self):
    
           
            self.pack(fill=tk.BOTH, expand=1)
    
            f = ttk.Frame(padding = 8)
    
            ttk.Label(f, text = "Combobox").pack()
            self.cbCombo = ttk.Combobox(f,)
            self.cbCombo.bind("<<ComboboxSelected>>", self.on_selected)
            self.cbCombo.pack()
            
            f.pack(fill=tk.BOTH, expand=1)
    
        def on_open(self,):
    
            self.set_combos()
            self.on_set_combo()
            
        def set_combos(self):
    
            index = 0
            self.dict_cars = {}
            voices = []
    
            #here your recordset, I've add some items and even a supposed primary key.....1,2,3,4,5
            rs = [(1, 'Hyundai', 'Elantra', 'TEST123'),
                  (2, 'Hyundai', 'I30', 'ABC123'),
                  (3, 'Hyundai', 'Azera', 'ABC123'),
                  (4, 'Hyundai', 'Sonata', 'ABC123'),
                  (5, 'Hyundai', 'I30 Fastback N', 'ABC123')]
    
            #here we coupling self.dict_cars with the combo index...
            for i in rs:
                self.dict_cars[index] = i[0]
                index += 1
                record = "{0} {1}".format(i[1], i[2])
                voices.append(record)
    
            self.cbCombo["values"] = voices        
            
    
        def on_selected(self, evt=None):
            #when you select an item on the combo it get the relative pk record from the dict
            index = self.cbCombo.current()
            pk = self.dict_cars[index]
            msg =  ("You have selected index {0} pk {1}".format(index, pk))
    
            messagebox.showinfo(self.master.title(),msg, parent=self)
            
        def on_set_combo(self):
            #it'use to select, on open a specific record, in that case the 5
            try:
                key = next(key
                           for key, value
                           in self.dict_cars.items()
                           if value == 5)
                self.cbCombo.current(key)
            except:
                pass
            
            
        def on_close(self):
            self.parent.on_exit()
    
    class App(tk.Tk):
        """Start here"""
    
        def __init__(self):
            super().__init__()
    
            self.protocol("WM_DELETE_WINDOW", self.on_exit)
                
            self.set_title()
            self.set_style()
           
            frame = Main(self,)
            frame.on_open()
            frame.pack(fill=tk.BOTH, expand=1)
    
        def set_style(self):
            self.style = ttk.Style()
            #('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
            self.style.theme_use("clam")
            
    
        def set_title(self):
            s = "{0}".format('Simple App')
            self.title(s)
            
        def on_exit(self):
            """Close all"""
            if messagebox.askokcancel("Simple App", "Do you want to quit?", parent=self):
                self.destroy()               
        
    if __name__ == '__main__':
        app = App()
        app.mainloop()
    

    enter image description here

    enter image description here