在Qt Quick中从ListView制作自定义TableView的典型方法

Ori*_*ent 5 qt listview tableview qml qtquick2

制作餐桌的最佳方法是什么ListView

比方说,给定一个2d字符串数组,delegate所有列都是Labels.如何以及何时仅使用QML计算每列的最大项目宽度?每个的内容Label不是恒定的(即implicitWidth在寿命期间是可变的).

发明这个TableView事实的实际原因是,TreeView将保留一步.

Mar*_* Ch 9

关于在QML中创建表的问题似乎相当频繁地发布,但我还没有看到编译所有不同选项的答案.有很多方法可以实现您的要求.我希望在这个答案中提供一些替代方案.

TableView(5.12及更高版本)

(2019年1月14日更新)

Qt 5.12包含一个名为的新Qt Quick项目TableView,该项目已经从头开始重新设计,以便对具有任意数量的行或列的数据模型具有良好的性能.它解决了之前TableView来自`Quick Control 1 的性能问题.

我不能提供这种方法的用法示例,因为我还没有使用它(我仍在使用Qt 5.9作为我的项目),但是在5.12及更高版本的Qt文档中有一些例子.

如果你正在使用Qt 5.12,并且你知道你需要水平滚动和垂直滚动表(在视图中有更多的行和列),那么这似乎是首选解决方案.

以下是Qt 5.11及更早版本的替代方法的摘要,或者如果由于某种原因您不想使用Qt 5.12 TableView(或许这些替代方法中的一种更适合您的数据模型?).

网格布局

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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

垂直ListView

使用Vertical创建表ListView有其优点和缺点.优点:

  • 滚动
  • 动态创建在可视区域之外的代理,这意味着更快的加载
  • 易于为固定宽度的列创建,其中文本被省略或包裹

缺点:

  • 对于垂直滚动ListView(通常是人们想要的),动态列宽很难实现...即列宽设置为完全适合列中的所有值

必须使用该列中所有模型数据的循环来计算列宽,这可能很慢,并且不是您想要经常执行的操作(例如,如果用户可以修改单元格内容并且您希望列调整大小).

通过仅计算列宽一次,将模型分配给ListView,并且具有固定宽度和计算宽度列的混合,可以实现合理的折衷.

警告:下面是计算列宽以适合最长文本的示例.如果您有一个大型模型,您应该考虑废弃Javascript循环并求助于固定宽度列(或相对于视图大小的固定比例).

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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

TableView(5.11及更早版本)

(来自Quick Controls 1)

QC1有一个TableView组件.QC2没有(在Qt 5.9中).有一个正在开发中,但没有保证时间表.

TableView由于性能问题一直不受欢迎,但它确实在Quick Controls 1.0到1.4之间得到了改进,它仍然是一个可用的组件.QC1和QC2可以在同一个应用程序中混合使用.

优点

  • 易于实现电子表格式用户可调整大小的列
  • 基于a ListView,所以很好地处理大量的行.
  • 只有类似于QTableViewWidgets的内置组件

缺点

  • 默认样式是一种桌面灰色.您可能会花费更多时间来尝试覆盖样式,而不是使用a从头开始ListView.
  • 自动调整列大小以适应最长内容并不真正实用/不起作用.

例:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)