Page 1 of 1

C++ plugin, events

Posted: Thu Aug 03, 2017 8:26 pm
by YourRuler
Greetings;
I've been working on a C++ plugin for a while now works with a mechanical arm to plot a slice of an object on a 2d plane.
For all of my testing to this point everything has been done synchronously - is there a way for me to implement events that the scripts can attach to, or a way to call a function that the javascript script has to update the display so that everything can be switched to async methods?

Re: C++ plugin, events

Posted: Tue Aug 08, 2017 11:39 am
by andrew
Your plugin can expose a class / methods to ECMAScript.

The example plugin exposes the C++ class "MyClass" and its member functions:
https://github.com/qcad/qcad/tree/maste ... mpleplugin

Bindings are defined in EcmaMyClass and initialised in RExamplePlugin::initScriptExtensions.

Re: C++ plugin, events

Posted: Tue Aug 08, 2017 2:01 pm
by YourRuler
I have a class with exposed functions that I am calling - I'm looking for a slightly more specific thing.
Some of the more intense processes that I have to run (For example, calibrating the device I've made to account for manufacturing size & angle inconsistencies) won't necessarily end if specific criteria aren't met.
Is there a way to expose C++ Event Handlers so they can be hooked by EMCAScript?
Since I'll be waiting for certain criteria to be met once the process has been started, I'd rather not have to loop continuously in the emca script calling one function over and over again if I don't have to.
Any suggestions to avoid that would be really appreciated, thank you

Re: C++ plugin, events

Posted: Tue Aug 08, 2017 2:28 pm
by andrew
You could define a signal in your class which you connect in ECMAScript. Your C++ process could then emit the signal when it is done or otherwise wants to communicate with the ECMAScript caller (progress reports, warnings, errors, etc).

myObject.mySignal.connect(this, "mySlot");

Signals can be exposed to ECMAScript like regular functions.

Re: C++ plugin, events

Posted: Tue Aug 08, 2017 3:48 pm
by YourRuler
Thanks for replying andrew; I haven't quite gotten it working yet.
When you say that signals can be exposed like regular functions, this is what that meant to me:

Code: Select all

//plugin.h

class MyPlugin : public QObject, public RPluginInterface
{
    Q_OBJECT
    Q_INTERFACES(RPluginInterface)
#if QT_VERSION >= 0x050000
    Q_PLUGIN_METADATA(IID "org.qcad.MyPlugin")
#endif

signals:
    void valueChanged(int newValue); //the signal I'd like to connect to from EMCAScript

public:
    virtual bool init();
    virtual void uninit(bool) {}
    virtual void postInit(InitStatus status);
    virtual void initScriptExtensions(QScriptEngine& engine);
    virtual RPluginInfo getPluginInfo();
    
    static QScriptValue InitializeArm(QScriptContext * context, QScriptEngine* engine); //this function has the emit() 
    
    static QScriptValue createEZClass(QScriptContext* context, QScriptEngine* engine);
    static QScriptValue EZClassToString(QScriptContext *context, QScriptEngine *engine);
    static EZClass* getSelfEZClass(const QString& fName, QScriptContext* context);
};

Code: Select all

// ... plugin.cpp
void MyPlugin::initScriptExtensions(QScriptEngine& engine) {
    QScriptValue* proto = new QScriptValue(engine.newVariant(qVariantFromValue((EZClass*)0)));

    // base class:
    QScriptValue dpt = engine.defaultPrototype(qMetaTypeId<QObject*>());
    proto->setPrototype(dpt);

    REcmaHelper::registerFunction(&engine, proto, EZArmPlugin::EZClassToString, "toString");
    
    REcmaHelper::registerFunction(&engine, proto, EZArmPlugin::InitializeArm,"InitializeArm");
    
    REcmaHelper::registerFunction(&engine, proto, EZArmPlugin::valueChanged,"MySignal"); //My attempt to register it as I normally do with functions
    
    engine.setDefaultPrototype(qMetaTypeId<EZClass*>(), *proto);
                        
    //qScriptRegisterMetaType<EZClass*>(&engine, toScriptValue, fromScriptValue, *proto);

    QScriptValue ctor = engine.newFunction(EZArmPlugin::createEZClass, *proto, 0);

    engine.globalObject().setProperty("EZClass", ctor, QScriptValue::SkipInEnumeration);
}
This doesn't compile however, is my lack of experience using signals to blame here?

Re: C++ plugin, events

Posted: Tue Aug 08, 2017 8:44 pm
by andrew
You're right. You cannot / don't have to register the signal. Qt does that automatically. I've updated the example plugin with a signal and a function to trigger the emittance of the signal from ECMAScript. In your use case, the signal would of course be emitted from C++ directly. The example plugin can be tested as follows:

Code: Select all

var m=new MyClass();
m.mySignal.connect(function() { EAction.handleUserMessage("Hello"); });
m.emitSignal();
This would print "Hello" into the command line output of QCAD.

Updated plugin example:
https://github.com/qcad/qcad/tree/maste ... mpleplugin

Re: C++ plugin, events

Posted: Wed Aug 09, 2017 2:50 pm
by YourRuler
Awesome, I got it working this morning. I just hadn't put the signal in the right class definition :roll:
Thanks for your help mate.

Re: C++ plugin, events

Posted: Wed Aug 09, 2017 9:09 pm
by YourRuler
One more thing I Guess, if you have a moment to spare -
The signal works; the function I specify in the JS runs when I emit it from the C++.
however, I don't have the variable I actually emitted.
Is it possible to have the variables passed to the function, or will I need to expose the variables by making them public and just access them that way?

Code: Select all

m.mySignal.connect(function() { EAction.handleUserMessage("Hello"); }); //What you did
m.mySignal.connect(function(aVariable) { //How I anticipated I could gain access to the variable emitted
	EAction.handleUserMessage(aVariable); 
});
when I use the script debugger and inspect MyClass I see that it contains mySignal(int)

Re: C++ plugin, events

Posted: Thu Aug 10, 2017 10:05 am
by andrew
Yes, this should definitely work:

Code: Select all

ecma> o=new MyClass();
MyClass(0x7fc672c5a660)
ecma> o.setInt(7);
undefined
ecma> o.mySignal.connect(function(value) {EAction.handleUserMessage("value: " + value);});
undefined
ecma> o.emitSignal();
undefined
This will output "value: 7" in the QCAD command line.

Re: C++ plugin, events

Posted: Thu Aug 10, 2017 10:04 pm
by YourRuler
hey andrew, turned out the reason I wasn't seeing the value on my real-world code was because I was attempting to pass back a std::list.
Once I converted to more primitive types all started working well. Thank you!

Re: C++ plugin, events

Posted: Thu Aug 10, 2017 11:43 pm
by andrew
YourRuler wrote:I was attempting to pass back a std::list. Once I converted to more primitive types all started working well.
OK, good to hear. A Qt type such as QList might be worth a try, though I have to admit that all my signals use more basic types as well.