界面开发无外乎是布局和界面的美化,而现代业务显示中最常见的非列表、表格莫属,列表表格的界面在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
- }
- }
- }
运行结果:

- #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