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

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

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