Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically create C++ object from QML

I want to dynamically create a C++ object from QML. I created a QObject derived class named Car and exposed it to QML using qmlRegisterType<Car>("org.qtproject.models", 1, 0, "Car");. Inside QML I am able to instantiate a Car object like this:

Car {
    id : car_1
    carName : "H1"
    carBrand : "Hummer"
    carPrice : 125000
} 

and then use the car_1 object and pass it back to C++ with ease if I need to. But what I would like is to create a Car object dynamically in QML, so I can pass it back to C++.

I tried:

MouseArea
{
    anchors.fill: parent
    onClicked: {
        component = Qt.createQmlObject("Car { id: car_1; carName : \"H1\"; carBrand : \"Hummer\"; carPrice : 125000; }",
                                       parent, "dynamicSnippet1");

        myCarModel.appendRowFromQml(component);
    }
}

but no luck. With the static approach, works fine:

MouseArea
{
    anchors.fill: parent
    onClicked: {
        myCarModel.appendRowFromQml(car_1);
    }
}

Is there a way to dynamically create a C++ object from the QML side? I also couldn't use Qt.createComponent because there is no *.qml file in which Car is defined, as Car was defined in C++.

like image 603
Jacob Krieg Avatar asked May 16 '16 11:05

Jacob Krieg


3 Answers

You can use a Loader .

Something like this:

Loader {
   id: carLoader
   active: false
   sourceComponent:
   Car {
       id : car_1
       carName : "H1"
       carBrand : "Hummer"
       carPrice : 125000
   } 

MouseArea
{
    anchors.fill: parent
    onClicked: carLoader.active:true
}
like image 167
user2520133 Avatar answered Nov 19 '22 11:11

user2520133


As I said in the comments, the problem is the variable component, which doesn't exist at that time.

So to fix the problem it is enough to replace the following code:

onClicked: {
    component = Qt.createQmlObject(...);

by:

onClicked: {
    var component = Qt.createQmlObject(...);
like image 27
Tarod Avatar answered Nov 19 '22 11:11

Tarod


Actually all qml objects are dynamically allocated. In your case Car has also. Loader and other alternatives are just for directing it over qml. So if you like to pass a qml object on C++ side, the only thing you need to have a slot/invokable function with Car * parameter. In your slot/invokable function, you must specify that you are taking the objects ownership to qml engine.

Suppose you have a Car class something similar like this,

class Car : public QObject {
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)

public:
    explicit Car(QObject *parent = Q_NULLPTR);
    ~Car();

    QString name();
    void setName(const QString &name);

signals:
    void nameChanged();

private:
    QString m_name;
};

And a Store class similar to this,

class Store : public QObject {
    Q_OBJECT
public:
    explicit Store(QObject *parent = Q_NULLPTR);

    Q_INVOKABLE void sell(Car *car);
};

And if you pass your Car object to Store object on qml,

Car {
    id: car1
    name: "H1"
}

MouseArea {
    anchors.fill: parent
    onClicked: Store.sell(car1);
}

then you must to specify the object ownership in your sell function,

void Store::sell(Car *car)
{
    qDebug() << car->name() << "just sold!!";
    QQmlEngine::setObjectOwnership(car, QQmlEngine::CppOwnership);
    delete car; // proof of the car is dynamically allocated
}
like image 1
cavitsinadogru Avatar answered Nov 19 '22 11:11

cavitsinadogru