0

I want to use a factory class in C++ to create objects which I can use/access in QML. But how do I access such newly created object in QML? Is this possible with the javascript?

My factory class in C++ creates an employeeobject which can be either Manager, SalesPerson or Engineer type all derived from Employee. Here is the code:

class EmployeeFactory : public QObject
{
    Q_OBJECT
public:
    enum
    {
        MANAGER,
        SALES_PERSON,
        ENGINEER
    };
    explicit EmployeeFactory(QObject *parent = 0);

    Q_INVOKABLE Employee *createEmployee(int type)
    {
        if (type == MANAGER )
        {
            qDebug() << "createEmployee(): Manager created";
            return new Manager;
        }
        else if(type == SALES_PERSON)
        {
            qDebug() << "createEmployee(): SalesPerson created";
            return new SalesPerson;

        }
        else if(type == ENGINEER)
        {
            qDebug() << "createEmployee(): Engineer created";
            return new Engineer;
        }

        qDebug() << "createEmployee(): Nothing created";
        return 0;
    }


signals:

public slots:
};

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

    QQmlApplicationEngine engine;

    EmployeeFactory * factory = new EmployeeFactory;

    qmlRegisterType<Employee>("MyModel", 1, 0, "employee");

    engine.rootContext()->setContextProperty("factory", factory);

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

    return app.exec();
}

Now in my QML code, I want to create the employee and access it.

Window {
    visible: true

    MouseArea {
        anchors.fill: parent
        onClicked: {

            // how do I access the return value `employee` here or how
            // do I return/access employee here
            employee e = factory.createEmployee(0) // This doesn't work, result in Expected token ';' error

            // once I have the employee, I would like to set its attributes like
            // e.name: "David"
        }
    }

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}
zar
  • 11,361
  • 14
  • 96
  • 178
  • There is typo in line `employee e = factory.createEmployee(0)`. Use `Employee`. – vcp Mar 15 '16 at 05:26
  • @vcp Didn't quite work, if I do `employee` instead it runs without error but if I access it in next line it says `employee is not defined'. – zar Mar 15 '16 at 05:34
  • Do not forget to import statement in QML `import MyModel 1.0`. – vcp Mar 15 '16 at 05:43
  • @vcp yes I have done that already – zar Mar 15 '16 at 05:44
  • You need to re-organize your classes, to have set and get method for class attributes. See example here : http://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html – vcp Mar 15 '16 at 06:08
  • 1
    @vcp that maybe complicated way. It turns out it is pretty much automatic, all I had to do was declare the line as `var e = factory.createEmployee(0)` – zar Mar 15 '16 at 06:28

2 Answers2

2

If you want a lighter weight version of a QObject, you can make Employee a Q_GADGET.

https://doc.qt.io/qt-5/qobject.html#Q_GADGET

vpicaver
  • 1,771
  • 1
  • 15
  • 16
1

If your employees are based on QObject, in that case, you don't need to do anything at all to use the objects in QML, as the meta system will take care. The downside is that QObject is quite huge, so if you have millions of employees you definitely don't want that. But for tens of thousands or less it should be OK.

And since JS is not strongly typed language, it won't be employee e =... but var e = factory.createEmployee(0) If you want to have it typed in properties, you will have to register it as a QML type, it can be as a regular or as a non-creatable, respectively using the qmlRegisterType() and qmlRegisterUncreatableType() functions, the latter is a type you can access and use but not create from QML.

I assume you have already exposed the factory exposed as a context property.

If you don't want your employees to be QObject derived, then you will have to give up on using the objects directly from QML(no, you don't, you can simply use Q_GADGET as vpicaver noted), you can register them to the Qt meta type system with Q_DECLARE_METATYPE(), which will allow you to use them as parameters from QML, but in order to access their data, you will have to use a QObject derived accessor/controller class, which will return the employee data from C++ to QML. Or you could use a model to manage, and expose the members as model roles, in that case you need to implement for example a QAbstractListModel.

Also, when creating QObject based objects from C++ and using them in QML, keep in mind object lifetime. You can explicitly specify whether QML or C++ manages the ownership and lifetime, and I've had my fair share of issues related to QML deleting objects while still in use, leading to crashes, at this point QML ownership seems rather clunky, but the behavior is only exhibited in more complex, highly dynamic scenarios, for "typical" QML usage it is probably ok.

dtech
  • 47,916
  • 17
  • 112
  • 190
  • Thanks, the last paragraph is interesting. I didn't thought about worrying lifetime of the object and specially since I am creating them in C++. How can I specify C++ is the owner and that they don't get auto deleted? – zar Mar 15 '16 at 22:09
  • Yes, you can set CPP ownership, this way the object can only be deleted by C++, you won't be able to use `destroy()` from QML. – dtech Mar 15 '16 at 22:09
  • By default, any `QObject` derived returned from a slot to QML will have `QQmlEngine::JavaScriptOwnership`. I have described the bug here, I also posted it into the Qt bug tracker, but so far no work has been done even though it is marked as critical: http://stackoverflow.com/questions/33792876/qml-garbage-collection-deletes-objects-still-in-use – dtech Mar 15 '16 at 22:11