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
|
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/concentriccircles
\title Concentric Circles Example
\ingroup examples-painting
\brief Demonstrates the improved quality that antialiasing and floating point precision gives.
\brief The Concentric Circles example shows the improved rendering
quality that can be obtained using floating point precision and
anti-aliasing when drawing custom widgets. The example also shows
how to do simple animations.
The application's main window displays several widgets which are
drawn using the various combinations of precision and
anti-aliasing.
\image concentriccircles-example.png
Anti-aliasing is one of QPainter'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.
The difference between floating point precision and integer
precision is a matter of accuracy, and is visible in the
application's main window: Even though the logic that is
calculating the circles' geometry is the same, floating points
ensure that the white spaces between each circle are of the same
size, while integers make two and two circles appear as if they
belong together. The reason is that the integer based precision
rely on rounding off non-integer calculations.
The example consists of two classes:
\list
\li \c CircleWidget is a custom widget which renders several animated
concentric circles.
\li \c Window is the application's main window displaying four \c
{CircleWidget}s drawn using different combinations of precision
and aliasing.
\endlist
First we will review the CircleWidget class, then we will take a
look at the Window class.
\section1 CircleWidget Class Definition
The CircleWidget class inherits QWidget, and is a custom widget
which renders several animated concentric circles.
\snippet painting/concentriccircles/circlewidget.h 0
We declare the \c floatBased and \c antialiased variables to hold
whether an instance of the class should be rendered with integer
or float based precision, and whether the rendering should be
anti-aliased or not. We also declare functions setting each of
these variables.
In addition we reimplement the QWidget::paintEvent() function to
apply the various combinations of precision and anti-aliasing when
rendering, and to support the animation. We reimplement the
QWidget::minimumSizeHint() and QWidget::sizeHint() functions to
give the widget a reasonable size within our application.
We declare the private \c nextAnimationFrame() slot, and the
associated \c frameNo variable holding the number of "animation
frames" for the widget, to facilitate the animation.
\section1 CircleWidget Class Implementation
In the constructor we make the widget's rendering integer based
and aliased by default:
\snippet painting/concentriccircles/circlewidget.cpp 0
We initialize the widget's \c frameNo variable, and set the
widget's background color using the QWidget::setBackgroundColor()
function which takes a \l {QPalette::ColorRole}{color role} as
argument; the QPalette::Base color role is typically white.
Then we set the widgets size policy using the
QWidget::setSizePolicy() function. QSizePolicy::Expanding means
that the widget's \l {QWidget::sizeHint()}{sizeHint()} is a
sensible size, but that the widget can be shrunk and still be
useful. The widget can also make use of extra space, so it should
get as much space as possible.
\snippet painting/concentriccircles/circlewidget.cpp 1
\codeline
\snippet painting/concentriccircles/circlewidget.cpp 2
The public \c setFloatBased() and \c setAntialiased() functions
update the widget's rendering preferences, i.e. whether the widget
should be rendered with integer or float based precision, and
whether the rendering should be anti-aliased or not.
The functions also generate a paint event by calling the
QWidget::update() function, forcing a repaint of the widget with
the new rendering preferences.
\snippet painting/concentriccircles/circlewidget.cpp 3
\codeline
\snippet painting/concentriccircles/circlewidget.cpp 4
The default implementations of the QWidget::minimumSizeHint() and
QWidget::sizeHint() functions return invalid sizes if there is no
layout for the widget, otherwise they return the layout's minimum and
preferred size, respectively.
We reimplement the functions to give the widget minimum and
preferred sizes which are reasonable within our application.
\snippet painting/concentriccircles/circlewidget.cpp 5
The nextAnimationFrame() slot simply increments the \c frameNo
variable's value, and calls the QWidget::update() function which
schedules a paint event for processing when Qt returns to the main
event loop.
\snippet painting/concentriccircles/circlewidget.cpp 6
A paint event is a request to repaint all or part of the
widget. The \c paintEvent() function is an event handler that can
be reimplemented to receive the widget's paint events. We
reimplement the event handler to apply the various combinations of
precision and anti-aliasing when rendering the widget, and to
support the animation.
First, we create a QPainter for the widget, and set its
antialiased flag to the widget's preferred aliasing. We also
translate the painters coordinate system, preparing to draw the
widget's cocentric circles. The translation ensures that the
center of the circles will be equivalent to the widget's center.
\snippet painting/concentriccircles/circlewidget.cpp 7
When painting a circle, we use the number of "animation frames" to
determine the alpha channel of the circle's color. The alpha
channel specifies the color's transparency effect, 0 represents a
fully transparent color, while 255 represents a fully opaque
color.
\snippet painting/concentriccircles/circlewidget.cpp 8
If the calculated alpha channel is fully transparent, we don't
draw anything since that would be equivalent to drawing a white
circle on a white background. Instead we skip to the next circle
still creating a white space. If the calculated alpha channel is
fully opaque, we set the pen (the QColor passed to the QPen
constructor is converted into the required QBrush by default) and
draw the circle. If the widget's preferred precision is float
based, we specify the circle's bounding rectangle using QRectF and
double values, otherwise we use QRect and integers.
The animation is controlled by the public \c nextAnimationFrame()
slot: Whenever the \c nextAnimationFrame() slot is called the
number of frames is incremented and a paint event is
scheduled. Then, when the widget is repainted, the alpha-blending
of the circles' colors change and the circles appear as animated.
\section1 Window Class Definition
The Window class inherits QWidget, and is the application's main
window rendering four \c {CircleWidget}s using different
combinations of precision and aliasing.
\snippet painting/concentriccircles/window.h 0
We declare the various components of the main window, i.e., the text
labels and a double array that will hold reference to the four \c
{CircleWidget}s. In addition we declare the private \c
createLabel() function to simplify the constructor.
\section1 Window Class Implementation
\snippet painting/concentriccircles/window.cpp 0
In the constructor, we first create the various labels and put
them in a QGridLayout.
\snippet painting/concentriccircles/window.cpp 1
Then we create a QTimer. The QTimer class is a high-level
programming interface for timers, and provides repetitive and
single-shot timers.
We create a timer to facilitate the animation of our concentric
circles; when we create the four CircleWidget instances (and add
them to the layout), we connect the QTimer::timeout() signal to
each of the widgets' \c nextAnimationFrame() slots.
\snippet painting/concentriccircles/window.cpp 2
Before we set the layout and window title for our main window, we
make the timer start with a timeout interval of 100 milliseconds,
using the QTimer::start() function. That means that the
QTimer::timeout() signal will be emitted, forcing a repaint of the
four \c {CircleWidget}s, every 100 millisecond which is the reason
the circles appear as animated.
\snippet painting/concentriccircles/window.cpp 3
The private \c createLabel() function is implemented to simlify
the constructor.
*/
|