QML中ListView使用cpp的Model

ListView的模型可以直接在qml上使用ListModel、XmlListModel 、ObjectModel等。
但总有时候需要用到c++的模型,比如说后台数据动态刷新等。

ListView使用c++的模型,必须要继承QAbstractItemModel或者其子类。
为了方便以后查看和理解,本文先做一些讲解,在文章最后面有完整的代码可以查看。

第一步 创新一个模型类

利用Qt创建一个c++的类,并继承QAbstractListModelQAbstractListModelQAbstractItemModel的子类。

class MyListViewModel : public QAbstractListModel
{
    Q_OBJECT
public:
    explicit MyListViewModel(QObject *parent = nullptr);
};

第二步 创建一个结构体

这个结构体,是用来表示ListView中的数据结构的,也可以创建一个类来使用,但我觉得结构体的代码更少,更美观(就是懒)。
定义好结构体后,再用一个QList放数据列表, ListView的数据就都在这个QList里头了。

private:
    struct Data{
        QString title_;
        QString content_;
        bool select_;
    };

    QList<Data> dataList_;

第三步 重写函数

我们在继承了QAbstractListModel后,有几个函数是必须要重写的,不然ListView可能会拿不到数据。
第一个:int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    这个是为了让ListView获取到数据的个数;
    因为我们的数据存放在QList中,所以获取QList的个数就好了。
第二个:virtual QHash<int, QByteArray> roleNames() const override;
    这个是为了给ListView获取数据时,能使用一个别名;
    利用这个别名,就能获取相对应的数据;
    这里需要定义一个enum来做配合。
第三个:QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    这个是ListView获取数据时调用的函数;
    数据是从QList中Data获取的;
    但到底是要获取title_,还是content_,需要跟函数roleNames打配合。
看代码:
MyListViewModel.h

public:
    // 这几个函数必须要重写
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    virtual QHash<int, QByteArray> roleNames() const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    
private:
    enum DataRoles{
        TitleRole = Qt::UserRole + 1,
        ContentRole,
        SelectRole,
    };

MyListViewModel.cpp

/** ListView需要获取数据的数量
 * @brief MyListViewModel::rowCount
 * @param parent
 * @return
 */
int MyListViewModel::rowCount(const QModelIndex &parent) const
{
    return dataList_.size();
}

/** ListView获取数据时,需要的别名
 * @brief MyListViewModel::roleNames
 * @return
 */
QHash<int, QByteArray> MyListViewModel::roleNames() const
{
    // 这样设置好后,ListView就可以在delegate中,
    // 直接使用title_、content_、select_来获取数据了。
    QHash<int, QByteArray> roles;
    roles[TitleRole] = "title_";
    roles[ContentRole] = "content_";
    roles[SelectRole] = "select_";
    return roles;
}

/** ListView获取数据,需要跟roleNames()打配合
 * @brief MyListViewModel::data
 * @param index
 * @param role
 * @return
 */
QVariant MyListViewModel::data(const QModelIndex &index, int role) const
{
    int row = index.row();
    if(row < 0 || row >= dataList_.count()) {
        return QVariant();
    }

    const Data &data = dataList_[row];
    switch (role) {
    case TitleRole:
        return data.title_;
    case ContentRole:
        return data.content_;
    case SelectRole:
        return data.select_;
    default:
        return QVariant();
    }
}

第四步 编写qml可调用函数

到这里的时候,模型已经基本完成了,只要QList中有数据,qml就可以显示出来。
但如果想要qml可以操作模型数据的话,还需要再另写函数。
这里就写一个添加和删除的代码。

Q_INVOKABLE void append(const QString &title, const QString &content, const bool &select);
Q_INVOKABLE void remove(int index);
void MyListViewModel::append(const QString &title, const QString &content, const bool &select)
{
    Data data;
    data.title_ = title;
    data.content_ = content;
    data.select_ = select;

    // 在增删改查数据时,都需要发送相对应的信号,这样qml才能及时刷新数据
    emit beginInsertRows(QModelIndex(), dataList_.size(), dataList_.size());
    dataList_.append(data);
    emit endInsertRows();
}

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

    // 在增删改查数据时,都需要发送相对应的信号,这样qml才能及时刷新数据
    emit beginRemoveRows(QModelIndex(), index, index);
    dataList_.removeAt(index);
    emit endRemoveRows();
}

第五步 在main函数中注册模型

这里是最重要的一步,如果这一步没有做的话,模型函数写得再漂亮也没用!
这里把它注册名为ListViewModel

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "MyListViewModel.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    // 最重要的一步,是需要把c++写好的模型注册一下,不然代码再好qml也不能使用
    MyListViewModel listModel;
    engine.rootContext()->setContextProperty("ListViewModel",&listModel);

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

第六步 写qml

现在就可以开始在qml中写ListView。

ListView {
	anchors.fill: parent
	model: ListViewModel

	delegate: Item {
		width: parent.width
		height: 40

		Text {
			id: titleText
			text: qsTr(title_)
			anchors.verticalCenter: parent.verticalCenter
			font.pixelSize: 18
			x: 20
		}

		Text {
			id: contentText
			text: qsTr(content_)
			anchors.verticalCenter: parent.verticalCenter
			font.pixelSize: 18
			x: 100
		}

		CheckBox {
			anchors.verticalCenter: parent.verticalCenter
			checked: select_
			x: 250
		}

		Button{
			id: deleteButton
			width: 50
			height: 20
			text: qsTr("删除")
			anchors.right: parent.right
			anchors.rightMargin: 20
			anchors.verticalCenter: parent.verticalCenter
			background: Rectangle{
				anchors.fill: parent
				color: "red"
			}
			onClicked: {
				ListViewModel.remove(index)
			}
		}
		
	}
}

另外再开一个定时器,每三秒就添加一个数据

property int titleCount: 0
    Timer{
        interval: 3000;
        running: true;
        repeat: true;
        onTriggered: {
            // CheckBox 就设置为,偶数时选中,奇数时不选中。
            ListViewModel.append(titleCount.toString(), "测试"+titleCount, titleCount%2==0 ? true : false);
            titleCount++;
        }
    }

第七步 运行看看效果

在这里插入图片描述

删除一个数据试试:
在这里插入图片描述

第八步 完整代码

MyListViewModel.h

#ifndef MYLISTVIEWMODEL_H
#define MYLISTVIEWMODEL_H

#include <QObject>
#include <QDebug>
#include <QAbstractListModel>

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

    // 这几个函数必须要重写
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    virtual QHash<int, QByteArray> roleNames() const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

    // 然后再添加几个,可以操作model的函数
    Q_INVOKABLE void append(const QString &title, const QString &content, const bool &select);
    Q_INVOKABLE void remove(int index);


private:
    enum DataRoles{
        TitleRole = Qt::UserRole + 1,
        ContentRole,
        SelectRole,
    };

    struct Data{
        QString title_;
        QString content_;
        bool select_;
    };

    QList<Data> dataList_;
};

#endif // MYLISTVIEWMODEL_H

MyListViewModel.cpp

#include "MyListViewModel.h"


MyListViewModel::MyListViewModel(QObject *parent)
{

}

/** ListView需要获取数据的数量
 * @brief MyListViewModel::rowCount
 * @param parent
 * @return
 */
int MyListViewModel::rowCount(const QModelIndex &parent) const
{
    return dataList_.size();
}

/** ListView获取数据时,需要的别名
 * @brief MyListViewModel::roleNames
 * @return
 */
QHash<int, QByteArray> MyListViewModel::roleNames() const
{
    // 这样设置好后,ListView就可以在delegate中,
    // 直接使用title_、content_、select_来获取数据了。
    QHash<int, QByteArray> roles;
    roles[TitleRole] = "title_";
    roles[ContentRole] = "content_";
    roles[SelectRole] = "select_";
    return roles;
}

/** ListView获取数据,需要跟roleNames()打配合
 * @brief MyListViewModel::data
 * @param index
 * @param role
 * @return
 */
QVariant MyListViewModel::data(const QModelIndex &index, int role) const
{
    int row = index.row();
    if(row < 0 || row >= dataList_.count()) {
        return QVariant();
    }

    const Data &data = dataList_[row];
    switch (role) {
    case TitleRole:
        return data.title_;
    case ContentRole:
        return data.content_;
    case SelectRole:
        return data.select_;
    default:
        return QVariant();
    }
}

void MyListViewModel::append(const QString &title, const QString &content, const bool &select)
{
    Data data;
    data.title_ = title;
    data.content_ = content;
    data.select_ = select;

    // 在增删改查数据时,都需要发送相对应的信号,这样qml才能及时刷新数据
    emit beginInsertRows(QModelIndex(), dataList_.size(), dataList_.size());
    dataList_.append(data);
    emit endInsertRows();
}

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

    // 在增删改查数据时,都需要发送相对应的信号,这样qml才能及时刷新数据
    emit beginRemoveRows(QModelIndex(), index, index);
    dataList_.removeAt(index);
    emit endRemoveRows();
}

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "MyListViewModel.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    // 最重要的一步,是需要把c++写好的模型注册一下,不然代码再好qml也不能使用
    MyListViewModel listModel;
    engine.rootContext()->setContextProperty("ListViewModel",&listModel);

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5

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

    ListView {
        anchors.fill: parent
        model: ListViewModel

        delegate: Item {
            width: parent.width
            height: 40

            Text {
                id: titleText
                text: qsTr(title_)
                anchors.verticalCenter: parent.verticalCenter
                font.pixelSize: 18
                x: 20
            }

            Text {
                id: contentText
                text: qsTr(content_)
                anchors.verticalCenter: parent.verticalCenter
                font.pixelSize: 18
                x: 100
            }

            CheckBox {
                anchors.verticalCenter: parent.verticalCenter
                checked: select_
                x: 250
            }

            Button{
                id: deleteButton
                width: 50
                height: 20
                text: qsTr("删除")
                anchors.right: parent.right
                anchors.rightMargin: 20
                anchors.verticalCenter: parent.verticalCenter
                background: Rectangle{
                    anchors.fill: parent
                    color: "red"
                }
                onClicked: {
                    ListViewModel.remove(index)
                }
            }
        }
    }


    property int titleCount: 0
    Timer{
        interval: 3000;
        running: true;
        repeat: true;
        onTriggered: {
            // CheckBox 就设置为,偶数时选中,奇数时不选中。
            ListViewModel.append(titleCount.toString(), "测试"+titleCount, titleCount%2==0 ? true : false);
            titleCount++;
        }
    }
}

QML使用QAbstractItemModel需要通过C++代码编写自定义的QAbstractItemModel子类。下面是一个简单的示例: 1. 编写自定义的QAbstractItemModel子类 ``` // mymodel.h #ifndef MYMODEL_H #define MYMODEL_H #include <QAbstractItemModel> class MyModel : public QAbstractItemModel { Q_OBJECT public: explicit MyModel(QObject *parent = nullptr); // 获取模型数据的数量和索引 int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; // 获取模型数据 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private: QStringList m_data; }; #endif // MYMODEL_H ``` ``` // mymodel.cpp #include "mymodel.h" MyModel::MyModel(QObject *parent) : QAbstractItemModel(parent) { // 初始化模型数据 m_data << "Item 1" << "Item 2" << "Item 3"; } int MyModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_data.count(); } int MyModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return 1; } QModelIndex MyModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid() || row < 0 || row >= m_data.count() || column < 0 || column >= 1) return QModelIndex(); return createIndex(row, column); } QVariant MyModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) return m_data.at(index.row()); return QVariant(); } ``` 2. 在QML使用自定义的QAbstractItemModel子类 ``` // main.qml import QtQuick 2.0 import QtQuick.Controls 2.0 ApplicationWindow { visible: true width: 400 height: 400 // 创建自定义模型 MyModel { id: myModel } // 使用ListView显示模型数据 ListView { anchors.fill: parent model: myModel delegate: Text { text: model.display } } } ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值