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 

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 m_data;
};
#include 

DataEntryModel::DataEntryModel(QObject *parent):
    QAbstractListModel(parent)
{
    // initialize our data (QList) 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 m_data存放数据,重写rowCount()、data()虚函数,QML内部实现是通过这两个接口获取行数和数据的,其中data()接口的第二个参数role使用的默认的类型Qt::DisplayRole,还可以自定义自己的类型,后面会做介绍。

注册QML类型:

qmlRegisterType("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
        }
    }
}

运行结果:

QML使用C++ model(基本用法)_第1张图片

 自定义类型的model

#include 

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 roleNames() const override;

private:
    QList 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 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 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 DataListModel::roleNames() const
{
    QHash 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"
                    }
                }


            }

        }
    }
}

QML使用C++ model(基本用法)_第2张图片

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

结语

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

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

你可能感兴趣的:(QML,qt,model/view)