Это скрипт Python 3.9, который извлекает данные из базы данных MySQL и преобразует их в список именованных кортежей, а затем передает их в PyQt6.QTreeWidget
.
Это должно быть list
из namedtuples
, потому что я собираюсь хранить списки тегов, упакованных в строки, выгруженные из json.dumps
в отдельных полях, затем используя json.loads
распаковать списки. Таким образом, фильтрацию по тегам можно легко выполнить с помощью issubset
метод.
Это будет основной интерфейс проекта музыкального проигрывателя с графическим интерфейсом пользователя, над которым я работаю, хотя он все еще остается голым, по крайней мере, часть иерархии дерева завершена.
Это проблеск базы данных:
Вот как выглядит окно:
А вот код:
import sys
import mysql.connector
from collections import namedtuple
from PyQt6.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem
song = namedtuple('song', 'artist title album')
app = QApplication(sys.argv)
conn = mysql.connector.connect(user="Estranger", password=********, host="127.0.0.1", port=3306, database="Music")
cursor = conn.cursor()
cursor.execute('select artist, title, album from songs')
songs = [song(*i) for i in cursor.fetchall()]
conn.commit()
tree = QTreeWidget()
tree.resize(1280,720)
tree.setWindowTitle('tree')
frame = tree.frameGeometry()
center = tree.screen().availableGeometry().center()
frame.moveCenter(center)
tree.move(frame.topLeft())
tree.setColumnCount(4)
tree.setHeaderLabels(['Name', 'Artist', 'Album', 'Title'])
entries = []
for p in sorted(set([_.artist for _ in songs])):
artist = QTreeWidgetItem([p])
for a in sorted(set([_.album for _ in songs if _.artist == p])):
album = QTreeWidgetItem([a, p, a])
for s in [_.title for _ in songs if _.artist == p and _.album == a]:
song = QTreeWidgetItem([s, p, a, s])
album.addChild(song)
artist.addChild(album)
entries.append(artist)
tree.insertTopLevelItems(0, entries)
tree.show()
sys.exit(app.exec())
(Пароль скрыт по очевидным причинам)
Я адаптировал свой код отсюда: https://doc.qt.io/qtforpython-6/tutorials/basictutorial/treewidget.html
Код там:
import sys
from PySide6.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem
data = {"Project A": ["file_a.py", "file_a.txt", "something.xls"],
"Project B": ["file_b.csv", "photo.jpg"],
"Project C": []}
app = QApplication()
tree = QTreeWidget()
tree.setColumnCount(2)
tree.setHeaderLabels(["Name", "Type"])
items = []
for key, values in data.items():
item = QTreeWidgetItem([key])
for value in values:
ext = value.split(".")[-1].upper()
child = QTreeWidgetItem([value, ext])
item.addChild(child)
items.append(item)
tree.insertTopLevelItems(0, items)
tree.show()
sys.exit(app.exec())
В этом примере данные уже являются иерархическими, это словарь, структурированный как дерево, но мои данные представляют собой список именованных кортежей, который является довольно плоским и больше подходит для таблицы.
Но я предпочитаю использовать древовидное представление для представления данных.
Поскольку мои данные плоские, а структура кода примера требует, чтобы данные были иерархическими, я использовал понимание списков плюс приведение к set
плюс sorted
функция для создания иерархии на лету, которая порождает много накладных расходов, что глупо.
Я действительно новичок в этом, это моя первая попытка создать программу с графическим интерфейсом, я искал в Google несколько дней и не нашел способа заполнить QTreeView
или же QTreeWidget
с данными из плоского списка, поэтому я написал свой собственный.
Мой вопрос: каков более умный и эффективный способ достичь того же результата (заполнить древовидную структуру данными из списка именованных кортежей) и как я могу использовать QTreeView
вместо QTreeWidget
(есть ли разница)?
Любая помощь приветствуется.
1 ответ
- У вас есть куча неструктурированного глобального кода; это должно быть организовано в функции и, возможно, классы
- Закройте курсор, как только вы закончите с ним, в идеале с помощью управления контекстом
- Упорядочивайте свой запрос, используя саму базу данных, чтобы сделать группировку менее болезненной; также запрос в порядке иерархии (исполнитель, альбом, песня)
- Тебе не нужно
entries
; просто добавляйте элементы по одному - 4 не должно быть жестко запрограммировано
- Ваши повторяющиеся представления о множестве можно значительно упростить (то есть отменить) с помощью
itertools.groupby
Этот пример кода локализует импорт MySQL, потому что у меня его нет; Вместо этого я создал поддельные данные.
import sys
from dataclasses import dataclass
from itertools import groupby
from typing import Iterable, Callable
from PyQt6.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem
@dataclass
class Song:
artist: str
album: str
title: str
@classmethod
def from_db(cls) -> Iterable['Song']:
from mysql.connector import connect
with connect(
user="Estranger",
password='********',
host="127.0.0.1",
port=3306,
database="Music",
) as conn, conn.cursor() as cursor:
cursor.execute(
'select artist, album, title from songs '
'order by artist, album'
)
for row in cursor.fetchall():
yield cls(*row)
@classmethod
def fake_rows(cls) -> Iterable['Song']:
return (
cls('Weird Al Yankovic', 'Running with Scissors', 'Pretty Fly for a Rabbi'),
cls('Weird Al Yankovic', 'Running with Scissors', 'Albuquerque'),
cls('Weird Al Yankovic', 'Off the Deep End', "I Can't Watch This"),
cls('Weird Al Yankovic', 'Off the Deep End', 'Smells like Nirvana'),
cls('The Arrogant Worms', "C'est Cheese", 'Mounted Animal Nature Trail'),
)
def by_artist(self): return self.artist
def by_album(self): return self.album
class GUI:
HEADERS = ('Name', 'Artist', 'Album', 'Title')
def __init__(self, songs):
app = QApplication(sys.argv)
tree = QTreeWidget()
# a reference needs to be held or this will be garbage-collected
self.tree = tree
tree.resize(1280, 720)
tree.setWindowTitle('tree')
frame = tree.frameGeometry()
center = tree.screen().availableGeometry().center()
frame.moveCenter(center)
tree.move(frame.topLeft())
tree.setColumnCount(len(self.HEADERS))
tree.setHeaderLabels(self.HEADERS)
for artist, after_artist in groupby(songs, Song.by_artist):
artist_node = QTreeWidgetItem((artist,))
for album, after_album in groupby(after_artist, Song.by_album):
album_node = QTreeWidgetItem((album, artist, album))
for song in after_album:
song_node = QTreeWidgetItem((song.title, artist, album, song.title))
album_node.addChild(song_node)
artist_node.addChild(album_node)
tree.addTopLevelItem(artist_node)
tree.show()
self.run: Callable[[], int] = app.exec
def main() -> None:
songs = Song.fake_rows()
gui = GUI(songs)
exit(gui.run())
if __name__ == '__main__':
main()