2

I am writing scripting wrappers for my SW so I can control my SW via scripts. The purpose of the wrapper classes is to separate the scripting interface from the actual class because I might not want to expose all of the signals and slots of the class. I'm using Qt 5.13 and QJSEngine.

My issue is that based on what I've read and what I've experimented it seems that to be able to expose an enum it needs to be within a class that inherits QObject and is exposed via newQObject()/setProperty(). However I do not want to expose the class Foo in the following example, but I still want to expose the enum Foo::Bar to the scripting environment. How do I do this? Q_ENUM seems to assume the enum is within a QObject and that the QObject is exposed (property) in the scripting environment. Here is a short example what I am doing (I want to be able to call FooWrapper::slot1() from the scripting environment):

class Foo : public QObject
{
    Q_OBJECT

public:
    Foo(QJSEngine& engine)
    {
        auto wrapper = new FooWrapper(this, engine);
    }

    enum class Bar
    {
        VAL1,
        VAL2
    };

public slots:
    void slot1(Bar bar);
    void slot2();
};

class FooWrapper : public QObject
{
    Q_OBJECT

public:
    Foo(Foo& foo, QJSEngine& engine)
        : foo(&foo)
    {
        QJSValue obj = engine.newQObject(this);
        auto gObj = engine.globalObject();
        gObj.setProperty("foo", obj);
    }

public slots:
    void slot1(Bar bar)
    {
        foo->slot1(bar);
    }

private:
    Foo* foo;
};
Dago
  • 1,349
  • 1
  • 11
  • 19

1 Answers1

1

Well this is clearly very late for Dago, the OP (sorry!). In case anyone else runs into this...

One option is same as for QML in general, which is that one can register enums and flags from a namespace. Using Q_ENUM_NS() and Q_FLAG_NS() (for flags the Q_DECLARE_FLAGS() call stays the same, also Q_DECLARE_OPERATORS_FOR_FLAGS() outside the namespace, same as with a class, if you want those).

There is my related answer here: How to access C++ enum from QML?

But for pure JSEngine usage it is enough to just set the namespace's QMetaObject as a property in the engine... no need for qmlRegisterUncreatableMetaObject... stuff.

So to sum up...

#include <QObject>  // required for the macros below

namespace MyNamespace
{
    Q_NAMESPACE        // required for meta object creation
    enum class Bar {
        VAL1,
        VAL2,
    };
    Q_ENUM_NS(Bar)     // register the enum in meta object data
}
// declare the enum's meta type (required if enum will be used in
// methods/functions exposed to JS. 
Q_DECLARE_METATYPE(MyNamespace::Bar)

Later in the QJSEngine initialization routine somewhere...

QJSEngine *jse = new QJSEngine();
...
// Register the namespace's meta object as a property of the global
// object (or any other object's property for that matter).
// The property name doesn't have to match the namespace name.
jse->globalObject().setProperty(
    "MyNamespace", 
    jse->newQMetaObject(&MyNamespace::staticMetaObject)
);

Then in the JS environment the enum is available as a property of the namespace itself.

QJSValue v = jse->evaluate("MyNamespace.VAL2");  // v.toInt() == 1

or in a script...

console.log('>>', MyNamespace.VAL2);  // output: >> 1

Another option may be to keep the enums in the class and register the class' QMetaObject as a property of the engine... just do not mark the constructor (or any other static methods) as Q_INVOKABLE.

HTH,
-Max

References:
https://doc.qt.io/qt-6/qjsengine.html#newQMetaObject
https://doc.qt.io/qt-6/qobject.html#Q_NAMESPACE
https://doc.qt.io/qt-6/qobject.html#Q_ENUM_NS

I cannot find a direct reference to registering namespace meta objects as I just described... I assure you it works (and has for quite a few versions).

A vague reference to it here, at the end of the section.. https://doc.qt.io/qt-5/qqmlengine.html#QML_ELEMENT

Another even less relevant (but possibly interesting): https://doc.qt.io/qt-6/qqmlengine.html#QML_EXTENDED_NAMESPACE

Maxim Paperno
  • 4,485
  • 2
  • 18
  • 22