У меня есть программа Python, подключенная к базе данных Sqlite3 с Tkinter на интерфейсе. Моя таблица базы данных (список тем) состоит из четырех столбцов: [id (уникальное целое число), тема (текст), серийный номер (уникальное целое число), is_active (логическое целое число)]. Вот моя программа:
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()
В настоящее время во время выполнения, когда я щелкаю элемент в списке (который в основном показывает все значения столбца темы, которые имеют is_active = 1 в той же строке), а затем нажимаю кнопку Tkinter, я получаю тему, которую я щелкнул. Вместо этого я хочу получить всю строку, на которую я нажал.
Есть несколько вещей, которые следует учитывать при выборе стола:
Рассмотрим эту таблицу (слева) и ее представление в графическом интерфейсе (справа):
Я хочу, чтобы GUI сначала показывал все предметы is_active=1 в списке. Затем я нажимаю «Собаки» (третий элемент в списке), а затем нажимаю кнопку, я хочу, чтобы программа распечатала мне всю строку (id = 1, subject = Dogs, serial = 5, is_active = 1).
Как мне добиться этого?
Кажется, это дает мне следующую ошибку: TypeError: индексы списка должны быть целыми числами или срезами, а не кортежем
используйте print( listbox.curselection() )
, чтобы увидеть, что вы получите - кажется, он возвращает не одно значение, а кортеж (value,)
, и вам может понадобиться [0]
, чтобы получить значение - data[listbox.curselection()[0]]
Единственный способ, которым я мог придумать использование Listbox
и получение всего значения строки, — это сохранить значение, вставленное в список, со ссылкой внутри словаря вместе с обычно увеличивающимся числом. Словарь имеет форму {idx:[id,subject]}
, вам действительно не нужно включать тему в список, вы можете сделать это и с помощью только идентификатора, но это может упростить вам понимание выбора с помощью темы.
Взглянем:
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()
Я значительно расширил код, чтобы сделать его более понятным, и я надеюсь, что вы тоже имели в виду этот вопрос. Я прокомментировал код, чтобы сделать его понятным на ходу.
Выход:
(1,'Dogs', 5, 1) #(Id, subject, serial, is_active)
Хотя, если бы вы использовали ttk.Treeview
, вы могли бы фактически создать два столбца, один с идентификатором, а другой с темой, поэтому гораздо проще извлекать данные, чем использовать список и хранить информацию в словаре и использовать его. для поиска в базе данных позже.
Где переменная data
, определенная в этом коде?
@Heikki О, data
был взят из вашего кода для части базы данных. В любом случае, я обновил ответ.
Это работает! У меня еще не было опыта работы со словарями (я новичок в этом деле), я видел туториалы, но без полезного контекста трудно представить, для чего его использовать, и поэтому легко не использовать все это. Хорошо, что это применимо к чему-то с пользой и целью. Это значительно облегчает понимание. Извините за долгое время ответа. Также на c = conn.cursor(
у вас отсутствует закрывающий )
, но вы не можете его отредактировать, потому что он содержит менее 6 символов.
@heikki, обязательно вставлю.
Вы можете использовать row_factory из sqlite3
, чтобы создать курсор словаря и получить аналогичный результат:
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()
И вывод:
{'id': 1, 'subject': 'Dogs', 'serial': 5, 'is_active': 1}
может быть, вы должны получить
data[listbox.curselection()]
вместоlistbox.get(listbox.curselection())