How to create new instance of a Q_GADGET struct in QML?
Asked Answered
E

1

7

I can emit signals with structs tagged with Q_GADGET from C++ to QML.

Is it possible send such a struct from QML to a C++ slot? My code fails on the first step: creating an instance in QML.

This code fails on the first line ...

var bs = new BatteryState()
bs.percentRemaining = 1.0
bs.chargeDate = new Date()
DataProvider.setBatteryState(bs)

... with error:

qrc:///main.qml:34: ReferenceError: BatteryState is not defined

I can emit a BatteryStatus struct from C++ to QML, but I would like to send one back as a single parameter to a slot.

Here is BatteryState.h & BatteryState.cpp:

// BatteryState.h
#pragma once

#include <QDate>
#include <QMetaType>

struct BatteryState
{
    Q_GADGET
    Q_PROPERTY(float percentRemaining  MEMBER percentRemaining)
    Q_PROPERTY(QDate date              MEMBER date)

public:
    explicit BatteryState();
    BatteryState(const BatteryState& other);
    virtual ~BatteryState();
    BatteryState& operator=(const BatteryState& other);
    bool operator!=(const BatteryState& other) const;
    bool operator==(const BatteryState& other) const;

    float percentRemaining;
    QDate date;
};
Q_DECLARE_METATYPE(BatteryState)

// BatteryState.cpp
#include "BatteryState.h"

BatteryState::BatteryState()
    : percentRemaining(), date(QDate::currentDate())
{}

BatteryState::BatteryState(const BatteryState& other)
    : percentRemaining(other.percentRemaining),
      date(other.date)
{}

BatteryState::~BatteryState() {}

BatteryState&BatteryState::operator=(const BatteryState& other)
{
    percentRemaining = other.percentRemaining;
    date = other.date;
    return *this;
}

bool BatteryState::operator!=(const BatteryState& other) const {
    return (percentRemaining != other.percentRemaining
            || date != other.date);
}

bool BatteryState::operator==(const BatteryState& other) const {
    return !(*this != other);
}

I register this type in main.cpp:

qRegisterMetaType<BatteryState>();

Recommendations?

Era answered 14/3, 2017 at 21:59 Comment(1)
doc.qt.io/qt-5/… ?Traipse
D
11

You don't create Q_GADGETs in QML, QML objects need to be QObject derived, and are not created via new - that's for JS objects only. Gadgets just generate meta data so that you can access their members and such from QML and pass around, that's about it.

Is it possible send such a struct from QML to a C++ slot?

It is possible to send, but it would not be created in QML. It could be returned to QML from a C++ function or be exposed as a property of some object.

struct Test {
    Q_GADGET
    Q_PROPERTY(int test MEMBER test)
  public:
    Test() : test(qrand()) {}
    int test;
    Q_SLOT void foo() { qDebug() << "foo"; }
};

class F : public QObject { // factory exposed as context property F
    Q_OBJECT
  public slots:
    Test create() { return Test(); }
    void use(Test t) { qDebug() << t.test; }
};


    // from QML
    property var tt: F.create()

    Component.onCompleted: {
      F.use(F.create()) // works
      var t = F.create()
      console.log(t.test) // works
      F.use(t) // works
      console.log(tt.test) // works
      F.use(tt) // works
      tt.test = 555
      F.use(tt) // works
      t.test = 666
      F.use(t) // works
      t.foo() // works
    }
Dorren answered 14/3, 2017 at 23:6 Comment(4)
I have a question. I tried a lot of way to auto completation on this way but I have no success. example if i typed t. +(ctrl+space) there is no option of struct members. How can do this?Fourhanded
I doubt there is anything you can do at the moment. Hopefully in time Creator will get better at providing auto completion in QML.Dorren
this results unknown type error. you need to declare it using Q_DECLARE_METATYPE() and register it using qRegisterMetaType<>(). see this answerTyrannous
You can create Q_GADGETS in QML using a C++ factory object registered to be accessible in QML, and a Q_INVOKABLE function in this factory which returns an instance of the Q_GADGET object. The object it returns should also be a member of the factory class. This object can then be passed from QML to C++ via Q_INVOKABLE functions. You can even store a Q_GADGET object as a QML property using the following syntax: property var my_property: my_factory.makeQGadgetObject()Platinum

© 2022 - 2024 — McMap. All rights reserved.