C++ qml 混合编程:QAbstractTableModel类的使用

平台:ubuntu16.04 x64和arm imx6d
Qt版本:5.6.2
背景: 最近开始常识使用qt quick开发项目,其界面开发能力没得说,非常强大。但开始后台业务开发的时候发现有些复杂结构的数据的传递时常困扰我。这次想实现c++代码完成数据的增删改查,qml界面TableView组件绑定model,自动更新c++代码处理后的model结果。通过继承QAbstractTableModel类来实现。中间查看了一些文章,发现可以实现model内容显示在TableView上,但是后台更新之后前端没有自动更新。研究了发现原来是更新方法忘了触发beginInsertRows和endInsertRows信号。下面贴一下主要过程和源码,数据库访问相关的都省略了,这篇主要就像记录一下QAbstractTableModel类的使用。
1、 在qt quick项目中新建一个Qt Item Model类,新建向导里面有现成的模板,直接选择,填写自己的类名,基类选QAbstractTableModel,下面的方法我这边都不勾选,具体可以按自己需求勾选,然后下一步直到完成添加;

2 按模板IDE自己生成了类文件,下面先贴源码。rowCount,columnCount,data,roleNames是父类的虚方法,必须要自己重写,insertUserInfo方法是我自己加的,用于表示model更新的操作,这里是添加,删改都类似,就不重复写了,只要别忘了触发beginInsertRows和endInsertRows信号就行,不然model内容更新了,前端表格组件里的内容是不会自动更新的。

userinfomodel.h

#ifndef USERINFOMODEL_H
#define USERINFOMODEL_H

#include <QAbstractTableModel>
#include <QtCore/QMetaEnum>

class UserInfoModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    enum DataRoles{
        userID = Qt::UserRole + 1,
        userName
    };
    Q_ENUM(DataRoles)
    explicit UserInfoModel(QObject *parent = 0);
    // Basic functionality:
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QHash<int, QByteArray> roleNames() const override;
    Q_INVOKABLE void insertUserInfo(QString userID,QString userName);
private:
    QList<QMap<QString,QVariant>> userList;
};

#endif // USERINFOMODEL_H

userinfomodel.cpp


#include "userinfomodel.h"

UserInfoModel::UserInfoModel(QObject *parent)
    : QAbstractTableModel(parent)
{
}

int UserInfoModel::rowCount(const QModelIndex &parent) const
{
    return userList.count();
}

int UserInfoModel::columnCount(const QModelIndex &parent) const
{
    return userList.at(0).size();
}

QVariant UserInfoModel::data(const QModelIndex &index, int role) const
{
    // FIXME: Implement me!
    QHash<int, QByteArray> temp=roleNames();
    QString key =  temp.value(role);
    return userList[index.row()].value(key);
}

QHash<int, QByteArray> ::roleNames() const
{
    QHash<int, QByteArray> roles;
    QMetaEnum metaEnum = QMetaEnum::fromType<DataRoles>();
    for (int i=0; i<metaEnum.keyCount(); ++i)
    {
        roles[metaEnum.value(i)]=QByteArray(metaEnum.key(i));
    }
    return roles;
}

void UserInfoModel::insertUserInfo(QString userID, QString userName)
{
    emit beginInsertRows(QModelIndex(),userList.count(),userList.count());
    QMap<QString,QVariant> mapTemp;
    mapTemp.insert("userID",userID);
    mapTemp.insert("userName",userName);
    userList.push_back(mapTemp);
    emit endInsertRows();
}

这样这个model就写好了。QList<QMap<QString,QVariant>> userList;用来放数据,然后自己增加了一个枚举DataRoles,可以对应数据表的列名。我这边用枚举的目的就是想实际项目后面不同表格都可以继承我自己写的这个类,然后把表结构以枚举的形式传入,这样就可以通过QMetaEnum类循环读取枚举包含的表结构,这样开发不同的表的时候,可以省去很多重复代码,后面还会考虑orm框架,不过大概看了一下好像c++的orm框架没有java,c#的好用,到时候再说吧。当然,你也可以在roleNames方法里面自己用字符串的形式把列名写死,这样不好的地方就是每一张表,你都得写一遍。

QHash<int, QByteArray> UserInfoModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    QMetaEnum metaColor = QMetaEnum::fromType<DataRoles>();
    for (int i=0; i<metaColor.keyCount(); ++i)
    {
        roles[metaColor.value(i)]=QByteArray(metaColor.key(i));
    }
    return roles;
}

3、 剩下来的事情就简单了,在main.cpp里面注册以下这个类
main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QtQml/QQmlContext>
#include "userinfomodel.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    qmlRegisterType<UserInfoModel>("UserInfoModel", 1, 0, "UserInfoModel");
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();
}

4、 在qml界面导入,然后就可以当成正常的qml组件用了
main.qml

import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Layouts 1.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.2
import UserInfoModel 1.0

Window {
    id:gridArea
    //anchors.centerIn: parent
    width: 1050;
    height: 457
    visible: true

    UserInfoModel{
        id:datamodel
    }
    TableView{
        id :tableView
        height: 409
        anchors.right: parent.right
        anchors.rightMargin: 0
        anchors.left: parent.left
        anchors.leftMargin: 0
        anchors.top: parent.top
        anchors.topMargin: 0
        frameVisible: false

        TableViewColumn {
            role: "userID"
            title: "ID"
            width: 80
        }
        TableViewColumn {
            role: "userName"
            title: "姓名"
            width: 80
        }

        model: datamodel

        //自定义表头代理
        headerDelegate:
            Rectangle{
            //width: 100;
            height: 20
            Text
            {
                anchors.verticalCenter: parent.verticalCenter
                anchors.horizontalCenter: parent.horizontalCenter
                text: styleData.value
                font.pixelSize: 16
                color: "#666666"
            }
        }
        itemDelegate:
            Item{


            Text {
                anchors.verticalCenter: parent.verticalCenter
                anchors.horizontalCenter: parent.horizontalCenter
                horizontalAlignment: Text.AlignHCenter
                color : "#333333"
                text: styleData.value
                font.pixelSize: 18
                elide: styleData.elideMode;
                width: parent.width

            }
        }


        focus:true
    }

    Button {
        id: button1
        x: 950
        y: 424
        text: qsTr("Button")
    }

    Connections {
        target: button1
        onClicked: {
            datamodel.insertUserInfo("002","张三");
        }
    }
}

5、 下图是最终效果,点击按钮,会添加002,张三,执行完datamodel.insertUserInfo(“002”,“张三”);之后,表格上也就自动更新好了。

在这里插入图片描述
结尾: 才开始使用qt quick开发项目,界面效果开发确实灵活,但资料相比传统qt是少了不少,只能靠自带文档。如果有疑问、错误或者更好地方法,可以留言一起沟通。

©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页