如何从 TableModel 添加 headers 到 qml TableView

How to add headers to qml TableView from TableModel

我需要向使用 Python 中定义的自定义模型的 TableView 添加 header。

我已经尝试用我的自定义 headers 覆盖 QAbstractTableModel 中的 headerData 函数。我正在执行此相同类型实现的 C++ 示例中描述的相同系列步骤:

不幸的是,header 仍然没有出现在 table 的顶部。然而,table 确实包含覆盖 QAbstractTableModel 中的数据函数的数据。

Python:

class RouteTableModel(QAbstractTableModel):

    def __init__(self, parent=None, *args): 
        super().__init__()
        self._datatable = None
        self._header = {
            0 : 'X',
            1 : 'Y',
            2 : 'Z'
        }

    def data(self, index, role=Value):
        i = index.row()
        j = index.column()
        if role == self.Value:
            return '{0}'.format(self._datatable[i][j]['value'])
        elif role == self.Selected:
            return self._datatable[i][j]['selected']
        else:
            return None

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return self._header[section]
            else:
                return None
        else:
            return None

# create the application instance
app = QApplication(sys.argv)

# create a QML engine
engine = QQmlApplicationEngine()

# instantiate the TableModel class
xyztablemodel = RouteTableModel()

engine.rootContext().setContextProperty('XYZTableModel', xyztablemodel)

# load main QML file and start app engine
engine.load('view.qml')

if not engine.rootObjects():
    sys.exit(-1)

sys.exit(app.exec_())

QML:

import QtQuick 2.12
import QtQuick.Controls 2.12
GridLayout {
    id: gridfpc2
    flow: GridLayout.TopToBottom
    rows: 4
    columns: 2
    rowSpacing: 20
    columnSpacing: 35

    TableView {
        id: xyztable
        Layout.rowSpan: 4
        // Layout.alignment: Qt.AlignCenter
        Layout.fillHeight: true

        model: XYZTableModel
        width: 350

        delegate: CustomComp.XYZTableDelegate {
            implicitWidth: parent.width / 3
            implicitHeight: 20
        }
    }
}

Python 或 qml 代码中没有出现错误消息。我希望 header 填充在 TableView 的列上方,但它们没有显示。

在您问题中指出的示例中,它来自与 QML 提供的 TableView 截然不同的 QTableView。在您的情况下,您使用的是 QtQuick 的 TableView。这个 TableView 没有 headers 所以你必须实现它,在我的例子中我将使用 Repeaters。另一方面,无法从 QML 访问 headerData,因此我将使用 @Slot() 实现 C++ 的 Q_INVOKABLE,将 returns 函数的变量类型的结果作为参数传递:

main.py

from PySide2 import QtCore, QtGui, QtQml


class RouteTableModel(QtCore.QAbstractTableModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._header = {0: "X", 1: "Y", 2: "Z"}

    def columnCount(self, parent=QtCore.QModelIndex()):
        return 3

    def rowCount(self, parent=QtCore.QModelIndex()):
        return 100

    def data(self, index, role=QtCore.Qt.DisplayRole):
        i = index.row()
        j = index.column()
        if role == QtCore.Qt.DisplayRole:
            return "{}-{}".format(i, j)

    @QtCore.Slot(int, QtCore.Qt.Orientation, result="QVariant")
    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role == QtCore.Qt.DisplayRole:
            if orientation == QtCore.Qt.Horizontal:
                return self._header[section]
            else:
                return str(section)


if __name__ == "__main__":
    import os
    import sys

    app = QtGui.QGuiApplication(sys.argv)

    engine = QtQml.QQmlApplicationEngine()

    xyztablemodel = RouteTableModel()
    engine.rootContext().setContextProperty("XYZTableModel", xyztablemodel)

    current_dir = os.path.dirname(os.path.realpath(__file__))
    filename = os.path.join(current_dir, "view.qml")

    engine.load(QtCore.QUrl.fromLocalFile(filename))

    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec_())

view.qml

import QtQuick 2.12
import QtQuick.Controls 2.4
import QtQuick.Window 2.11

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    color: '#222222'

    TableView {
        id: tableView
        columnWidthProvider: function (column) { return 100; }
        rowHeightProvider: function (column) { return 60; }
        anchors.fill: parent
        leftMargin: rowsHeader.implicitWidth
        topMargin: columnsHeader.implicitHeight
        model: XYZTableModel
        width: 350
        delegate: Rectangle {
            implicitWidth: 100
            implicitHeight: 50
            Text {
                text: display
            }
        }

        Rectangle { // mask the headers
            z: 3
            color: "#222222"
            y: tableView.contentY
            x: tableView.contentX
            width: tableView.leftMargin
            height: tableView.topMargin
        }

        Row {
            id: columnsHeader
            y: tableView.contentY
            z: 2
            Repeater {
                model: tableView.columns > 0 ? tableView.columns : 1
                Label {
                    width: tableView.columnWidthProvider(modelData)
                    height: 35
                    text: XYZTableModel.headerData(modelData, Qt.Horizontal)
                    color: '#aaaaaa'
                    font.pixelSize: 15
                    padding: 10
                    verticalAlignment: Text.AlignVCenter

                    background: Rectangle { color: "#333333" }
                }
            }
        }
        Column {
            id: rowsHeader
            x: tableView.contentX
            z: 2
            Repeater {
                model: tableView.rows > 0 ? tableView.rows : 1
                Label {
                    width: 40
                    height: tableView.rowHeightProvider(modelData)
                    text: XYZTableModel.headerData(modelData, Qt.Vertical)
                    color: '#aaaaaa'
                    font.pixelSize: 15
                    padding: 10
                    verticalAlignment: Text.AlignVCenter
                    background: Rectangle { color: "#333333" }
                }
            }
        }

        ScrollIndicator.horizontal: ScrollIndicator { }
        ScrollIndicator.vertical: ScrollIndicator { }
    }
}