source: trunk/doc/src/examples/context2d.qdoc@ 1168

Last change on this file since 1168 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 14.1 KB
RevLine 
[2]1/****************************************************************************
2**
[846]3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
[561]4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
[2]6**
7** This file is part of the documentation of the Qt Toolkit.
8**
[846]9** $QT_BEGIN_LICENSE:FDL$
[2]10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
[846]13** Software or, alternatively, in accordance with the terms contained in a
14** written agreement between you and Nokia.
[2]15**
[846]16** GNU Free Documentation License
17** Alternatively, this file may be used under the terms of the GNU Free
18** Documentation License version 1.3 as published by the Free Software
19** Foundation and appearing in the file included in the packaging of this
20** file.
[2]21**
[561]22** If you have questions regarding the use of this file, please contact
23** Nokia at [email protected].
[2]24** $QT_END_LICENSE$
25**
26****************************************************************************/
27
28/*!
29 \example script/context2d
30 \title Context2D Example
31
32 This Qt Script example is an implementation of the Context2D API.
33
34 \image context2d-example.png
35
36 Context2D is part of the specification for the HTML \c{<canvas>}
37 element. It can be used to draw graphics via scripting. A good
38 resource for learning more about the HTML \c{<canvas>} element is
39 the \l{http://developer.mozilla.org/en/docs/HTML:Canvas}{Mozilla Developer Center}.
40
41 \section1 Using The HTML Canvas Element in a Web Browser
42
43 First, let's look at how the \c{<canvas>} element is typically
44 used in a web browser. The following HTML snippet defines a
45 canvas of size 400x400 pixels with id \c{mycanvas}:
46
47 \code
48 <canvas width="400" height="400" id="mycanvas">Fallback content goes here.</canvas>
49 \endcode
50
51 To draw on the canvas, we must first obtain a reference to the
52 DOM element corresponding to the \c{<canvas>} tag and then call
53 the element's getContext() function. The resulting object
54 implements the Context2D API that we use to draw.
55
56 \code
57 <script>
58 var canvas = document.getElementById("mycanvas");
59 var ctx = canvas.getContext("2d");
60
61 // Draw a face
62 ctx.beginPath();
63 ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
64 ctx.moveTo(110,75);
65 ctx.arc(75,75,35,0,Math.PI,false); // Mouth
66 ctx.moveTo(65,65);
67 ctx.arc(60,65,5,0,Math.PI*2,true); // Left eye
68 ctx.moveTo(95,65);
69 ctx.arc(90,65,5,0,Math.PI*2,true); // Right eye
70 ctx.stroke();
71 </script>
72 \endcode
73
74 When the page is rendered by a browser that supports the
75 \c{<canvas>} tag, this would be the result:
76
77 \image context2d-example-smileysmile.png
78
79 \section1 Using Qt Script to script a Canvas
80
81 The goal of this example is to be able to evaluate scripts
82 that use the Context2D API, and render the results. Basic
83 interaction (mouse, keyboard) should also be supported.
84 In other words, we want to present scripts with an execution
85 environment that very much resembles that of a web browser. Of
86 course, our environment is only a small subset of what a browser
87 provides; i.e. we don't provide a full DOM API, only what is
88 needed to run "self-contained" Context2D scripts (i.e. scripts
89 that don't depend on other parts of the DOM document).
90
91 Our "Context2D-browser" is set up through the following steps:
92 \list
93 \o Create an Environment.
94 \o Create a Context2D, and a QContext2DCanvas widget to render it.
95 \o Add the canvas object to the environment; this will enable
96 scripts to obtain a reference to it.
97 \o Evaluate scripts in the environment.
98 \endlist
99
100 Once a script has been evaluated, the application handles any
101 timer events and input events that occur subsequently
102 (i.e. forwards events to their associated script targets).
103
104 \section1 The Context2D Class
105
106 The "heart" of this example is the Context2D C++ class that implements
107 the drawing API. Its interface is defined in terms of properties
108 and slots. Note that this class isn't tied to Qt Script in any
109 way.
110
111 \snippet examples/script/context2d/context2d.h 0
112
113 The properties define various aspects of the Context2D
114 configuration.
115
116 \snippet examples/script/context2d/context2d.h 1
117
118 The slots define the operations that can be performed.
119
120 \snippet examples/script/context2d/context2d.h 2
121
122 The changed() signal is emitted when the contents of the drawing
123 area has changed, so that clients associated with the Context2D
124 object (i.e. the canvas widget that renders it) are notified.
125
126 \section2 Implementation
127
128 Conveniently enough, the concepts, data structures and operations
129 of the Context2D API map more or less directly to Qt's painting
130 API. Conceptually, all we have to do is initialize a QPainter
131 according to the Context2D properties, and use functions like
132 QPainter::strokePath() to do the painting. Painting is done on a
133 QImage.
134
135 \snippet examples/script/context2d/context2d.cpp 0
136
137 The property accessors and most of the slots manipulate the
138 internal Context2D state in some way. For the \c{lineCap}
139 property, Context2D uses a string representation; we therefore
140 have to map it from/to a Qt::PenCapStyle. The \c{lineJoin}
141 property is handled in the same fashion. All the property setters
142 also set a \e{dirty flag} for the property; this is used to
143 decide which aspects of the QPainter that need to be updated
144 before doing the next painting operation.
145
146 \snippet examples/script/context2d/context2d.cpp 3
147
148 The implementation of the \c{fillStyle} property is interesting,
149 since the value can be either a string or a \c{CanvasGradient}.
150 We handle this by having the property be of type QVariant,
151 and check the actual type of the value to see how to handle the
152 write.
153
154 \snippet examples/script/context2d/context2d.cpp 1
155
156 Context2D does not have a concept of a paint event; painting
157 operations can happen at any time. We would like to be efficient,
158 and not have to call QPainter::begin() and QPainter::end() for
159 every painting operation, since typically many painting operations
160 will follow in quick succession. The implementations of the
161 painting operations use a helper function, beginPainting(), that
162 activates the QPainter if it isn't active already, and updates
163 the state of the QPainter (brush, pen, etc.) so that it reflects
164 the current Context2D state.
165
166 \snippet examples/script/context2d/context2d.cpp 2
167
168 The implementation of each painting operation ends by calling
169 scheduleChange(), which will post a zero-timer event if one is
170 not already pending. When the application returns to the event
171 loop later (presumably after all the drawing operations have
172 finished), the timer will trigger, QPainter::end() will be
173 called, and the changed() signal is emitted with the new
174 image as argument. The net effect is that there will typically
175 be only a single (QPainter::begin(), QPainter::end()) pair
176 executed for the full sequence of painting operations.
177
178 \section1 The Canvas Widget
179
180 \snippet examples/script/context2d/qcontext2dcanvas.h 0
181
182 The QContext2DCanvas class provides a widget that renders
183 the contents of a Context2D object. It also provides a
184 minimal scripting API, most notably the getContext() function.
185
186 \snippet examples/script/context2d/qcontext2dcanvas.cpp 3
187
188 The constructor connects to the changed() signal of the
189 Context2D object, so that the widget can update itself
190 when it needs to do so. Mouse tracking is enabled so that
191 mouse move events will be received even when no mouse
192 buttons are depressed.
193
194 \snippet examples/script/context2d/qcontext2dcanvas.cpp 0
195
196 The getContext() function asks the environment to wrap the
197 Context2D object; the resulting proxy object makes the
198 Context2D API available to scripts.
199
200 \snippet examples/script/context2d/qcontext2dcanvas.cpp 1
201
202 The paintEvent() function simply paints the contents that
203 was last received from the Context2D object.
204
205 \snippet examples/script/context2d/qcontext2dcanvas.cpp 2
206
207 The canvas widget reimplements mouse and key event handlers, and
208 forwards these events to the scripting environment. The
209 environment will take care of delivering the event to the proper
210 script target, if any.
211
212 \section1 The Environment
213
214 \snippet examples/script/context2d/environment.h 0
215
216 The Environment class provides a scripting environment where a
217 Canvas C++ object can be registered, looked up by ID (name),
218 and where scripts can be evaluated. The environment has a
219 \c{document} property, just like the scripting environment of a
220 web browser, so that scripts can call
221 \c{document.getElementById()} to obtain a reference to a canvas.
222
223 \snippet examples/script/context2d/environment.h 1
224
225 The Environment class provides the timer attributes of the DOM
226 Window Object interface. This enables us to support scripts that
227 do animation, for example.
228
229 \snippet examples/script/context2d/environment.h 2
230
231 The scriptError() signal is emitted when evaluation of a script
232 causes a script exception. For example, if a mouse press handler
233 or timeout handler causes an exception, the environment's client(s)
234 will be notified of this and can report the error.
235
236 \snippet examples/script/context2d/environment.cpp 0
237
238 The constructor initializes the environment. First it creates
239 the QScriptEngine that will be used to evaluate scripts. It
240 creates the Document object that provides the getElementById()
241 function. Note that the QScriptEngine::ExcludeSuperClassContents
242 flag is specified to avoid the wrapper objects from exposing properties
243 and methods inherited from QObject. Next, the environment wraps
244 a pointer to \e{itself}; this is to prepare for setting this object
245 as the script engine's Global Object. The properties of the standard
246 Global Object are copied, so that these will also be available in
247 our custom Global Object. We also create two self-references to the
248 object; again, this is to provide a minimal level of compabilitity
249 with the scripting environment that web browsers provide.
250
251 \snippet examples/script/context2d/environment.cpp 5
252
253 The addCanvas() function adds the given canvas to the list of
254 registered canvas objects. The canvasByName() function looks up
255 a canvas by QObject::objectName(). This function is used to
256 implement the \c{document.getElementById()} script function.
257
258 \snippet examples/script/context2d/environment.cpp 1
259
260 The setInterval() and clearInterval() implementations use a QHash
261 to map from timer ID to the QScriptValue that holds the expression
262 to evaluate when the timer is triggered. A helper function,
263 maybeEmitScriptError(), is called after invoking the script handler;
264 it will emit the scriptError() signal if the script engine has an
265 uncaught exception.
266
267 \snippet examples/script/context2d/environment.cpp 2
268
269 The toWrapper() functions creates a QScriptValue that wraps the
270 given QObject. Note that the QScriptEngine::PreferExistingWrapperObject
271 flag is specified; this guarantees that a single, unique wrapper
272 object will be returned, even if toWrapper() is called several times
273 with the same argument. This is important, since it is possible that
274 a script can set new properties on the resulting wrapper object (e.g.
275 event handlers like \c{onmousedown}), and we want these to persist.
276
277 \snippet examples/script/context2d/environment.cpp 3
278
279 The handleEvent() function determines if there exists a handler
280 for the given event in the environment, and if so, invokes that
281 handler. Since the script expects a DOM event, the Qt C++ event
282 must be converted to a DOM event before it is passed to the
283 script. This mapping is relatively straightforward, but again,
284 we only implement a subset of the full DOM API; just enough to
285 get most scripts to work.
286
287 \snippet examples/script/context2d/environment.cpp 4
288
289 The newFakeDomEvent() function is a helper function that creates
290 a new script object and initializes it with default values for
291 the attributes defined in the DOM Event and DOM UIEvent
292 interfaces.
293
294 \snippet examples/script/context2d/environment.h 3
295
296 The Document class defines two slots that become available to
297 scripts: getElementById() and getElementsByTagName().
298 When the tag name is "canvas", getElementsByTagName() will
299 return a list of all canvas objects that are registered in
300 the environment.
301
302 \section1 The Application Window
303
304 \snippet examples/script/context2d/window.cpp 0
305
306 The Window constructor creates an Environment object and
307 connects to its scriptError() signal. It then creates a
308 Context2D object, and a QContext2DCanvas widget to hold it.
309 The canvas widget is given the name \c{tutorial}, and added to the
310 environment; scripts can access the canvas by e.g.
311 \c{document.getElementById('tutorial')}.
312
313 \snippet examples/script/context2d/window.cpp 1
314
315 The window contains a list widget that is populated with
316 available scripts (read from a \c{scripts/} folder).
317
318 \snippet examples/script/context2d/window.cpp 2
319
320 When an item is selected, the corresponding script is
321 evaluated in the environment.
322
323 \snippet examples/script/context2d/window.cpp 3
324
325 When the "Run in Debugger" button is clicked, the Qt Script debugger will
326 automatically be invoked when the first statement of the script is
327 reached. This enables the user to inspect the scripting environment and
328 control further execution of the script; e.g. he can single-step through
329 the script and/or set breakpoints. It is also possible to enter script
330 statements in the debugger's console widget, e.g. to perform custom
331 Context2D drawing operations, interactively.
332
333 \snippet examples/script/context2d/window.cpp 4
334
335 If the evaluation of a script causes an uncaught exception, the Qt Script
336 debugger will automatically be invoked; this enables the user to get an
337 idea of what went wrong.
338
339*/
Note: See TracBrowser for help on using the repository browser.