QScriptContext类
QScriptContext类表示一个qt脚本的功能调用。
QScriptContext类提供对“this”对象的访问以及传递到脚本函数的参数。当你编写一个从脚本代码调用本机函数(C++)时,需要访问此类。例如,下面的脚本代码:
foo(20.5, "hello", new Object())
被执行时,QScriptContext对象将会被创建,而且上下文将会携带作为QScriptValues的参数。在这种情况下,第一个参数将会包含一个小数值20.5,第二个QScriptValue包含字符串“hello”,第三个QScriptValue包含一个qt脚本对象。
使用argumentCount()去获取传递给函数的参数数量,而且使用argument()并给一个确定的索引去获取一个确定位置的参数。argumentsObject()功能返回一个脚本数组对象,该数据对象包含所有的参数。你可以使用QScriptValueIterator去迭代它的元素,或者使用QScriptValue::call()将数组作为参数传递给另一个脚本功能。
使用thisObject()去获取与这个功能调用相关的对象,使用setThisObject()去设置这个对象。如果你正在执行一个本地的实例方法,你可能要获取这个thisObject(),然后访问一个或更多它的属性:
QScriptValue Person_prototype_fullName(QScriptContext *context, QScriptEngine *engine)
{
QScriptValue self = context->thisObject();
QString result;
result += self.property("firstName").toString();
result += QLatin1String(" ");
result += self.property("lastName").toString();
return result;
}
使用isCalledAsConstructor()去确定某个功能调用是否作为一个构造函数调用。当函数作为构造函数调用时,thisObject()包含该函数要初始化的新构造对象。
使用throwValue()或者throwError()去丢弃一个例外。
使用callee()去获取这个表示被调用函数的QScriptValue。这中方式可以被用来递归地调用功能。
使用parentContext()去获取一个指向上下文的指针,这个上下文位于活动栈中的上下文之前。这个功能通常用来作调试使用。
activationObject()函数返回一个对象,这个对象被用于保持与这个功能调用相关的本地变量。你可以通过调用setActivationObject()替换这个活动对象。这些功能之中一个典型的用法是当你想要上下文中的脚本代码被执行时,可以去实现这样一个include()功能:
QScriptValue myInclude(QScriptContext *ctx, QScriptEngine *eng)
{
QString fileName = ctx->argument(0).toString();
QString contents = readTheFile(fileName);
ctx->setActivationObject(ctx->parentContext()->activationObject());
ctx->setThisObject(ctx->parentContext()->thisObject());
return eng->evaluate(contents, fileName);
}
使用backtrace()功能去获取一个与这个上下文相关的人为可读取的函数栈。这是很有用的,当作为调试使用时。toString()函数提供一个上下文的文本表示。
使用engine()去获取一个指向存在于该上下文中的QScriptEngine的指针。
QScriptValue类
QScriptValue类扮演着装载Qt脚本数据类型容器的角色。
QScriptValue支持ECMA-262标准类型:原始类型,像未定义的、空、二值、数值、字符串,还有对象类型。此外,Qt脚本内置了对QVariant、QObject和QMetaObject的支持。
对于基于对象的类型,使用QScriptEngine类中的newT()函数去创建一个期望类型的QScriptValue。对于原始类型,使用QScriptValue构造函数的重载之一。
像isBool()、isUndefined()形式为isT()的函数可以被用来测试某个值是否是一个确定的类型。像toBool()、toString()形式为toT()的函数可以将QScriptValue转换成另一种类型。你也可以使用通用的转换函数qscriptvalue_cast()。
对象值有0或者更多的属性,这些属性本身就是QScriptValues。使用setProperty()去设置一个对象的属性,然后调用property()去获取一个属性的值。
QScriptEngine myEngine;
QScriptValue myObject = myEngine.newObject();
QScriptValue myOtherObject = myEngine.newObject();
myObject.setProperty("myChild", myOtherObject);
myObject.setProperty("name", "John Doe");
每部分性质可以有一系列的属性;这些被指定为可选择的参数去调用setProperty()函数。某部分性质上的属性可以通过调用propertyFlags()功能来查询这些属性。下面的代码片段创建了一个不能被脚本代码修改的性质:
QScriptValue val(&myEngine, 123);
myObject.setProperty("myReadOnlyProperty", val, QScriptValue::ReadOnly);
如果你想要迭代一个脚本对象的性质,可以使用QScriptValueIterator类。
一个对象值有一个内部的prototype属性,这属性可以通过prototype()和setPrototype()访问。添加了一个prototype的属性是在所有拥有这个prototype对象间所共享的,这称为基于原型的继承。实际上,这意味着(默认本身没有请求的属性,property()将自动尝试在prototype()找属性(prototype()中查找,依此类推)。注意这个基于原型的查找并不是通过调用setProperty()得到的。setProperty()总会在脚本对象自身中创建属性。
通过调用call()可以调用功能对象。通过调用construct()可以构造新的对象。
使用equals()、strictlyEquals()和lessThan()去比较2个QScriptValues。
对象值可以定制与他们相关联的数据,具体可以看setData()和data()功能。默认情况下,这个数据是不被脚本获取的,它可以被用来存储任何与这个脚本对象有关系的数据。典型地,这被用来存储一个C++对象类型,这个类型包含着本地的目标数据。
注意一个QScriptValue对于一个对象来说,只是一个引用关系。拷贝一个QScriptValue,仅仅是拷贝目标对象的引用,而非对象本身。如果你想要拷贝目标对象本身,可以采用迭代的方法。
QScriptEngine
QScriptEngine类为一个脚本代码提供一个可执行的环境。
脚本执行
使用evaluate()去执行脚本代码,这与内置的脚本eval函数是相同的功能。
QScriptEngine myEngine;
QScriptValue three = myEngine.evaluate("1 + 2");
evaluate函数返回一个QScriptValue,这个QScriptValue保持这执行的结果。QScriptValue类提供转换结果到C++变量类型的功能。
下面的代码片段展示了一个脚本功能如何被定义的,还有如何被调用的:
QScriptValue fun = myEngine.evaluate("(function(a, b) { return a + b; })");
QScriptValueList args;
args << 1 << 2;
QScriptValue threeAgain = fun.call(QScriptValue(), 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);
这里我们将文件的名字作为第2个参数传递给evaluate()函数,这并不影响函数的执行,第二个参数是一个通用字符串,用于识别用于调试的脚本。
引擎配置
globalObject()函数返回脚本引擎的全局对象。全局对象的属性可以被任何脚本代码获取。典型地,在执行用户脚本前,你可能想要添加一个或多个全局对象属性去配置一个脚本引擎:
myEngine.globalObject().setProperty("myNumber", 123);
...
QScriptValue myNumberPlusOne = myEngine.evaluate("myNumber + 1");
在脚本环境中增加定制的属性是一种标准的方法之一。通常情况下,这些定制的属性通过newQObject函数或newObject函数来创建对象所得。
脚本例外
evaluate函数可以抛出一个脚本例外。在这种情况下,返回值就是那个错误对象的返回值。你可以调用hasUncaughtException函数去检查一个执行是否造成一个例外。在这种情况下,你可以调用toString函数去获取一个错误信息。 当前未捕获异常也可以通过uncaught exception()获得。调用clearExceptions函数将会导致未捕获的异常也被清掉。
QScriptValue result = myEngine.evaluate(...);
if (myEngine.hasUncaughtException()) {
int line = myEngine.uncaughtExceptionLineNumber();
qDebug() << "uncaught exception at line" << line << ":" << result.toString();
}
checkSyntax函数可以被用来确定代码是否可以被evaluate函数执行。
脚本对象创建
使用newObject函数去创建一个标准的Qt脚本对象。这与C++中的new Object()功能是一样的。在QScriptValue中,你可以使用指定的功能对象去执行这个脚本对象。相似地,使用newArray函数去创建一个qt脚本数组对象。使用newDate函数去创建一个Date对象;使用newRegExp函数去创建一个RegExp对象。
对象集成
使用newQObject函数去包裹一个对象指针。newQObject函数返回一个脚本对象的代理。对象中的属性、子类、信号和槽都是可用的。不需要绑定代码,因为它是在Qt元对象系统中动态地使用的。
QPushButton button;
QScriptValue scriptButton = myEngine.newQObject(&button);
myEngine.globalObject().setProperty("button", scriptButton);
myEngine.evaluate("button.checkable = true");
qDebug() << scriptButton.property("checkable").toBoolean();
scriptButton.property("show").call(); // call the show() slot
使用qScriptConnect函数去连接一个C++信号和一个脚本功能。这与QObject::connect()是相似的。当一个脚本功能被一个C++信号触发后,执行过程中可以造成一个脚本例外。你可以连接signalHandlerException信号去捕获这样一个例外。
使用newQMetaOject函数去包裹一个元对象,这个函数返回一个代理的脚本对象。代理对象中属性都是可以使用的。
支持定制的C++类型
使用newVariant函数去包裹一个QVariant对象。这个可以被用来存储定制的C++类型。当然了,这些类型是已经注册进Qt的元对象系统的。为了使这些类型变成可编写脚本的,你可以调用setDefaultPrototype函数去关联一个代理对象。原型对象定义了脚本API。不像对象集成一样,这里没有自动的绑定机制。例如,你不得不自己创建脚本API。
调用fromScriptValue函数将QScriptValue转换成另一种类型;调用toScriptValue函数将另一种值转换成QScriptValue。你可以指定如何使用qScriptRegisterMetaType函数和qScriptRegisterSequenceMetaType函数去执行C++类型的转换。默认情况下,Qt脚本将会使用QVariant去存储定制的类型值。
导入扩展
调用importExtension函数将基于插件的扩展导入到引擎中;调用availableExtensions函数去获取一个名为所有可用扩展的链表;调用importedExtensions函数去获取一个名为所有已经导入的链表。
调用pushContext函数去打开一个新的变量区域;调用popContext函数去关闭当前的区域。这是有用的,如果你正在执行一个扩展功能,这个扩展功能执行包含了临时变量定义的脚本代码。当执行结束完成时,这些临时变量会被安全释放。
本地功能
使用newFunctions函数去包裹本地的C++功能。这些功能一定要有签名、QScriptEngine::FunctionSignature。你可以将这些本地功能作为参数传递给newFunction函数。在这里是一个例子:
QScriptValue myAdd(QScriptContext *context, QScriptEngine *engine)
{
QScriptValue a = context->argument(0);
QScriptValue b = context->argument(1);
return a.toNumber() + b.toNumber();
}
为了将这个功能暴露给脚本代码,你可以将作为一个属性设置到全局对象中:
QScriptValue fun = myEngine.newFunction(myAdd);
myEngine.globalObject().setProperty("myAdd", fun);
一旦这被做后,脚本代码可以以与“普通”脚本函数完全相同的方式调用函数:
QScriptValue result = myEngine.evaluate("myAdd(myNumber, 1)");
长运行脚本
在主线程(GUI)线程中,如果你需要执行可能长时间运行的脚本,为了使得GUI保持响应,你应该首先调用setProcessEventsInterval函数。你可以调用abortEvaluation函数去终止一个当前运行的脚本。你可以调用isEvaluating函数去确定一个引擎是否正在运行一个脚本。
垃圾回收
当Qt的脚本对象不再被调用时,它们可能需要垃圾回收。目前自动垃圾回收机制无法保证一定会触发运行。
可以调用collectGarbage函数去显式请求垃圾回收。
可以调用reportAdditionalMemoryCost()函数来指示Qt脚本对象占用的内存不是由脚本环境管理的。上报增加的内存损耗更容易触发垃圾回收机制。当很多定制的本地的Qt脚本对象被回收时,上报内存损耗是很有用的。
内核调试跟踪设备
自从Qt4.4开始,可以通过qscriptengineageent接口通知与脚本执行相关的事件(例如脚本函数调用和语句执行);这可以被用来执行脚本引擎的调试和跟踪。