接着上一章的讲解
当connect()或者disconnect()成功调用,将返回undefined;否则,将投递一个脚本异常,你可以从结果Error object中获取error信息,例如:
try {
myQObject.somethingChanged.connect(myQObject, "slotThatDoesntExist");
} catch (e) {
print(e);
}
脚本触发信号
在脚本中触发信号,你可以简单的调用信号函数,并传递相关的参数:
myQObject.somethingChanged("hello");
在脚本中不支持定义信号,信号必须在c++中定义。
重载信号和槽
当信号和槽被重载时,Qt脚本将尝试根据QScriptValue的类型选择被调用的函数,例如,如果你的类中有myOverloadedSlot(int) 和myOverloadedSlot(QString)两种槽函数,下面的脚本代码将合理运行:
myQObject.myOverloadedSlot(10); // will call the int overload
myQObject.myOverloadedSlot("10"); // will call the QString overload
用array-style属性你也能指定一个详细的重载函数,array-style使用C++函数的归一化签名作为属性名称:
myQObject['myOverloadedSlot(int)']("10"); // call int overload; the argument is converted to an int
myQObject['myOverloadedSlot(QString)'](10); // call QString overload; the argument is converted to a string
如果重载的函数有不同数量的参数,Qt将选择重载函数通过参数数量最符合被要求槽的参数数量。
重载信号,如果你尝试通过名字连接信号,Qt脚本会触发一个错误,您必须使用要连接的特定过载的完整规范化签名来引用信号。
访问属性
QObject对象的属性在Qt脚本对象中对应相应的属性,当你再脚本中操作一个属性时,C++中的属性的get/set方法将自动被调用,例如,如果你的C++类中声明了一个属性,向下面这样:
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled)
脚本代码中可以做下面的事情:
myQObject.enabled = true;
// ...
myQObject.enabled = !myQObject.enabled;
访问子对象列表
QObject中每一个命名的孩子在脚本封装的对象中是默认有效的,例如,如果你有一个QDialog,QDialog有一个子Widget对象的objectName属性是“okButton”,你可以通过下面的表达式在脚本中访问子对象:
myDialog.okButton
因为objectName是它自己的一个Q_PROPERTY,你能在脚本中操作这个名字,例如,重命名这个对象:
myDialog.okButton.objectName = "cancelButton";
// from now on, myDialog.cancelButton references the button
你也能用findchild()和findChildren()查找children,这两个函数的行为是定义的 QObject::findChild() 和 QObject::findChildren(),例如,我们可以这些函数或正则表达式查找Objects:
var okButton = myDialog.findChild("okButton");
if (okButton != null) {
// do something with the OK button
okButton.text='hello';
}
var buttons = myDialog.findChildren(RegExp("button[0-9]+"));
for (var i = 0; i < buttons.length; ++i) {
// do something with buttons[i]
}
You typically want to use findChild() when manipulating a form that uses nested layouts; that way the script is isolated from the details about which particular layout a widget is located in。
上面的英文我的理解是: 当操作一个窗口时,通常想用findChild()查找布局下的窗口,并更改窗口,使窗口更加美观。
控制QObject的所有权
Qt脚本适用垃圾收集器回收不在需要的脚本对象的内存;在脚本环境中一个对象如果不在被使用,它的内存能被自动回收。当脚本封装的C++对象被回收时Qt脚本让你控制C++ QObject会做什么。当你通过传递ownership modeQScriptEngine::newQObject()的第二个参数来创建一个脚本对象时。
了解Qt脚本处理ownership是很重要的,因为它能帮助你避免内存泄漏。
Qt Ownership
将QScriptEngine::ScriptOwnership指定为所有权模式将导致脚本引擎获得QObject的完全所有权,并在确定这样做是安全的时删除它(即,当脚本代码中不再有对它的引用时)。如果QObject没有父对象,和/或QObject是在脚本引擎的上下文中创建的,并且不打算超过脚本引擎,则此所有权模式是适当的。
例如, 构造函数构造仅在脚本环境中使用的QObjects是很好的候选项(脚本中创建的QObject对象,只在脚本中使用):
QScriptValue myQObjectConstructor(QScriptContext *context, QScriptEngine *engine)
{
// let the engine manage the new object's lifetime.
return engine->newQObject(new MyQObject(), QScriptEngine::ScriptOwnership);
}
Auto-Ownership
QScriptEngine::AutoOwnership是基于QObject是否有父对象,如果QObject没有父对象,并且在脚本中不在被使用,那么Qt脚本收集器将回收该内存。
如果其他人也删除了这个QObejct将会发生什么?
在脚本外没有注意到所有权而删除一个封装的QObject对象是有可能的,在这种情况下,封装的对象将仍然是一个对象(不像C++指针,脚本对象不会变成null),任何访问脚本属性的尝试将触发一个异常。
注意即使删除一个QObject对象QScriptValue::isQObject()将仍然返回true,因为这样仅仅是测试脚本对象的类型,不是内部指针是否为非空。换句话说,如果QScriptValue::isQObject()返回true,但是QScriptValue::toQObject()返回null,则QObject已经在Qt脚本外被删除。
自定义访问QObject对象
QScriptEngine::newQObject()可以带三个参数,这些参数允许你控制访问QObject的很多种情况。
QScriptEngine::ExcIncludeChildObjects指定QObject的子对象不应该作为一个脚本对象的属性存在。
QScriptEngine::ExcIncludeSuperClassProperties和QScriptEngine::ExcIncludeSuperClassMethods能用来允许导出QObject的父类的成员,这对于定义“纯”接口非常有用,从脚本的角度来看,继承的成员没有意义。您不希望脚本作者能够更改对象的objectName属性或调用deleteLater()槽。
QScriptEngine::AutoCreateDynamicProperties指定应该将QObject中不存在的属性创建为QObject的动态属性,而不是Qt Script包装器对象的属性。如果希望新属性真正成为QObject的持久属性,而不是与包装器对象一起销毁的属性(如果QObject多次用QScriptEngine::newQObject()包装,则不共享这些属性),则应该使用此选项。
QScriptEngine::SkipMethodsInEnumeration指定在for-in脚本语句中枚举QObject包装器的属性时应跳过信号和插槽。这在定义原型对象时很有用,因为根据约定,原型的属性不应该是可枚举的。
从脚本中创建一个新的基于QObjor的类
QScriptEngine::newQObject()函数用来封装一个已经存在的QObject对象的实例,以便脚本可以使用它。一个不同的情况是你想在脚本中构建一个新的对象,而不是仅仅访问一个已经存在的对象。
Qt meta-type 系统不支持基于QObject类的构造函数的动态绑定,如果你想在脚本中创建一个新对象,Qt脚本可以为您生成一个合理的脚本构造函数,查看QScriptEngine::scriptValueFromQMetaObject().
你也能用QScriptEngine::newFunction()封装你自己的的工厂函数,然后添加到脚本环境当中,查看QScriptEngine::newQMetaObject() 相关的例子.