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