Проблема
Я пытаюсь отобразить очень простые 2D-данные в Qt Quick, используя QML TableView и Qt для Python/PySide6 . Вот пример того, что я хочу создать:
На создание этой таблицы в LabVIEW у меня ушло всего несколько минут. Однако я совершенно не понимаю, как правильно это сделать в QML и Python, используя одноэлементный подход.
Что я читал
- для Qt 6.7
Код: Выделить всё
QStandardItemModel - для Qt для Python 6.7
Код: Выделить всё
QStandardItemModel - Тип QML для Qt 6.7
Код: Выделить всё
TreeView - Бесчисленное количество сообщений на StackOverflow и форумах, где я безуспешно пытался воспроизвести предполагаемые решения.
Пример на чистом QML, который работает
Официальная документация, к сожалению, выделяет простейший случай, в котором модель определена исключительно в QML.Код: Выделить всё
import QtQuick
import QtQuick.Window
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.qmlmodels
import com.simplified
Window {
width: 740
height: 540
visible: true
title: "Python log viewer"
TableView {
id: log
anchors.fill: parent
columnSpacing: 1
rowSpacing: 1
clip: true
model: TableModel {
TableModelColumn {
display: "Timestamp"
}
TableModelColumn {
display: "Logger"
}
TableModelColumn {
display: "Level"
}
TableModelColumn {
display: "Message"
}
rows: [
{
"Timestamp": "today",
"Logger": "root",
"Level": "DEBUG",
"Message": "This is a debug message"
},
{
"Timestamp": "today",
"Logger": "root",
"Level": "INFO",
"Message": "This is an info message"
}
]
}
delegate: Rectangle {
border.width: 1
clip: true
Text {
text: display
anchors.centerIn: parent
}
}
}
}

Конечно, столбец сообщений необходимо сделать шире, и я не уверен, как с помощью этой настройки отображать имена заголовков, но это, по крайней мере, показывает, что остальная часть QML верна, и проблема связана с моей моделью.
QML плюс Python не работают
Код: Выделить всё
// Main.qml
import QtQuick
import QtQuick.Window
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.qmlmodels
import com.simplified
Window {
width: 740
height: 540
visible: true
title: "Python log viewer"
TableView {
id: log
anchors.fill: parent
columnSpacing: 1
rowSpacing: 1
clip: true
model: Simplified.log
delegate: Rectangle {
border.width: 1
clip: true
Text {
text: display
anchors.centerIn: parent
}
}
}
}
Код: Выделить всё
# main.py
QML_IMPORT_NAME = "com.simplified"
QML_IMPORT_MAJOR_VERSION = 1
# Core dependencies
from pathlib import Path
import sys
# Package dependencies
from PySide6.QtCore import QObject, Signal, Property, Qt
from PySide6.QtGui import QGuiApplication, QStandardItemModel, QStandardItem
from PySide6.QtQml import QQmlApplicationEngine, QmlElement, QmlSingleton
LOG_ENTRIES = [
{
"Timestamp": "2024-07-01 19:16:03.326",
"Name": "root.child",
"Level": "DEBUG",
"Message": "This is a debug message",
},
{
"Timestamp": "2024-07-01 19:16:03.326",
"Name": "root.child",
"Level": "INFO",
"Message": "This is an info message",
},
]
FIELD_NAMES = ["Timestamp", "Name", "Level", "Message"]
@QmlElement
@QmlSingleton
class Simplified(QObject):
log_changed = Signal()
def __init__(self) -> None:
super().__init__()
_ = self.log
self.log_changed.emit()
@Property(QStandardItemModel, notify=log_changed)
def log(self):
lines = LOG_ENTRIES
table = QStandardItemModel(len(lines), len(FIELD_NAMES))
table.setHorizontalHeaderLabels(FIELD_NAMES)
for line in lines:
row = [QStandardItem(str(line[key])) for key in FIELD_NAMES]
table.appendRow(row)
return table
if __name__ == "__main__":
application = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
qml_file = Path(__file__).resolve().parent / "qml" / "Main.qml"
engine.load(qml_file)
if not engine.rootObjects():
sys.exit(-1)
engine.singletonInstance("com.simplified", "Simplified")
sys.exit(application.exec())
Вот набор закомментированных кодов, которые я пробовал, среди множество других перестановок:
Код: Выделить всё
# table.setItemRoleNames(
# {i: FIELD_NAMES[i].encode() for i in range(len(FIELD_NAMES))}
# )
# for row_index, line in enumerate(lines):
# for column_index, key in enumerate(FIELD_NAMES):
# item = QStandardItem(str(line[key]))
# table.setItem(row_index, column_index, item)
# for line in lines:
# row = QStandardItem()
# for role_index, key in enumerate(FIELD_NAMES):
# row.setData(str(line[key]), Qt.ItemDataRole + role_index)
# table.appendRow(row)
# for line in lines:
# row = QStandardItem()
# for role_index, key in enumerate(FIELD_NAMES):
# row.appendColumn([QStandardItem(str(line[key]))])
# table.appendRow(row)
Вопросы
- Каков самый простой и прямой способ отображения 2D-данных в QML TableView из Python?
- Где стоит ли мне пойти узнать об этом больше? Официальная документация в этом отношении почти бесполезна.
- Конечно, мне бы хотелось иметь возможность лучше оформлять вещи и получать заголовки. отображение имен, особенно такое, что имена заголовков не совпадают с именами ключей в словарях. Для некоторых из этих целей я знаю, что существует DelegateChooser, но я даже до него не добрался.
Я должен признать, что здесь у меня все очень плохо, и я испытываю большое разочарование, особенно из-за экосистемы, где я мог бы создать это всего за несколько минут. Помимо того, что я изо всех сил пытаюсь прочитать о том, как это должно работать и заставить его работать, и абсолютно нулевой обратной связи от Qt, я также испытываю следующие проблемы:
- < li>Вы не можете использовать именованные аргументы с Qt для Python. Он генерирует тихую ошибку (из-за №2 или, возможно, всегда) и не работает. Это явно ошибка.
- Я часто получаю следующую странную ошибку:
Обычно это означает, что в определении класса есть ошибка, из-за которой невозможно создать экземпляр, но Qt полностью поглощает эту ошибку. В самом простом одноэлементном классе я не могу воспроизвести эту ошибку (т. е. я действительно вижу фактическую причину и отчет о проблеме Python), но почти во всех случаях я получаю это сообщение, не связанное с ошибкой, которое заставляет меня гадать, как что не так.
Код: Выделить всё
: qmlRegisterSingletonType(): "Simplified" is not available because the callback function returns a null pointer. Traceback (most recent call last): File "main.py", line 88, in engine.singletonInstance("com.simplified", "Simplified") TypeError: Callback returns 0 value. - Я получил следующую ошибку, которая также кажется ошибкой, учитывая, что она сообщает «FIXME» для функции Qt :
Код: Выделить всё
FIXME qt_isinstance({0: 'Timestamp', 1: 'Name', 2: 'Level', 3: 'Message'}, typing.Dict[int, PySide6.QtCore.QByteArray]): Subscripted generics cannot be used with class and instance checks
Подробнее здесь: https://stackoverflow.com/questions/787 ... -tableview