Displaying QStandardItemModel from Python in a QML TableView

Please note that versions of this question have been asked before, but the various comments and proposed solutions have not worked. They either cover C++, for which I have tried replicating the solution without success, or more often than not, they are using older versions of Qt Quick and are methods that are no longer workable in the newer versions.


Problem

I am trying to display very simple 2D data in Qt Quick using a QML TableView and Qt for Python / PySide6. Here is an example of what I am looking to create:

This table takes me just a few minutes to create in LabVIEW. However, I am completely at a loss for how to properly do this in QML and with Python using the singleton approach.

What I’ve read

  • QStandardItemModel for Qt 6.7
  • QStandardItemModel for Qt for Python 6.7
  • TreeView QML type for Qt 6.7
  • Countless StackOverflow and forum posts, where I have tried to replicate supposed solutions to no avail.

A pure QML example that kind of works

The official documentation unfortunately highlights the simplest case in which the model is defined purely in 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
            }
        }
    }
}

This looks like:

Of course, the message column needs to be made wider, and I’m not sure how to get header names displayed using this setup, but it at least shows that the rest of the QML is correct and that the issue is with my model.

QML plus Python that doesn’t work

// 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())

No matter how I’ve tried building up the items and rows in the QStandardItemModel, I have been unable to get anything to display. I have also tried all sorts of things in the text field item inside the Text component.

Here’s a set of commented out code I’ve tried, amongst many other permutations:

        # 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)

I have also tried using a combination of the two solutions, trying to provide a list model for the rows of a TableModel, but that property expects a JSON array. I was not able to convert anything in Python to something accepted by the rows property.

Questions

  1. What is the simplest, most direct way to display 2D data in a QML TableView from Python?

  2. Where should I go to learn more about this? The official documentation is close to useless along these lines.

  3. Of course, I’d like to be able to style things better and get header names displaying, especially such that the header names are not the same names as the key names in the dictionaries. For some of this, I know that there is DelegateChooser, but I haven’t even gotten there.

Some side issues

I must admit that I am completely struggling here and experiencing a lot of frustration, especially coming from an ecosystem where I could have built this in just a few minutes. In addition to struggling to read about how this should work and get it to work and the absolutely zero feedback Qt is giving me, I am also experiencing the following issues:

  1. You cannot used named arguments with Qt for Python. It generates a silent error (due to #2 or possibly always) and doesn’t work. This is clearly a bug.

  2. I often get the following bizarre error:

    <Unknown File>: qmlRegisterSingletonType(): "Simplified" is not available because the callback function returns a null pointer.
    Traceback (most recent call last):
      File "main.py", line 88, in <module>
        engine.singletonInstance("com.simplified", "Simplified")
    TypeError: Callback returns 0 value.
    

    Usually this means there’s an error in the class definition such that it can’t instantiate, but Qt is completely eating the error. In the most simple of singleton classes, I cannot reproduce this error (i.e., I actually see the actual cause and report of the Python issue), but in almost all cases, I’m getting this non-error message that leaves me guessing as to what’s wrong.

  3. I have received the following error, which also seems like a bug given that it reports “FIXME” for a Qt function:

    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
    

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật