Canonical way to create custom TableView from ListView in Qt Quick

What's the best way to make a table from ListView

?

Let's say given a 2d array of strings and delegate

for all columns Label

s. How and when to calculate the maximum element width for each column when using QML only? The content of each is Label

not permanent (i.e., it implicitWidth

is changeable over a lifetime).

The practical reason for the invention TableView

is that 1 step TreeView

will remain.

+3


source to share


1 answer


Questions about creating tables in QML seem to be posted quite often, but I have not yet seen an answer compiling all the different options. There are many ways to accomplish what you ask. I hope to provide a number of alternatives in this answer, however this will take me a while.

To get started, I used a suggestion from the comments with GridLayout

.

GridLayout

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    ListModel {
        id: listModel
        ListElement { name: 'item1'; code: "alpha"; language: "english" }
        ListElement { name: 'item2'; code: "beta"; language: "french" }
        ListElement { name: 'item3'; code: "long-code"; language: "long-language" }
    }

    GridLayout {
        flow: GridLayout.TopToBottom
        rows: listModel.count
        columnSpacing: 0
        rowSpacing: 0

        Repeater {
            model: listModel

            delegate: Label {
                Layout.fillHeight: true
                Layout.fillWidth: true
                Layout.preferredHeight: implicitHeight
                Layout.preferredWidth: implicitWidth
                background: Rectangle { border.color: "red" }
                text: name
            }
        }
        Repeater {
            model: listModel

            delegate: Label {
                Layout.fillHeight: true
                Layout.fillWidth: true
                Layout.preferredHeight: implicitHeight
                Layout.preferredWidth: implicitWidth
                background: Rectangle { border.color: "green" }
                text: code
            }
        }
        Repeater {
            model: listModel

            delegate: Label {
                Layout.fillHeight: true
                Layout.fillWidth: true
                Layout.preferredHeight: implicitHeight
                Layout.preferredWidth: implicitWidth
                background: Rectangle { border.color: "blue" }
                text: language
            }
        }
    }
}

      

Vertical ListView

Creating a vertical table ListView

has advantages and disadvantages. Pros:

  • scrolling
  • Dynamically creating delegates that are outside the visible area, which should mean faster loading
  • Easily create fixed-width columns where text is stripped or wrapped

Minuses:

  • For vertical scrolling ListView

    (which is usually what people want), dynamic column width is difficult to achieve ... that is, the column width will fully match all values ​​in the column

Column widths must be calculated using a loop over all the model data within that column, which can be slow and not something you would like to do often (for example, if the user can change the contents of the cells and you want the column to resize).

A reasonable compromise can only be reached by calculating the column widths once, when the model is assigned ListView

and has a mixture of fixed width and calculated width columns.

Warning. Below is an example of calculating column widths to fit long text. If you have a large model, you should consider going through a Javascript loop and using fixed width columns (or fixed proportions relative to the size of the view).



import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    ListModel {
        id: listModel
        ListElement { name: 'item1'; code: "alpha"; language: "english" }
        ListElement { name: 'item2'; code: "beta"; language: "french" }
        ListElement { name: 'item3'; code: "long-code"; language: "long-language" }
    }

    ListView {
        property var columnWidths: ({"name": 100, "code": 50}) // fixed sizes or minimum sizes
        property var calculatedColumns: ["code", "language"]   // list auto sized columns in here

        orientation: Qt.Vertical
        anchors.fill: parent
        model: listModel

        TextMetrics {
            id: textMetrics
        }

        onModelChanged: {
            for (var i = 0; i < calculatedColumns.length; i++) {
                var role = calculatedColumns[i]
                if (!columnWidths[role]) columnWidths[role] = 0
                var modelWidth = columnWidths[role]
                for(var j = 0; j < model.count; j++){
                    textMetrics.text = model.get(j)[role]
                    modelWidth = Math.max(textMetrics.width, modelWidth)
                }
                columnWidths[role] = modelWidth
            }
        }

        delegate: RowLayout {

            property var columnWidths: ListView.view.columnWidths
            spacing: 0

            Label {
                Layout.fillHeight: true
                Layout.fillWidth: true
                Layout.preferredHeight: implicitHeight
                Layout.preferredWidth: columnWidths.name
                background: Rectangle { border.color: "red" }
                text: name
            }

            Label {
                Layout.fillHeight: true
                Layout.fillWidth: true
                Layout.preferredHeight: implicitHeight
                Layout.preferredWidth: columnWidths.code
                background: Rectangle { border.color: "green" }
                text: code
            }

            Label {
                Layout.fillHeight: true
                Layout.fillWidth: true
                Layout.preferredHeight: implicitHeight
                Layout.preferredWidth: columnWidths.language
                background: Rectangle { border.color: "blue" }
                text: language
            }
        }
    }
}

      

TableView

(from Quick Controls 1)

QC1 has a component TableView

. QC2 is not (in Qt 5.9). There is one in development, but no guaranteed time.

TableView

was unpopular due to performance issues, but it got improvements between Quick Controls 1.0 to 1.4 and remained a useful component. QC1 and QC2 can be mixed in one application.

Arguments

  • easy to get columns with user editable table style tables.
  • based ListView

    , therefore handles large numbers of lines well.
  • only inline component like QTableView

    from widgets

against

  • the default style is a kind of desktop gray. You could spend more time trying to override the style than if you started from scratch with ListView

    .
  • Automatically resizing columns to fit the longest content isn't very practical / doesn't work.

Example:

import QtQuick 2.7
import QtQuick.Controls 1.4 as QC1
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

ApplicationWindow {
    visible: true
    width: 400
    height: 200

    ListModel {
        id: listModel
        ListElement { name: 'item1'; code: "alpha"; language: "english" }
        ListElement { name: 'item2'; code: "beta"; language: "french" }
        ListElement { name: 'item3'; code: "long-code"; language: "long-language" }
    }

    QC1.TableView {
        id: tableView
        width: parent.width
        model: listModel

        QC1.TableViewColumn {
            id: nameColumn
            role: "name"
            title: "name"
            width: 100
        }
        QC1.TableViewColumn {
            id: codeColumn
            role: "code"
            title: "code"
            width: 100
        }
        QC1.TableViewColumn {
            id: languageColumn
            role: "language"
            title: "language"
            width: tableView.viewport.width - nameColumn.width - codeColumn.width
        }
    }
}

      

+4


source







All Articles