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

