Динамически создавать объект qml из объекта С++ (с помощью setContextProperty)

Я пытаюсь динамически создать объект qml в С++, используя объект класса С++. Ниже приведен минимальный код для моего подхода. При выполнении этого кода и после нажатия происходит сбой приложения (см. комментарий в main.qml).

Я вставил приведенный ниже код, и его можно скачать здесь.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "scene.h"
int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

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

    QQmlContext* context = engine.rootContext();
    context->setContextProperty("sc", &sc);

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

    return app.exec();
}

сцена.h

#ifndef SCENE_H
#define SCENE_H

#include <QObject>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
class scene : public QObject
{
    Q_OBJECT
public:
    explicit scene(QQmlApplicationEngine& engine, QObject *parent = nullptr);
    QQmlApplicationEngine& engine;

public slots:
    void create_rect_object();
};

#endif // SCENE_H

сцена.cpp

#include "scene.h"

scene::scene(QQmlApplicationEngine &engine, QObject *parent) : engine(this->engine),QObject(parent)
{

}    
void scene::create_rect_object()
{
    QQmlComponent component(&engine, QUrl::fromLocalFile("myrect.qml"));
    QObject *object = component.create();
    object->setProperty("width", 200);
    object->setProperty("height", 150);
    object->setProperty("color", "blue");
}

main.qml

import QtQuick 2.11
import QtQuick.Window 2.11

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    Rectangle{
        anchors.fill: parent
        color: "red"
        MouseArea{
            anchors.fill: parent
            onClicked: {
                console.log("Before click");
                sc.create_rect_object(); // application is crashing here
                console.log("after click");
            }
        }
    }
}

myrect.qml

import QtQuick 2.0

Rectangle {
    id:id_rec
    width: 100
    height: 100
    color: "green"
    x:0
    y:0
}

Обновить

Создаваемый объект не является дочерним элементом корня главного окна, а является дочерним элементом одного из элементов в цепочке дочерних элементов корня главного окна. Псевдоструктура выглядит так, как показано ниже.

main.qml

Window {       

    customitem1{
        id:id_ci1

    }    
    customitem2{
        id:id_ci1        
    }       
}

customitem1.qml

Item {       

    customitem3{
        id:id_ci3

    }    
    customitem3{
        id:id_ci4        
    }       
}

person nayab    schedule 21.10.2018    source источник
comment
какова ваша логика для создания этих объектов?   -  person Redanium    schedule 23.10.2018
comment
Что именно вы подразумеваете под логикой?   -  person nayab    schedule 24.10.2018
comment
как вы собираетесь создавать эти объекты? случайно или есть схема вы следуете?   -  person Redanium    schedule 24.10.2018
comment
Эти объекты перетаскиваются пользователем, а затем заканчиваются, свойства сохраняются в базе данных. Позже, когда база данных будет перезагружена, эти объекты должны быть в той же позиции с теми же свойствами. Короче восстановление прежнего контекста.   -  person nayab    schedule 25.10.2018
comment
Я говорю о СОЗДАНИИ этих объектов, а не о ВЗАИМОДЕЙСТВИИ или ПОСЛЕ СОЗДАНИЯ.   -  person Redanium    schedule 25.10.2018


Ответы (1)


[ОБНОВЛЕНО]

У вас есть две ошибки из-за сбоя и одна из-за отсутствия отображения прямоугольников.

1.Ваш scene список инициализаторов элементов конструктора является ложным, что приводит к сбою приложения.

(СОВЕТ: используйте разные имена для членов класса, добавляя к ним префикс m_, например: m_engine для ЧИТАЕМОСТИ и не путайтесь запутанно)

//Correct WAY
class Something
{
 private:
    int m_value1;
    double m_value2;
    char m_value3;

 public:
    //#################  YOUR CASE  ###############################
    Something(int number) : m_value1(number), m_value2(2.2), m_value3('c') // directly initialize our member variables
    {
    // No need for assignment here
    }
    //#############################################################
    Something() : m_value1(1), m_value2(2.2), m_value3('c') // directly initialize our member variables
    {
    // No need for assignment here
    }
    void print()
    {
         std::cout << "Something(" << m_value1 << ", " << m_value2 << ", " << m_value3 << ")\n";
    }
}

а должно быть так:

scene::scene(QQmlApplicationEngine &engine, QObject *parent) : engine(engine),QObject(parent)

вместо

scene::scene(QQmlApplicationEngine &engine, QObject *parent) : engine(this->engine),QObject(parent)

2. url из myrect.qml, который вы получаете из локального файла, который не найден во время времени выполнения, также вызвал сбой приложения, и одно из решений — загрузить его из вашего qrc. файл

 QQmlComponent component(&engine, QUrl("qrc:/myrect.qml"));

3. И вы заметите, что после щелчка у вас нет прямоугольников, потому что создаваемые прямоугольники не имеют родителя, и, изменив ваш create_rect_object() (в этом примере родителем является невидимый корень нашего окна contentItem), вы получите некоторые прямоугольники :)

//A QQuickWindow always has a single invisible root item containing all of its content.
//To add items to this window, reparent the items to the contentItem or to an existing item in the scene.
//http://doc.qt.io/qt-5/qquickwindow.html#contentItem-prop



void scene::create_rect_object()
{
    QQmlComponent component(&engine, QUrl("qrc:/myrect.qml"));

    QObject *object = component.create();
    QQuickItem *item = qobject_cast<QQuickItem*>(object);
    // Set the parent of our created qml rect
    item->setParentItem((QQuickItem*)((QQuickWindow *) engine.rootObjects()[0])->contentItem());
    //Set some random position and color
    item->setProperty("color", QColor::fromRgb(QRandomGenerator::global()->generate()));
    item->setX(20+qFloor(QRandomGenerator::global()->generateDouble()*20));
    item->setY(20+qFloor(QRandomGenerator::global()->generateDouble()*20));

}

Поиск объектов QML из C++

Для поиска объектов и использования их как parentItem вы должны установить objectName вашего объекта qml

Rectangle {
             ...
             objectName : "rect_1"
             ...
}

и на С++

QObject* obj = dynamic_cast<QObject*>(engine.rootObjects()[0]).findChild("rect_1");
person Redanium    schedule 21.10.2018
comment
спасибо за ответ, это работает для указанного кода, но у меня проблема с setParentItem, потому что мой объект не находится прямо в главном окне. Я имею в виду, что создаваемый объект находится внутри цепочки детей. Я обновлю вопрос с более подробной информацией. - person nayab; 22.10.2018
comment
@nayab проверьте обновление, и мы закончили :) не забудьте отметить ответ, если он соответствует вашим потребностям - person Redanium; 25.10.2018
comment
Я уже пытался найти родительский объект, но он не был найден. В любом случае спасибо за вашу помощь. Я решил это в javascript. - person nayab; 26.10.2018
comment
@nayab попробуй понять свое дерево объектов и дочерние объекты - person Redanium; 26.10.2018