前言
这段时间在写qml界面,c++与qml交互中用到mode是常事,但遇到些小问题,在解决的过程中,顺便记录一下model的使用。
一、自定义listmodel
model 只实现了基本的功能,不过对于存放QObject来说,也够用
- qmlobjectlistmodel.h
#pragma once
#include <QAbstractListModel>
class QmlObjectListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit QmlObjectListModel(QObject *parent = nullptr);
void append(QObject *object);
void clearAndDelete();
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// Editable:
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
QHash<int, QByteArray> roleNames() const override;
private:
QList<QObject*> m_objectList;
static const int ObjectRole;
};
qmlobjectlistmodel.cpp
#include "qmlobjectlistmodel.h"
const int QmlObjectListModel::ObjectRole = Qt::UserRole;
QmlObjectListModel::QmlObjectListModel(QObject *parent)
: QAbstractListModel(parent)
{
}
void QmlObjectListModel::append(QObject *object)
{
const int rowOfInsert = m_objectList.count();
beginInsertRows(QModelIndex(), rowOfInsert, rowOfInsert);
m_objectList.append(object);
endInsertRows();
}
void QmlObjectListModel::clearAndDelete()
{
beginResetModel();
for (int i = 0; i < m_objectList.count(); i++) {
m_objectList[i]->deleteLater();
}
m_objectList.clear();
endResetModel();
}
int QmlObjectListModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_objectList.count();
}
QVariant QmlObjectListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() < 0 || index.row() >= m_objectList.count())
return QVariant();
if (role == ObjectRole)
return QVariant::fromValue(m_objectList[index.row()]);
return QVariant();
}
bool QmlObjectListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == ObjectRole) {
m_objectList.replace(index.row(), value.value<QObject*>());
emit dataChanged(index, index);
return true;
}
return false;
}
QHash<int, QByteArray> QmlObjectListModel::roleNames() const
{
QHash<int, QByteArray> hash;
hash[ObjectRole] = "object";
return hash;
}
二、使用
1.准备一个要存放于model的类
这个很简,就是基于QObject的类,里面放了两个变量,且使用Q_PROPERTY进行属性封装
- todoitem.h
class ToDoItem : public QObject
{
Q_OBJECT
public:
ToDoItem();
~ToDoItem();
Q_PROPERTY(bool done READ done WRITE setDone NOTIFY doneChanged)
Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged)
bool done() const;
void setDone(bool newDone);
QString description() const;
void setDescription(const QString &newDescription);
signals:
void doneChanged();
void descriptionChanged();
private:
bool m_done = true;
QString m_description = "wwaa11";
};
- todoitem.cpp
ToDoItem::ToDoItem()
{
qDebug() << __func__;
}
ToDoItem::~ToDoItem()
{
qDebug() << __func__;
}
bool ToDoItem::done() const
{
return m_done;
}
void ToDoItem::setDone(bool newDone)
{
if (m_done == newDone)
return;
m_done = newDone;
emit doneChanged();
}
QString ToDoItem::description() const
{
return m_description;
}
void ToDoItem::setDescription(const QString &newDescription)
{
if (m_description == newDescription)
return;
m_description = newDescription;
emit descriptionChanged();
}
2.再准备一个model管理类
这个要不的根据自己的使用情况来定,主要取决你你的model的使用方式
class ToDoListObject : public QObject
{
Q_OBJECT
public:
explicit ToDoListObject(QObject *parent = nullptr);
Q_PROPERTY(QmlObjectListModel* todoModel READ todoModel CONSTANT)
Q_INVOKABLE void addOne();
QmlObjectListModel *todoModel();
private:
QmlObjectListModel m_object;
};
ToDoListObject::ToDoListObject(QObject* parent)
: QObject { parent }
{
}
void ToDoListObject::addOne()
{
static int i = 0;
auto todo = new ToDoItem();
todo->setDescription(QString::number(i));
todo->setDone(i%2);
m_object.append(todo);
qDebug() << todo->done() << todo->description();
i++;
}
QmlObjectListModel *ToDoListObject::todoModel()
{
return &m_object;
}
3. 注册
注册位置随意,但必须要在加载qml界面之前注册,我这里就直接面main里注册了
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QQuickStyle::setStyle("Material");
QGuiApplication app(argc, argv);
/// c++ 类型注册到qml
qmlRegisterType<ToDoItem>("ToDoItem", 1, 0, "ToDoItem");
qmlRegisterType<ToDoListObject>("ToDoListObject", 1, 0, "ToDoListObject");
QQmlApplicationEngine engine;
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();
}
4.QML代码
main.qml 没什么好说的
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id:root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
ToDoList{
anchors.centerIn: parent
}
}
重点看下ToDoList.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import ToDoItem 1.0
import ToDoListObject 1.0
Frame {
ToDoListObject{
id: todoList
}
ColumnLayout{
ListView{
id: listView
implicitHeight: 250
implicitWidth: 250
clip: true
model: todoList.todoModel
delegate: RowLayout{
// 属性使用方式1
property ToDoItem todo: model.object
// 属性使用方式2
// property ToDoItem todo: object
width: listView.width
CheckBox{
checked: parent.todo.done
// 属性使用方式3
// property ToDoItem todo: object
// checked: todo.done
}
TextField{
Layout.fillWidth: true
text: parent.todo.description
}
}
}
Button{
implicitHeight: 50
implicitWidth: 250
text: "add"
onClicked: {
todoList.addOne();
}
}
}
}
这里我选择在qml里初始化ToDoListObject,当然也可以在cpp里进行初始化,不过注册方式会有所不同.
将ToDoListObject的model直接绑定到ListView的model属性即可
这里重点要说的是每个委托组件里怎么使用我们存放的Object类
1.直接使用object来访问是可以的,但不太友好,在QtCreator里编辑的时候,不能代码补全提示.
2.在qml里将object进行类型转换,转成我们需要的类型,这时就可以进行代码补全提示
具体看代码里的三第注释说明.
完!!!