Matt Brauer
Matt Brauer

Reputation: 231

How to add headers to qml TableView from TableModel

I need to add a header to a TableView that uses a custom model defined in Python.

I've tried overriding the headerData function in QAbstractTableModel with my custom headers. I'm following the same series of steps described in this C++ example of the same type of implementation: Header to a TableView

Unfortunately, the headers still don't show up at the top of the table. The table does however contain the data from overriding the data function in 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
        }
    }
}

No error messages occur in the Python or qml code. I'd expect the header to populate above the columns in the TableView, but they do not show up.

Upvotes: 2

Views: 4452

Answers (1)

eyllanesc
eyllanesc

Reputation: 243955

In the example you indicate in your question, it is from a QTableView that is very different from the TableView offered by QML. In your case you are using the TableView of QtQuick. This TableView has no headers so you must implement it, in my example I will use Repeaters. On the other hand headerData is not accessible from QML, so I will implement the Q_INVOKABLE of C++ using @Slot(), passing as an argument the result of the type of variable that returns the function:

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 { }
    }
}

Upvotes: 4

Related Questions