Qt Script(二)

Customizing Access to the QObject
自定义访问QObject
QScriptEngine::newQObject() can take a third argument which allows you to control various aspects of the access to the QObject through the Qt Script wrapper object it returns.
QScriptEngine::newQObject()可以传递第三个参数,允许通过返回的Qt Script封装的对象来控制访问QObject的各个方面。
QScriptEngine::ExcludeChildObjects specifies that child objects of the QObject should not appear as properties of the wrapper object.
QScriptEngine::ExcludeChildObjects指定QObject不应该作为属性出现在封装对象中的子对象。
QScriptEngine::ExcludeSuperClassProperties and QScriptEngine::ExcludeSuperClassMethods can be used to avoid exposing members that are inherited from the QObject’s superclass. This is useful for defining a “pure” interface where inherited members don’t make sense from a scripting perspective; e.g., you don’t want script authors to be able to change the objectName property of the object or invoke the deleteLater() slot.
可以避免暴露继承自QObject父类的成员函数。这是有用的对定义一个纯接口,从脚本的角度看继承的没有意义的成员函数。
QScriptEngine::AutoCreateDynamicProperties specifies that properties that don’t already exist in the QObject should be created as dynamic properties of the QObject, rather than as properties of the Qt Script wrapper object. If you want new properties to truly become persistent properties of the QObject, rather than properties that are destroyed along with the wrapper object (and that aren’t shared if the QObject is wrapped multiple times with QScriptEngine::newQObject()), you should use this option.
AutoCreateDynamicProperties指定在QObject不存在的属性应该被当作QObject的动态属性来创建而不是作为Qt Script封装对象的属性.如果你想要新的属性真正成为QObject的持久属性
而不是随着封装对象被销毁并且如果QObject用QScriptEngine::newQObject()封装多次不能被共享。你应该使用这个选项。
QScriptEngine::SkipMethodsInEnumeration specifies that signals and slots should be skipped when enumerating the properties of the QObject wrapper in a for-in script statement. This is useful when defining prototype objects, since by convention function properties of prototypes should not be enumerable.
SkipMethodsInEnumeration指定当在一个for-in脚本语句中枚举QObject封装的属性时,信号跟槽应该被跳过。当定义原型对象时这是有用的因为按照约定函数属性不应该被枚举。
Making a QObject-based Class New-able from a Script
从一个脚本中创建一个基于QObject的新类
The QScriptEngine::newQObject() function is used to wrap an existing QObject instance, so that it can be made available to scripts. A different scenario is that you want scripts to be able to construct new objects, not just access existing ones.
newQObject函数被用来封装已经存在的QObject实例因此它对脚本是可用的。一个不同的情形是你想脚本可以构造一个新的对象而不仅仅是访问已经存在的。
The Qt meta-type system currently does not provide dynamic binding of constructors for QObject-based classes. If you want to make such a class new-able from scripts, Qt Script can generate a reasonable script constructor for you; see QScriptEngine::scriptValueFromQMetaObject().
Qt元类型系统目前并没有为基于QObject的类的构造函数提供动态的绑定。如果你想要创造这样一个新的类,Qt Script可以产生一个有意义的脚本构造函数。
You can also use QScriptEngine::newFunction() to wrap your own factory function, and add it to the script environment; see QScriptEngine::newQMetaObject() for an example.
你也可以用newFunction来封装你自己的工厂函数并且把它添加到脚本环境。看newQMetaObject的例子。

Enum Values
枚举值
Values for enums declared with Q_ENUMS are not available as properties of individual wrapper objects; rather, they are properties of the QMetaObject wrapper object that can be created with QScriptEngine::newQMetaObject().
用Q_ENUMS声明的枚举值不能作为独立封装对象的属性来访问,它们是QMetaObjecr封装对象的属性,可以用newQMetaObject()来创建
Conversion Between Qt Script and C++ Types
在Qt Script和C++之间转换类型
Qt Script will perform type conversion when a value needs to be converted from the script side to the C++ side or vice versa; for instance, when a C++ signal triggers a script function, when you access a QObject property in script code, or when you call QScriptEngine::toScriptValue() or QScriptEngine::fromScriptValue() in C++. Qt Script provides default conversion operations for many of the built-in Qt types. You can change the conversion operation for a type (including your custom C++ types) by registering your own conversion functions with qScriptRegisterMetaType().
Qt Script将执行类型转换当一个值需要从脚本转换到C++反之亦然。比如当一个C++信号触发一个脚本函数,当你在脚本代码中访问一个QObject的属性。或者当你在C++中调用toScriptValue或者fromScriptValue。Qt Script为很多内置的Qt类型提供一个默认的转换操作通过用qScriptRegusterMetaType注册你自己的转换函数.
Default Conversion from Qt Script to C++
从Qt Script默认转换到C++
The following table describes the default conversion from a QScriptValue to a C++ type.
下面的表格描述了从一个QSriptValue到一个C++类型的默认转换
C++ Type Default Conversion
bool QScriptValue::toBool()
int QScriptValue::toInt32()
uint QScriptValue::toUInt32()
float float(QScriptValue::toNumber())
double QScriptValue::toNumber()
short short(QScriptValue::toInt32())
ushort QScriptValue::toUInt16()
char char(QScriptValue::toInt32())
uchar unsigned char(QScriptValue::toInt32())
long long(QScriptValue::toInteger())
ulong ulong(QScriptValue::toInteger())
qlonglong qlonglong(QScriptValue::toInteger())
qulonglong qulonglong(QScriptValue::toInteger())
QString An empty string if the QScriptValue is null or undefined; QScriptValue::toString() otherwise.
如果QScriptValue是一个null或者undefined转换成的QString将是空字符串
QDateTime QScriptValue::toDateTime()
QDate QScriptValue::toDateTime().date()
QRegExp QScriptValue::toRegExp()
QObject* QScriptValue::toQObject()
QWidget* QScriptValue::toQObject()
QVariant QScriptValue::toVariant()
QChar If the QScriptValue is a string, the result is the first character of the string, or a null QChar if the string is empty; otherwise, the result is a QChar constructed from the unicode obtained by converting the QScriptValue to a ushort.如果QScriptValue是一个字符串,结果将是字符串的第一个值,或者如果字符串是空则是一个null,否则结果是从QScriptValue转换成一个ushort的unicode构造的QChar.
QStringList If the QScriptValue is an array, the result is a QStringList constructed from the result of QScriptValue::toString() for each array element; otherwise, the result is an empty QStringList.
如果QScriptValue是一个数组,结果是一个QStringList,对每一个数组元素使用toString的结果。否则结果是一个空的QStringList
QVariantList If the QScriptValue is an array, the result is a QVariantList constructed from the result of QScriptValue::toVariant() for each array element; otherwise, the result is an empty QVariantList.
如果QScriptValue是一个数组,结果是一个QVariantList,对每一个数据元素使用toVariant的结果构造的。否则是一个空的QVariantList.
QVariantMap If the QScriptValue is an object, the result is a QVariantMap with a (key, value) pair of the form (propertyName, propertyValue.toVariant()) for each property, using QScriptValueIterator to iterate over the object’s properties.
如果QScriptValue是一个对象,QVariantMap是来自每个属性的(propertyName,propertyValue.toVariant())键值对,使用QScriptValueIterator来迭代对象的属性。
QObjectList If the QScriptValue is an array, the result is a QObjectList constructed from the result of QScriptValue::toQObject() for each array element; otherwise, the result is an empty QObjectList.
如果QScriptValue是一个数组,QObjectList被对每个数组元素使用toQObject的结果构造的
否则是空的
QList If the QScriptValue is an array, the result is a QList constructed from the result of QScriptValue::toInt32() for each array element; otherwise, the result is an empty QList.
如果QScriptValue是一个数组,QList被对每个元素使用toInt32的结果构造的。
Additionally, Qt Script will handle the following cases:
另外,Qt Script将处理下面的情况
If the QScriptValue is a QObject and the target type name ends with * (i.e., it is a pointer), the QObject pointer will be cast to the target type with qobject_cast().
如果一个QScriptValue是一个对象并且目标类型名是以*结束,则一个QObject指针将可以用qobject_cast强转成目标类型
If the QScriptValue is a QVariant and the target type name ends with * (i.e., it is a pointer), and the userType() of the QVariant is the type that the target type points to, the result is a pointer to the QVariant’s data.
如果QScriptValue是一个QVariant并且目标类型是以*结束,并且并且QVariant的userType也是目标的指针类型,结果是一个指向QVariant的data的指针。
If the QScriptValue is a QVariant and it can be converted to the target type (according to QVariant::canConvert()), the QVariant will be cast to the target type with qvariant_cast().
如果QScriptValue是一个QVariant并且它可以被转换成目标类型(根据QVariant::canConvert()),QVariant将使用qvariant_cast强转成目标类型.
Default Conversion from C++ to Qt Script
从C++到Qt Script的默认转换
The following table describes the default behavior when a QScriptValue is constructed from a C++ type:
下面的表描述了当一个QScriptValue被C++类型构造的时候的默认行为
C++ Type Default Construction
void QScriptEngine::undefinedValue()
bool QScriptValue(engine, value)
int QScriptValue(engine, value)
uint QScriptValue(engine, value)
float QScriptValue(engine, value)
double QScriptValue(engine, value)
short QScriptValue(engine, value)
ushort QScriptValue(engine, value)
char QScriptValue(engine, value)
uchar QScriptValue(engine, value)
QString QScriptValue(engine, value)
long If the input fits in an int, QScriptValue(engine, int(value)); otherwise, QScriptValue(engine, double(value)). Note that the latter conversion can be lossy.
如果输入适合在一个int中QScriptValue(engine,int(value))否则QScriptValue(engine,double(value))注意后面的转换可能会丢失数据。
ulong If the input fits in a uint, QScriptValue(engine, uint(value)); otherwise, QScriptValue(engine, double(value)). Note that the latter conversion can be lossy.
qlonglong QScriptValue(engine, qsreal(value)). Note that the conversion may lead to loss of precision, since not all 64-bit integers can be represented using the qsreal type.
qulonglong QScriptValue(engine, qsreal(value)). Note that the conversion may lead to loss of precision, since not all 64-bit unsigned integers can be represented using the qsreal type.
QChar QScriptValue(this, value.unicode())
QDateTime QScriptEngine::newDate(value)
QDate QScriptEngine::newDate(value)
QRegExp QScriptEngine::newRegExp(value)
QObject* QScriptEngine::newQObject(value)
QWidget* QScriptEngine::newQObject(value)
QVariant QScriptEngine::newVariant(value)
QStringList A new script array (created with QScriptEngine::newArray()), whose elements are created using the QScriptValue(QScriptEngine *, QString) constructor for each element of the list.
一个新的脚本数组被创建,每一个数组元素用list元素构造的QScriptValue(QScriptEngine *, QString)
QVariantList A new script array (created with QScriptEngine::newArray()), whose elements are created using QScriptEngine::newVariant() for each element of the list.
QVariantMap A new script object (created with QScriptEngine::newObject()), whose properties are initialized according to the (key, value) pairs of the map.
QObjectList A new script array (created with QScriptEngine::newArray()), whose elements are created using QScriptEngine::newQObject() for each element of the list.
QList A new script array (created with QScriptEngine::newArray()), whose elements are created using the QScriptValue(QScriptEngine *, int) constructor for each element of the list.
Other types (including custom types) will be wrapped using QScriptEngine::newVariant(). For null pointers of any type, the result is QScriptEngine::nullValue().
其他类型包括自定义类型将使用newVariant封装。对于任意类型的指针,结果是nullValue()
How to Design and Implement Application Objects
如何设计和实现应用对象
This section explains how to implement application objects and provides the necessary technical background material.
这部分解释如何实现应用对象并且提供必须的技术背景资料
Making a C++ object available to Scripts Written in Qt Script
使得C++对象可以访问在Qt Script中写的脚本
Making C++ classes and objects available to a scripting language is not trivial because scripting languages tend to be more dynamic than C++, and it must be possible to introspect objects (query information such as function names, function signatures, properties, etc., at run-time). Standard C++ does not provide features for this.
使得C++类和对象可以访问脚本语言不是简单的,因为脚本语言倾向于比C++更动态并且它必须有对象反省(在运行时查询函数名,函数签名,属性等等信息),标准C++不能提供这些特性。
We can achieve the functionality we want by extending C++, using C++’s own facilities so our code is still standard C++. The Qt meta-object system provides the necessary additional functionality. It allows us to write using an extended C++ syntax, but converts this into standard C++ using a small utility program called moc (Meta-Object Compiler). Classes that wish to take advantage of the meta-object facilities are either subclasses of QObject, or use the Q_OBJECT macro. Qt has used this approach for many years and it has proven to be solid and reliable. Qt Script uses this meta-object technology to provide scripters with dynamic access to C++ classes and objects.
我们可以通过扩展C++实现这个功能,使用C++本身的能力,因此我们的代码仍是标准的C++。Qt元对象系统提供了附加的必要信息功能。它允许我们适应扩展的C++语言来写但是转换成标准的C++代码使用一个很小的moc工具。希望有元对象功能的类同时也是QObject的子类,或者使用了Q_OBJECT宏。Qt使用这种方法多年并且它已经被改进的很稳定和可靠。Qt Script使用了元对象技术给脚本者提供了动态访问C++类和对象。
To completely understand how to make C++ objects available to Qt Script, some basic knowledge of the Qt meta-object system is very helpful. We recommend that you read about the Qt Object Model and The Meta-Object System, which are useful for understanding how to implement application objects.
为了完全理解如何让Qt Script访问C++对象,一些Qt元对象的基础知识是很有用的。我们要求你读关于Qt对象模型和元对象系统,是非常有用的对如何实现应用对象的理解。
However, this knowledge is not essential in the simplest cases. To make an object available in Qt Script, it must derive from QObject. All classes which derive from QObject can be introspected and can provide the information needed by the scripting engine at run-time; e.g., class name, functions, signatures. Because we obtain the information we need about classes dynamically at run-time, there is no need to write wrappers for QObject derived classes.
然而,在简单的例子中这种只是不是必须的。为了使一个对象在Qt Script中可用,它必须派生自QObject。所有派生自QObject的类可以被反省并且可以提供脚本引擎运行时需要的信息。比如类名,函数,签名.这不是必须的为QObject的派生对象写封装。
Making C++ Class Member Functions Available in Qt Script
使得C++类的成员函数在Qt Script中可用
The meta-object system also makes information about signals and slots dynamically available at run-time. By default, for QObject subclasses, only the signals and slots are automatically made available to scripts. This is very convenient because, in practice, we normally only want to make specially chosen functions available to scripters. When you create a QObject subclass, make sure that the functions you want to expose to Qt Script are public slots.
元对象系统也使得关于信号跟槽的信息也可以在运行时动态访问。默认对于QObject的子类,只有信号跟槽是自动可以被脚本可用。这是飞创方便的。通常我们只是想选择一些函数让脚本可用。当你创建一个QObject子类,确保你想暴露给Qt Script的函数是公共槽。
For example, the following class definition enables scripting only for certain functions:
例如下面类的定义只对特定的函数对脚本可用。
class MyObject : public QObject
{
Q_OBJECT

public:
MyObject( … );

void aNonScriptableFunction();

public slots: // these functions (slots) will be available in Qt Script
void calculate( … );
void setEnabled( bool enabled );
bool isEnabled() const;

private:
….
};
In the example above, aNonScriptableFunction() is not declared as a slot, so it will not be available in Qt Script. The other three functions will automatically be made available in Qt Script because they are declared in the public slots section of the class definition.
在上面的例子,aNonScriptableFunction函数没有被声明成一个槽,因此它不能在Qt Script中访问。其他三个函数自动可以被Qt Script访问因为它们被声明在类的public slots区域。
It is possible to make any function script-invokable by specifying the Q_INVOKABLE modifier when declaring the function:
它是可能的使得任何函数都是脚本可以调用的通过指定Q_INVOKEABLE修饰符当声明函数的时候。
class MyObject : public QObject
{
Q_OBJECT

public:
Q_INVOKABLE void thisMethodIsInvokableInQtScript();
void thisMethodIsNotInvokableInQtScript();

...

};
Once declared with Q_INVOKABLE, the method can be invoked from Qt Script code just as if it were a slot. Although such a method is not a slot, you can still specify it as the target function in a call to connect() in script code; connect() accepts both native and non-native functions as targets.
一旦使用了Q_INVOKABLE声明,方法就可以从Qt Script代码中被调用,就像一个slot.尽管这样一个方法不是一个槽,你仍可以指定它作为一个目标函数在脚本代码中的connect中。connect()接收本地和非本地的函数作为目标函数。
As discussed in Default Conversion from Qt Script to C++, Qt Script handles conversion for many C++ types. If your function takes arguments for which Qt Script does not handle conversion, you need to supply conversion functions. This is done using the qScriptRegisterMetaType() function.
在默认转换中一个讨论是从Qt Script到C++,Qt Script处理了很多C++类型的转换。如果你的函数带有Qt Script不能处理的转换,你需要提供转换函数,使用qScriptRegisterMetaType()函数完成。
Making C++ Class Properties Available in Qt Script
使得C++类的属性在Qt Script中可用
In the previous example, if we wanted to get or set a property using Qt Script we would have to write code like the following:
在前面的例子中,如果我们想要使用Qt Script get或者set一个属性,我们必须写像下面的代码
var obj = new MyObject;
obj.setEnabled( true );
print( “obj is enabled: ” + obj.isEnabled() );
Scripting languages often provide a property syntax to modify and retrieve properties (in our case the enabled state) of an object. Many script programmers would want to write the above code like this:
脚本语言通常提供一个修改和检索对象属性的语法。许多脚本开发者想要像这样写上面的代码
var obj = new MyObject;
obj.enabled = true;
print( “obj is enabled: ” + obj.enabled );
To make this possible, you must define properties in the C++ QObject subclass. For example, the following MyObject class declaration declares a boolean property called enabled, which uses the function setEnabled(bool) as its setter function and isEnabled() as its getter function:
为了做到这一点,你必须在C++QObject子类中定义属性。例如下面MyObject类的声明中声明一个被叫做enabled的布尔属性使用函数setEnabled(bool)作为它的setter并且isEnabled()作为它的getter函数。
class MyObject : public QObject
{
Q_OBJECT
// define the enabled property
Q_PROPERTY( bool enabled WRITE setEnabled READ isEnabled )

public:
MyObject( … );

void aNonScriptableFunction();

public slots: // these functions (slots) will be available in Qt Script
void calculate( … );
void setEnabled( bool enabled );
bool isEnabled() const;

private:
….

};
The only difference from the original code is the use of the macro Q_PROPERTY, which takes the type and name of the property, and the names of the setter and getter functions as arguments.
跟原始代码唯一不同的是使用了Q_PROPERTY宏,带有属性的类型和名称。并且setter的名字和getter函数作为参数
If you don’t want a property of your class to be accessible in Qt Script, you set the SCRIPTABLE attribute to false when declaring the property; by default, the SCRIPTABLE attribute is true. For example:
如果你不想在Qt Script中让你的类属性被访问。你可以设置SCRIPTABLE属性为false当声明属性的时候。默认情况下SCRIPTABLE属性是true.
Q_PROPERTY(int nonScriptableProperty READ foo WRITE bar SCRIPTABLE false)
Reacting to C++ Objects Signals in Scripts
让C++信号在脚本中起作用
In the Qt object model, signals are used as a notification mechanism between QObjects. This means one object can connect a signal to another object’s slot and, every time the signal is emitted, the slot is called. This connection is established using the QObject::connect() function.
在Qt对象模型,信号被用来作为QObject之间的一个通知机制。这意味这一个对象可以连接信号到另一个对象的槽,每次信号被发射,槽就会被调用.这种连接使用connect函数连接。
The signals and slots mechanism is also available to Qt Script programmers. The code to declare a signal in C++ is the same, regardless of whether the signal will be connected to a slot in C++ or in Qt Script.
信号跟槽机制也可以在Qt Script中使用。声明一个信号的代码跟C++中是相同的.不管信号是在C++或者Qt Script中被连接。
class MyObject : public QObject
{
Q_OBJECT
// define the enabled property
Q_PROPERTY( bool enabled WRITE setEnabled READ isEnabled )

public:
MyObject( … );

void aNonScriptableFunction();

public slots: // these functions (slots) will be available in Qt Script
void calculate( … );
void setEnabled( bool enabled );
bool isEnabled() const;

signals: // the signals
void enabledChanged( bool newState );

private:
….

};
The only change we have made to the code in the previous section is to declare a signals section with the relevant signal. Now, the script writer can define a function and connect to the object like this:
我们对前面异界代码做的唯一的改变是在声明信号区域声明徐昂关的信号。现在脚本开发者可以向下面一样定义一个函数并且连接到对象
function enabledChangedHandler( b )
{
print( “state changed to: ” + b );
}

function init()
{
var obj = new MyObject();
// connect a script function to the signal
obj[“enabledChanged(bool)”].connect(enabledChangedHandler);
obj.enabled = true;
print( “obj is enabled: ” + obj.enabled );
}
Design of Application Objects
应用对象的设计
The previous section described how to implement C++ objects which can be used in Qt Script. Application objects are the same kind of objects, and they make your application’s functionality available to Qt Script scripters. Since the C++ application is already written in Qt, many objects are already QObjects. The easiest approach would be to simply add all these QObjects as application objects to the scripting engine. For small applications this might be sufficient, but for larger applications this is probably not the right approach. The problem is that this method reveals too much of the internal API and gives script programmers access to application internals which should not be exposed.
前面的章节描述了如何实现C++对象可以在Qt Script中使用。应用都喜爱那个是一类相同的对象并且它们使得你的应用程序的功能在Qt Script中的脚本可用。因为C++应用已经在Qt中写好了,很多对象是QObjects.最简单的办法
是简单地将所有这些对象作为应用对象添加到脚本引擎。对于小的应用这可能是足够的但是对更大一点的应用这就是不太正确的办法。问题是这种办法暴露了太多的内部API并且给了脚本开发者访问应用不应该被暴露的内部。
Generally, the best way of making application functionality available to scripters is to code some QObjects which define the applications public API using signals, slots, and properties. This gives you complete control of the functionality made available by the application. The implementations of these objects simply call the functions in the application which do the real work. So, instead of making all your QObjects available to the scripting engine, just add the wrapper QObjects.
通常地,使得应用程序的功能能被脚本访问的最好的办法是编写一些定义公有API的QObject
使用信号,槽和属性。通过应用给了你完全的控制那些功能可以给访问。这些对象的实现简单地调用了在应用程序中工作的函数因此替代使得你的所有对象对脚本引擎都可访问。只是添加了封装对象。
Returning QObject Pointers
返回QObject指针
If you have a slot that returns a QObject pointer, you should note that, by default, Qt Script only handles conversion of the types QObject* and QWidget*. This means that if your slot is declared with a signature like “MyObject* getMyObject()”, Qt Script doesn’t automatically know that MyObject* should be handled in the same way as QObject* and QWidget*. The simplest way to solve this is to only use QObject* and QWidget* in the method signatures of your scripting interface.
如果你有一个返回一个QObject指针的槽,你应该注意,默认情况下Qt Script只处理QObject*和QWidget*类型转换,这意味这如果你的槽声明带有MyObject*的标签
Qt Script不能自动知道MyObject*应该被以跟QObject*和QWidget*一样的方式处理。解决这个问题最简单的办法是在你的脚本接口的方法签名中使用QObject*和QWidget*。
Alternatively, you can register conversion functions for your custom type with the qScriptRegisterMetaType() function. In this way, you can preserve the precise typing in your C++ declarations, while still allowing pointers to your custom objects to flow seamlessly between C++ and scripts. Example:
另一种是你可以用qScriptRegisterMetaType()为你自定义类型注册转换函数.在用这种方式,你可以保存在你C++中声明的精确的类型,同事仍允许指向你自定义对象的指针在C++和SCript中无缝的传递。
class MyObject : public QObject
{
Q_OBJECT

};

Q_DECLARE_METATYPE(MyObject*)

QScriptValue myObjectToScriptValue(QScriptEngine engine, MyObject const &in)
{ return engine->newQObject(in); }

void myObjectFromScriptValue(const QScriptValue &object, MyObject* &out)
{ out = qobject_cast”<”MyObject*”>”(object.toQObject()); }

qScriptRegisterMetaType(&engine, myObjectToScriptValue, myObjectFromScriptValue);
Function Objects and Native Functions
函数对象和本地函数
In Qt Script, functions are first-class values; they are objects that can have properties of their own, just like any other type of object. They can be stored in variables and passed as arguments to other functions. Knowing how function calls in Qt Script behave is useful when you want to define and use your own script functions. This section discusses this matter, and also explains how you can implement native functions; that is, Qt Script functions written in C++, as opposed to functions written in the scripting language itself. Even if you will be relying mostly on the dynamic QObject binding that Qt Script provides, knowing about these powerful concepts and techniques is important to understand what’s actually going on when script functions are executed.
在Qt Script中,函数是第一类值;它们是可以拥有自己属性的对象,就像任何对象类型。它们被存储在变量中并且作为参数传递给其他函数。了解Qt Script中函数调用行为是有用的当你想要定义和使用你自己的脚本函数。这个章节谈论这个问题。并且解释你应该如何实现本地函数。就是说Qt Script函数是在C++中被写而不是在脚本语言中被写。即使你主要依赖QtScript提供的动态QObject绑定。了解诶这些强大的概念和技术对理解当脚本函数被执行的时候实际上发生了什么是重要的。
Calling a Qt Script Function from C++
从C++调用一个Qt Script函数
Calling a Qt Script function from C++ is achieved with the QScriptValue::call() function. A typical scenario is that you evaluate a script that defines a function, and at some point you want to call that function from C++, perhaps passing it some arguments, and then handle the result. The following script defines a Qt Script object that has a toKelvin() function:
从C++调用一个Qt Script函数用call函数实现。一个典型的情景是你评估定义了一个函数的脚本并且在某个时刻你想从C++中调用这个函数。或许给它传递一些参数然后处理结果。下面的脚本定义了一个有taKelvin函数的Qt Script对象。
({ unitName: “Celsius”,
toKelvin: function(x) { return x + 273; }
})
The toKelvin() function takes a temperature in Kelvin as argument, and returns the temperature converted to Celsius. The following snippet shows how the toKelvin() function might be obtained and called from C++:
toKelvin函数有一个带有一个温度作为参数并且返回转换成Celsius的温度。下面片段展示了如何从C++中获取和调用toKelvin函数
QScriptValue object = engine.evaluate(“({ unitName: ‘Celsius’, toKelvin: function(x) { return x + 273; } })”);
QScriptValue toKelvin = object.property(“toKelvin”);
QScriptValue result = toKelvin.call(object, QScriptValueList() << 100);
qDebug() << result.toNumber(); // 373
If a script defines a global function, you can access the function as a property of QScriptEngine::globalObject(). For example, the following script defines a global function add():
如果脚本定义了一个全局函数,你可以通过globalObject()的属性来访问这个函数。例如下面定义了一个全局的add函数。
function add(a, b) {
return a + b;
}
C++ code might call the add() function as follows:
C++代码可能像下面一样调用add函数。
QScriptValue add = engine.globalObject().property(“add”);
qDebug() << add.call(QScriptValue(), QScriptValueList() << 1 << 2).toNumber(); // 3
As already mentioned, functions are just values in Qt Script; a function by itself is not “tied to” a particular object. This is why you have to specify a this object (the first argument to QScriptValue::call()) that the function should be applied to.
正如,在Qt Script中只是些值,一个函数本身并不与一个特殊对象关联。这极速hi你为什么必须指定一个this(call的第一个参数)
If the function is supposed to act as a method (i.e. it can only be applied to a certain class of objects), it is up to the function itself to check that it is being called with a compatible this object.
如果假定函数的行为跟方法一样(它只能被特定的类对象调用),它正在被用一个兼容的this对象调用是取决于函数本身的检查。
Passing an invalid QScriptValue as the this argument to QScriptValue::call() indicates that the Global Object should be used as the this object; in other words, that the function should be invoked as a global function.
传递一个无效的QScriptValue作为call的参数,暗示全局对象应该作为this对象。换句话说函数应该作为一个全局函数来调用。
The this Object
this对象
When a Qt Script function is invoked from a script, the way in which it is invoked determines the this object when the function body is executed, as the following script example illustrates:
当一个Qt Script函数被一个脚本调用的时候,函数被调用的方式取决于当函数体被执行的时候this对象,下面脚本实例说明
var getProperty = function(name) { return this[name]; };

name = “Global Object”; // creates a global variable
print(getProperty(“name”)); // “Global Object”

var myObject = { name: ‘My Object’ };
print(getProperty.call(myObject, “name”)); // “My Object”

myObject.getProperty = getProperty;
print(myObject.getProperty(“name”)); // “My Object”

getProperty.name = “The getProperty() function”;
getProperty.getProperty = getProperty;
getProperty.getProperty(“name”); // “The getProperty() function”
An important thing to note is that in Qt Script, unlike C++ and Java, the this object is not part of the execution scope. This means that member functions (i.e., functions that operate on this) must always use the this keyword to access the object’s properties. For example, the following script probably doesn’t do what you want:
在Qt Script中需要注意的一件重要的事情是不像C++和Java,this镀锡i昂不是执行范围的一部分。这意味着成员函数必须总使用this关键字来访问对象的属性。例如下面脚本可能不是你想要的结果。
var o = { a: 1, b: 2, sum: function() { return a + b; } };
print(o.sum()); // reference error, or sum of global variables a and b!!
You will get a reference error saying that ‘a is not defined’ or, worse, two totally unrelated global variables a and b will be used to perform the computation, if they exist. Instead, the script should look like this:
你将得到一个错误的引用提示a没有被定义或者更糟的是,两个没有关联的全局变量a,b将被用来执行计算,如果它们存在,代替脚本应该像下面这样写
var o = { a: 1, b: 2, sum: function() { return this.a + this.b; } };
print(o.sum()); // 3
Accidentally omitting the this keyword is a typical source of error for programmers who are used to the scoping rules of C++ and Java.
意外省略this关键字是一个错误典型来源对那些习惯C++和Java作用域的开发者。
Wrapping a Native Function
封装一个本地函数
Qt Script provides QScriptEngine::newFunction() as a way of wrapping a C++ function pointer; this enables you to implement a function in C++ and add it to the script environment, so that scripts can invoke your function as if it were a “normal” script function. Here is how the previous getProperty() function can be written in C++:
Qt Script提供newFunction()作为一种封装C++函数指针的方法。这使得你可以在C++中实现一个函数并且将它添加到脚本环境,以便于脚本调用你的函数好像它是一个普通的脚本函数。前面的getProperty函数可以在C++中写
QScriptValue getProperty(QScriptContext *ctx, QScriptEngine *eng)
{
QString name = ctx->argument(0).toString();
return ctx->thisObject().property(name);
}
Call QScriptEngine::newFunction() to wrap the function. This will produce a special type of function object that carries a pointer to the C++ function internally. Once the resulting wrapper has been added to the scripting environment (e.g., by setting it as a property of the Global Object), scripts can call the function without having to know nor care that it is, in fact, a native function.
调用newFunction来封装函数.这将产生一个特殊的类型的函数对象带有一个指向C++函数的指针。一旦封装的结果被添加到脚本环境.脚本将可以调用函数不用关心它是不是一个本地函数。
Note that the name of the C++ function doesn’t matter in the scripting sense; the name by which the function is invoked by scripts depends only on what you call the script object property in which you store the function wrapper.
注意C++函数名在脚本中并不重要.那个函数被脚本调用取决于你调用脚本对象的属性,你存储在函数封装中的属性。
It is currently not possible to wrap member functions; i.e., methods of a C++ class that require a this object.
目前它还不能封装成员函数,一个C++类的方法要求一个this对象。
The QScriptContext Object
QScriptContext对象
A QScriptContext holds all the state associated with a particular invocation of your function. Through the QScriptContext, you can:
一个QScriptContext包含了和你的函数特殊调用关联的所有状态。通过QScriptContext你可以
Get the arguments that were passed to the function.
得到传递给函数的参数
Get the this object.
得到this对象
Find out whether the function was called with the new operator (the significance of this will be explained later).
发现函数是否被用new操作符调用
Throw a script error.
抛出一个脚本错误
Get the function object that’s being invoked.
得到被调用的函数对象
Get the activation object (the object used to hold local variables).
The following sections explain how to make use of this functionality.
得到激活对象(对象通常被存储在local变量中),下面的章节解释如何使用这项功能。
Processing Function Arguments
处理函数参数
Two things are worth noting about function arguments:
关于函数参数有两件事情需要关注。
Any script function — including native functions — can be invoked with any number of arguments. This means that it is up to the function itself to check the argument count if necessary, and act accordingly (e.g., throw an error if the number of arguments is too large, or prepare a default value if the number is too small).
任何脚本函数包括本地函数可以被调用用任意数量的参数。这意味着取决于函数本身来检查函数数量如果需要
A value of any type can be supplied as an argument to any function. This means that it is up to you to check the type of the arguments if necessary, and act accordingly (e.g., throw an error if an argument is not an object of a certain type).
任何类型的值可以作为参数被传递给任何函数。这意味着取决于你检查参数类型如果需要
In summary: Qt Script does not automatically enforce any constraints on the number or type of arguments involved in a function call.
总之Qt Script不能自动地强制对被调用的函数的参数数量和类型有任何约束。
Formal Parameters and the Arguments Object
正式参数和参数对象
A native Qt Script function is analogous to a script function that defines no formal parameters and only uses the built-in arguments variable to process its arguments. To see this, let’s first consider how a script would normally define an add() function that takes two arguments, adds them together and returns the result:
一个本地的Qt Script函数是跟一个定义没有参数并且使用内置参数变量来处理它的参数的脚本函数是类似的。为了明白这个,让我们首先思考一个脚本如何定义一个带有两个参数的add函数,将它们想加并且返回结果。
function add(a, b) {
return a + b;
}
When a script function is defined with formal parameters, their names can be viewed as mere aliases of properties of the arguments object; for example, in the add(a, b) definition’s function body, a and arguments[0] refer to the same variable. This means that the add() function can equivalently be written like this:
当使用正式的参数定义的脚本函数,它们的名字可以被视为仅仅是参数对象的别名。例如在add定义的函数体内,a和argumenys[0]引用相同的变量。这意味着add函数等价于下面的写法
function add() {
return arguments[0] + arguments[1];
}
This latter form closely matches what a native implementation typically looks like:
后一种形式更接近匹配一个典型的本地实现。
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
{
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return a + b;
}
Checking the Number of Arguments
检查参数数量
Again, remember that the presence (or lack) of formal parameter names in a function definition does not affect how the function may be invoked; add(1, 2, 3) is allowed by the engine, as is add(42). In the case of the add() function, the function really needs two arguments in order to do something useful. This can be expressed by the script definition as follows:
再一次记住在一个函数定义中形参的名字不会影响函数被调用。add(1,2,3)是被引擎允许的
同样add(42)也是。这种情况下add函数确实需要两个参数为了做一些有用的事情。脚本定义可以像下面一样被表达。
function add() {
if (arguments.length != 2)
throw Error(“add() takes exactly two arguments”);
return arguments[0] + arguments[1];
}
This would result in an error being thrown if a script invokes add() with anything other than two arguments. The native function can be modified to perform the same check:
如果一个脚本用不是两个参数调用add,本地函数可以被修改执行相同的检查。
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
{
if (ctx->argumentCount() != 2)
return ctx->throwError(“add() takes exactly two arguments”);
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return a + b;
}
Checking the Types of Arguments
检查参数类型
In addition to expecting a certain number of arguments, a function might expect that those arguments are of certain types (e.g., that the first argument is a number and that the second is a string). Such a function should explicitly check the type of arguments and/or perform a conversion, or throw an error if the type of an argument is incompatible.
除了期望特定数量的参数,一个函数可能期望这些参数是特定的类型(比如第一个参数是number第二个参数是string)这样一个函数应该检查参数的类型或执行一个转换或者抛出一个错误如果参数类型是不兼容的。
As it is, the native implementation of add() shown above doesn’t have the exact same semantics as the script counterpart; this is because the behavior of the Qt Script + operator depends on the types of its operands (for example, if one of the operands is a string, string concatenation is performed). To give the script function stricter semantics (namely, that it should only add numeric operands), the argument types can be tested:
事实上,上面展示的add本地实现没有对应相同语义的脚本,这是因为Qt Script+操作符依赖于他参数的类型。为了给脚本函数更严格的语义参数类型应该被这样检测。
function add() {
if (arguments.length != 2)
throw Error(“add() takes exactly two arguments”);
if (typeof arguments[0] != “number”)
throw TypeError(“add(): first argument is not a number”);
if (typeof arguments[1] != “number”)
throw TypeError(“add(): second argument is not a number”);
return arguments[0] + arguments[1];
}
Then an invocation like add(“foo”, new Array()) will cause an error to be thrown.然后类似于add(“foo”,new Array())的调用将会导致一个错误被抛出。
The C++ version can call QScriptValue::isNumber() to perform similar tests:
C++版本可以调用isNumber来执行类似的测试。
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
{
if (ctx->argumentCount() != 2)
return ctx->throwError(“add() takes exactly two arguments”);
if (!ctx->argument(0).isNumber())
return ctx->throwError(QScriptContext::TypeError, “add(): first argument is not a number”);
if (!ctx->argument(1).isNumber())
return ctx->throwError(QScriptContext::TypeError, “add(): second argument is not a number”);
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return a + b;
}
A less strict script implementation might settle for performing an explicit to-number conversion before applying the + operator:
一个不那么严格的脚本实现可能满足于执行一个明确的tonumber转换在应用+操作符之前。
function add() {
if (arguments.length != 2)
throw Error(“add() takes exactly two arguments”);
return Number(arguments[0]) + Number(arguments[1]);
}
In a native implementation, this is equivalent to calling QScriptValue::toNumber() without performing any type test first, since QScriptValue::toNumber() will automatically perform a type conversion if necessary.
在一个本地函数实现中,不执行任何类型测试等价于调用toNumber,因为toNumber将自动执行一个类型转换如果需要。
To check if an argument is of a certain object type (class), scripts can use the instanceof operator (e.g., “arguments[0] instanceof Array” evaluates to true if the first argument is an Array object); native functions can call QScriptValue::instanceOf().
为了检查一个参数是否是某种特定的对象类型,脚本可以使用instanceof操作符,本地函数可以调用instanceOf函数
To check if an argument is of a custom C++ type, you typically use qscriptvalue_cast() and check if the result is valid. For object types, this means casting to a pointer and checking if it is non-zero; for value types, the class should have an isNull(), isValid() or similar method. Alternatively, since most custom types are transported in QVariants, you can check if the script value is a QVariant using QScriptValue::isVariant(), and then check if the QVariant can be converted to your type using QVariant::canConvert().
为了检查一个参数是不是一个自定义的C++类型,你可以使用qscriptvalue_cast()并且检查结果是否有效。对对象类型来说,这意味这强制转换成一个指针并且检查结果是否为非0;对于值类型来说,类应该有isNull(),isValid()或者类似的方法。因为大部分自定义类型被传递在QVariants中,你可以检查脚本值是否是一个QVariant通过使用isVariant()并且检查QVariant是否可以被转换成你的类型通过使用canConvert().
Functions with Variable Numbers of Arguments
具有数量可变的参数的函数
Because of the presence of the built-in arguments object, implementing functions that take a variable number of arguments is simple. In fact, as we have seen, in the technical sense all Qt Script functions can be seen as variable-argument functions. As an example, consider a concat() function that takes an arbitrary number of arguments, converts the arguments to their string representation and concatenates the results; for example, concat(“Qt”, ” “, “Script “, 101) would return “Qt Script 101”. A script definition of concat() might look like this:
由于内置了参数对象,实现带有变参的函数是简单的。事实上正如我们所看到的,在技术意义上所有Qt Script函数都可以被看作变参函数。例如思考一个concat函数带有不确定的参数数量,将参数转换为string并且将结果连接起来.例如concat(“Qt”,” “,”Script”,101)将返回“Qt Script 101”。一个concat的定义可能是这样的。
function concat() {
var result = “”;
for (var i = 0; i < arguments.length; ++i)
result += String(arguments[i]);
return result;
}
Here is an equivalent native implementation:
这有一个相等的本地实现
QScriptValue concat(QScriptContext *ctx, QScriptEngine *eng)
{
QString result = “”;
for (int i = 0; i < ctx->argumentCount(); ++i)
result += ctx->argument(i).toString();
return result;
}
A second use case for a variable number of arguments is to implement optional arguments. Here’s how a script definition typically does it:
第二个使用变参的例子是实现可选参数.这里有典型的脚本定义实现它。
function sort(comparefn) {
if (comparefn == undefined)
comparefn = fn; /* replace fn with the built-in comparison function */
else if (typeof comparefn != “function”)
throw TypeError(“sort(): argument must be a function”);
// …
}
And here’s the native equivalent:
并且这有等价的本地实现。
QScriptValue sort(QScriptContext *ctx, QScriptEngine *eng)
{
QScriptValue comparefn = ctx->argument(0);
if (comparefn.isUndefined())
comparefn = /* the built-in comparison function */;
else if (!comparefn.isFunction())
return ctx->throwError(QScriptContext::TypeError, “sort(): argument is not a function”);

}
A third use case for a variable number of arguments is to simulate C++ overloads. This involves checking the number of arguments and/or their type at the beginning of the function body (as already shown), and acting accordingly. It might be worth thinking twice before doing this, and instead favor unique function names; e.g., having separate processNumber(number) and processString(string) functions rather than a generic process(anything) function. On the caller side, this makes it harder for scripts to accidentally call the wrong overload (since they don’t know or don’t comprehend your custom sophisticated overloading resolution rules), and on the callee side, you avoid the need for potentially complex (read: error-prone) checks to resolve ambiguity.
变参的第三个用例是模拟C++的重载函数。这就涉及在函数一开始就检查参数的数量和类型。在实现之前值得反复思考并且不是分离两个不同名称的函数processNumber(number),proessString(string)而是一个更通用的process(anything)函数。在调用者那边,这使得脚本更难意外的调用到错误的重载。并且在被调用者一边,你可以避免为了检查解决歧义的需要带来的复杂。
Accessing the Arguments Object
访问参数对象
Most native functions use the QScriptContext::argument() function to access function arguments. However, it is also possible to access the built-in arguments object itself (the one referred to by the arguments variable in script code), by calling the QScriptContext::argumentsObject() function. This has three principal applications:
大部分的本地函数使用argument函数来访问函数参数,然而它也是可以的通过内置的参数对象本身来访问通过调用argumentsObject函数。这有三个主要的应用:
The arguments object can be used to easily forward a function call to another function. In script code, this is what it typically looks like:
参数对象可以被前一个函数用来调用另一个函数。在脚本代码中,可能像下面一样。
function foo() {
// Let bar() take care of this.
print(“calling bar() with ” + arguments.length + “arguments”);
var result = bar.apply(this, arguments);
print(“bar() returned” + result);
return result;
}
For example, foo(10, 20, 30) would result in the foo() function executing the equivalent of bar(10, 20, 30). This is useful if you want to perform some special pre- or post-processing when calling a function (e.g., to log the call to bar() without having to modify the bar() function itself, like the above example), or if you want to call a “base implementation” from a prototype function that has the exact same “signature”. In C++, the forwarding function might look like this:
例如foo(10,20,30)结果是foo函数执行等价于bar(10,20,30).只是游泳的如果你想执行一些特殊的预处理当调用一个函数的时候或者如果你想要从一个有相同标签的原型函数调用基础实现。在C++中,函数可能像下面一样。
QScriptValue foo(QScriptContext *ctx, QScriptEngine *eng)
{
QScriptValue bar = eng->globalObject().property(“bar”);
QScriptValue arguments = ctx->argumentsObject();
qDebug() << “calling bar() with” << arguments.property(“length”).toInt32() << “arguments”;
QScriptValue result = bar.apply(ctx->thisObject(), arguments);
qDebug() << “bar() returned” << result.toString();
return result;
}
The arguments object can serve as input to a QScriptValueIterator, providing a generic way to iterate over the arguments. A debugger might use this to display the arguments object in a general purpose “Qt Script Object Explorer”, for example.
参数对象可以作为QScriptValueIterator的输入,提供一种通用的方法来迭代你的参数。一个调试器可能使用了这种难过方法来显示参数对象在通用“Qt脚本对象资源管理器”。
The arguments object can be serialized (e.g., with JSON) and transferred to another entity (e.g., a script engine running in another thread), where the object can be deserialized and passed as argument to another script function.
参数对象可以被序列化的实体,这个实体可以被反序列化并且作为参数被传递给另一个脚本函数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值