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

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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