QML使用C++ model(基本用法)

前言

界面开发无外乎是布局和界面的美化,而现代业务显示中最常见的非列表、表格莫属,列表表格的界面在Qt中采用model/view进行开发,model/view能够很好实现业务与界面的分离,对于复杂的业务来说是非常有用的。

Qt QML的model/view相对于widget更简单易用。QML提供有一些model,如数组、键值对等,这些model都相对比较简易,处理复杂的数据和多样的业务就比较费劲了。

所以,对于复杂的数据,Qt提供了另外一种方式来解决,就是使用C++的model,即model的数据和业务处理都是用C++。我们熟知的用C++写业务,QML来写页面,QML使用C++的model来显示,这就是一个很典型的C++和QML分工合作的体现。

下面就具体来看看是如何使用的

基本用法

C++的model需要继承自QAbstractListModel,并重写QAbstractListModel的虚函数

#include <QAbstractListModel>

class DataEntryModel : public QAbstractListModel
{
    Q_OBJECT
public:
    explicit DataEntryModel(QObject *parent = nullptr);
    ~DataEntryModel();

public: // QAbstractItemModel interface
    int rowCount(const QModelIndex &parent) const override;
    QVariant data(const QModelIndex &index, int role) const override;
private:
    QList<QString> m_data;
};
#include <QColor>

DataEntryModel::DataEntryModel(QObject *parent):
    QAbstractListModel(parent)
{
    // initialize our data (QList<QString>) with a list of color names
    m_data = QColor::colorNames();
}

DataEntryModel::~DataEntryModel()
{

}

int DataEntryModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    // return our data count
    return m_data.count();
}

QVariant DataEntryModel::data(const QModelIndex &index, int role) const
{
    // the index returns the requested row and column information.
    // we ignore the column and only use the row information
    int row = index.row();

    // boundary check for the row
    if(row < 0 || row >= m_data.count()) {
        return QVariant();
    }

    // A model can return data for different roles.
    // The default role is the display role.
    // it can be accesses in QML with "model.display"
    switch(role) {
    case Qt::DisplayRole:
        // Return the color name for the particular row
        // Qt automatically converts it to the QVariant type
        return m_data.value(row);
    }

    // The view asked for other data, just return an empty QVariant
    return QVariant();
}

该例子只做基本的显示,维护一个QList<QString> m_data存放数据,重写rowCount()、data()虚函数,QML内部实现是通过这两个接口获取行数和数据的,其中data()接口的第二个参数role使用的默认的类型Qt::DisplayRole,还可以自定义自己的类型,后面会做介绍。

注册QML类型:

qmlRegisterType<DataEntryModel>("org.example", 1, 0, "DataEntryModel");

QML调用:

import QtQuick 2.15
import QtQuick.Window 2.15
import org.example 1.0

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    ListView {
        id: view
        anchors.fill: parent
        model: DataEntryModel {}
        delegate: Text {
            // use the defined model role "display"
            text: model.display
        }
    }
}

运行结果:

 自定义类型的model

#include <QAbstractListModel>

class ModelData;
class DataListModel : public QAbstractListModel
{
    Q_OBJECT
public:
    explicit DataListModel(QObject *parent = nullptr);

    enum AnimalRoles {
        TypeRole = Qt::UserRole + 1,
        SizeRole1
    };

    int rowCount(const QModelIndex & parent = QModelIndex()) const override;
    QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
    Q_INVOKABLE void insert(int index, const ModelData &data);
    Q_INVOKABLE void append(const ModelData &data);
    Q_INVOKABLE void remove(int index);
    Q_INVOKABLE void append(const QVariantMap map);

signals:
    void countChanged(int arg);

private:
    int count() const;

protected:
    QHash<int, QByteArray> roleNames() const override;

private:
    QList<ModelData> m_list;
};


class ModelData
{
public:
    ModelData(const QString &type, const QString &size):
        m_type(type), m_size(size)
    {

    }

    QString type() const {return m_type;}
    QString size() const {return m_size;}

    void setType(const QString &type) {m_type = type;}
    void setSize(const QString & size) {m_size = size;}

private:
    QString m_type;
    QString m_size;
};

代码中可以看到维护了一个自定义类型的list-QList<ModelData> m_list,并增加了两个枚举类型,对应自定义ModelData里的成员变量,并提供了增删的接口,还重写了setData(),实现该接口QML可直接对数据进行修改,这个model相当于支持了增删改的功能。这个model就比上面的要复杂得多,能够应付一般的项目使用了

DataListModel::DataListModel(QObject *parent):
    QAbstractListModel(parent)
{

}

int DataListModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return m_list.count();
}

QVariant DataListModel::data(const QModelIndex &index, int role) const
{
    if (index.row() < 0 || index.row() >= m_list.count())
        return QVariant();

    const ModelData &data = m_list[index.row()];
    // qDebug() << "row: " << index.row();

    if (role == TypeRole)
        return data.type();
    else if (role == SizeRole1)
        return data.size();

    return QVariant();
}

bool DataListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (index.row() < 0 || index.row() >= m_list.count())
        return false;

    ModelData &data = m_list[index.row()];
    if (role == TypeRole)
        data.setType(value.toString());
    else if (role == SizeRole1)
        data.setSize(value.toString());

    QVector<int> roles = {role};
    emit dataChanged(index, index, roles);
    return true;
}

void DataListModel::insert(int index, const ModelData &data)
{
    if(index < 0 || index > m_list.count()) {
        return;
    }

    beginInsertRows(QModelIndex(), index, index);
    m_list.insert(index, data);
    endInsertRows();
    emit countChanged(m_list.count());
}

void DataListModel::append(const ModelData &data)
{
    insert(count(), data);
}

void DataListModel::remove(int index)
{
    if(index < 0 || index >= m_list.count()) {
        return;
    }

    beginRemoveRows(QModelIndex(), index, index);
    m_list.removeAt( index );
    endRemoveRows();
    emit countChanged(m_list.count());
}

void DataListModel::append(const QVariantMap map)
{
    QString type = map["type"].toString();
    QString size = map["size"].toString();

    ModelData data(type, size);

    insert(count(), data);
}

int DataListModel::count() const
{
    return rowCount(QModelIndex());
}

QHash<int, QByteArray> DataListModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[TypeRole] = "type";
    roles[SizeRole1] = "size";
    return roles;
}

QML使用

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import org.example 1.0

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    DataListModel {
        id: myModel

        Component.onCompleted: {
            //增加数据
            myModel.append({"type": "11","size": "222"})
            myModel.append({"type": "222","size": "333"})
            myModel.append({"type": "333","size": "444"})
        }
    }

    ListView {
        id: listview
        clip: true
        width: parent.width
        height: parent.height

        model: myModel
        delegate: Item {
            id: delegate
            width: listview.width
            height: 30
            Row {
                spacing: 5
                Text {
                    //取值
                    text: type
                }

                Text {
                    //取值
                    text: size
                    color: "red"
                }

                Button  {
                    height: parent.height
                    onClicked: {
                        //修改值
                        size = "0000"
                    }
                }


            }

        }
    }
}

 可以看到取值是通过type、size就能拿到数据,这些关键词是在roleNames()中定义的,猜测应该是通过QHash<int, QByteArray>的value取到key值,即role类型,再用role通过data接口获取值进行显示的,具体是不是这样得去看源码才知道,没有具体研究过。

结语

以上就是QML显示C++model的基本用法介绍,QML book关于这方面的介绍点这里查看。下一篇将介绍C++model的一些高级用法,如过滤排序撤销。

Demo github地址:GitHub - a137748099/QMLModelView: c++ model with QML view

  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值