Лучший способ получить доступ к структуре cpp в QML

#qt #qml #qtquick2

#qt #qml #qtquick2 #qt-быстрый #qvariant

Вопрос:

Мне нужно передавать структуры между cpp и QML. Если я использую свойство, я должен создать отдельный набор и получить функции, моя структура содержит минимум 5 членов, поэтому я чувствовал, что нехорошо использовать set и get для всех этих членов. Ниже приведен пример того, что я пытаюсь сделать :

MyClass.h

 #include <QObject>
#include <QDebug>
using namespace std;

struct MyStruct {
Q_GADGET
int m_val;
QString m_name1;
QString m_name2;
QString m_name3;
QString m_name4;
Q_PROPERTY(int val MEMBER m_val)
Q_PROPERTY(QString name1 MEMBER m_name1)
Q_PROPERTY(QString name2 MEMBER m_name2)
Q_PROPERTY(QString name3 MEMBER m_name3)
Q_PROPERTY(QString name4 MEMBER m_name4)
};

class MyClass:public QObject
    {
        Q_OBJECT
    Q_PROPERTY(MyStruct mystr READ getMyStruct
                WRITE setMyStruct NOTIFY myStructChanged)

public:
    explicit MyClass(QObject *parent = nullptr);
    MyStruct strObj;

     // Edit: changed get function
     MyStruct getMyStruct() const
     {
         return strObj;
     }

// Edit: Added set function
     void setMyStruct(myStruct val)
        {
            mystr = val;
            emit myStructChanged();
        }

signals:
void myStructChanged();

}
 

main.cpp

 #include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>
#include <QObject>

#include "MyClass.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    MyClass classObj;

    engine.rootContext()->setContextProperty("classObj",amp;classObj);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}
 

Main.qml

 import QtQuick 2.6
import QtQuick.Controls 2.2
import QtQuick.Window 2.3

ApplicationWindow {

    id: applicationWindow

    visible: true
    width: 600
    height: 400
    title: qsTr("My App")

    MainForm{
        id : mainform

        Component.onCompleted: {
        console.log("name====" classObj.mystr.name1)

        //EDIT added more code to explain the use case.
        classObj.myStr.name1 = "abc"  //Calls setter 
        classObj.mystr.name2 = "ans" // Calls setter 
        }
    }
}
 

Если я печатаю только (classObj.myVariant) , я получаю QVariant (MyStruct), но когда я пытаюсь получить доступ к любому параметру, например classObj.myVariant.name1 , я получаю «undefined«, а также как установить вариант из QML?

[ОБНОВЛЕНИЕ] — Также следует добавить MyStruct в Q_DECLARE_METATYPE, как показано ниже: Q_DECLARE_METATYPE(MyStruct)

Ответ №1:

Вам нужны метаданные для доступа к объектам C из QML.

Для QObject непроизводных это достигается с помощью Q_GADGET макроса и предоставления элементов в качестве свойств:

 struct MyStruct {
    Q_GADGET
    int m_val;
    QString m_name1;
    QString m_name2;
    QString m_name3;
    QString m_name4;
    Q_PROPERTY(int val MEMBER m_val)
    Q_PROPERTY(QString name1 MEMBER m_name1)
    Q_PROPERTY(QString name2 MEMBER m_name2)
    Q_PROPERTY(QString name3 MEMBER m_name3)
    Q_PROPERTY(QString name4 MEMBER m_name4)
};
 

Комментарии:

1. Это сработало!!! если мне нужно заполнить QVariant из QML. Как это сделать. Не могли бы вы привести пример?

2. @pra7 что вы подразумеваете под «заполнением»? Создавать или просто присваивать значения элементам?

3. Да, просто присваивая значения членам, чтобы cpp мог получить доступ к значениям, установленным QML.

4. Ну, просто yourvar.val = 555 .

5. Нет, это невозможно. Если это уже работает, вам не нужно ничего дополнительно делать. Если вы хотите сохранить повторение, просто напишите функцию, которая устанавливает все, что принимает целевой объект и 5 значений свойств.

Ответ №2:

  1. ваша структура или простой класс должны иметь Q_GADGET как минимум
  2. вы должны объявить свойства, чтобы получить доступ из qml
  3. вы должны объявить свою структуру / класс с помощью Q_DECLARE_METATYPE()
  4. вы должны зарегистрировать его с помощью qRegisterMetaType<>() где-нибудь перед загрузкой файла qml с помощью движка, такого как main.cpp

итак, у вас будет что-то вроде этого:

 //review carefully
struct MyStruct {
    Q_GADGET    //<-- 1.
    Q_PROPERTY(QString str1 MEMBER m_str1)    //<-- 2.
public:    //<-- important
    QString m_str1;
};
Q_DECLARE_METATYPE(MyStruct)    //<-- 3.
 

и использовать где-нибудь:

 class Controller : public QObject
{
    Q_OBJECT
public:
    explicit Controller(QObject *parent = nullptr);
    Q_INVOKABLE MyStruct setNewConfig(QString v);    //<-- e.g.
    //...
}
 

main.cpp

 //...
qmlRegisterType<Controller>("AppKernel", 1, 0, "Controller");
qRegisterMetaType<MyStruct>();    //<-- 4.
//...
engine.load(url);
//...
 

поэтому его можно использовать в qml
main.qml

 //...
    Controller {
        id: con
    }
    FileDialog {
        id: fileDialog
        nameFilters: ["Config file (*)"]
        onAccepted: {
            var a = con.setNewConfig(file);
            console.log(a.str1);    //<-- yeah! it is here
        }
    }
//...
 

ПРИМЕЧАНИЕ 1: будьте осторожны, кажется, что вложенные классы / структуры не поддерживаются Qt meta

ПРИМЕЧАНИЕ 2: вы можете отображать struct точно так же, как class . Наследовать QObject и использовать Q_OBJECT . Смотрите эту статью от Евгения Леготского

ПРИМЕЧАНИЕ 3: Приведенные выше инструкции делают структуру / класс известными qml, и вы можете получить доступ к свойствам / элементам, но не могут быть созданы в qml. см. Документ Qt

ПРИМЕЧАНИЕ 4: Имейте в виду, что qmlRegisterType<>() этот метод помечен как «устаревший» в Qt 5.15 . Держите себя в курсе 😉