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
            }
        }
    }
}
相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript