在 Qt Quick 中从 ListView 制作自定义 TableView 的规范方法 [英] Canonical way to make custom TableView from ListView in Qt Quick

查看:33
本文介绍了在 Qt Quick 中从 ListView 制作自定义 TableView 的规范方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

What is the best way to make table from ListView?

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

Say, given a 2d array of strings and delegate for all the columns are Labels. How and when to calculate maximum item width for each column while using only QML? Content of each Label is not constant (i.e. implicitWidth is mutable during lifetime).

发明 TableView 的实际原因是,到 TreeView 的 1 步将保留.

Practical reason to invent the TableView is the fact, that 1 step to TreeView will remain.

推荐答案

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

Questions about creating tables in QML seem to get posted fairly frequently, but I am yet to see an answer compiling all the different options. There are lots of ways to achieve what you are asking. I hope to provide in this answer a number of alternatives.

(2021 年 7 月 16 日更新)

(Updated 16/07/2021)

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

Qt 5.12 includes a new Qt Quick item called TableView, which has been redesigned from the ground up to have good performance for a data model with any number of rows or columns. It resolves the performance problems which were present in the previous TableView from`Quick Controls 1.

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

At the time of creating this answer TableView did not exist, but I have provided a usage example for the new TableView in a more recent answer here: https://stackoverflow.com/a/68347396/5414907

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

It provides good built-in support for sizing the column widths based on the delegate implicitWidth, but it does so only for the rows in the viewport, which means that scrolling could reveal data which does not fit in the column, unless you force a forceLayout().

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

If you are using Qt 5.12, and you know that you will need both horizontal scrolling and vertical scrolling for your table (there are more rows AND columns than can fit in the view), then this would seem to be the first choice solution.

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

Qt provided a performance comparison of the new TableView vs the old one here: http://blog.qt.io/blog/2018/12/20/tableview-performance/

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

Below are a summary of alternative approaches for Qt 5.11 and earlier, or if for some reason you do not want to use the Qt 5.12 TableView (perhaps one of these alternative approaches better suits your data model?).

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 创建表格有其优点和缺点.优点:

Vertical ListView

Creating a table with the Vertical ListView has its advantages and disadvantages. Pros:

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

缺点:

  • 对于一个垂直滚动的ListView(这通常是人们想要的),动态列宽很难实现……即列宽设置为完全适合列中的所有值
  • For a vertical scrolling ListView (which is usually what people want), dynamic column width is difficult to achieve... i.e. column width is set to completely fit all values in the column

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

Column widths must be calculated using a loop over all the model data inside that column, which could be slow and is not something you would want to perform often (for example if user can modify cell contents and you want the column to resize).

当模型分配给 ListView 时,只计算一次列宽,并且混合使用固定宽度和计算宽度的列,可以实现合理的折衷.

A reasonable compromise can be achieved by only calculating the column widths once, when the model is assigned to the ListView, and having a mixture of fixed-width and calculated-width columns.

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

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

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)

TableView (5.11 and earlier)

(from Quick Controls 1)

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

QC1 has a TableView component. QC2 does not (in Qt 5.9). There is one in development, but with no guaranteed timescale.

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

TableView has been unpopular due to performance issues, but it did receive improvements between Quick Controls 1.0 to 1.4, and it remains a useable component. QC1 and QC2 can be mixed in the same application.

优点

  • 轻松实现电子表格样式的用户可调整大小的列
  • 基于 ListView,因此可以很好地处理大量行.
  • 只有类似于 Widgets 中的 QTableView 的内置组件
  • easy to achieve spreadsheet-style user-resizable columns
  • based on a ListView, so handles large numbers of rows well.
  • only built-in component resembling the QTableView from Widgets

缺点

  • 默认样式是一种桌面灰色.与使用 ListView 从头开始​​相比,您可能会花费更多时间尝试覆盖样式.
  • 自动调整列大小以适应最长的内容不太实用/实际上不起作用.
  • default styling is a sort of desktop-grey. You might spend more time trying to override the styling than if you started from scratch using a ListView.
  • auto resizing columns to fit longest contents not really practical / doesn't really work.

例子:

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

这篇关于在 Qt Quick 中从 ListView 制作自定义 TableView 的规范方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆