QML TableView 实例演示 + 可能遇到的一些问题(Qt_6_5_3)

一、可能遇到的一些问题

Q1:如何禁用拖动?

在TableView下加一句代码即可:

javascript 复制代码
interactive: false

补充:这个属性并不专属于TableView,而是一个通用属性。很多Controls下的控件都可以使用,其主要作用就是控制交互的。

举个例子:像width、height、color这些都属于通用属性,并不是说只有一个控件需要指定它的宽高、颜色。这种通用特性在QML是非常常见的。

或许你想给某个控件实现某种功能,没准这个功能其实就是其他控件中的一个属性罢了。

Q2:设置了标题栏之后,程序直接卡死?

可能是Layout布局的问题,把Layout布局换成Item试试。

Q3:表头数据和内容数据重叠显示了?或者一片空白?

大概率还是布局的问题,检查一下吧!

补充:在 HorizontalHeaderView 中使用了 syncView 属性,就相当于是将 HorizontalHeaderView 和指定的 TableView 绑定了,它默认会有个布局,就是把水平标题栏放在表格内容的上方,但是左右的对齐方式可能并不是我们想要的,所以在做整体布局时需要注意这一点。

Q4:不想让用户编辑输入框?

将TextField设为只读就可以啦!

javascript 复制代码
readOnly: true

还有其它问题?

上面的几个问题是我在做这个实例的时候遇到的,如果你有别的问题可以在评论区留言,我会逐一回复哦~

二、QML TableView 实例演示

下面截图中的红框部分就是最终的效果,虽然看似简陋,实际确实有点简陋。。。。

好消息是,这个只是我用来演示给大家看的,并不是我个人审美就这样。。。。所以,等大家学会了怎么去用 TableView 这个控件,就按照自己的想法和需求去设计它吧!

实现步骤1:TableModel

在QML中,像TableView、ListView这种控件,视图(View)和数据(model)都是分开的。所以我们需要依次去完善他们。

那么,先来实现一个model吧!

javascript 复制代码
    //表头数据放TableModelColumn里,内容数据放rows里,由TableModel统一管理
    TableModel
    {
        id: tablemodel_2
        TableModelColumn { display: "状态" }  //后面表头会用到
        TableModelColumn { display: "简称" }
        TableModelColumn { display: "售价" }
        TableModelColumn { display: "库存" }

        rows:   //表格里具体显示的内容
                [
            {
                "状态": true,
                "简称": "AK-74自动步枪",
                "售价": 149,
                "库存": 500
            },

            {
                "状态": true,
                "简称": "81式自动步枪",
                "售价": 199,
                "库存": 300
            },

            {
                "状态": true,
                "简称": "M16突击步枪",
                "售价": 119,
                "库存": 100
            },

            {
                "状态": true,
                "简称": "SCAR突击步枪",
                "售价": 129,
                "库存": 100
            },

            {
                "状态": true,
                "简称": "HKG36突击步枪",
                "售价": 159,
                "库存": 100
            }
        ]
    }

在上面的代码片段中,最外层是1个TableModel,这个TableModel和TableView是同级的,不是写在TableView下面的哈,文章最后我会把完整的代码也发出来。

我们这个TableModel定义1个id叫tablemodel_2,因为等会后面需要通过id来调用这个TableModel里面的数据。

接着,我们连续写了4行TableModelColumn,这个是后面要用到的水平标题栏的数据,每一个TableModelColumn就代表1列。

最后是1个 rows,这个rows后面紧跟的一个是列表[ ],列表里面放的就是除表头以外的数据了。形式就是花括号里一对对的键和值,而键就是上面TableModelColumn中的文本,也就是列名,值才是每个单元格中具体要显示出来的数据。

然后你可能会想到,哎呀~这么多行代码下去,实际就显示5行数据,如果列很多、数据成千上万,这代码得写多长啊!

这点不用担心啦!因为QML的专家早就考虑到了。如果数据量很少,而且是固定的,model直接写出来就行了。如果数据多,那最好用C++的模型类来提供。需要注意的是:C++模型必须是QAbstractItemModel的子类。

这个C++的模型类有点复杂,后面我会专门写一篇文章来介绍它。

实现步骤2:TableView

做完数据层(model),下面我们就来做具体的外观(View)吧!请看下面代码:

View层,大概分为2个部分,蓝色框框里是一些整体的属性设置,绿色框框里是针对每一列数据的显示细节的描述。

在delegate中,我们写了4个DelegateChoice。一共是5行数据,为什么我们只写了4个?因为如果你只是简单的显示model里的数据、不会有交互,那么就统一用一个DelegateChoice就行啦!

什么叫不会有交互?你看前面截图中第一列的数据是不是都是复选框?复选框是干嘛的?不就是给用户去勾选、去交互的嘛~

所以像这种后期会交互的数据列,就要单独的DelegateChoice去写。我展开第一个DelegateChoice中的代码给你看一下:

javascript 复制代码
DelegateChoice {
                column: 0   //指定哪一列可编辑

                delegate: Rectangle
                {
                    implicitWidth: 150
                    implicitHeight: 40

                    color: t_tableView_2.currentRow === row ? "#c8e5b3" : "#eeeeee"

                    CheckBox
                    {
                        anchors.verticalCenter: parent.verticalCenter
                        anchors.left: parent.left

                        checked: model.display  //CheckBox的默认状态就是模型里的值
                        onToggled: model.display = checked  //编辑即更新数据到模型

                        Material.accent: "green"
                    }
                }
            }

看到了吗?里面竟然还有一个delegate?这个最里层的delegate就是设置具体的单元格的显示方式啦!

可能有了解ListView的小伙伴好奇了,我之前做ListView的时候只需要1个delegate就行了,怎么这个TableView还要套2层delegate,外面竟然还有个什么DelegateChoice?这个DelegateChoice又是什么?干啥用的?

这个DelegateChoice是按列去设置的,每一列都有不同的情况和需求,如果我们这个表格是涉及到交互、而不单单是给用户看看的,那就需要用DelegateChoice对具体的列去设置,因为可能第1列全部是复选框,第3列又全都是单行输入框,怎么都得去单独设置~

实现步骤3:HorizontalHeaderView

这个HorizontalHeaderView就是水平标题栏啦!

javascript 复制代码
HorizontalHeaderView {
        id: horizontalHeaderView

        syncView: t_tableView_2

        interactive: false  //禁用拖动

        delegate: Rectangle {
            implicitWidth: 150; implicitHeight: 40; color: "transparent"

            Text {
                text: tablemodel_2.columns[index].display
                font.bold: true

                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.left
                anchors.leftMargin: 10
            }
        }
    }

这个水平标题栏和TableView也是同级的,不要写到TableView里面去喽!

在QML中,标题栏和表格是分开设置的,所以设置了标题栏之后需要通过syncView属性去绑定具体的TableView。

然后这个水平标题栏也是默认有交互效果的,如果你不想让它动,就也用interactive属性禁用吧!

实现步骤4:降序、升序

排序功能就是对model中的数据进行冒泡排序,如果默认是降序,那么对降序取反就是升序啦!

冒泡排序是写在了MouseArea中的onClicked信号下,细心的小伙伴会发现,这个MouseArea覆盖的是Text,而不是Rectangle。

这是因为,如果直接覆盖Rectangle,那么HorizontalHeaderView自带的列宽调整功能就失效啦!

javascript 复制代码
    HorizontalHeaderView {
        id: horizontalHeaderView

        syncView: t_tableView_2

        interactive: false            //禁用拖动

        property bool isDesc: true    //默认表格内的数据是降序排列

        delegate: Rectangle {
            implicitWidth: 150; implicitHeight: 40; color: "transparent"

            Text {
                text: tablemodel_2.columns[index].display + " " + ( horizontalHeaderView.isDesc ? "∨" : "∧" )   //添加排序标识符号
                font.bold: true
                font.family: "Microsoft YaHei"

                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.left
                anchors.leftMargin: 10

                MouseArea {
                    anchors.fill: parent    //排序生效区域覆盖文字区域而不是整个矩形,否则表头的列宽调整功能将失效

                    onClicked: {
                        var headerKey = tablemodel_2.columns[index].display

                        for ( var i = tablemodel_2.rowCount -1; i > 0; i-- ) {
                            for ( var j = 0; j < i; j++ ) {
                                if ( horizontalHeaderView.isDesc ) {
                                    if ( tablemodel_2.getRow(j)[headerKey] > tablemodel_2.getRow(j + 1)[headerKey]) {
                                        tablemodel_2.moveRow(j, j + 1, 1)
                                    }
                                }

                                else {
                                    if ( tablemodel_2.getRow(j)[headerKey] < tablemodel_2.getRow(j + 1)[headerKey] ) {
                                        tablemodel_2.moveRow(j, j + 1, 1)
                                    }
                                }
                            }
                        }

                        horizontalHeaderView.isDesc = !horizontalHeaderView.isDesc
                    }
                }
            }
        }
    }

完整代码

javascript 复制代码
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.qmlmodels    //TableModel需要用到的库

Item {

    //表头数据放TableModelColumn里,内容数据放rows里,由TableModel统一管理
    TableModel
    {
        id: tablemodel_2
        TableModelColumn { display: "状态" }  //后面表头会用到
        TableModelColumn { display: "简称" }
        TableModelColumn { display: "售价" }
        TableModelColumn { display: "库存" }

        rows:   //表格里具体显示的内容
                [
            {
                "状态": true,
                "简称": "AK-74自动步枪",
                "售价": 149,
                "库存": 500
            },

            {
                "状态": true,
                "简称": "81式自动步枪",
                "售价": 199,
                "库存": 300
            },

            {
                "状态": true,
                "简称": "M16突击步枪",
                "售价": 119,
                "库存": 100
            },

            {
                "状态": true,
                "简称": "SCAR突击步枪",
                "售价": 129,
                "库存": 100
            },

            {
                "状态": true,
                "简称": "HKG36突击步枪",
                "售价": 159,
                "库存": 100
            }
        ]
    }

    TableView
    {
        id: t_tableView_2

        width: contentWidth
        height: contentHeight

        anchors.top: horizontalHeaderView.bottom

        interactive: false  //禁止拖动
        rowSpacing: 1       //行间距。列间距是columnSpacing

        model: tablemodel_2

        //行选择,选中事件的处理
        selectionModel: ItemSelectionModel {}

        //指定可编辑的列,把CheckBox控件放进表格中
        delegate: DelegateChooser {
            DelegateChoice {
                column: 0   //指定哪一列可编辑

                delegate: Rectangle
                {
                    implicitWidth: 150
                    implicitHeight: 40

                    color: t_tableView_2.currentRow === row ? "#c8e5b3" : "#eeeeee"

                    CheckBox
                    {
                        anchors.verticalCenter: parent.verticalCenter
                        anchors.left: parent.left

                        checked: model.display  //CheckBox的默认状态就是模型里的值
                        onToggled: model.display = checked  //编辑即更新数据到模型

                        Material.accent: "green"
                    }
                }
            }

            DelegateChoice {
                column: 2

                delegate: Rectangle
                {
                    implicitWidth: 150
                    implicitHeight: 40

                    color: t_tableView_2.currentRow === row ? "#c8e5b3" : "#eeeeee"

                    BasicTextField
                    {
                        anchors.verticalCenter: parent.verticalCenter
                        anchors.left: parent.left
                        anchors.leftMargin: 10

                        width: 80; height: 30

                        text: model.display

                        onAccepted: model.display = text            //回车更新数据
                        onEditingFinished: model.display = text     //焦点改变更新数据
                    }
                }
            }

            DelegateChoice {
                column: 3

                delegate: Rectangle
                {
                    implicitWidth: 150; implicitHeight: 40
                    color: t_tableView_2.currentRow === row ? "#c8e5b3" : "#eeeeee"

                    BasicTextField
                    {
                        anchors.verticalCenter: parent.verticalCenter
                        anchors.left: parent.left
                        anchors.leftMargin: 10

                        width: 80; height: 30

                        text: model.display

                        onAccepted: model.display = text            //回车更新数据
                        onEditingFinished: model.display = text     //焦点改变更新数据
                    }
                }
            }

            DelegateChoice   //默认显示方式,不指定具体列
            {
                delegate: Rectangle
                {
                    implicitWidth: 150; implicitHeight: 40

                    //选中行变色(生效前提是:为selectionModel分配一个ItemSelectionModel)
                    color: t_tableView_2.currentRow === row ? "#c8e5b3" : "#eeeeee"

                    Text
                    {
                        text: display

                        anchors.verticalCenter: parent.verticalCenter
                        anchors.left: parent.left
                        anchors.leftMargin: 10
                    }
                }
            }
        }
    }

    HorizontalHeaderView {
        id: horizontalHeaderView

        syncView: t_tableView_2

        interactive: false  //禁用拖动

        delegate: Rectangle {
            implicitWidth: 150; implicitHeight: 40; color: "transparent"

            Text {
                text: tablemodel_2.columns[index].display
                font.bold: true

                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.left
                anchors.leftMargin: 10
            }
        }
    }
}
相关推荐
neter.asia8 分钟前
C# 窗体应用程序嵌套web网页,基于谷歌浏览器内核(含源码)
开发语言·c#
2301_8091774725 分钟前
sqoop,flume草稿
开发语言
潜意识起点40 分钟前
Java数组:静态初始化与动态初始化详解
java·开发语言·python
竹影卿心42 分钟前
Java连接HANA数据库
java·数据库·windows
点云SLAM1 小时前
C++创建文件夹和文件夹下相关操作
开发语言·c++·算法
2301_809177471 小时前
2025.01.15python商业数据分析
开发语言·python
_小柏_1 小时前
C/C++基础知识复习(46)
c语言·开发语言·c++
SomeB1oody1 小时前
【Rust自学】6.4. 简单的控制流-if let
开发语言·前端·rust
明月逐人归4641 小时前
输出语句及变量定义
开发语言·python
.m2 小时前
Windows配置IE浏览器不自动跳转到Edge
windows