|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the documentation of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** |
|
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. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 /*! |
|
43 \example painting/painterpaths |
|
44 \title Painter Paths Example |
|
45 |
|
46 The Painter Paths example shows how painter paths can be used to |
|
47 build complex shapes for rendering. |
|
48 |
|
49 \image painterpaths-example.png |
|
50 |
|
51 The QPainterPath class provides a container for painting |
|
52 operations, enabling graphical shapes to be constructed and |
|
53 reused. |
|
54 |
|
55 A painter path is an object composed of a number of graphical |
|
56 building blocks (such as rectangles, ellipses, lines, and curves), |
|
57 and can be used for filling, outlining, and clipping. The main |
|
58 advantage of painter paths over normal drawing operations is that |
|
59 complex shapes only need to be created once, but they can be drawn |
|
60 many times using only calls to QPainter::drawPath(). |
|
61 |
|
62 The example consists of two classes: |
|
63 |
|
64 \list |
|
65 \o The \c RenderArea class which is a custom widget displaying |
|
66 a single painter path. |
|
67 \o The \c Window class which is the applications main window |
|
68 displaying several \c RenderArea widgets, and allowing the user |
|
69 to manipulate the painter paths' filling, pen, color |
|
70 and rotation angle. |
|
71 \endlist |
|
72 |
|
73 First we will review the \c Window class, then we will take a look |
|
74 at the \c RenderArea class. |
|
75 |
|
76 \section1 Window Class Definition |
|
77 |
|
78 The \c Window class inherits QWidget, and is the applications main |
|
79 window displaying several \c RenderArea widgets, and allowing the |
|
80 user to manipulate the painter paths' filling, pen, color and |
|
81 rotation angle. |
|
82 |
|
83 \snippet examples/painting/painterpaths/window.h 0 |
|
84 |
|
85 We declare three private slots to respond to user input regarding |
|
86 filling and color: \c fillRuleChanged(), \c fillGradientChanged() |
|
87 and \c penColorChanged(). |
|
88 |
|
89 When the user changes the pen width and the rotation angle, the |
|
90 new value is passed directly on to the \c RenderArea widgets using |
|
91 the QSpinBox::valueChanged() signal. The reason why we must |
|
92 implement slots to update the filling and color, is that QComboBox |
|
93 doesn't provide a similar signal passing the new value as |
|
94 argument; so we need to retrieve the new value, or values, before |
|
95 we can update the \c RenderArea widgets. |
|
96 |
|
97 \snippet examples/painting/painterpaths/window.h 1 |
|
98 |
|
99 We also declare a couple of private convenience functions: \c |
|
100 populateWithColors() populates a given QComboBox with items |
|
101 corresponding to the color names Qt knows about, and \c |
|
102 currentItemData() returns the current item for a given QComboBox. |
|
103 |
|
104 \snippet examples/painting/painterpaths/window.h 2 |
|
105 |
|
106 Then we declare the various components of the main window |
|
107 widget. We also declare a convenience constant specifying the |
|
108 number of \c RenderArea widgets. |
|
109 |
|
110 \section1 Window Class Implementation |
|
111 |
|
112 In the implementation of the \c Window class we first declare the |
|
113 constant \c Pi with six significant figures: |
|
114 |
|
115 \snippet examples/painting/painterpaths/window.cpp 0 |
|
116 |
|
117 In the constructor, we then define the various painter paths and |
|
118 create corresponding \c RenderArea widgets which will render the |
|
119 graphical shapes: |
|
120 |
|
121 \snippet examples/painting/painterpaths/window.cpp 1 |
|
122 |
|
123 We construct a rectangle with sharp corners using the |
|
124 QPainterPath::moveTo() and QPainterPath::lineTo() |
|
125 functions. |
|
126 |
|
127 QPainterPath::moveTo() moves the current point to the point passed |
|
128 as argument. A painter path is an object composed of a number of |
|
129 graphical building blocks, i.e. subpaths. Moving the current point |
|
130 will also start a new subpath (implicitly closing the previously |
|
131 current path when the new one is started). The |
|
132 QPainterPath::lineTo() function adds a straight line from the |
|
133 current point to the given end point. After the line is drawn, the |
|
134 current point is updated to be at the end point of the line. |
|
135 |
|
136 We first move the current point starting a new subpath, and we |
|
137 draw three of the rectangle's sides. Then we call the |
|
138 QPainterPath::closeSubpath() function which draws a line to the |
|
139 beginning of the current subpath. A new subpath is automatically |
|
140 begun when the current subpath is closed. The current point of the |
|
141 new path is (0, 0). We could also have called |
|
142 QPainterPath::lineTo() to draw the last line as well, and then |
|
143 explicitly start a new subpath using the QPainterPath::moveTo() |
|
144 function. |
|
145 |
|
146 QPainterPath also provide the QPainterPath::addRect() convenience |
|
147 function, which adds a given rectangle to the path as a closed |
|
148 subpath. The rectangle is added as a clockwise set of lines. The |
|
149 painter path's current position after the rect has been added is |
|
150 at the top-left corner of the rectangle. |
|
151 |
|
152 \snippet examples/painting/painterpaths/window.cpp 2 |
|
153 |
|
154 Then we construct a rectangle with rounded corners. As before, we |
|
155 use the QPainterPath::moveTo() and QPainterPath::lineTo() |
|
156 functions to draw the rectangle's sides. To create the rounded |
|
157 corners we use the QPainterPath::arcTo() function. |
|
158 |
|
159 QPainterPath::arcTo() creates an arc that occupies the given |
|
160 rectangle (specified by a QRect or the rectangle's coordinates), |
|
161 beginning at the given start angle and extending the given degrees |
|
162 counter-clockwise. Angles are specified in degrees. Clockwise arcs |
|
163 can be specified using negative angles. The function connects the |
|
164 current point to the starting point of the arc if they are not |
|
165 already connected. |
|
166 |
|
167 \snippet examples/painting/painterpaths/window.cpp 3 |
|
168 |
|
169 We also use the QPainterPath::arcTo() function to construct the |
|
170 ellipse path. First we move the current point starting a new |
|
171 path. Then we call QPainterPath::arcTo() with starting angle 0.0 |
|
172 and 360.0 degrees as the last argument, creating an ellipse. |
|
173 |
|
174 Again, QPainterPath provides a convenience function ( |
|
175 QPainterPath::addEllipse()) which creates an ellipse within a |
|
176 given bounding rectangle and adds it to the painter path. If the |
|
177 current subpath is closed, a new subpath is started. The ellipse |
|
178 is composed of a clockwise curve, starting and finishing at zero |
|
179 degrees (the 3 o'clock position). |
|
180 |
|
181 \snippet examples/painting/painterpaths/window.cpp 4 |
|
182 |
|
183 When constructing the pie chart path we continue to use a |
|
184 combination of the mentioned functions: First we move the current |
|
185 point, starting a new subpath. Then we create a line from the |
|
186 center of the chart to the arc, and the arc itself. When we close |
|
187 the subpath, we implicitly construct the last line back to the |
|
188 center of the chart. |
|
189 |
|
190 \snippet examples/painting/painterpaths/window.cpp 5 |
|
191 |
|
192 Constructing a polygon is equivalent to constructing a rectangle. |
|
193 |
|
194 QPainterPath also provide the QPainterPath::addPolygon() |
|
195 convenience function which adds the given polygon to the path as a |
|
196 new subpath. Current position after the polygon has been added is |
|
197 the last point in polygon. |
|
198 |
|
199 \snippet examples/painting/painterpaths/window.cpp 6 |
|
200 |
|
201 Then we create a path consisting of a group of subpaths: First we |
|
202 move the current point, and create a circle using the |
|
203 QPainterPath::arcTo() function with starting angle 0.0, and 360 |
|
204 degrees as the last argument, as we did when we created the |
|
205 ellipse path. Then we move the current point again, starting a |
|
206 new subpath, and construct three sides of a square using the |
|
207 QPainterPath::lineTo() function. |
|
208 |
|
209 Now, when we call the QPainterPath::closeSubpath() fucntion the |
|
210 last side is created. Remember that the |
|
211 QPainterPath::closeSubpath() function draws a line to the |
|
212 beginning of the \e current subpath, i.e the square. |
|
213 |
|
214 QPainterPath provide a convenience function, |
|
215 QPainterPath::addPath() which adds a given path to the path that |
|
216 calls the function. |
|
217 |
|
218 \snippet examples/painting/painterpaths/window.cpp 7 |
|
219 |
|
220 When creating the text path, we first create the font. Then we set |
|
221 the font's style strategy which tells the font matching algorithm |
|
222 what type of fonts should be used to find an appropriate default |
|
223 family. QFont::ForceOutline forces the use of outline fonts. |
|
224 |
|
225 To construct the text, we use the QPainterPath::addText() function |
|
226 which adds the given text to the path as a set of closed subpaths |
|
227 created from the supplied font. The subpaths are positioned so |
|
228 that the left end of the text's baseline lies at the specified |
|
229 point. |
|
230 |
|
231 \snippet examples/painting/painterpaths/window.cpp 8 |
|
232 |
|
233 To create the Bezier path, we use the QPainterPath::cubicTo() |
|
234 function which adds a Bezier curve between the current point and |
|
235 the given end point with the given control point. After the curve |
|
236 is added, the current point is updated to be at the end point of |
|
237 the curve. |
|
238 |
|
239 In this case we omit to close the subpath so that we only have a |
|
240 simple curve. But there is still a logical line from the curve's |
|
241 endpoint back to the beginning of the subpath; it becomes visible |
|
242 when filling the path as can be seen in the applications main |
|
243 window. |
|
244 |
|
245 \snippet examples/painting/painterpaths/window.cpp 9 |
|
246 |
|
247 The final path that we construct shows that you can use |
|
248 QPainterPath to construct rather complex shapes using only the |
|
249 previous mentioned QPainterPath::moveTo(), QPainterPath::lineTo() |
|
250 and QPainterPath::closeSubpath() functions. |
|
251 |
|
252 \snippet examples/painting/painterpaths/window.cpp 10 |
|
253 |
|
254 Now that we have created all the painter paths that we need, we |
|
255 create a corresponding \c RenderArea widget for each. In the end, |
|
256 we make sure that the number of render areas is correct using the |
|
257 Q_ASSERT() macro. |
|
258 |
|
259 \snippet examples/painting/painterpaths/window.cpp 11 |
|
260 |
|
261 Then we create the widgets associated with the painter paths' fill |
|
262 rule. |
|
263 |
|
264 There are two available fill rules in Qt: The Qt::OddEvenFill rule |
|
265 determine whether a point is inside the shape by drawing a |
|
266 horizontal line from the point to a location outside the shape, |
|
267 and count the number of intersections. If the number of |
|
268 intersections is an odd number, the point is inside the |
|
269 shape. This rule is the default. |
|
270 |
|
271 The Qt::WindingFill rule determine whether a point is inside the |
|
272 shape by drawing a horizontal line from the point to a location |
|
273 outside the shape. Then it determines whether the direction of the |
|
274 line at each intersection point is up or down. The winding number |
|
275 is determined by summing the direction of each intersection. If |
|
276 the number is non zero, the point is inside the shape. |
|
277 |
|
278 The Qt::WindingFill rule can in most cases be considered as the |
|
279 intersection of closed shapes. |
|
280 |
|
281 \snippet examples/painting/painterpaths/window.cpp 12 |
|
282 |
|
283 We also create the other widgets associated with the filling, the |
|
284 pen and the rotation angle. |
|
285 |
|
286 \snippet examples/painting/painterpaths/window.cpp 16 |
|
287 |
|
288 We connect the comboboxes \l {QComboBox::activated()}{activated()} |
|
289 signals to the associated slots in the \c Window class, while we |
|
290 connect the spin boxes \l |
|
291 {QSpinBox::valueChanged()}{valueChanged()} signal directly to the |
|
292 \c RenderArea widget's respective slots. |
|
293 |
|
294 \snippet examples/painting/painterpaths/window.cpp 17 |
|
295 |
|
296 We add the \c RenderArea widgets to a separate layout which we |
|
297 then add to the main layout along with the rest of the widgets. |
|
298 |
|
299 \snippet examples/painting/painterpaths/window.cpp 18 |
|
300 |
|
301 Finally, we initialize the \c RenderArea widgets by calling the \c |
|
302 fillRuleChanged(), \c fillGradientChanged() and \c |
|
303 penColorChanged() slots, and we set the inital pen width and |
|
304 window title. |
|
305 |
|
306 \snippet examples/painting/painterpaths/window.cpp 19 |
|
307 \codeline |
|
308 \snippet examples/painting/painterpaths/window.cpp 20 |
|
309 \codeline |
|
310 \snippet examples/painting/painterpaths/window.cpp 21 |
|
311 |
|
312 The private slots are implemented to retrieve the new value, or |
|
313 values, from the associated comboboxes and update the RenderArea |
|
314 widgets. |
|
315 |
|
316 First we determine the new value, or values, using the private \c |
|
317 currentItemData() function and the qvariant_cast() template |
|
318 function. Then we call the associated slot for each of the \c |
|
319 RenderArea widgets to update the painter paths. |
|
320 |
|
321 \snippet examples/painting/painterpaths/window.cpp 22 |
|
322 |
|
323 The \c populateWithColors() function populates the given combobox |
|
324 with items corresponding to the color names Qt knows about |
|
325 provided by the static QColor::colorNames() function. |
|
326 |
|
327 \snippet examples/painting/painterpaths/window.cpp 23 |
|
328 |
|
329 The \c currentItemData() function simply return the current item |
|
330 of the given combobox. |
|
331 |
|
332 \section1 RenderArea Class Definition |
|
333 |
|
334 The \c RenderArea class inherits QWidget, and is a custom widget |
|
335 displaying a single painter path. |
|
336 |
|
337 \snippet examples/painting/painterpaths/renderarea.h 0 |
|
338 |
|
339 We declare several public slots updating the \c RenderArea |
|
340 widget's associated painter path. In addition we reimplement the |
|
341 QWidget::minimumSizeHint() and QWidget::sizeHint() functions to |
|
342 give the \c RenderArea widget a reasonable size within our |
|
343 application, and we reimplement the QWidget::paintEvent() event |
|
344 handler to draw its painter path. |
|
345 |
|
346 \snippet examples/painting/painterpaths/renderarea.h 1 |
|
347 |
|
348 Each instance of the \c RenderArea class has a QPainterPath, a |
|
349 couple of fill colors, a pen width, a pen color and a rotation |
|
350 angle. |
|
351 |
|
352 \section1 RenderArea Class Implementation |
|
353 |
|
354 The constructor takes a QPainterPath as argument (in addition to |
|
355 the optional QWidget parent): |
|
356 |
|
357 \snippet examples/painting/painterpaths/renderarea.cpp 0 |
|
358 |
|
359 In the constructor we initialize the \c RenderArea widget with the |
|
360 QPainterPath parameter as well as initializing the pen width and |
|
361 rotation angle. We also set the widgets \l |
|
362 {QWidget::backgroundRole()}{background role}; QPalette::Base is |
|
363 typically white. |
|
364 |
|
365 \snippet examples/painting/painterpaths/renderarea.cpp 1 |
|
366 \codeline |
|
367 \snippet examples/painting/painterpaths/renderarea.cpp 2 |
|
368 |
|
369 Then we reimplement the QWidget::minimumSizeHint() and |
|
370 QWidget::sizeHint() functions to give the \c RenderArea widget a |
|
371 reasonable size within our application. |
|
372 |
|
373 \snippet examples/painting/painterpaths/renderarea.cpp 3 |
|
374 \codeline |
|
375 \snippet examples/painting/painterpaths/renderarea.cpp 4 |
|
376 \codeline |
|
377 \snippet examples/painting/painterpaths/renderarea.cpp 5 |
|
378 \codeline |
|
379 \snippet examples/painting/painterpaths/renderarea.cpp 6 |
|
380 \codeline |
|
381 \snippet examples/painting/painterpaths/renderarea.cpp 7 |
|
382 |
|
383 The various public slots updates the \c RenderArea widget's |
|
384 painter path by setting the associated property and make a call to |
|
385 the QWidget::update() function, forcing a repaint of the widget |
|
386 with the new rendering preferences. |
|
387 |
|
388 The QWidget::update() slot does not cause an immediate repaint; |
|
389 instead it schedules a paint event for processing when Qt returns |
|
390 to the main event loop. |
|
391 |
|
392 \snippet examples/painting/painterpaths/renderarea.cpp 8 |
|
393 |
|
394 A paint event is a request to repaint all or parts of the |
|
395 widget. The paintEvent() function is an event handler that can be |
|
396 reimplemented to receive the widget's paint events. We reimplement |
|
397 the event handler to render the \c RenderArea widget's painter |
|
398 path. |
|
399 |
|
400 First, we create a QPainter for the \c RenderArea instance, and |
|
401 set the painter's render hints. The QPainter::RenderHints are used |
|
402 to specify flags to QPainter that may, or may not, be respected by |
|
403 any given engine. QPainter::Antialiasing indicates that the engine |
|
404 should anti-alias the edges of primitives if possible, i.e. put |
|
405 additional pixels around the original ones to smooth the edges. |
|
406 |
|
407 \snippet examples/painting/painterpaths/renderarea.cpp 9 |
|
408 |
|
409 Then we scale the QPainter's coordinate system to ensure that the |
|
410 painter path is rendered in the right size, i.e that it grows with |
|
411 the \c RenderArea widget when the application is resized. When we |
|
412 constructed the various painter paths, they were all rnedered |
|
413 within a square with a 100 pixel width wich is equivalent to \c |
|
414 RenderArea::sizeHint(). The QPainter::scale() function scales the |
|
415 coordinate system by the \c RenderArea widget's \e current width |
|
416 and height divided by 100. |
|
417 |
|
418 Now, when we are sure that the painter path has the right size, we |
|
419 can translate the coordinate system to make the painter path |
|
420 rotate around the \c RenderArea widget's center. After we have |
|
421 performed the rotation, we must remember to translate the |
|
422 coordinate system back again. |
|
423 |
|
424 \snippet examples/painting/painterpaths/renderarea.cpp 10 |
|
425 |
|
426 Then we set the QPainter's pen with the instance's rendering |
|
427 preferences. We create a QLinearGradient and set its colors |
|
428 corresponding to the \c RenderArea widget's fill colors. Finally, |
|
429 we set the QPainter's brush (the gradient is automatically |
|
430 converted into a QBrush), and draw the \c RenderArea widget's |
|
431 painter path using the QPainter::drawPath() function. |
|
432 */ |