Qml与C++借助QAbstractListModel构建model/view
Qt中包含了一系列的项视图类,它们使用model/view的架构去管理数据之间的关系以及它们被展示给用户的方式。这种由这种架构引进的功能分离特性给了开发者很大的灵活性去自定义自己的展示方式,并且提供了一个编制的模型接口以使很多种的数据源都可以和现存的视图相结合。我想借助C++构建model,然后Qml中进行View,上网查找只是看到了许多QStringListModel类等的讲解,但是该类对于qml不是完全暴露,model每次更改都伴随着重新加载注册。然后终于在说明文档里看到了QAbstractListModel类,可是网上都是寥寥数语,都不详细,对于qml中如何使用真是抓破了头啊,终于弄了一天,晚上有点起色,现就总结到的和大家分享,日后还有补充。其实在欢迎里面搜索abstractitemmodel会有简单的例子。下面讲讲我总结到的问题:
class QmlObjectListModel : public QAbstractListModel
{
Q_OBJECT
public:
QmlObjectListModel(QObject* parent = NULL);
~QmlObjectListModel();
Q_PROPERTY(int count READ count NOTIFY countChanged)
/// Returns true if any of the items in the list are dirty. Requires each object to have
/// a dirty property and dirtyChanged signal.
Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged)
Q_INVOKABLE QObject* get(int index) { return _objectList[index]; }
// Property accessors
int count(void) const;
bool dirty(void) const { return _dirty; }
void setDirty(bool dirty);
void append(QObject* object);
void append(QList<QObject*> objects);
QObjectList swapObjectList(const QObjectList& newlist);
void clear(void);
QObject* removeAt(int i);
QObject* removeOne(QObject* object) { return removeAt(indexOf(object)); }
void insert(int i, QObject* object);
void insert(int i, QList<QObject*> objects);
QObject* operator[](int i);
const QObject* operator[](int i) const;
bool contains(QObject* object) { return _objectList.indexOf(object) != -1; }
int indexOf(QObject* object) { return _objectList.indexOf(object); }
template<class T> T value(int index) { return qobject_cast<T>(_objectList[index]); }
/// Calls deleteLater on all items and this itself.
void deleteListAndContents(void);
/// Clears the list and calls deleteLater on each entry
void clearAndDeleteContents(void);
signals:
void countChanged(int count);
void dirtyChanged(bool dirtyChanged);
private slots:
void _childDirtyChanged(bool dirty);
private:
// Overrides from QAbstractListModel
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames(void) const override;
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
private:
QList<QObject*> _objectList;
bool _dirty;
bool _skipDirtyFirstItem;
static const int ObjectRole;
static const int TextRole;
};
首先就是创建QAbstractListModel的子类,当然需要宏Q_OBJECT(进入元系统的关键),然后其他函数自己看着定义,这里我主要说一下void append(QObject* object);我采用这种方法,将其他QObject的实例装进_objectList里,这样不管是不是数据,只要是QObject的子类,都可以装进去然后与Qml交互了。
然后说一下重载的函数,分别是
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames(void) const override;
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
其中roleNames用来定义角色(可以理解为区分每一个数据项的标记名称,类似于模型下标,具体用法qml里面会讲到),QVariant data则是根据qml中提供的角色名称获取对应的QObject指针,这样就能够获得不同的模型内容了。当然,这些你只是需要重载为适合自己的函数,具体的调用获取,就交给Qt自己吧,有一些是qml创建模型view时就调用,有的是qml里面手动获取,具体细则看完再体会。
#ifndef QmlObjectListModel_H
#define QmlObjectListModel_H
#include <QAbstractListModel>
class QmlObjectListModel : public QAbstractListModel
{
Q_OBJECT
public:
QmlObjectListModel(QObject* parent = NULL);
~QmlObjectListModel();
Q_PROPERTY(int count READ count NOTIFY countChanged)
/// Returns true if any of the items in the list are dirty. Requires each object to have
/// a dirty property and dirtyChanged signal.
Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged)
Q_INVOKABLE QObject* get(int index) { return _objectList[index]; }
// Property accessors
int count(void) const;
bool dirty(void) const { return _dirty; }
void setDirty(bool dirty);
void append(QObject* object);
void append(QList<QObject*> objects);
QObjectList swapObjectList(const QObjectList& newlist);
void clear(void);
QObject* removeAt(int i);
QObject* removeOne(QObject* object) { return removeAt(indexOf(object)); }
void insert(int i, QObject* object);
void insert(int i, QList<QObject*> objects);
QObject* operator[](int i);
const QObject* operator[](int i) const;
bool contains(QObject* object) { return _objectList.indexOf(object) != -1; }
int indexOf(QObject* object) { return _objectList.indexOf(object); }
template<class T> T value(int index) { return qobject_cast<T>(_objectList[index]); }
/// Calls deleteLater on all items and this itself.
void deleteListAndContents(void);
/// Clears the list and calls deleteLater on each entry
void clearAndDeleteContents(void);
signals:
void countChanged(int count);
void dirtyChanged(bool dirtyChanged);
private slots:
void _childDirtyChanged(bool dirty);
private:
// Overrides from QAbstractListModel
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames(void) const override;
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
private:
QList<QObject*> _objectList;
bool _dirty;
bool _skipDirtyFirstItem;
static const int ObjectRole;
static const int TextRole;
};
#endif
下一步,我们构建一个我们需要添加进qml中的数据模型:Polygons
class Polygons : public QObject
{
Q_OBJECT
public:
explicit Polygons(QObject *parent = nullptr);
Q_PROPERTY(QmlObjectListModel* polygons READ polygons CONSTANT)
QmlObjectListModel* polygons (void) { return &_polygons; }
signals:
public slots:
private:
QmlObjectListModel _polygons;
};
其实也很简单,就是利用Q_PROPERTY将QmlObjectListModel* polygons注册到元系统中,将polygons作为可以在qml中使用的类,构造函数实现:
Polygons::Polygons(QObject *parent) : QObject(parent)
{
for(int i=1;i<=20;i++)
{
Object* myObject =new Object();
myObject->setNumber(i);
_polygons.append(myObject);
}
}
Object是我自己创建的一个类,具有存储数据的功能
class Object: public QObject
{
Q_OBJECT
Q_PROPERTY(int number READ getNumber WRITE setNumber NOTIFY sendNumberChange)
public:
Object();
int getNumber() const;
void setNumber(int);
signals:
void sendNumberChange();
private:
int m_Number=100;
};
当然,Object中也需要将属性注册到元系统中,下面最关键的来了,对类进行注册:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
Polygons polygons;
qmlRegisterUncreatableType<Polygons>("Object",1,0,"Polygons","Reference only");
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("myPolygons",&polygons);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();}
是不是很简单啊,主要就是qmlRegisterUncreatableType注册Polygons类,这样可以避免qml单独创建实例,然后设置Polygons的上下文名称,这样就可以在qml中使用了。下面是qml的实现:
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListView {
x:100
y:100
width: 200; height: 250
model: myPolygons.polygons
delegate: Text {
text: "Animal: " +model.object.number
MouseArea {
anchors.fill: parent
onClicked: {
console.log(qsTr('Clicked on background. Text: "' + model.object.number + '"'))
}
}
}
}
更简单了吧,model: myPolygons.polygons表示从Polygons类的polygons属性中获取,正好就是之前的QmlObjectListModel 类,model.object.number中的object正好是之前QmlObjectListModel 里面设置的roleNames(void),内部通过Data将数据读取到model中,我们通过角色名称调取其中的数据进行显示,当然我们还可以在QmlObjectListModel 中注册一些东西到元系统中,我们就可以通过model.object.balabala了。
下一步需要更新一如何在C++中获取qml的数据,加油!!!