如今QtScript是Qt纯粹兼容的一部分,缺乏QML集成以及它已经在一段时间内不会更新/维护,而是有利于新引擎。
所以在qt5中将使用QJSEngine来代替 QtScript
QJSEngine
使用evaluate () 来评估脚本代码。
QJSEngine myEngine;
QJSValue three = myEngine.evaluate("1 + 2");
evaluate () 返回一个保存评估结果的QJSValue。QJSValue类提供了将结果转换为各种 C++ 类型的函数(例如QJSValue::toString () 和QJSValue::toNumber ())。
以下代码片段显示了如何定义脚本函数,然后使用QJSValue::call () 从 C++ 调用:
QJSValue fun = myEngine.evaluate("(function(a, b) { return a + b; })");
QJSValueList args;
args << 1 << 2;
QJSValue threeAgain = fun.call(args);
从上面的片段可以看出,脚本是以字符串的形式提供给引擎的。加载脚本的一种常见方法是读取文件的内容并将其传递给evaluate ():
QString fileName = "helloworld.qs";
QFile scriptFile(fileName);
if (!scriptFile.open(QIODevice::ReadOnly))
// handle error
QTextStream stream(&scriptFile);
QString contents = stream.readAll();
scriptFile.close();
myEngine.evaluate(contents, fileName);
这里我们将文件名作为第二个参数传递给evaluate ()。这不会以任何方式影响评估;第二个参数是存储在Error对象中用于调试目的的通用字符串。
对于更大的功能,您可能希望将代码和数据封装到模块中。模块是包含脚本代码、变量等的文件,并使用导出语句来描述其与应用程序其余部分的接口。在 import 语句的帮助下,一个模块可以引用其他模块的功能。这允许以安全的方式从较小的连接构建块构建脚本应用程序。相比之下,使用evaluate () 的方法存在一个风险,即来自一次evaluate () 调用的内部变量或函数会意外污染全局对象并影响后续评估。
以下示例提供了一个可以添加数字的模块:
export function sum(left, right)
{
return left + right
}
这个模块可以用 QJSEngine::import() 加载,如果它保存在 name 下math.mjs:
QJSvalue module = myEngine.importModule("./math.mjs");
QJSValue sumFunction = module.property("sum");
QJSValue result = sumFunction.call(args);
模块还可以使用 import 语句使用其他模块的功能:
import { sum } from "./math.mjs";
export function addTwice(left, right)
{
return sum(left, right) * 2;
}
引擎配置
globalObject () 函数返回与脚本引擎关联的全局对象。可以从任何脚本代码访问全局对象的属性(即它们是全局变量)。通常,在评估“用户”脚本之前,您需要通过向全局对象添加一个或多个属性来配置脚本引擎:
myEngine.globalObject().setProperty("myNumber", 123);
...
QJSValue myNumberPlusOne = myEngine.evaluate("myNumber + 1");
向脚本环境添加自定义属性是提供特定于您的应用程序的脚本 API 的标准方法之一。通常这些自定义属性是由newQObject () 或newObject () 函数创建的对象。
脚本异常
evaluate () 可以抛出脚本异常(例如由于语法错误)。如果是,那么evaluate () 返回抛出的值(通常是一个Error对象)。使用QJSValue::isError () 来检查异常。
有关错误的详细信息,请使用QJSValue::toString () 获取错误消息,并使用QJSValue::property () 查询Error对象的属性。以下属性可用:
- name
- message
- fileName
- lineNumber
- stack
QJSValue result = myEngine.evaluate(...);
if (result.isError())
qDebug()
<< "Uncaught exception at line"
<< result.property("lineNumber").toInt()
<< ":" << result.toString();
脚本对象创建
使用newObject () 创建一个 JavaScript 对象;这是脚本语句的 C++ 等效项new Object()。您可以使用QJSValue中的对象特定功能来操作脚本对象(例如QJSValue::setProperty ())。同样,使用newArray () 创建一个 JavaScript 数组对象。
QObject 集成
使用newQObject () 包装一个QObject(或子类)指针。newQObject () 返回一个代理脚本对象;QObject的属性、子项以及信号和槽可用作代理对象的属性。不需要绑定代码,因为它是使用 Qt 元对象系统动态完成的。
QPushButton *button = new QPushButton;
QJSValue scriptButton = myEngine.newQObject(button);
myEngine.globalObject().setProperty("button", scriptButton);
myEngine.evaluate("button.checkable = true");
qDebug() << scriptButton.property("checkable").toBool();
scriptButton.property("show").call(); // call the show() slot
使用newQMetaObject () 包装一个QMetaObject;这为您提供了基于QObject的类的“脚本表示” 。newQMetaObject () 返回一个代理脚本对象;类的枚举值可用作代理对象的属性。
可以从脚本调用暴露给元对象系统的构造函数(使用Q_INVOKABLE )以使用 JavaScriptOwnership 创建一个新的QObject实例。例如,给定以下类定义:
class MyObject : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE MyObject() {}
};
staticMetaObjectfor 类可以像这样暴露给 JavaScript :
QJSValue jsMetaObject = engine.newQMetaObject(&MyObject::staticMetaObject);
engine.globalObject().setProperty("MyObject", jsMetaObject);
然后可以在 JavaScript 中创建类的实例:
engine.evaluate("var myObject = new MyObject()");
注意:目前只支持使用Q_OBJECT宏的类;不可能将Q_GADGET staticMetaObject类暴露给JavaScript。
动态 QObject 属性
不支持动态QObject属性。例如,以下代码将不起作用:
QJSEngine engine;
QObject *myQObject = new QObject();
myQObject->setProperty("dynamicProperty", 3);
QJSValue myScriptQObject = engine.newQObject(myQObject);
engine.globalObject().setProperty("myObject", myScriptQObject);
qDebug() << engine.evaluate("myObject.dynamicProperty").toInt();
QJSEngine 提供了一个兼容的 ECMAScript 实现。默认情况下,日志等熟悉的实用程序不可用,但可以通过installExtensions () 函数安装它们。
QJSValue
QJSValue支持ECMA-262标准中定义的类型:基本类型,未定义、空、布尔、数字和字符串;以及对象和数组类型。此外,还为QVariant和QObject等Qt/C++类型提供了内置支持。
对于基于对象的类型(包括Date和RegExp),使用QJSEngine中的newT()函数(例如QJSEngine::newObject())创建所需类型的QJSValue。对于基元类型,使用QJSValue构造函数重载之一。对于其他类型,例如注册的小工具类型,如QPoint,可以使用QJSEngine::toScriptValue。
名为isT()的方法(例如isBool()、isUndefined())可用于测试某个值是否属于特定类型。名为toT()的方法(例如toBool()、toString())可用于将QJSValue转换为另一种类型。还可以使用通用的qjsvalue_cast()函数。
对象值具有零个或多个属性,这些属性本身就是QJSValue。使用setProperty()设置对象的属性,并调用property()检索属性的值。
QJSEngine myEngine;
QJSValue myObject = myEngine.newObject();
QJSValue myOtherObject = myEngine.newObject();
myObject.setProperty("myChild", myOtherObject);
myObject.setProperty("name", "John Doe");
QJSValue for 数组
要使用QJSValue创建数组,请使用QJSEngine::newArray()
// Assumes that this class was declared in QML.
QJSValue jsArray = engine->newArray(3);
要设置数组中的单个元素,请使用 setProperty(quint32 arrayIndex, const QJSValue &value) 重载。例如,要用整数填充上面的数组:
for (int i = 0; i < 3; ++i) {
jsArray.setProperty(i, QRandomGenerator::global().generate());
}
要确定数组的长度,请访问“length”属性。要访问数组元素,请使用属性(quint32 arrayIndex)重载。下面的代码将我们在上面创建的数组读回一个列表:
QVector<int> integers;
const int length = jsArray.property("length").toInt();
for (int i = 0; i < length; ++i) {
integers.append(jsArray.property(i).toInt());
}