summaryrefslogtreecommitdiff
path: root/examples/widgets/doc/src/concentriccircles.qdoc
blob: ffdb09869fd8bba23ba930cde5c8eb8685a0b551 (plain)
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.
*/