doc/src/examples/mandelbrot.qdoc
changeset 0 1918ee327afb
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     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 */