Как я могу получить строку в таблице Sqlite3 с помощью виджета Tkinter Listbox?

У меня есть программа 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, я получаю тему, которую я щелкнул. Вместо этого я хочу получить всю строку, на которую я нажал.

Есть несколько вещей, которые следует учитывать при выборе стола:

  1. Столбец темы может иметь одинаковое значение в двух или более разных строках.
  2. Элементы в списке упорядочены по серийному номеру.
  3. Если значение строки is_active равно 0 (False), оно не будет отображаться в списке. В списке не будет пустых строк, и их место займет следующая строка is_active=1 (True).

Рассмотрим эту таблицу (слева) и ее представление в графическом интерфейсе (справа):

Я хочу, чтобы GUI сначала показывал все предметы is_active=1 в списке. Затем я нажимаю «Собаки» (третий элемент в списке), а затем нажимаю кнопку, я хочу, чтобы программа распечатала мне всю строку (id = 1, subject = Dogs, serial = 5, is_active = 1).

Как мне добиться этого?

может быть, вы должны получить data[listbox.curselection()] вместо listbox.get(listbox.curselection())

furas 09.12.2020 22:19

Кажется, это дает мне следующую ошибку: TypeError: индексы списка должны быть целыми числами или срезами, а не кортежем

Heikki 09.12.2020 22:46

используйте print( listbox.curselection() ), чтобы увидеть, что вы получите - кажется, он возвращает не одно значение, а кортеж (value,), и вам может понадобиться [0], чтобы получить значение - data[listbox.curselection()[0]]

furas 09.12.2020 22:54
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
1 219
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Единственный способ, которым я мог придумать использование 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 10.12.2020 22:15

@Heikki О, data был взят из вашего кода для части базы данных. В любом случае, я обновил ответ.

Delrius Euphoria 10.12.2020 22:19

Это работает! У меня еще не было опыта работы со словарями (я новичок в этом деле), я видел туториалы, но без полезного контекста трудно представить, для чего его использовать, и поэтому легко не использовать все это. Хорошо, что это применимо к чему-то с пользой и целью. Это значительно облегчает понимание. Извините за долгое время ответа. Также на c = conn.cursor( у вас отсутствует закрывающий ), но вы не можете его отредактировать, потому что он содержит менее 6 символов.

Heikki 11.12.2020 16:08

@heikki, обязательно вставлю.

Delrius Euphoria 11.12.2020 16:25

Вы можете использовать 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}

Другие вопросы по теме