Доступ к модели C ++ из QML - PullRequest
       16

Доступ к модели C ++ из QML

2 голосов
/ 26 апреля 2019

Я создал минимальный рабочий пример. Надеюсь это будет понятно. Моя проблема в том, что я не могу создать модель для своего элемента верхнего уровня для дальнейшего доступа к элементам. Вот как выглядит архитектура объектов классов:

CTop
    x times CMiddle
        y times CBottom

Это древовидная архитектура. Вот код:

CBottom.h:

#ifndef CBOTTOM_H
#define CBOTTOM_H

#include <QObject>
#include <QtQml>

class CBottom : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)

public:

    CBottom(QObject *parent = nullptr) : QObject(parent)
    {

    }

    CBottom(const QString& name, QObject *parent = nullptr) : QObject(parent)
    {
        qmlRegisterType<CBottom>("Bottom", 1, 0, "Bottom");
        m_name = name;
    }

    QString name()
    {
        return m_name;
    }

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

signals:

    void nameChanged();

private:

    QString m_name;
};

#endif // CBOTTOM_H

CMiddle.h:

#ifndef CMIDDLE_H
#define CMIDDLE_H

#include <QObject>
#include <QtQml>
#include <QVector>

#include "cbottom.h"

class CMiddle : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)

public:

    CMiddle(QObject *parent = nullptr) : QObject(parent)
    {

    }

    CMiddle(const QString& name, const QStringList& bottoms, QObject *parent = nullptr) : QObject(parent)
    {
        qmlRegisterType<CMiddle>("Middle", 1, 0, "Middle");
        m_name = name;

        foreach (auto bottom, bottoms)
        {
            m_bottoms.append(new CBottom(bottom));
        }
    }

    QString name()
    {
        return m_name;
    }

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

signals:

    void nameChanged();

private:

    QString m_name;
    QVector<CBottom*> m_bottoms;
};

#endif // CMIDDLE_H

CTop.h:

#ifndef CTOP_H
#define CTOP_H

#include <QObject>
#include <QtQml>

#include "cmiddle.h"

class CTop : public QObject
{
    Q_OBJECT

public:

    CTop(QObject *parent = nullptr) : QObject(parent)
    {

    }

    CTop(const QStringList& middles, QObject *parent = nullptr) : QObject(parent)
    {
        qmlRegisterType<CTop>("Top", 1, 0, "Top");
        int i = 0;

        foreach (auto middle, middles)
        {
            QStringList bottoms;
            bottoms.append("A" + QString(i));
            bottoms.append("B" + QString(i));
            bottoms.append("C" + QString(i));
            i++;

            m_middles.append(new CMiddle(middle, bottoms));
        }
    }

    Q_INVOKABLE QVector<CMiddle*>& middles()
    {
        return m_middles;
    }

    Q_INVOKABLE CMiddle* middle(const int index)
    {
        return m_middles[index];
    }

private:

    QVector<CMiddle*> m_middles;

};

#endif // CTOP_H

main.c:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QString>
#include <QVector>
#include <QStringList>
#include <QVariant>
#include <QQmlContext>

#include "ctop.h"


int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QStringList middles;
    middles.append("FirstMiddle");
    middles.append("SecondMiddle");

    CTop* top = new CTop(middles);

    QQmlApplicationEngine engine;
    QQmlContext *ctxt = engine.rootContext();
    ctxt->setContextProperty("theTop", QVariant::fromValue(top));

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

main.qml:

import QtQuick 2.9
import QtQuick.Window 2.2

import Top 1.0

Window
{
    width: 600
    height: 400
    visible: true

    Repeater
    {
        model: theTop.middles();
        delegate: txtComp;
    }

    Component
    {
        id: txtComp;

        Text
        {
            text: name;
        }
    }

}

Вопрос в том, почему имя одного из CMiddle объектов не отображается в коде QML? Я хотел получить компоненты CMiddle (в качестве модели) из экспортированных в QML CTop компонентов. Если бы этот код работал, я бы пошел дальше и создал другую модель для доступа к CBottom объектам внутри каждого CMiddle объекта.

Например, я заметил, что этот код QML работает:

import QtQuick 2.9
import QtQuick.Window 2.2

import Top 1.0

Window
{
    width: 600
    height: 400
    visible: true

    Repeater
    {
        model: theTop;
        delegate: txtComp;
    }

    Component
    {
        id: txtComp;

        Text
        {
            text: middle(0).name;
        }
    }

}

enter image description here

Это не имеет особого смысла, но показывает, что компонент CTop правильно отображается в части QML.

Почему выходные данные Qvector из CMiddle указателей выступают в качестве модели в части QML?

1 Ответ

1 голос
/ 26 апреля 2019

QML не знает QList<CMiddle *>, вместо этого вы должны использовать QList<QObject *>:

CTop.h

// ...
Q_INVOKABLE QList<QObject*> middles(){
    QList<QObject*> objects;
    for(CMiddle *m : qAsConst(m_middles)){
        objects << m;
    }
    return objects;
}
// ...

Также другая ошибка заключается в том, что top долженне обязательно быть указателем, и вы можете передать его без использования QVariant::fromValue(), поскольку это QObject:

main.cpp

// ...
CTop top(middles);
QQmlApplicationEngine engine;
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("theTop", &top);

Также вПовторитель для получения элемента модели с использованием modelData :

main.qml

//...
Component{
    id: txtComp;
    Text{
        text: modelData.name
    }
}
//...

ПолныйПроект можно найти здесь

...