0

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

James Cook
  • 334
  • 4
  • 16
  • Does this answer your question? [How to make a flat list out of list of lists?](https://stackoverflow.com/questions/952914/how-to-make-a-flat-list-out-of-list-of-lists) – Henry Yik Nov 09 '20 at 04:19

2 Answers2

1

Use extend instead of append.

def get_active_loan_cars():
    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.extend(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)
Novel
  • 13,406
  • 2
  • 25
  • 41
  • Thanks for your answer. Iive changed my code please see edit.. Still not getting all items in `loan_list` to display – James Cook Nov 09 '20 at 05:34
1

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

1966bc
  • 1,148
  • 1
  • 6
  • 11