|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 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:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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 */ |