【问题标题】:Canonical way to make custom TableView from ListView in Qt Quick在 Qt Quick 中从 ListView 制作自定义 TableView 的规范方法
【发布时间】:2017-07-18 13:47:52
【问题描述】:

ListView 制作表格的最佳方法是什么?

假设给定一个二维字符串数组,delegate 的所有列都是Labels。仅使用 QML 时如何以及何时计算每列的最大项目宽度?每个Label 的内容不是恒定的(即implicitWidth 在生命周期内是可变的)。

发明TableView 的实际原因是,到TreeView 的一步仍然存在。

【问题讨论】:

  • 1.规范的方式不是使用二维字符串数组。它将是 QAbstractItemModel-descendent 或 ListModel
  • 2.您无法确定最长的delegate 的理想大小,除非您创建了它。通常你会想要懒惰地创建delegates,所以你不会确定长度。无论如何,从设计的角度来看,最好根据窗口大小设置大小。否则,下次使用视图时,文本会变长,破坏您宝贵的设计。对于内容创建者无法限制自己的情况,请使用elide。如果你不想偷懒,可以用GridLayout 和一些Repeaters 来填充它。
  • 没有很好的方法来做到这一点,但有一种方法。我现在没有时间提供答案,但看看我对这个问题的回答:45029968/how-do-i-set-the-combobox-width-to-fit-the-largest-item。原理是连接onModelChanged,并使用一个javascript循环和一个TextMetrics组件来计算模型中每个文本字符串的宽度。 onModelChanged 只会被调用一次。 onWidthChanged 等其他信号处理程序可能会导致递归。
  • @derM 我只是忘记了,QML 中没有整数宽度。
  • @MahdiKhalili Qt 5.12 中有TableView。再也不用自己发明了。

标签: qt listview qml tableview qtquick2


【解决方案1】:

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

TableView(5.12 及更高版本)

(2021 年 7 月 16 日更新)

Qt 5.12 包含一个名为TableView 的新Qt Quick 项目,它经过重新设计,可以为具有任意数量的行或列的数据模型提供良好的性能。它解决了之前的TableView from`Quick Controls 1 中存在的性能问题。

在创建此答案时 TableView 不存在,但我在最近的答案中提供了新 TableView 的使用示例:https://stackoverflow.com/a/68347396/5414907

它为基于委托implicitWidth 调整列宽提供了良好的内置支持,但它仅对视口中的行这样做,这意味着滚动可能会显示不适合列的数据,除非你强制使用forceLayout()

如果您使用的是 Qt 5.12,并且您知道您的表格需要水平滚动和垂直滚动(视图中的行和列数过多),那么这似乎是首选解决方案。

Qt 在此处提供了新 TableView 与旧 TableView 的性能比较:http://blog.qt.io/blog/2018/12/20/tableview-performance/

下面是 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
            }
        }
    }
}

垂直列表视图

使用 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
            }
        }
    }
}

TableView(5.11 及更早版本)

(来自快速控制 1)

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

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

优点

  • 轻松实现电子表格样式的用户可调整大小的列
  • 基于ListView,因此可以很好地处理大量行。
  • 只有类似于 Widgets 中的 QTableView 的内置组件

缺点

  • 默认样式是一种桌面灰色。与使用 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
        }
    }
}

【讨论】:

    猜你喜欢
    • 2017-09-11
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 2016-03-12
    • 1970-01-01
    相关资源
    最近更新 更多