简体   繁体   English

如何使用 Tkinter 列表框小部件在 Sqlite3 表中获取一行?

[英]How can I get a row in Sqlite3 table with Tkinter Listbox widget?

I have Python program connected to Sqlite3 database with Tkinter on the frontend.我有 Python 程序连接到 Sqlite3 数据库,前端有 Tkinter。 My database table (subjectlist) consists of four columns: [id (unique interger), subject (text), serial (unique interger), is_active (boolean interger)].我的数据库表(主题列表)由四列组成:[id(唯一整数)、主题(文本)、序列(唯一整数)、is_active(布尔整数)]。 Here is my program:这是我的程序:

import sqlite3
from tkinter import *

conn = sqlite3.connect('database.db')
c = conn.cursor()
c.execute('SELECT COUNT() FROM subjectlist WHERE is_active = 1')
number = c.fetchone()[0]

c.execute('SELECT * FROM subjectlist WHERE is_active = 1 ORDER BY serial')
data = c.fetchall()

c.close
conn.close()


root = Tk()

listbox = Listbox(root)
listbox.pack()
for i in range(number):
    listbox.insert(END, data[i][1])

def get_serial():
    print(listbox.get(listbox.curselection()))

btn = Button(root, text="Show row", command=lambda: get_serial())
btn.pack()

mainloop()

Currently at runtime when I click item on listbox (which basically shows all subject column values that have is_active=1 on the same row) and then press Tkinter button I get the subject I clicked.目前在运行时,当我单击列表框上的项目(基本上显示同一行上具有 is_active=1 的所有主题列值)然后按 Tkinter 按钮时,我得到了我单击的主题。 Instead I want to get the whole row I clicked.相反,我想获得我点击的整行。

There are few things to consider about the table:关于表,有几点需要考虑:

  1. Subject column may have same value on two or more different rows.主题列在两个或多个不同的行上可能具有相同的值。
  2. Items on the listbox are aranged in order by the serial列表框上的项目按序列号排列
  3. If rows is_active value is 0 (False) it will not be displayed on the listbox.如果 rows is_active 值为 0 (False),它将不会显示在列表框中。 There will be no empty rows on the listbox and the next is_active=1 (True) row will take its place.列表框上不会有空行,下一个 is_active=1 (True) 行将取而代之。

Consider this table (left) and its representation on GUI (right):考虑此表(左)及其在 GUI 上的表示(右):

左边的表格,右边的列表框与给定表格的表示

I want GUI to first show all of is_active=1 the subjects in the listbox.我希望 GUI 首先显示列表框中的所有 is_active=1 主题。 Then I click "Dogs" (third item on what the listbox shows) and then I click the button, I want the program to print me the whole row (id=1, subject=Dogs, serial=5, is_active=1).然后我单击“Dogs”(列表框显示的第三项),然后单击按钮,我希望程序打印整行(id=1,subject=Dogs,serial=5,is_active=1)。

How would I go about achieving this?我将如何 go 实现这一目标?

The only way I could think of using Listbox and getting the entire row value pulled out is keeping the value inserted into the listbox, kept with a reference inside a dictionary, along with a normally increasing number.我能想到的使用Listbox并提取整个行值的唯一方法是保持插入到列表框中的值,与字典中的引用一起保存,以及通常增加的数字。 The dictionary is in the form of {idx:[id,subject]} , indeed you dont need to include subject in the list, you can do it with just the id too, but it can make you easier to understand selection with subject.字典是{idx:[id,subject]}的形式,确实你不需要在列表中包含主题,你也可以只用 id 来做,但它可以让你更容易理解主题的选择。

Take a look:看一看:

from tkinter import *
import sqlite3

conn = sqlite3.connect('database.db')
c = conn.cursor() 
c.execute('SELECT * FROM subjectlist ORDER BY serial')
data = c.fetchall()
conn.close()

root = Tk()

dictio = {} # Empty dictionary to keep reference of appended values

listbox = Listbox(root)
listbox.pack()

a = 0 # A normally increasing number as the key of the dict
tot_rows = len(data) # Total number of rows
for i in range(tot_rows):
    if data[i][3]: # If is_active == 1
        dictio[a] = [data[i][0],data[i][1]] # Append the subject as the a-th item to dict, along with its id number
        listbox.insert(END, data[i][1]) # Inseert the data #add the subject to listbox
        a += 1 # Increase the number by 1

def get_serial():
    conn = sqlite3.connect('database.db')
    c = conn.cursor()
    val = listbox.curselection()[0] # Index of our current selection
    sel = dictio[val] # Get value from the dictionary based on the indexed number
    ids = sel[0] # Index the first item of the list
        
    c.execute('SELECT * FROM subjectlist WHERE `id`=?',(ids,)) # Search the database based on id of item
    print(c.fetchall()) # Print the item out as a tuple
    conn.close() # Close the connection

btn = Button(root, text="Show row", command=get_serial)
btn.pack()

listbox.bind('<Double-Button-1>',lambda e=None:get_serial()) # Bonus :p

mainloop()

I've expanded the code a lot to make it more understandable and I hope this is what you meant by the question too.我已经对代码进行了很多扩展以使其更易于理解,我希望这也是您提出的问题的意思。 I've commented the code to make it understandable on the go too.我已经对代码进行了注释,以使其在 go 上也可以理解。

Output: Output:

(1,'Dogs', 5, 1) #(Id, subject, serial, is_active) 

Though if you were to use a ttk.Treeview , you could actually create two columns, one with id and the other with subject, so its much easier to pull out data from, rather than using a listbox and keeping the info inside a dictionary and using it to search the database later on.虽然如果您要使用ttk.Treeview ,您实际上可以创建两列,一列带有 id,另一列带有主题,因此更容易从中提取数据,而不是使用列表框并将信息保存在字典中以后用它来搜索数据库。

You can use row_factory of sqlite3 to create a dictionary cursor and get similar output you want:您可以使用sqlite3row_factory创建字典 cursor 并获得您想要的类似 output :

import sqlite3
from tkinter import *

def dict_factory(cursor, row):
    return {col[0]:row[idx] for idx, col in enumerate(cursor.description)}

conn = sqlite3.connect('database.db')
conn.row_factory = dict_factory
c = conn.cursor()

c.execute('SELECT * FROM subjectlist WHERE is_active = 1 ORDER BY serial')
data = c.fetchall()

c.close
conn.close()


root = Tk()

listbox = Listbox(root)
listbox.pack()

for rec in data:
    listbox.insert(END, rec["subject"])

def get_serial():
    selected = listbox.curselection()
    if selected:
        print(data[selected[0]])
    else:
        print("No item selected")

btn = Button(root, text="Show row", command=get_serial)
btn.pack()

mainloop()

And the output:和 output:

{'id': 1, 'subject': 'Dogs', 'serial': 5, 'is_active': 1}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM