2023-01-18 Qt QObjectListModel的简单使用


前言

这段时间在写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进行类型转换,转成我们需要的类型,这时就可以进行代码补全提示
具体看代码里的三第注释说明.


完!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值