qml自定义ListModel(多层嵌套)

484 阅读4分钟
Person.h
#ifndef CPPACCESSQML_CALCULATOR_H
#define CPPACCESSQML_CALCULATOR_H

#include <QObject>
#include <QDebug>
#include "Student.h"

class Person : public QObject {
Q_OBJECT
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(int age READ getAge WRITE setAge NOTIFY ageChanged)
    Q_PROPERTY(QList<Student*> students READ getStudents WRITE setStudents NOTIFY studentsChanged)

public:
    explicit Person(QString name = "", int age = 0, QList<Student*> students = {}, QObject *parent = nullptr);
    ~Person() override;

    QString getName() const { return m_name; }
    void setName(const QString &name) { m_name = name; emit nameChanged(m_name); }

    int getAge() { return m_age; }
    void setAge(int age) { m_age = age; emit ageChanged(m_age); }

    QList<Student*> getStudents() const { return m_students; }
    void setStudents(const QList<Student*> &students) {
        m_students = students;
        emit studentsChanged();
    }

    Q_INVOKABLE void printName() { qDebug() << "name:" << m_name; }

private:
    QString m_name;
    int m_age;
    QList<Student*> m_students;

signals:
    void nameChanged(const QString &newName);
    void ageChanged(int newAge);
    void studentsChanged();
};

#endif //CPPACCESSQML_CALCULATOR_H
Person.cpp
#include "Person.h"
#include<QVariantMap> // 添加这个头文件
#include<QVariantList> // 添加这个头文件

Person::~Person() {}

Person::Person(QString name, int age, QList<Student*> students, QObject *parent): QObject(parent), m_name(name), m_age(age), m_students(students)  {

}
Student.h
// Student.h
#ifndef STUDENT_H
#define STUDENT_H

#include<QObject>
#include "Book.h"

class Student : public QObject
{
Q_OBJECT
    Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged)
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(QString level READ level WRITE setLevel NOTIFY levelChanged)
    Q_PROPERTY(QList<Book*> books READ getBooks WRITE setBooks NOTIFY booksChanged)

public:
    explicit Student(QObject *parent = nullptr);
    explicit Student(int id, QString name, QString level, QList<Book*>);

    int id() const { return m_id; }
    void setId(int id) {
        m_id = id;
        emit idChanged();
    }

    QString name() const { return m_name; }
    void setName(const QString &name) {
        m_name = name;
        emit nameChanged();
    }

    QString level() const { return m_level; }
    void setLevel(const QString &level) {
        m_level = level;
        emit levelChanged();
    }

    QList<Book*> getBooks() const { return m_books; }
    void setBooks(const QList<Book*> &books) {
        m_books = books;
        emit booksChanged();
    }

signals:
    void idChanged();
    void nameChanged();
    void levelChanged();
    void booksChanged();

private:
    int m_id;
    QString m_name;
    QString m_level;
    QList<Book*> m_books;
};

#endif // STUDENT_H
Student.cpp
// Student.cpp
#include "Student.h"

Student::Student(QObject *parent) : QObject(parent), m_id(0), m_name(""), m_level("") {}

Student::Student(int id, QString name, QString level, QList<Book*> books): m_id(id), m_name(name), m_level(level), m_books(books) {

}
Book.h
// Book.h
#ifndef BOOK_H
#define BOOK_H

#include<QObject>
#include<QString>

class Book : public QObject
{
Q_OBJECT
    Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)

public:
    explicit Book(QString title, QObject *parent = nullptr);

    QString title() const { return m_title; }
    void setTitle(const QString &title) {
        m_title = title;
        emit titleChanged();
    }

signals:
    void titleChanged();

private:
    QString m_title;
};

#endif // BOOK_H
Book.cpp
//
// Created by 17343 on 2024-06-22.
//

#include "Book.h"

Book::Book(QString title, QObject *parent) : QObject(parent), m_title(title) {

}
MyListModel.h
#include<QAbstractListModel>
#include<QStringList>
#include<QQmlListProperty>
#include "Person.h"

class MyListModel : public QAbstractListModel {
Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Person> persons READ getPersons)
public:
    explicit MyListModel(QObject *parent = nullptr);
    ~MyListModel();

    // 实现QAbstractListModel中的抽象方法
    int rowCount(const QModelIndex &parent) const override;
    QVariant data(const QModelIndex &index, int role) const override;

    QHash<int, QByteArray> roleNames() const override;

    // 添加数据的函数
    Q_INVOKABLE void addPerson(const QVariantMap &data);
    Q_INVOKABLE void updateData(int row, const QVariant &data, const QString &roleName);

    QQmlListProperty<Person> getPersons() {
        return QQmlListProperty<Person>(this, &m_persons);
    }

private:
    QList<Person*> m_persons;

    enum Roles {
        NameRole = Qt::UserRole + 1,
        AgeRole,
        StudentsListRole,
        StudentsVariantListRole
    };
};
MyListModel.cpp
#include "MyListModel.h"

MyListModel::MyListModel(QObject *parent) : QAbstractListModel(parent) {
    m_persons.append(new Person("AA", 11, {
        new Student(1, "1", "1", {
            new Book("java11"),
            new Book("java22"),
            new Book("java33"),
        }),
        new Student(2, "2", "2", {
            new Book("python11"),
            new Book("python22"),
            new Book("python33"),
        })})
    );
    m_persons.append(new Person("BB", 22, {new Student(1, "11", "222", {}), new Student(2, "22", "8", {})}));
    m_persons.append(new Person("CC", 33, {new Student(1, "111", "dd", {}), new Student(2, "222", "88", {})}));
    m_persons.append(new Person("DD", 44, {new Student(1, "1111", "333", {}), new Student(2, "2222", "889", {})}));
}

MyListModel::~MyListModel() {}

int MyListModel::rowCount(const QModelIndex &parent) const {
    if (parent.isValid()) {
        return 0;
    }
    return m_persons.count();
}

QVariant MyListModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid() || index.row() >= m_persons.count()) {
        return QVariant();
    }

    Person *person = m_persons.at(index.row());
    if (role == NameRole) {
        return person->getName();
    } else if (role == AgeRole) {
        return person->getAge();
    } else if (role == StudentsListRole) { //返回QList,自动解析下一级的List列表
        QList<QVariant> studentsVariantList;
            foreach (Student *student, person->getStudents()) {
                studentsVariantList<< QVariant::fromValue(student);
        }
        return studentsVariantList;
    } else if (role == StudentsVariantListRole) { //返回QVariantList,每级的List列表需要手动转换
        QVariantList studentsVariantList;
                foreach (Student *student, person->getStudents()) {
                QVariantMap studentMap;
                studentMap["id"] = student->id();
                studentMap["name"] = student->name();
                studentMap["level"] = student->level();

                //===
                QVariantList booksVariantList;
                        foreach (Book *book, student->getBooks()) {
                        QVariantMap bookMap;
                        bookMap["title"] = book->title();
                        booksVariantList << bookMap;
                    }
                //===

                studentMap["books"] = booksVariantList;
                studentsVariantList << studentMap;
            }
        return studentsVariantList;
    }

    return QVariant();
}

QHash<int, QByteArray> MyListModel::roleNames() const {
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[AgeRole] = "age";
    roles[StudentsListRole] = "studentsList";
    roles[StudentsVariantListRole] = "studentsVariantList";
    return roles;
}

void MyListModel::addPerson(const QVariantMap &data) {
    QString name = data["name"].toString();
    int age = data["age"].toInt();
    Person *person = new Person(name, age);
    beginInsertRows(QModelIndex(), rowCount(QModelIndex()), rowCount(QModelIndex()));
    m_persons.append(person);
    endInsertRows();
}

void MyListModel::updateData(int row, const QVariant &data, const QString &roleName) {
    if (roleName == "name") {
        m_persons.at(row)->setName(data.toString());
    } else if (roleName == "age") {
        m_persons.at(row)->setAge(data.toInt());
    }
    dataChanged(createIndex(row, 0), createIndex(row, 0), {NameRole, AgeRole});

    //====================tes startt==============
    //Person下的Students数据更新不需要调用dataChanged方法
    m_persons.at(row)->getStudents().at(0)->setId(666);
    m_persons.at(row)->getStudents().at(0)->setName("xiaoming");
    //Student下的Books数据更新不需要调用dataChanged方法
    m_persons.at(0)->getStudents().at(0)->getBooks().at(0)->setTitle("new title11111111111111111111111111111111111111");
    m_persons.at(0)->getStudents().at(0)->getBooks().at(1)->setTitle("new title22222222222222222222222222222222222");
    //========================test end==================
}
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include "MyListModel.h"
#include "Person.h"

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    QQmlApplicationEngine engine;
    qmlRegisterType<MyListModel>("com.example", 1, 0, "MyListModel");
    qmlRegisterType<Person>("com.example", 1, 0, "Person");
    engine.load(QUrl(QStringLiteral("../main.qml"))); //纯路径加载qml文件
    return QApplication::exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import com.example 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Custom ListModel with Person Example")

    ListView {
        id: listView
        spacing: 10
        anchors.fill: parent
        model: MyListModel {
            id: myListModel
        }

        delegate: Row {
            spacing: 10
            Text {
                text: "【person】"
            }
            Text {
                text: name
            }
            Text {
                text: age
            }

            Column {
                Repeater {
                    model: studentsList // studentsVariantList
                    delegate: Row {
                        Text {
                            text: "【student】id = " + modelData.id + ",name = " + modelData.name + ",level = " + modelData.level
                        }
                        Column {
                            Repeater {
                                model: modelData.books
                                delegate: Text {
                                    text: "【book】title = " + modelData.title
                                }
                            }
                        }
                    }
                }
            }

        }
    }

    Person {
        id: pp
        name: "dddd"
        age: 44
    }

    Button {
        id: addButton
        text: pp.name
        anchors.bottom: parent.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: {
            myListModel.addPerson({name: "[" + Math.random() * 100 + "]", age: myListModel.rowCount() + 1});
        }
    }

    Row {
        anchors.left: parent.left
        anchors.bottom: parent.bottom
        Button {
            text: "updateName"
            onClicked: myListModel.updateData(3, "wlzhou", "name")
        }
        Button {
            text: "updateName"
            onClicked: myListModel.updateData(3, 100, "age")
        }
    }
}
运行效果

image.png