Application Scripting with QJSEngine
Jump to navigation
Jump to search
Introduction
This page is the result of struggling to figure out how to script an application with the new QJSEngine. QJSEngine is replacing the QtScript module that was marked as deprecated from Qt 5.5.
There are some documentation on the QJSEngine but that documentation does not contain all of the information needed to script a full application, or to support complex scripting. This page was also initially created with no prior experience with QtScript.
Requirements
The requirements to evaluate the script engine were as follow:
- To add and use custom functions from within the current script that is getting executed.
- To add functions that can be used by executing scripts such as supporting functions. These functions can come from the application or other scripts.
- To add C++ objects to the script engine that can be used by current script.
- Log from the script as well as from the functions.
Steps to script an application
The following steps were identified as the needed steps to getting a script to work.
- Create the script engine.
- Create the objects that should be available to the scripts
- Add objects to the engine.
- Create functions that should be available.
- Add the functions as needed.
- Run the script, making use of objects and functions added previously.
Example code
The following code can be used as a guideline to achieve the above requirements.
main.cpp
#include <QCoreApplication>
#include <QJSEngine>
#include <QDebug>
#include "MyObject.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
/* Create the script engine. */
QJSEngine myEngine;
/* Install the console extension inside the engine.
* This will allow one to call 'console.log()' from the script and
* script functions for logging. */
myEngine.installExtensions(QJSEngine::ConsoleExtension);
/* Create the QObject that should be available inside the script.
* It must be a QObject and functions that are callable from the
* script must be marked with the Q_INVOKABLE keyword. */
MyObject* myObj = new MyObject();
/* Add an QObject to the engine. The object must first be created as
* a QJSValue and then this value is then registered with a script
* specific name. This name is used to access the object with from
* the script. All functions marked as Q_INVOKABLE or functions that
* are slots will be callable from the script. When calling a function
* the object instance must be used. */
QJSValue scriptObject = myEngine.newQObject(myObj);
myEngine.globalObject().setProperty("obj", scriptObject);
/* A wrapper can be added around a specific function on the added objects
* so that the caller does not need to specify the instance when calling
* said function. This can be useful for global type functions where there
* will only be one instance of an object in the script environment. */
myEngine.globalObject().setProperty("doSomething", scriptObject.property("doSomething"));
/* A new function can be added to the script as needed. This function can probably
* be imported but otherwise it can be manually added. The evaluate function is used
* to create the value in the engine. */
QJSValue newFunctionVal = myEngine.evaluate("function newFunction(value) { "
" console.log('In newFunction(value): '); "
" console.log(' - value: ' + value); "
" return value * value; "
"} ");
/* Now a script can be evaluated. The QJSEngine docs load the contents from a file but
* for simplicity this example creates the script inline. It 'should' not matter how
* the contents string gets generated or where it comes from, what matters is the content. */
QString contents = "function f(a, b) { "
" console.log('In f(a, b): '); "
" console.log(' - a: ' + a); "
" console.log(' - b: ' + b); "
" return a + b; "
"} "
"function(a, b) { "
" console.log('In main function: '); "
" console.log(' - a: ' + a); "
" console.log(' - b: ' + b); "
" a2 = newFunction(a); "
" b2 = obj.doSomething(b); "
" b3 = doSomething(b2); // <- No instance "
" return f(a2, b3); "
"} ";
QJSValue ans = myEngine.evaluate(contents);
/* Now the script environment is set up and one can execute the
* base function in the last QJSValue.
* First set up the arguments that must be passed to the script
* and then call the function. */
QJSValueList args;
args << 5 << 6;
qDebug().noquote() << "Before calling the function...";
qDebug().noquote() << "-------------------------------------";
/* The QJSValue::call() function will call the function in the last answer
* that does not have a name given to it. It seems like this is treated
* as the main function. From there other functions can be called as needed. */
QJSValue result = ans.call(args);
qDebug().noquote() << "=====================================";
qDebug().noquote() << "Value is: " << result.toString();
}
MyObject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
#include <QDebug>
class MyObject
: public QObject
{
Q_OBJECT
public:
MyObject() {}
Q_INVOKABLE int doSomething(int number)
{
qDebug() << "Doing something with number: " << number;
return number * 2;
}
};
#endif // MYOBJECT_H