1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation ([email protected])
|
---|
6 | **
|
---|
7 | ** This file is part of the documentation of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:FDL$
|
---|
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 a
|
---|
14 | ** written agreement between you and Nokia.
|
---|
15 | **
|
---|
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.
|
---|
21 | **
|
---|
22 | ** If you have questions regarding the use of this file, please contact
|
---|
23 | ** Nokia at [email protected].
|
---|
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
|
---|
|
---|