You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python QAbstractTableModel对接QML TableView无渲染问题及信号需求

问题描述

我希望将类JSON格式的字典列表数据在QML的TableView中展示,但目前表格无法可视化且无任何报错或警告。程序运行过程中,字典列表或字典内的值会发生变化,因此需要实现相应的信号。PointsTableModel必须使用ProjectManagement类中的数据。我曾通过Qt.labs.qmlmodels、TableModel、TableModelColumns等实现功能,但代码过于杂乱,希望从Python端进行控制。这是我首次尝试用Python创建表格模型并在QML中使用,问题可能出在模型传递至QML的环节。


现有代码

main.py

import sys
from pathlib import Path

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Property

from ProjectManagement import ProjectManagement


class BG(QObject):
    def __init__(self):
        QObject.__init__(self)
        self._pm = ProjectManagement()

    @Property(QObject, constant=True)
    def pm(self):
        return self._pm


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    background = BG()
    engine.rootContext().setContextProperty("bg", background)

    qml_file = Path(__file__).resolve().parent / "main.qml"
    engine.load(qml_file)

    app.aboutToQuit.connect(engine.deleteLater)
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec())

ProjectManagement.py

from PySide6 import QtCore


class ProjectManagement(QtCore.QObject):
    def __init__(self):
        QtCore.QObject.__init__(self)

        print("ProjectManagement START")
        data = [
            {
                "Id": "1",
                "Coordinates": [
                    {
                        "X": " 5477262.484",
                        "Y": " 7447371.856",
                        "Z": " 639.947"
                    }
                ],
                "Code": "",
                "Value": " 48",
                "Date": "",
                "Comment": " 4.947",
                "Text": "",
                "Symbol": "",
                "Color": "#000000",
                "Scale": 1,
                "Visible": True,
                "Visible text": True
            },
            {
                "Id": "2",
                "Coordinates": [
                    {
                        "X": " 5477262.496",
                        "Y": " 7447371.946",
                        "Z": " 639.911"
                    }
                ],
                "Code": "",
                "Value": " 26",
                "Date": "",
                "Comment": " 4.911\n",
                "Text": "",
                "Symbol": "",
                "Color": "#000000",
                "Scale": 1,
                "Visible": True,
                "Visible text": True
            },
            {
                "Id": "3",
                "Coordinates": [
                    {
                        "X": " 5477262.440",
                        "Y": " 7447372.086",
                        "Z": " 640.088"
                    }
                ],
                "Code": "",
                "Value": " 2",
                "Date": "",
                "Comment": " 5.088\n",
                "Text": "",
                "Symbol": "",
                "Color": "#000000",
                "Scale": 1,
                "Visible": True,
                "Visible text": True
            },
            {
                "Id": "4",
                "Coordinates": [
                    {
                        "X": " 5477262.468",
                        "Y": " 7447372.128",
                        "Z": " 640.106"
                    }
                ],
                "Code": "",
                "Value": " 3",
                "Date": "",
                "Comment": " 5.106",
                "Text": "",
                "Symbol": "",
                "Color": "#000000",
                "Scale": 1,
                "Visible": True,
                "Visible text": True
            }
        ]
        self._tab_model = PointsTableModel(data)
        print(self._tab_model.rowCount())

    @Property(QtCore.QAbstractTableModel, constant=True)
    def tab_model(self):
        return self._tab_model


class PointsTableModel(QtCore.QAbstractTableModel):
    def __init__(self, database):
        QtCore.QAbstractTableModel.__init__(self)
        print("PointsTableModel START")
        self.database = database

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.database) if not parent.isValid() else 0

    def columnCount(self, parent=QtCore.QModelIndex()):
        return 14 if not parent.isValid() else 0

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return None
        if orientation == QtCore.Qt.Horizontal:
            headers = ('Vis', 'Id', 'X', 'Y', 'Z', 'Code', 'Val', 'Date', 'Com', 'Txt', 'Txt vis', 'Symbol', 'Color', 'Scale')
            return headers[section] if 0 <= section < len(headers) else None
        else:
            return f"{section}"

    def data(self, index=QtCore.QModelIndex(), role=QtCore.Qt.DisplayRole):
        if not index.isValid() or role != QtCore.Qt.DisplayRole:
            return None

        column = index.column()
        row = index.row()
        if row >= len(self.database) or column >= 14:
            return None

        row_data = self.database[row]
        if column == 0:
            return str(row_data['Visible'])
        elif column == 1:
            return row_data['Id']
        elif column == 2:
            return row_data['Coordinates'][0]['X']
        elif column == 3:
            return row_data['Coordinates'][0]['Y']
        elif column == 4:
            return row_data['Coordinates'][0]['Z']
        elif column == 5:
            return row_data['Code']
        elif column == 6:
            return row_data['Value']
        elif column == 7:
            return row_data['Date']
        elif column == 8:
            return row_data['Comment'].strip()
        elif column == 9:
            return row_data['Text']
        elif column == 10:
            return str(row_data['Visible text'])
        elif column == 11:
            return row_data['Symbol']
        elif column == 12:
            return row_data['Color']
        elif column == 13:
            return str(row_data['Scale'])
        return None

    def update_data(self, new_data):
        """更新数据并通知视图"""
        self.beginResetModel()
        self.database = new_data
        self.endResetModel()

    def update_item(self, row, column, new_value):
        """更新单个项并通知视图"""
        if row >= len(self.database):
            return
        row_data = self.database[row]
        if column == 0:
            row_data['Visible'] = new_value
        elif column == 1:
            row_data['Id'] = new_value
        elif column == 2:
            row_data['Coordinates'][0]['X'] = new_value
        elif column == 3:
            row_data['Coordinates'][0]['Y'] = new_value
        elif column == 4:
            row_data['Coordinates'][0]['Z'] = new_value
        elif column == 5:
            row_data['Code'] = new_value
        elif column == 6:
            row_data['Value'] = new_value
        elif column == 7:
            row_data['Date'] = new_value
        elif column == 8:
            row_data['Comment'] = new_value
        elif column == 9:
            row_data['Text'] = new_value
        elif column == 10:
            row_data['Visible text'] = new_value
        elif column == 11:
            row_data['Symbol'] = new_value
        elif column == 12:
            row_data['Color'] = new_value
        elif column == 13:
            row_data['Scale'] = new_value
        self.dataChanged.emit(self.index(row, column), self.index(row, column))

main.qml

import QtQuick
import QtQuick.Window
import QtQuick.Controls

Window {
    title: qsTr("Hello World")
    visible: true
    visibility: Qt.WindowFullScreen

    HorizontalHeaderView {
        id: horizontalHeader
        anchors.left: tabView.left
        anchors.top: parent.top
        syncView: tabView
        clip: true
        model: tabView.model
    }

    VerticalHeaderView {
        id: verticalHeader
        anchors.top: tabView.top
        anchors.left: parent.left
        syncView: tabView
        clip: true
    }

    TableView {
        id: tabView
        anchors.left: verticalHeader.right
        anchors.top: horizontalHeader.bottom
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        clip: true
        columnSpacing: 10
        rowSpacing: 10

        model: bg.pm.tab_model
        delegate: Button {
            text: model.display
            highlighted: true
            width: tabView.columnWidth(model.column)
        }
    }
}

关键修复点
  1. 暴露模型为QML可访问属性:在ProjectManagement类中,将tab_model用@Property装饰器包装,确保QML能正确识别并访问该模型。
  2. 模型方法规范:
    • rowCount和columnCount增加父索引有效性判断,符合Qt模型规范。
    • headerData添加角色判断,只处理DisplayRole。
    • data方法增加索引有效性判断,将布尔值、数字转为字符串,避免QML无法正确显示。
  3. 数据更新机制:添加update_data和update_item方法,配合beginResetModel()、endResetModel()和dataChanged信号,实现数据变化时的视图自动更新。
  4. QML视图修正:修正HorizontalHeaderView的model设置,直接绑定tabView.model,并给delegate的Button设置宽度,确保内容显示完整。

内容的提问来源于stack exchange,提问作者Daniel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 21:34:55