source: trunk/doc/src/examples/mandelbrot.qdoc@ 357

Last change on this file since 357 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 16.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the documentation of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*!
43 \example threads/mandelbrot
44 \title Mandelbrot Example
45
46 The Mandelbrot example shows how to use a worker thread to
47 perform heavy computations without blocking the main thread's
48 event loop.
49
50 The heavy computation here is the Mandelbrot set, probably the
51 world's most famous fractal. These days, while sophisticated
52 programs such as \l{XaoS} that provide real-time zooming in the
53 Mandelbrot set, the standard Mandelbrot algorithm is just slow
54 enough for our purposes.
55
56 \image mandelbrot-example.png Screenshot of the Mandelbrot example
57
58 In real life, the approach described here is applicable to a
59 large set of problems, including synchronous network I/O and
60 database access, where the user interface must remain responsive
61 while some heavy operation is taking place. The \l
62 network/blockingfortuneclient example shows the same principle at
63 work in a TCP client.
64
65 The Mandelbrot application supports zooming and scrolling using
66 the mouse or the keyboard. To avoid freezing the main thread's
67 event loop (and, as a consequence, the application's user
68 interface), we put all the fractal computation in a separate
69 worker thread. The thread emits a signal when it is done
70 rendering the fractal.
71
72 During the time where the worker thread is recomputing the
73 fractal to reflect the new zoom factor position, the main thread
74 simply scales the previously rendered pixmap to provide immediate
75 feedback. The result doesn't look as good as what the worker
76 thread eventually ends up providing, but at least it makes the
77 application more responsive. The sequence of screenshots below
78 shows the original image, the scaled image, and the rerendered
79 image.
80
81 \table
82 \row
83 \o \inlineimage mandelbrot_zoom1.png
84 \o \inlineimage mandelbrot_zoom2.png
85 \o \inlineimage mandelbrot_zoom3.png
86 \endtable
87
88 Similarly, when the user scrolls, the previous pixmap is scrolled
89 immediately, revealing unpainted areas beyond the edge of the
90 pixmap, while the image is rendered by the worker thread.
91
92 \table
93 \row
94 \o \inlineimage mandelbrot_scroll1.png
95 \o \inlineimage mandelbrot_scroll2.png
96 \o \inlineimage mandelbrot_scroll3.png
97 \endtable
98
99 The application consists of two classes:
100
101 \list
102 \o \c RenderThread is a QThread subclass that renders
103 the Mandelbrot set.
104 \o \c MandelbrotWidget is a QWidget subclass that shows the
105 Mandelbrot set on screen and lets the user zoom and scroll.
106 \endlist
107
108 If you are not already familiar with Qt's thread support, we
109 recommend that you start by reading the \l{Thread Support in Qt}
110 overview.
111
112 \section1 RenderThread Class Definition
113
114 We'll start with the definition of the \c RenderThread class:
115
116 \snippet examples/threads/mandelbrot/renderthread.h 0
117
118 The class inherits QThread so that it gains the ability to run in
119 a separate thread. Apart from the constructor and destructor, \c
120 render() is the only public function. Whenever the thread is done
121 rendering an image, it emits the \c renderedImage() signal.
122
123 The protected \c run() function is reimplemented from QThread. It
124 is automatically called when the thread is started.
125
126 In the \c private section, we have a QMutex, a QWaitCondition,
127 and a few other data members. The mutex protects the other data
128 member.
129
130 \section1 RenderThread Class Implementation
131
132 \snippet examples/threads/mandelbrot/renderthread.cpp 0
133
134 In the constructor, we initialize the \c restart and \c abort
135 variables to \c false. These variables control the flow of the \c
136 run() function.
137
138 We also initialize the \c colormap array, which contains a series
139 of RGB colors.
140
141 \snippet examples/threads/mandelbrot/renderthread.cpp 1
142
143 The destructor can be called at any point while the thread is
144 active. We set \c abort to \c true to tell \c run() to stop
145 running as soon as possible. We also call
146 QWaitCondition::wakeOne() to wake up the thread if it's sleeping.
147 (As we will see when we review \c run(), the thread is put to
148 sleep when it has nothing to do.)
149
150 The important thing to notice here is that \c run() is executed
151 in its own thread (the worker thread), whereas the \c
152 RenderThread constructor and destructor (as well as the \c
153 render() function) are called by the thread that created the
154 worker thread. Therefore, we need a mutex to protect accesses to
155 the \c abort and \c condition variables, which might be accessed
156 at any time by \c run().
157
158 At the end of the destructor, we call QThread::wait() to wait
159 until \c run() has exited before the base class destructor is
160 invoked.
161
162 \snippet examples/threads/mandelbrot/renderthread.cpp 2
163
164 The \c render() function is called by the \c MandelbrotWidget
165 whenever it needs to generate a new image of the Mandelbrot set.
166 The \c centerX, \c centerY, and \c scaleFactor parameters specify
167 the portion of the fractal to render; \c resultSize specifies the
168 size of the resulting QImage.
169
170 The function stores the parameters in member variables. If the
171 thread isn't already running, it starts it; otherwise, it sets \c
172 restart to \c true (telling \c run() to stop any unfinished
173 computation and start again with the new parameters) and wakes up
174 the thread, which might be sleeping.
175
176 \snippet examples/threads/mandelbrot/renderthread.cpp 3
177
178 \c run() is quite a big function, so we'll break it down into
179 parts.
180
181 The function body is an infinite loop which starts by storing the
182 rendering parameters in local variables. As usual, we protect
183 accesses to the member variables using the class's mutex. Storing
184 the member variables in local variables allows us to minimize the
185 amout of code that needs to be protected by a mutex. This ensures
186 that the main thread will never have to block for too long when
187 it needs to access \c{RenderThread}'s member variables (e.g., in
188 \c render()).
189
190 The \c forever keyword is, like \c foreach, a Qt pseudo-keyword.
191
192 \snippet examples/threads/mandelbrot/renderthread.cpp 4
193 \snippet examples/threads/mandelbrot/renderthread.cpp 5
194 \snippet examples/threads/mandelbrot/renderthread.cpp 6
195 \snippet examples/threads/mandelbrot/renderthread.cpp 7
196
197 Then comes the core of the algorithm. Instead of trying to create
198 a perfect Mandelbrot set image, we do multiple passes and
199 generate more and more precise (and computationally expensive)
200 approximations of the fractal.
201
202 If we discover inside the loop that \c restart has been set to \c
203 true (by \c render()), we break out of the loop immediately, so
204 that the control quickly returns to the very top of the outer
205 loop (the \c forever loop) and we fetch the new rendering
206 parameters. Similarly, if we discover that \c abort has been set
207 to \c true (by the \c RenderThread destructor), we return from
208 the function immediately, terminating the thread.
209
210 The core algorithm is beyond the scope of this tutorial.
211
212 \snippet examples/threads/mandelbrot/renderthread.cpp 8
213 \snippet examples/threads/mandelbrot/renderthread.cpp 9
214
215 Once we're done with all the iterations, we call
216 QWaitCondition::wait() to put the thread to sleep by calling,
217 unless \c restart is \c true. There's no use in keeping a worker
218 thread looping indefinitely while there's nothing to do.
219
220 \snippet examples/threads/mandelbrot/renderthread.cpp 10
221
222 The \c rgbFromWaveLength() function is a helper function that
223 converts a wave length to a RGB value compatible with 32-bit
224 \l{QImage}s. It is called from the constructor to initialize the
225 \c colormap array with pleasing colors.
226
227 \section1 MandelbrotWidget Class Defintion
228
229 The \c MandelbrotWidget class uses \c RenderThread to draw the
230 Mandelbrot set on screen. Here's the class definition:
231
232 \snippet examples/threads/mandelbrot/mandelbrotwidget.h 0
233
234 The widget reimplements many event handlers from QWidget. In
235 addition, it has an \c updatePixmap() slot that we'll connect to
236 the worker thread's \c renderedImage() signal to update the
237 display whenever we receive new data from the thread.
238
239 Among the private variables, we have \c thread of type \c
240 RenderThread and \c pixmap, which contains the last rendered
241 image.
242
243 \section1 MandelbrotWidget Class Implementation
244
245 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 0
246
247 The implementation starts with a few contants that we'll need
248 later on.
249
250 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 1
251
252 The interesting part of the constructor is the
253 qRegisterMetaType() and QObject::connect() calls. Let's start
254 with the \l{QObject::connect()}{connect()} call.
255
256 Although it looks like a standard signal-slot connection between
257 two \l{QObject}s, because the signal is emitted in a different
258 thread than the receiver lives in, the connection is effectively a
259 \l{Qt::QueuedConnection}{queued connection}. These connections are
260 asynchronous (i.e., non-blocking), meaning that the slot will be
261 called at some point after the \c emit statement. What's more, the
262 slot will be invoked in the thread in which the receiver lives.
263 Here, the signal is emitted in the worker thread, and the slot is
264 executed in the GUI thread when control returns to the event loop.
265
266 With queued connections, Qt must store a copy of the arguments
267 that were passed to the signal so that it can pass them to the
268 slot later on. Qt knows how to take of copy of many C++ and Qt
269 types, but QImage isn't one of them. We must therefore call the
270 template function qRegisterMetaType() before we can use QImage
271 as parameter in queued connections.
272
273 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 2
274 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 3
275 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 4
276
277 In \l{QWidget::paintEvent()}{paintEvent()}, we start by filling
278 the background with black. If we have nothing yet to paint (\c
279 pixmap is null), we print a message on the widget asking the user
280 to be patient and return from the function immediately.
281
282 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 5
283 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 6
284 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 7
285 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 8
286
287 If the pixmap has the right scale factor, we draw the pixmap directly onto
288 the widget. Otherwise, we scale and translate the \l{The Coordinate
289 System}{coordinate system} before we draw the pixmap. By reverse mapping
290 the widget's rectangle using the scaled painter matrix, we also make sure
291 that only the exposed areas of the pixmap are drawn. The calls to
292 QPainter::save() and QPainter::restore() make sure that any painting
293 performed afterwards uses the standard coordinate system.
294
295 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 9
296
297 At the end of the paint event handler, we draw a text string and
298 a semi-transparent rectangle on top of the fractal.
299
300 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 10
301
302 Whenever the user resizes the widget, we call \c render() to
303 start generating a new image, with the same \c centerX, \c
304 centerY, and \c curScale parameters but with the new widget size.
305
306 Notice that we rely on \c resizeEvent() being automatically
307 called by Qt when the widget is shown the first time to generate
308 the image the very first time.
309
310 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 11
311
312 The key press event handler provides a few keyboard bindings for
313 the benefit of users who don't have a mouse. The \c zoom() and \c
314 scroll() functions will be covered later.
315
316 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 12
317
318 The wheel event handler is reimplemented to make the mouse wheel
319 control the zoom level. QWheelEvent::delta() returns the angle of
320 the wheel mouse movement, in eights of a degree. For most mice,
321 one wheel step corresponds to 15 degrees. We find out how many
322 mouse steps we have and determine the zoom factor in consequence.
323 For example, if we have two wheel steps in the positive direction
324 (i.e., +30 degrees), the zoom factor becomes \c ZoomInFactor
325 to the second power, i.e. 0.8 * 0.8 = 0.64.
326
327 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 13
328
329 When the user presses the left mouse button, we store the mouse
330 pointer position in \c lastDragPos.
331
332 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 14
333
334 When the user moves the mouse pointer while the left mouse button
335 is pressed, we adjust \c pixmapOffset to paint the pixmap at a
336 shifted position and call QWidget::update() to force a repaint.
337
338 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 15
339
340 When the left mouse button is released, we update \c pixmapOffset
341 just like we did on a mouse move and we reset \c lastDragPos to a
342 default value. Then, we call \c scroll() to render a new image
343 for the new position. (Adjusting \c pixmapOffset isn't sufficient
344 because areas revealed when dragging the pixmap are drawn in
345 black.)
346
347 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 16
348
349 The \c updatePixmap() slot is invoked when the worker thread has
350 finished rendering an image. We start by checking whether a drag
351 is in effect and do nothing in that case. In the normal case, we
352 store the image in \c pixmap and reinitialize some of the other
353 members. At the end, we call QWidget::update() to refresh the
354 display.
355
356 At this point, you might wonder why we use a QImage for the
357 parameter and a QPixmap for the data member. Why not stick to one
358 type? The reason is that QImage is the only class that supports
359 direct pixel manipulation, which we need in the worker thread. On
360 the other hand, before an image can be drawn on screen, it must
361 be converted into a pixmap. It's better to do the conversion once
362 and for all here, rather than in \c paintEvent().
363
364 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 17
365
366 In \c zoom(), we recompute \c curScale. Then we call
367 QWidget::update() to draw a scaled pixmap, and we ask the worker
368 thread to render a new image corresponding to the new \c curScale
369 value.
370
371 \snippet examples/threads/mandelbrot/mandelbrotwidget.cpp 18
372
373 \c scroll() is similar to \c zoom(), except that the affected
374 parameters are \c centerX and \c centerY.
375
376 \section1 The main() Function
377
378 The application's multithreaded nature has no impact on its \c
379 main() function, which is as simple as usual:
380
381 \snippet examples/threads/mandelbrot/main.cpp 0
382*/
Note: See TracBrowser for help on using the repository browser.