~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to doc/html/porting-qsa.html

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2008-08-01 11:30:30 UTC
  • mto: (15.1.1 lenny) (1.3.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 55.
  • Revision ID: james.westby@ubuntu.com-20080801113030-c33y1z0l21t6cj5r
Tags: upstream-4.4.1
ImportĀ upstreamĀ versionĀ 4.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
<!DOCTYPE html
3
3
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
4
4
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
 
<!-- /fasttmp/mkdist-qt-4.4.0-1209388211/qt-x11-opensource-src-4.4.0/doc/src/porting-qsa.qdoc -->
 
5
<!-- /fasttmp/mkdist-qt-4.4.1-1217326953/qt-all-opensource-src-4.4.1/doc/src/porting-qsa.qdoc -->
6
6
<head>
7
7
  <title>Qt 4.4: Moving from QSA to Qt Script</title>
8
8
  <link href="classic.css" rel="stylesheet" type="text/css" />
36
36
<a name="classes-vs-objects-and-properties"></a>
37
37
<h3>Classes vs. Objects and Properties</h3>
38
38
<p>QSA implements classes and inheritance much in a familiar way to users of other object oriented languages, like C++ and Java. However, the ECMAScript 3.0 standard defines that everything is an object, and objects can have named properties. For instance to create an point object with the properties x and y one would write the following Qt Script code:</p>
39
 
<pre>     point = new Object();
40
 
     point.x = 12;
41
 
     point.y = 35;</pre>
 
39
<pre> point = new Object();
 
40
 point.x = 12;
 
41
 point.y = 35;</pre>
42
42
<p>The object <tt>point</tt> in this case is constructed as a plain object and we assign two properties, <tt>x</tt> and <tt>y</tt>, to it with the values 12 and 35. The <tt>point</tt> object is assigned to the &quot;Global Object&quot; as the named property <tt>point</tt>. The global object can be considered the global namespace of the script engine. Similarly, global functions are named properties of the global object; for example:</p>
43
 
<pre>     function manhattanLength(point) {
44
 
         return point.x + point.y;
45
 
     }</pre>
 
43
<pre> function manhattanLength(point) {
 
44
     return point.x + point.y;
 
45
 }</pre>
46
46
<p>An equivalent construction that illustrates that the function is a property of the global object is the following assignment:</p>
47
 
<pre>     manhattanLength = function(point) {
48
 
        return point.x + point.y;
49
 
     }</pre>
 
47
<pre> manhattanLength = function(point) {
 
48
    return point.x + point.y;
 
49
 }</pre>
50
50
<p>Since functions are objects, they can be assigned to objects as properties, becoming member functions:</p>
51
 
<pre>     point.manhattanLength = function() {
52
 
         return this.x + this.y;
53
 
     }
54
 
     print(point.manhattanLength()); // prints 47</pre>
 
51
<pre> point.manhattanLength = function() {
 
52
     return this.x + this.y;
 
53
 }
 
54
 print(point.manhattanLength()); // prints 47</pre>
55
55
<p>In the code above, we see the first subtle difference between QSA and Qt Script. In QSA one would write the point class like this:</p>
56
 
<pre>     class Point() {
57
 
         var x;
58
 
         var y;
59
 
         function manhattanLength() { return x + y; }
60
 
     }</pre>
 
56
<pre> class Point() {
 
57
     var x;
 
58
     var y;
 
59
     function manhattanLength() { return x + y; }
 
60
 }</pre>
61
61
<p>where in the <tt>manhattanLength()</tt> function we access <tt>x</tt> and <tt>y</tt> directly because, when the function is called, the <tt>this</tt> object is implicitly part of the current scope, as in C++. In Qt Script, however, this is not the case, and we need to explicitly access the <tt>x</tt> and <tt>y</tt> values via <tt>this</tt>.</p>
62
62
<p>All the code above runs with QSA except the assignment of a function to <tt>point.manhattanLength</tt>, which we repeat here for clarity:</p>
63
 
<pre>     point.manhattanLength = function() {
64
 
         return this.x + this.y;
65
 
     }
66
 
     print(point.manhattanLength()); // prints 47</pre>
 
63
<pre> point.manhattanLength = function() {
 
64
     return this.x + this.y;
 
65
 }
 
66
 print(point.manhattanLength()); // prints 47</pre>
67
67
<p>This is because, in QSA, the value of <tt>this</tt> is decided based on the location of the declaration of the function it is used in. In the code above, the function is assigned to an object, but it is declared in the global scope, hence there will be no valid <tt>this</tt> value. In Qt Script, the value of <tt>this</tt> is decided at run-time, hence you could have assigned the <tt>manhattanLength()</tt> function to any object that had <tt>x</tt> and <tt>y</tt> values.</p>
68
68
<a name="constructors"></a>
69
69
<h3>Constructors</h3>
70
70
<p>In the code above, we use a rather awkward method for constructing the objects, by first instantiating them, then manually assigning properties to them. In QSA, the proper way to solve this is to implement a constructor in the class:</p>
71
 
<pre>     class Car {
72
 
         var regNumber;
73
 
         function Car(regnr) {
74
 
             regNumber = regnr;
75
 
         }
 
71
<pre> class Car {
 
72
     var regNumber;
 
73
     function Car(regnr) {
 
74
         regNumber = regnr;
76
75
     }
77
 
     var car = new Car(&quot;ABC 123&quot;);</pre>
 
76
 }
 
77
 var car = new Car(&quot;ABC 123&quot;);</pre>
78
78
<p>The equivalent in Qt Script is to create a constructor function:</p>
79
 
<pre>     function Car(regnr) {
80
 
         this.regNumber = regnr;
81
 
     }
82
 
     var car = new Car(&quot;ABC 123&quot;);</pre>
 
79
<pre> function Car(regnr) {
 
80
     this.regNumber = regnr;
 
81
 }
 
82
 var car = new Car(&quot;ABC 123&quot;);</pre>
83
83
<p>As we can see, the constructor is just a normal function. What is special with is how we call it, namely prefixed with the <tt>new</tt> keyword. This will create a new object and call the <tt>Car()</tt> function with the newly created object as the <tt>this</tt> pointer. So, in a sense, it is equivalent to:</p>
84
 
<pre>     var car = new Object();
85
 
     car.constructor = function(regnr) { ... }
86
 
     car.constructor();</pre>
 
84
<pre> var car = new Object();
 
85
 car.constructor = function(regnr) { ... }
 
86
 car.constructor();</pre>
87
87
<p>This is similar to the manhattenLength() example above. Again, the main difference between QSA and Qt Script is that one has to explicitly use the keyword <tt>this</tt> to access the members and that instead of declaring the variable, <tt>regNumber</tt>, we just extend the <tt>this</tt> object with the property.</p>
88
88
<a name="member-functions-and-prototypes"></a>
89
89
<h3>Member Functions and Prototypes</h3>
90
90
<p>As we saw above, one way of creating member functions of a Qt Script object is to assign the member function to the object as a property and use the <tt>this</tt> object inside the functions. So, if we add a <tt>toString</tt> function to the <tt>Car</tt> class</p>
91
 
<pre>     class Car {
92
 
         var regNumber;
93
 
         function Car(regnr) {
94
 
             regNumber = regnr;
95
 
         }
96
 
         function toString() {
97
 
             return regNumber;
98
 
         }
99
 
     }</pre>
 
91
<pre> class Car {
 
92
     var regNumber;
 
93
     function Car(regnr) {
 
94
         regNumber = regnr;
 
95
     }
 
96
     function toString() {
 
97
         return regNumber;
 
98
     }
 
99
 }</pre>
100
100
<p>one could write this in Qt Script as:</p>
101
 
<pre>     function Car(regnr) {
102
 
         this.regNumber = regnr;
103
 
         this.toString = function() { return this.regNumber; }
104
 
     }</pre>
 
101
<pre> function Car(regnr) {
 
102
     this.regNumber = regnr;
 
103
     this.toString = function() { return this.regNumber; }
 
104
 }</pre>
105
105
<p>In QSA, the member functions were part of the class declaration, and were therefore shared between all instances of a given class. In Qt Script, each instance has a instance member for each function. This means that more memory is used when multiple instances are used. Qt Script uses prototypes to remedy this.</p>
106
106
<p>The basic prototype-based inheritance mechanism works as follows. Each Qt Script object has an internal link to another object, its prototype. When a property is looked up in an object, and the object itself does not have the property, the interpreter searches for the property in the prototype object instead; if the prototype has the property then that property is returned. If the prototype object does not have the property, the interpreter searches for the property in the prototype of the prototype object, and so on.</p>
107
107
<p>This chain of objects constitutes a prototype chain. The chain of prototype objects is followed until the property is found or the end of the chain is reached.</p>
108
108
<p>To make the <tt>toString()</tt> function part of the prototype, we write code like this:</p>
109
 
<pre>     function Car(regnr) {
110
 
         this.regNumber = regnr;
111
 
     }
112
 
     Car.prototype.toString = function() { return this.regNumber; }</pre>
 
109
<pre> function Car(regnr) {
 
110
     this.regNumber = regnr;
 
111
 }
 
112
 Car.prototype.toString = function() { return this.regNumber; }</pre>
113
113
<p>Here, we made the <tt>toString()</tt> function part of the prototype so that, when we call <tt>car.toString()</tt> it will be resolved via the internal prototype object of the car object. Note, however, that the <tt>this</tt> object is still the original object that the function was called on, namely <tt>car</tt>.</p>
114
114
<a name="inheritance"></a>
115
115
<h3>Inheritance</h3>
116
116
<p>Now that we've seen how to use prototypes to create a &quot;class&quot; members in Qt Script, let's see how we can use prototypes to create polymorphism. In QSA you would write</p>
117
 
<pre>     class GasolineCar extends Car {
118
 
         function GasolineCar(regnr) {
119
 
             Car(regnr);
120
 
         }
121
 
         function toString() {
122
 
             return &quot;GasolineCar(&quot; + regNumber + &quot;)&quot;;
123
 
         }
124
 
     }</pre>
 
117
<pre> class GasolineCar extends Car {
 
118
     function GasolineCar(regnr) {
 
119
         Car(regnr);
 
120
     }
 
121
     function toString() {
 
122
         return &quot;GasolineCar(&quot; + regNumber + &quot;)&quot;;
 
123
     }
 
124
 }</pre>
125
125
<p>With Qt Script, we acheive the same effect by creating a prototype chain. The default prototype of an object is a plain <tt>Object</tt> without any special members, but it is possible to replace this object with another prototype object.</p>
126
 
<pre>     function GasolineCar(regnr) {
127
 
         Car(regnr);
128
 
     }
129
 
     GasolineCar.prototype = new Car();
130
 
     GasolineCar.prototype.toString = function() {
131
 
         return &quot;GasolineCar(&quot; + this.regNumber + &quot;)&quot;;
132
 
     }</pre>
 
126
<pre> function GasolineCar(regnr) {
 
127
     Car(regnr);
 
128
 }
 
129
 GasolineCar.prototype = new Car();
 
130
 GasolineCar.prototype.toString = function() {
 
131
     return &quot;GasolineCar(&quot; + this.regNumber + &quot;)&quot;;
 
132
 }</pre>
133
133
<p>In the code above, we have a constructor, <tt>GasolineCar</tt>, which calls the &quot;base class&quot; implementation of the constructor to initialize the <tt>this</tt> object with the property <tt>regNumber</tt>, based on the values passed in the constructor. The interesting line in this case is the line after the constructor where we change the default prototype for <tt>GasolineCar</tt> to be an instance of type <tt>Car</tt>. This means that all members available in a <tt>Car</tt> object are now available in all <tt>GasolineCar</tt> objects. In the last line, we replace the <tt>toString()</tt> function in the prototype with our own, thus overriding the <tt>toString()</tt> for all instances of <tt>GasolineCar</tt>.</p>
134
134
<a name="static-members"></a>
135
135
<h3>Static Members</h3>
136
136
<p>QSA allowed users to declare static members in classes, and these could be accessed both through instances of the class and through the class itself. For example, the following variable is accessed through the <tt>Car</tt> class:</p>
137
 
<pre>     class Car {
138
 
         static var globalCount = 0;
139
 
     }
140
 
     print(Car.globalCount);</pre>
 
137
<pre> class Car {
 
138
     static var globalCount = 0;
 
139
 }
 
140
 print(Car.globalCount);</pre>
141
141
<p>The equivalent in Qt Script is to assign variables that should appear as static members as properties of the constructor function. For example:</p>
142
 
<pre>     Car.globalCount = 0;
143
 
     print(Car.globalCount);</pre>
 
142
<pre> Car.globalCount = 0;
 
143
 print(Car.globalCount);</pre>
144
144
<p>Note that in QSA, static member variables were also accessible in instances of the given class. In Qt Script, with the approach illustrated above, the variable is a member of the constructor object only, and thus only accessible through <tt>Car.globalCount</tt>.</p>
145
145
<a name="the-built-in-functions-and-library"></a>
146
146
<h2>The Built-in Functions and Library</h2>
193
193
<a name="making-qobjects-accessible-from-scripts"></a>
194
194
<h3>Making QObjects Accessible from Scripts</h3>
195
195
<p>There are two different ways of making <a href="qobject.html">QObject</a>s accessible from scripts in QSA. The first method is via the <tt>QSInterpreter::addTransientObject()</tt> and <tt>QSProject::addObject()</tt> functions. In this case objects are added to the global namespace of the interpreter using their object names as the names of the variables.</p>
196
 
<pre>     QPushButton *button = new QPushButton();
197
 
     button-&gt;setObjectName(&quot;button&quot;);
198
 
     interpreter-&gt;addTransientObject(button);</pre>
 
196
<pre> QPushButton *button = new QPushButton();
 
197
 button-&gt;setObjectName(&quot;button&quot;);
 
198
 interpreter-&gt;addTransientObject(button);</pre>
199
199
<p>The code above adds the button to the global namespace under the name &quot;button&quot;. One obvious limitation here is that there is potential for either unnamed <a href="qobject.html">QObject</a>s or objects whose names conflict. Qt Script provides a more flexible way of adding QObjects to the scripting environment.</p>
200
 
<pre>     QPushButton *button = new QPushButton();
201
 
     QScriptValue scriptButton = engine.newQObject(button);
202
 
     engine.globalObject().setProperty(&quot;button&quot;, scriptButton);</pre>
 
200
<pre> QPushButton *button = new QPushButton();
 
201
 QScriptValue scriptButton = engine.newQObject(button);
 
202
 engine.globalObject().setProperty(&quot;button&quot;, scriptButton);</pre>
203
203
<p>In the code above we create a <a href="qpushbutton.html">QPushButton</a> and wrap it in a script value using the function, <a href="qscriptengine.html#newQObject">QScriptEngine::newQObject</a>(). This gives us a script value that we put into the global object using the name &quot;button&quot;. The concept of objects and properties discussed above is quite visible here in the public C++ API as well. We have no dependency on the object's name and we can also resolve name conflicts more gracefully. Here, we operate directly on <a href="qscriptvalue.html">QScriptValue</a> objects. This is the actual object that is being passed around inside the script engine, so we actually have low-level access to the internal script data structures, far beyond that which is possible in QSA. Properties, signals and slots of the <a href="qobject.html">QObject</a> are accessible to the scripter in Qt Script, just like in QSA.</p>
204
204
<p>The other way to expose <a href="qobject.html">QObject</a>s in QSA was to create a <tt>QSObjectFactory</tt> that made it possible to instantiate QObjects from scripts.</p>
205
205
<p>Below is listed some code from the filter example in the QSA package.</p>
206
 
<pre>     ModuleFactory::ModuleFactory()
207
 
     {
208
 
         registerClass( &quot;ImageSource&quot;, &amp;ImgSource::staticMetaObject);
209
 
         ...
210
 
     }
211
 
 
212
 
     QObject *ModuleFactory::create( const QString &amp;type,
213
 
                                     const QVariantList &amp;,
214
 
                                     QObject * )
215
 
     {
216
 
         if ( type == &quot;ImageSource&quot; )
217
 
             return new ImgSource();
218
 
         ...
219
 
     }
220
 
 
221
 
     ...
222
 
 
223
 
     interpreter.addObjectFactory(new ModuleFactory());</pre>
 
206
<pre> ModuleFactory::ModuleFactory()
 
207
 {
 
208
     registerClass( &quot;ImageSource&quot;, &amp;ImgSource::staticMetaObject);
 
209
     ...
 
210
 }
 
211
 
 
212
 QObject *ModuleFactory::create( const QString &amp;type,
 
213
                                 const QVariantList &amp;,
 
214
                                 QObject * )
 
215
 {
 
216
     if ( type == &quot;ImageSource&quot; )
 
217
         return new ImgSource();
 
218
     ...
 
219
 }
 
220
 
 
221
 ...
 
222
 
 
223
 interpreter.addObjectFactory(new ModuleFactory());</pre>
224
224
<p>The equivalent in Qt Script is written in much the same way as constructors are written in scripts. We register a callback C++ function under the name &quot;ImageSource&quot; in the global namespace and return the <a href="qobject.html">QObject</a> from this function:</p>
225
 
<pre>     QScriptValue construct_QPushButton(QScriptContext *, QScriptEngine *engine) {
226
 
         return engine-&gt;newQObject(new QPushButton());
227
 
     }
228
 
 
229
 
     ...
230
 
 
231
 
     QScriptValue constructor = engine.newFunction(construct_QPushButton);
232
 
     QScriptValue value =
233
 
         engine.newQMetaObject(&amp;QPushButton::staticMetaObject,
234
 
                               constructor);
235
 
     engine.globalObject().setProperty(&quot;QPushButton&quot;, value);</pre>
 
225
<pre> QScriptValue construct_QPushButton(QScriptContext *, QScriptEngine *engine) {
 
226
     return engine-&gt;newQObject(new QPushButton());
 
227
 }
 
228
 
 
229
 ...
 
230
 
 
231
 QScriptValue constructor = engine.newFunction(construct_QPushButton);
 
232
 QScriptValue value =
 
233
     engine.newQMetaObject(&amp;QPushButton::staticMetaObject,
 
234
                           constructor);
 
235
 engine.globalObject().setProperty(&quot;QPushButton&quot;, value);</pre>
236
236
<p>In the Qt Script case we use the same approach that we use to expose a <a href="qobject.html">QObject</a>, namely via <a href="qscriptengine.html#newQObject">QScriptEngine::newQObject</a>(). This function also has the benefit that it is possible to specify if the <a href="qobject.html">QObject</a> should expose properties and slots of its base class. It is also possible to specify custom ownership rules.</p>
237
237
<p>The reader might question why we don't add the constructor function directly into the namespace, but create a meta-object script value for it in addition. The plain function would certainly be good enough, but by creating a <a href="qmetaobject.html">QMetaObject</a> based constructor we get the enums on <a href="qpushbutton.html">QPushButton</a> for free in the <a href="qpushbutton.html">QPushButton</a> function object. Exposing enums in QSA is rather painful in comparison.</p>
238
238
<p>If we want to add more &quot;static&quot; data to the <a href="qpushbutton.html">QPushButton</a> type in Qt Script, we're free to add properties, similar to how we did for the script. It is also possible to add custom functions to a Qt Script <a href="qpushbutton.html">QPushButton</a> instance by setting more properties on it, such as making the <a href="qabstractbutton.html#text-prop">setText()</a> C++ function available. It is also possible to acheive this by installing a custom prototype, and be memory efficient, as discussed in the script example above.</p>
247
247
<table width="100%" cellspacing="0" border="0"><tr class="address">
248
248
<td width="30%">Copyright &copy; 2008 <a href="trolltech.html">Trolltech</a></td>
249
249
<td width="40%" align="center"><a href="trademarks.html">Trademarks</a></td>
250
 
<td width="30%" align="right"><div align="right">Qt 4.4.0</div></td>
 
250
<td width="30%" align="right"><div align="right">Qt 4.4.1</div></td>
251
251
</tr></table></div></address></body>
252
252
</html>