C++ plugin, events

Discussion forum for C++ and script developers who are using the QCAD development platform or who are looking to contribute to QCAD (translations, documentation, etc).

Moderator: andrew

Forum rules

Always indicate your operating system and QCAD version.

Attach drawing files, scripts and screenshots.

Post one question per topic.

Post Reply
YourRuler
Newbie Member
Posts: 9
Joined: Thu Mar 23, 2017 7:41 pm

C++ plugin, events

Post by YourRuler » Thu Aug 03, 2017 8:26 pm

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?

User avatar
andrew
Site Admin
Posts: 9037
Joined: Fri Mar 30, 2007 6:07 am

Re: C++ plugin, events

Post by andrew » Tue Aug 08, 2017 11:39 am

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.

YourRuler
Newbie Member
Posts: 9
Joined: Thu Mar 23, 2017 7:41 pm

Re: C++ plugin, events

Post by YourRuler » Tue Aug 08, 2017 2:01 pm

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

User avatar
andrew
Site Admin
Posts: 9037
Joined: Fri Mar 30, 2007 6:07 am

Re: C++ plugin, events

Post by andrew » Tue Aug 08, 2017 2:28 pm

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.

YourRuler
Newbie Member
Posts: 9
Joined: Thu Mar 23, 2017 7:41 pm

Re: C++ plugin, events

Post by YourRuler » Tue Aug 08, 2017 3:48 pm

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?

User avatar
andrew
Site Admin
Posts: 9037
Joined: Fri Mar 30, 2007 6:07 am

Re: C++ plugin, events

Post by andrew » Tue Aug 08, 2017 8:44 pm

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

YourRuler
Newbie Member
Posts: 9
Joined: Thu Mar 23, 2017 7:41 pm

Re: C++ plugin, events

Post by YourRuler » Wed Aug 09, 2017 2:50 pm

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.

YourRuler
Newbie Member
Posts: 9
Joined: Thu Mar 23, 2017 7:41 pm

Re: C++ plugin, events

Post by YourRuler » Wed Aug 09, 2017 9:09 pm

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)

User avatar
andrew
Site Admin
Posts: 9037
Joined: Fri Mar 30, 2007 6:07 am

Re: C++ plugin, events

Post by andrew » Thu Aug 10, 2017 10:05 am

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.

YourRuler
Newbie Member
Posts: 9
Joined: Thu Mar 23, 2017 7:41 pm

Re: C++ plugin, events

Post by YourRuler » Thu Aug 10, 2017 10:04 pm

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!

User avatar
andrew
Site Admin
Posts: 9037
Joined: Fri Mar 30, 2007 6:07 am

Re: C++ plugin, events

Post by andrew » Thu Aug 10, 2017 11:43 pm

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.

Post Reply

Return to “QCAD Programming, Script Programming and Contributing”