Take a look at how the TableView works in Qt6 with QML. If we refer to the example from the official documentation, we find this code:
import QtQuick 2.14 import Qt.labs.qmlmodels 1.0 TableView { anchors.fill: parent columnSpacing: 1 rowSpacing: 1 clip: true model: TableModel { TableModelColumn { display: "name" } TableModelColumn { display: "color" } rows: [ { "name": "cat", "color": "black" }, { "name": "dog", "color": "brown" }, { "name": "bird", "color": "white" } ] } delegate: Rectangle { implicitWidth: 100 implicitHeight: 50 border.width: 1 Text { text: display anchors.centerIn: parent } } }
It produces this:
If we add more columns to the model in the following manner:
model: TableModel { TableModelColumn { display: "name" } TableModelColumn { display: "color" } TableModelColumn { display: "feet" } TableModelColumn { display: "fly" } rows: [ { "name": "cat", "color": "black", "feet": "four", "fly": "no" }, { "name": "dog", "color": "brown", "feet": "four", "fly": "no" }, { "name": "bird", "color": "white", "feet": "two", "fly": "yes" } ] }
we have:
In this approach, not all elements are visible because each cell is of the same size. While this uniformity can be visually appealing, it may not always be practical. Next, let’s see how we can add a custom header and adjust the cells to have widths based on the size of the elements within them.
Let’s start with the header:
HorizontalHeaderView { id: horizontalHeader anchors.left: tableView.left anchors.top: parent.top syncView: tableView clip: true model: ["Name", "Color", "Feet", "Fly"] delegate: Rectangle { implicitWidth: txt.width + dp(5) implicitHeight: txt.height + dp(5) color: "darkgray" AppText { id: txt text: modelData anchors.centerIn: parent color: "white" } } }
First, we attach the header to the left side of the table and set the syncView
to the tableView
. This ensures synchronization between the table and the header, meaning each header column aligns with its corresponding column in the tableView
. Next, we define the labels for the header. Finally, we see in the delegate how to display the header’s cells.
The tableView:
TableView { id: tableView anchors.left: parent.left anchors.right: parent.right anchors.top: horizontalHeader.bottom anchors.bottom: parent.bottom columnSpacing: 1 rowSpacing: 1 clip: true contentWidth: parent.width columnWidthProvider: column => { return -1; } model: // is the same delegate: Rectangle { implicitWidth: Math.max(horizontalHeader.columnWidth(column), txtRow.width) + dp(5) implicitHeight: txtRow.height + dp(10) border.color: "darkgray" Text { id: txtRow text: display anchors.centerIn: parent } } }
The model remains the same as previously mentioned and is not included here. Regarding the positioning, the top of the table is anchored to the bottom of the header, as one would expect. The code enabling cell width to be based on the element’s width within the cell is as follows: columnWidthProvider: column => { return -1; }
. The columnWidthProvider
property expects a function that returns the width for a column. However, if it returns -1, the size is automatically calculated based on the size of the element in the cell.
Another important aspect to note is the use of the following code:
implicitWidth: Math.max(horizontalHeader.columnWidth(column), txtRow.width) + dp(5)
Why is this necessary? It ensures that if the text in the header of a column is wider than the text in the data cells, the size of the header remains readable. This code calculates the maximum width between the header and the row text, and then adds a small margin (dp(5)
) for better visual spacing.
The final result is:
“Note: English is not my native language, so please excuse any errors. I would appreciate your assistance in correcting them.”