![]() |
Home · All Classes · Main Classes · Grouped Classes · Modules · Functions | ![]() |
[Previous: QtSvg Module] [Qt's Modules] [Next: QtXml Module]
The QtScript module provides classes for making Qt applications scriptable. More...
QScriptContext | Represents a Qt Script function invocation |
---|---|
QScriptEngine | Environment for evaluating Qt Script code |
QScriptExtensionPlugin | Abstract base for custom QScript extension plugins |
QScriptValue | Acts as a container for the Qt Script data types |
QScriptValueIterator | Java-style iterator for QScriptValue |
QScriptable | Access to the Qt Script environment from Qt C++ member functions |
Applications that use Qt's Script classes need to be configured to be built against the QtScript module. To include the definitions of the module's classes, use the following directive:
#include <QtScript>
To link against the module, add this line to your qmake .pro file:
QT += script
The QtScript module is part of the Qt Desktop Edition and the Qt Open Source Edition.
Qt Script is based on the ECMAScript scripting language, as defined in standard ECMA-262. Microsoft's JScript, and Netscape's JavaScript are also based on the ECMAScript standard. For an overview of ECMAScript, see the ECMAScript reference. If you are not familiar with the ECMAScript language, there are several existing tutorials and books that cover this subject, such as JavaScript: The Definitive Guide.
To evaluate script code, you create a QScriptEngine and call its evaluate() function, passing the script code (text) to evaluate as argument.
QScriptEngine engine; qDebug() << "the magic number is:" << engine.evaluate("1 + 2").toNumber();
The return value will be the result of the evaluation (represented as a QScriptValue object); this can be converted to standard C++ and Qt types.
Custom properties can be made available to scripts by registering them with the script engine. This is most easily done by setting properties of the script engine's Global Object:
QScriptValue val(&engine, 123); engine.globalObject().setProperty("foo", val); qDebug() << "foo times two is:" << engine.evaluate("foo * 2").toNumber();
This places the properties in the script environment, thus making them available to script code.
Any QObject subclass can be made available for use with scripts.
When a QObject is passed to the QScriptEngine::newQObject() function, a QtScript wrapper object is created that can be used to make the QObject's signals, slots, properties, and child objects available to scripts. Because QtScript uses Qt's meta-object system, there is no need to implement any additional wrappers or bindings.
Here's an example of making an instance of a QObject subclass available to script code under the name "myObject":
QScriptEngine engine; QObject *someObject = new MyObject; QScriptValue objectValue = engine.newQObject(someObject); engine.globalObject().setProperty("myObject", objectValue);
The newQObject() function accepts two additional optional arguments: one is the ownership mode, and the other is a collection of options that allow you to control certain aspects of how the QScriptValue that wraps the QObject should behave. We will come back to the usage of these arguments later.
To connect to a signal, you invoke the connect() function of the signal from script code. There are three overloads of connect().
connect(function)
In this form of connection, the argument to connect() is the function to connect to the signal.
function myInterestingScriptFunction() { ... } ... myQObject.somethingChanged.connect(myInterestingScriptFunction);
The argument can be a QtScript function, as in the above example, or it can be a QObject slot, as in the following example:
myQObject.somethingChanged.connect(myOtherQObject.doSomething);
When the argument is a QObject slot, the argument types of the signal and slot do not necessarily have to be compatible; QtScript will, if necessary, perform conversion of the signal arguments to match the argument types of the slot.
To disconnect from a signal, you invoke the signal's disconnect() function, passing the function to disconnect as argument:
myQObject.somethingChanged.disconnect(myInterestingFunction); myQObject.somethingChanged.disconnect(myOtherQObject.doSomething);
When a script function is invoked in response to a signal, the `this' object will be the object that emitted the signal.
connect(thisObject, function)
In this form of the connect() function, the first argument is the object that will be bound to the variable, this, when the function specified using the second argument is invoked.
If you have a push button in a form, you typically want to do something involving the form in response to the button's clicked signal; passing the form as the this object makes sense in such a case.
var obj = { x: 123 }; var fun = function() { print(this.x); }; myQObject.somethingChanged.connect(obj, fun);
To disconnect from the signal, pass the same arguments to disconnect():
myQObject.somethingChanged.disconnect(obj, fun);
connect(thisObject, functionName)
In this form of the connect() function, the first argument is the object that will be bound to the variable, this, when a function is invoked in response to the signal. The second argument specifies the name of a function that is connected to the signal, and this refers to a member function of the object passed as the first argument (thisObject in the above scheme).
Note that the function is resolved when the connection is made, not when the signal is emitted.
var obj = { x: 123, fun: function() { print(this.x); } }; myQObject.somethingChanged.connect(obj, "fun");
To disconnect from the signal, pass the same arguments to disconnect():
myQObject.somethingChanged.disconnect(obj, "fun");
To emit a signal from script code, you simply invoke the signal function, passing the relevant arguments:
myQObject.somethingChanged("hello");
When a signal or slot is overloaded, QtScript will attempt to pick the right overload based on the actual types of the QScriptValue arguments involved in the function invocation. For example, if your class has slots myOverloadedSlot(int) and myOverloadedSlot(QString), the following script code will behave reasonably:
myQObject.myOverloadedSlot(10); // will call the int overload myQObject.myOverloadedSlot("10"); // will call the QString overload
You can specify a particular overload by using array-style property access with the normalized signature of the C++ function as the property name:
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
If the overloads have different number of arguments, QtScript will pick the overload with the argument count that best matches the actual number of arguments passed to the slot.
For overloaded signals, QtScript will connect to the most general overload, unless you refer to the signal with its full normalized signature.
The properties of the QObject are available as properties of the corresponding Qt Script object. When you manipulate a property in script code, the C++ get/set method for that property will automatically be invoked. For example, if your C++ class has a property declared as follows:
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled)
then script code can do things like the following:
myQObject.enabled = true; ... myQObject.enabled = !myQObject.enabled;
Every named child of the QObject is by default available as a property of the corresponding Qt Script object. For example, if you have a QDialog whose objectName() is "okButton", you can access this object in script code through the expression
myDialog.okButton
Since objectName is itself a Q_PROPERTY, you can manipulate the name in script code:
myDialog.okButton.objectName = "cancelButton";
// from now on, myDialog.cancelButton references the button
You can also use the functions findChild() and findChildren() to find children. These two functions behave identically to QObject::findChild() and QObject::findChildren(), respectively. Example:
var okButton = myDialog.findChild("okButton"); if (okButton != null) { // do something with the OK button } 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.
By default, the script engine does not take ownership of the QObject that is passed to QScriptEngine::newQObject(). You can change that by specifying a different ownership mode as the second argument (QScriptEngine::QtOwnership is the default). Specifying QScriptEngine::ScriptOwnership will cause the script engine to take full ownership of the QObject and delete it when it deems that it is safe to do so. If the QObject does not have a parent object, and/or the QObject is created in context of the script engine and is not intended to outlive the script engine, this ownership mode is appropriate. For example, a constructor function that constructs QObjects only to be used in the script environment is a good candidate:
QScriptValue myQObjectConstructor(QScriptContext *context, QScriptEngine *engine)
{
// let the engine manage the new object's lifetime.
return engine->newQObject(new MyQObject(), QScriptEngine::ScriptOwnership);
}
Another ownership mode is QScriptEngine::AutoOwnership, where the ownership is based on whether the QObject has a parent or not. If the QtScript garbage collector finds that the QObject is no longer referenced within the script environment, the QObject will be deleted only if it does not have a parent.
QScriptEngine::newQObject() can take a third argument which allows you to control various aspects of the access to the QObject through the QtScript wrapper object (the value returned by newQObject()). QScriptEngine::ExcludeChildObjects specifies that child objects of the QObject should not appear as properties of the wrapper object. QScriptEngine::ExcludeSuperClassProperties and QScriptEngine::ExcludeSuperClassMethods can be used to avoid exposing members that are inherited from the QObject's superclass; this is useful e.g. when you want to define 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). 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 QtScript wrapper object. If you want new properties to truly become persistent properties of the QObject, rather than properties that die when the wrapper object dies (and that aren't shared if the QObject is wrapped multiple times with newQObject()), you should use this option.
QtScript 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, or when you call QScriptEngine::toScriptValue() or QScriptEngine::fromScriptValue(). QtScript provides default conversion for many of the built-in Qt types. You can change the conversion for a type by registering your own conversion functions with qScriptRegisterMetaType().
The following table describes the default conversion from a QScriptValue to a C++ type:
C++ Type | Default Conversion |
---|---|
bool | QScriptValue::toBoolean() |
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()) |
qlonglong | qlonglong(QScriptValue::toInteger()) |
qulonglong | qulonglong(QScriptValue::toInteger()) |
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. |
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. |
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. |
QVariantMap | If the QScriptValue is an object, the result is a QVariantMap with a (key, value) pair (propertyName, propertyValue.toVariant()) for each property, using QScriptValueIterator to iterate over the object's properties. |
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. |
QList<int> | If the QScriptValue is an array, the result is a QList<int> constructed from the result of QScriptValue::toInt32() for each array element; otherwise, the result is an empty QList<int>. |
Additionally, QtScript will handle the following cases:
The following table describes the default behavior when a QScriptValue is constructed from a C++ type:
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) |
qlonglong | QScriptValue(engine, qsreal(value)). Note that the conversion may lead to loss of precision, since not all 64-bit integers can be represented as a qsreal. |
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 as a qsreal. |
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. |
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 a new script array (created with QScriptEngine::newArray()), whose elements are created using QScriptEngine::newQObject() for each element of the list. |
QList<int> | 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 will be wrapped using QScriptEngine::newVariant(). For null pointers of any type, the result is QScriptEngine::nullValue().
This section explains how to implement application objects and provides the necessary technical background material.
Making C++ classes and objects available to a scripting language is not trivial since scripting languages are more dynamic than C++ and it must be possible to introspect objects (query information such as functions names, function signatures, properties, etc., at runtime). Standard C++ doesn't provide for this.
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.
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 the Qt Object Model. The information in this document and the documents it links to are very useful for understanding how to implement application objects, however this knowledge is not essential. To make an object available in Qt Script, it must derive from QObject. All classes which derive from QObject are introspective and can provide the information needed by the scripting engine, e.g. classname, functions, signatures, etc., at runtime. Because we obtain the information we need about classes dynamically at run time, there is no need to write wrappers for QObject derived classes.
The meta object system makes information about signals and slots dynamically available at runtime. This means that for QObject derived classes, 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 be available to scripters are public slots:
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.
It is possible to make any function script-invokable by specifying the Q_INVOKABLE modifier when declaring the function:
class MyObject : public QObject { Q_OBJECT public: Q_INVOKABLE void thisMethodIsInvokableInQtScript(); void thisMethodIsNotInvokableInQtScript(); ... };
Once declared with Q_INVOKABLE, the method can be invoked from QtScript 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 any function as target, both native and non-native).
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:
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. The class declaration of MyObject must look like the following to declare a property enabled of the type bool, which should use the function setEnabled(bool) as its setter function and the function isEnabled() as its getter function:
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.
If you don't want a property of your class to be accessible in QtScript, you set the SCRIPTABLE attribute to false when declaring the property; by default, the SCRIPTABLE attribute is true. Example:
Q_PROPERTY(int nonScriptableProperty READ foo WRITE bar SCRIPTABLE false)
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 fired (emitted) the slot is called. This connection is established using the QObject::connect() function. This mechanism is also available to Qt Script programmers. The C++ code for declaring a signal is no different for a C++ class that is to be used by Qt Script than a C++ class used with Qt.
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 this time is to declare a signals section, and declare the relevant signal in it. Now the script writer can write 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 );
}
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. 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 you make available. The implementation of these objects simply calls 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.
QtScript implements all the built-in classes and functions defined in ECMA-262.
The Date parsing and string conversion functions are implemented using QDateTime::fromString() and QDateTime::toString(), respectively.
The RegExp class is a wrapper around QRegExp. The QRegExp semantics do not precisely match the semantics for regular expressions defined in ECMA-262.
var o = new Object();
(o.__proto__ === Object.prototype); // this evaluates to true
var o = new Object();
o.__defineGetter__("x", function() { return 123; });
var y = o.x; // 123
var o = new Object();
o.__defineSetter__("x", function(v) { print("and the value is:", v); });
o.x = 123; // will print "and the value is: 123"
[Previous: QtSvg Module] [Qt's Modules] [Next: QtXml Module]
Copyright © 2007 Trolltech | Trademarks | Qt 4.3.0rc1 |