Отображение QStandardItemModel из Python в QML TableViewPython

Программы на Python
Anonymous
Отображение QStandardItemModel из Python в QML TableView

Сообщение Anonymous »

Обратите внимание, что варианты этого вопроса задавались и раньше, но различные комментарии и предложенные решения не сработали. Они либо охватывают C++, для которого я безуспешно пытался воспроизвести решение, либо, чаще всего, используют старые версии Qt Quick и представляют собой методы, которые больше не работают в новых версиях.

Проблема

Я пытаюсь отобразить очень простые 2D-данные в Qt Quick, используя QML TableView и Qt для Python/PySide6 . Вот пример того, что я хочу создать:
Изображение

На создание этой таблицы в LabVIEW у меня ушло всего несколько минут. Однако я совершенно не понимаю, как правильно это сделать в QML и Python, используя одноэлементный подход.

Что я читал

  • Код: Выделить всё

    QStandardItemModel
    для Qt 6.7
  • Код: Выделить всё

    QStandardItemModel
    для Qt для Python 6.7
  • Код: Выделить всё

    TreeView
    Тип QML для Qt 6.7
  • Бесчисленное количество сообщений на 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())
Как бы я ни пытался создать элементы и строки в QStandardItemModel, мне не удалось отобразить ничего. Я также пробовал разные вещи в элементе поля text внутри компонента Text.
Вот набор закомментированных кодов, которые я пробовал, среди множество других перестановок:

Код: Выделить всё

        # 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)
Я также пробовал использовать комбинацию этих двух решений, пытаясь предоставить модель списка для строк TableModel, но это свойство ожидает JSON множество. Мне не удалось преобразовать что-либо в Python во что-то, приемлемое для свойства rows.

Вопросы

  • Каков самый простой и прямой способ отображения 2D-данных в QML TableView из Python?
  • Где стоит ли мне пойти узнать об этом больше? Официальная документация в этом отношении почти бесполезна.
  • Конечно, мне бы хотелось иметь возможность лучше оформлять вещи и получать заголовки. отображение имен, особенно такое, что имена заголовков не совпадают с именами ключей в словарях. Для некоторых из этих целей я знаю, что существует DelegateChooser, но я даже до него не добрался.
< h4>Некоторые побочные проблемы
Я должен признать, что здесь у меня все очень плохо, и я испытываю большое разочарование, особенно из-за экосистемы, где я мог бы создать это всего за несколько минут. Помимо того, что я изо всех сил пытаюсь прочитать о том, как это должно работать и заставить его работать, и абсолютно нулевой обратной связи от Qt, я также испытываю следующие проблемы:
  • < li>Вы не можете использовать именованные аргументы с Qt для Python. Он генерирует тихую ошибку (из-за №2 или, возможно, всегда) и не работает. Это явно ошибка.
  • Я часто получаю следующую странную ошибку:

    Код: Выделить всё

    : 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.
    
    Обычно это означает, что в определении класса есть ошибка, из-за которой невозможно создать экземпляр, но Qt полностью поглощает эту ошибку. В самом простом одноэлементном классе я не могу воспроизвести эту ошибку (т. е. я действительно вижу фактическую причину и отчет о проблеме Python), но почти во всех случаях я получаю это сообщение, не связанное с ошибкой, которое заставляет меня гадать, как что не так.
  • Я получил следующую ошибку, которая также кажется ошибкой, учитывая, что она сообщает «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

Вернуться в «Python»