diff options
Diffstat (limited to 'examples/corelib/threads')
32 files changed, 2493 insertions, 0 deletions
diff --git a/examples/corelib/threads/README b/examples/corelib/threads/README new file mode 100644 index 0000000000..6c20844564 --- /dev/null +++ b/examples/corelib/threads/README @@ -0,0 +1,7 @@ +This folder contains examples for the use of the threading-related classes +in Qt Core. For examples using the higher-level Qt Concurrent module, +check out the "qtconcurrent" folder instead. + +Documentation for examples can be found via the Examples and Tutorials link +in the main Qt documentation. The examples and their documentation can also +be opened from the Examples tab of Qt Creator's Welcome mode. diff --git a/examples/corelib/threads/doc/images/mandelbrot-example.png b/examples/corelib/threads/doc/images/mandelbrot-example.png Binary files differnew file mode 100644 index 0000000000..f5817834e1 --- /dev/null +++ b/examples/corelib/threads/doc/images/mandelbrot-example.png diff --git a/examples/corelib/threads/doc/images/mandelbrot_scroll1.png b/examples/corelib/threads/doc/images/mandelbrot_scroll1.png Binary files differnew file mode 100644 index 0000000000..b800455821 --- /dev/null +++ b/examples/corelib/threads/doc/images/mandelbrot_scroll1.png diff --git a/examples/corelib/threads/doc/images/mandelbrot_scroll2.png b/examples/corelib/threads/doc/images/mandelbrot_scroll2.png Binary files differnew file mode 100644 index 0000000000..704ea0a7c1 --- /dev/null +++ b/examples/corelib/threads/doc/images/mandelbrot_scroll2.png diff --git a/examples/corelib/threads/doc/images/mandelbrot_scroll3.png b/examples/corelib/threads/doc/images/mandelbrot_scroll3.png Binary files differnew file mode 100644 index 0000000000..8b48211444 --- /dev/null +++ b/examples/corelib/threads/doc/images/mandelbrot_scroll3.png diff --git a/examples/corelib/threads/doc/images/mandelbrot_zoom1.png b/examples/corelib/threads/doc/images/mandelbrot_zoom1.png Binary files differnew file mode 100644 index 0000000000..30190e44eb --- /dev/null +++ b/examples/corelib/threads/doc/images/mandelbrot_zoom1.png diff --git a/examples/corelib/threads/doc/images/mandelbrot_zoom2.png b/examples/corelib/threads/doc/images/mandelbrot_zoom2.png Binary files differnew file mode 100644 index 0000000000..148ac777b8 --- /dev/null +++ b/examples/corelib/threads/doc/images/mandelbrot_zoom2.png diff --git a/examples/corelib/threads/doc/images/mandelbrot_zoom3.png b/examples/corelib/threads/doc/images/mandelbrot_zoom3.png Binary files differnew file mode 100644 index 0000000000..a669f4b5fe --- /dev/null +++ b/examples/corelib/threads/doc/images/mandelbrot_zoom3.png diff --git a/examples/corelib/threads/doc/images/queuedcustomtype-example.png b/examples/corelib/threads/doc/images/queuedcustomtype-example.png Binary files differnew file mode 100644 index 0000000000..4399b631d7 --- /dev/null +++ b/examples/corelib/threads/doc/images/queuedcustomtype-example.png diff --git a/examples/corelib/threads/doc/src/mandelbrot.qdoc b/examples/corelib/threads/doc/src/mandelbrot.qdoc new file mode 100644 index 0000000000..75d424e6a4 --- /dev/null +++ b/examples/corelib/threads/doc/src/mandelbrot.qdoc @@ -0,0 +1,370 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example threads/mandelbrot + \title Mandelbrot Example + \ingroup qtconcurrent-mtexamples + + \brief The Mandelbrot example demonstrates multi-thread programming + using Qt. It shows how to use a worker thread to + perform heavy computations without blocking the main thread's + event loop. + + \image mandelbrot-example.png Screenshot of the Mandelbrot example + + The heavy computation here is the Mandelbrot set, probably the + world's most famous fractal. These days, while sophisticated + programs such as \l{http://xaos.sourceforge.net/}{XaoS} that provide real-time zooming in the + Mandelbrot set, the standard Mandelbrot algorithm is just slow + enough for our purposes. + + In real life, the approach described here is applicable to a + large set of problems, including synchronous network I/O and + database access, where the user interface must remain responsive + while some heavy operation is taking place. The \l + {Blocking Fortune Client Example} shows the same principle at + work in a TCP client. + + The Mandelbrot application supports zooming and scrolling using + the mouse or the keyboard. To avoid freezing the main thread's + event loop (and, as a consequence, the application's user + interface), we put all the fractal computation in a separate + worker thread. The thread emits a signal when it is done + rendering the fractal. + + During the time where the worker thread is recomputing the + fractal to reflect the new zoom factor position, the main thread + simply scales the previously rendered pixmap to provide immediate + feedback. The result doesn't look as good as what the worker + thread eventually ends up providing, but at least it makes the + application more responsive. The sequence of screenshots below + shows the original image, the scaled image, and the rerendered + image. + + \table + \row + \li \inlineimage mandelbrot_zoom1.png + \li \inlineimage mandelbrot_zoom2.png + \li \inlineimage mandelbrot_zoom3.png + \endtable + + Similarly, when the user scrolls, the previous pixmap is scrolled + immediately, revealing unpainted areas beyond the edge of the + pixmap, while the image is rendered by the worker thread. + + \table + \row + \li \inlineimage mandelbrot_scroll1.png + \li \inlineimage mandelbrot_scroll2.png + \li \inlineimage mandelbrot_scroll3.png + \endtable + + The application consists of two classes: + + \list + \li \c RenderThread is a QThread subclass that renders + the Mandelbrot set. + \li \c MandelbrotWidget is a QWidget subclass that shows the + Mandelbrot set on screen and lets the user zoom and scroll. + \endlist + + If you are not already familiar with Qt's thread support, we + recommend that you start by reading the \l{Thread Support in Qt} + overview. + + \section1 RenderThread Class Definition + + We'll start with the definition of the \c RenderThread class: + + \snippet threads/mandelbrot/renderthread.h 0 + + The class inherits QThread so that it gains the ability to run in + a separate thread. Apart from the constructor and destructor, \c + render() is the only public function. Whenever the thread is done + rendering an image, it emits the \c renderedImage() signal. + + The protected \c run() function is reimplemented from QThread. It + is automatically called when the thread is started. + + In the \c private section, we have a QMutex, a QWaitCondition, + and a few other data members. The mutex protects the other data + member. + + \section1 RenderThread Class Implementation + + \snippet threads/mandelbrot/renderthread.cpp 0 + + In the constructor, we initialize the \c restart and \c abort + variables to \c false. These variables control the flow of the \c + run() function. + + We also initialize the \c colormap array, which contains a series + of RGB colors. + + \snippet threads/mandelbrot/renderthread.cpp 1 + + The destructor can be called at any point while the thread is + active. We set \c abort to \c true to tell \c run() to stop + running as soon as possible. We also call + QWaitCondition::wakeOne() to wake up the thread if it's sleeping. + (As we will see when we review \c run(), the thread is put to + sleep when it has nothing to do.) + + The important thing to notice here is that \c run() is executed + in its own thread (the worker thread), whereas the \c + RenderThread constructor and destructor (as well as the \c + render() function) are called by the thread that created the + worker thread. Therefore, we need a mutex to protect accesses to + the \c abort and \c condition variables, which might be accessed + at any time by \c run(). + + At the end of the destructor, we call QThread::wait() to wait + until \c run() has exited before the base class destructor is + invoked. + + \snippet threads/mandelbrot/renderthread.cpp 2 + + The \c render() function is called by the \c MandelbrotWidget + whenever it needs to generate a new image of the Mandelbrot set. + The \c centerX, \c centerY, and \c scaleFactor parameters specify + the portion of the fractal to render; \c resultSize specifies the + size of the resulting QImage. + + The function stores the parameters in member variables. If the + thread isn't already running, it starts it; otherwise, it sets \c + restart to \c true (telling \c run() to stop any unfinished + computation and start again with the new parameters) and wakes up + the thread, which might be sleeping. + + \snippet threads/mandelbrot/renderthread.cpp 3 + + \c run() is quite a big function, so we'll break it down into + parts. + + The function body is an infinite loop which starts by storing the + rendering parameters in local variables. As usual, we protect + accesses to the member variables using the class's mutex. Storing + the member variables in local variables allows us to minimize the + amout of code that needs to be protected by a mutex. This ensures + that the main thread will never have to block for too long when + it needs to access \c{RenderThread}'s member variables (e.g., in + \c render()). + + The \c forever keyword is, like \c foreach, a Qt pseudo-keyword. + + \snippet threads/mandelbrot/renderthread.cpp 4 + \snippet threads/mandelbrot/renderthread.cpp 5 + \snippet threads/mandelbrot/renderthread.cpp 6 + \snippet threads/mandelbrot/renderthread.cpp 7 + + Then comes the core of the algorithm. Instead of trying to create + a perfect Mandelbrot set image, we do multiple passes and + generate more and more precise (and computationally expensive) + approximations of the fractal. + + If we discover inside the loop that \c restart has been set to \c + true (by \c render()), we break out of the loop immediately, so + that the control quickly returns to the very top of the outer + loop (the \c forever loop) and we fetch the new rendering + parameters. Similarly, if we discover that \c abort has been set + to \c true (by the \c RenderThread destructor), we return from + the function immediately, terminating the thread. + + The core algorithm is beyond the scope of this tutorial. + + \snippet threads/mandelbrot/renderthread.cpp 8 + \snippet threads/mandelbrot/renderthread.cpp 9 + + Once we're done with all the iterations, we call + QWaitCondition::wait() to put the thread to sleep by calling, + unless \c restart is \c true. There's no use in keeping a worker + thread looping indefinitely while there's nothing to do. + + \snippet threads/mandelbrot/renderthread.cpp 10 + + The \c rgbFromWaveLength() function is a helper function that + converts a wave length to a RGB value compatible with 32-bit + \l{QImage}s. It is called from the constructor to initialize the + \c colormap array with pleasing colors. + + \section1 MandelbrotWidget Class Definition + + The \c MandelbrotWidget class uses \c RenderThread to draw the + Mandelbrot set on screen. Here's the class definition: + + \snippet threads/mandelbrot/mandelbrotwidget.h 0 + + The widget reimplements many event handlers from QWidget. In + addition, it has an \c updatePixmap() slot that we'll connect to + the worker thread's \c renderedImage() signal to update the + display whenever we receive new data from the thread. + + Among the private variables, we have \c thread of type \c + RenderThread and \c pixmap, which contains the last rendered + image. + + \section1 MandelbrotWidget Class Implementation + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 0 + + The implementation starts with a few contants that we'll need + later on. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 1 + + The interesting part of the constructor is the + qRegisterMetaType() and QObject::connect() calls. Let's start + with the \l{QObject::connect()}{connect()} call. + + Although it looks like a standard signal-slot connection between + two \l{QObject}s, because the signal is emitted in a different + thread than the receiver lives in, the connection is effectively a + \l{Qt::QueuedConnection}{queued connection}. These connections are + asynchronous (i.e., non-blocking), meaning that the slot will be + called at some point after the \c emit statement. What's more, the + slot will be invoked in the thread in which the receiver lives. + Here, the signal is emitted in the worker thread, and the slot is + executed in the GUI thread when control returns to the event loop. + + With queued connections, Qt must store a copy of the arguments + that were passed to the signal so that it can pass them to the + slot later on. Qt knows how to take of copy of many C++ and Qt + types, but QImage isn't one of them. We must therefore call the + template function qRegisterMetaType() before we can use QImage + as parameter in queued connections. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 2 + \snippet threads/mandelbrot/mandelbrotwidget.cpp 3 + \snippet threads/mandelbrot/mandelbrotwidget.cpp 4 + + In \l{QWidget::paintEvent()}{paintEvent()}, we start by filling + the background with black. If we have nothing yet to paint (\c + pixmap is null), we print a message on the widget asking the user + to be patient and return from the function immediately. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 5 + \snippet threads/mandelbrot/mandelbrotwidget.cpp 6 + \snippet threads/mandelbrot/mandelbrotwidget.cpp 7 + \snippet threads/mandelbrot/mandelbrotwidget.cpp 8 + + If the pixmap has the right scale factor, we draw the pixmap directly onto + the widget. Otherwise, we scale and translate the \l{Coordinate + System}{coordinate system} before we draw the pixmap. By reverse mapping + the widget's rectangle using the scaled painter matrix, we also make sure + that only the exposed areas of the pixmap are drawn. The calls to + QPainter::save() and QPainter::restore() make sure that any painting + performed afterwards uses the standard coordinate system. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 9 + + At the end of the paint event handler, we draw a text string and + a semi-transparent rectangle on top of the fractal. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 10 + + Whenever the user resizes the widget, we call \c render() to + start generating a new image, with the same \c centerX, \c + centerY, and \c curScale parameters but with the new widget size. + + Notice that we rely on \c resizeEvent() being automatically + called by Qt when the widget is shown the first time to generate + the image the very first time. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 11 + + The key press event handler provides a few keyboard bindings for + the benefit of users who don't have a mouse. The \c zoom() and \c + scroll() functions will be covered later. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 12 + + The wheel event handler is reimplemented to make the mouse wheel + control the zoom level. QWheelEvent::delta() returns the angle of + the wheel mouse movement, in eights of a degree. For most mice, + one wheel step corresponds to 15 degrees. We find out how many + mouse steps we have and determine the zoom factor in consequence. + For example, if we have two wheel steps in the positive direction + (i.e., +30 degrees), the zoom factor becomes \c ZoomInFactor + to the second power, i.e. 0.8 * 0.8 = 0.64. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 13 + + When the user presses the left mouse button, we store the mouse + pointer position in \c lastDragPos. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 14 + + When the user moves the mouse pointer while the left mouse button + is pressed, we adjust \c pixmapOffset to paint the pixmap at a + shifted position and call QWidget::update() to force a repaint. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 15 + + When the left mouse button is released, we update \c pixmapOffset + just like we did on a mouse move and we reset \c lastDragPos to a + default value. Then, we call \c scroll() to render a new image + for the new position. (Adjusting \c pixmapOffset isn't sufficient + because areas revealed when dragging the pixmap are drawn in + black.) + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 16 + + The \c updatePixmap() slot is invoked when the worker thread has + finished rendering an image. We start by checking whether a drag + is in effect and do nothing in that case. In the normal case, we + store the image in \c pixmap and reinitialize some of the other + members. At the end, we call QWidget::update() to refresh the + display. + + At this point, you might wonder why we use a QImage for the + parameter and a QPixmap for the data member. Why not stick to one + type? The reason is that QImage is the only class that supports + direct pixel manipulation, which we need in the worker thread. On + the other hand, before an image can be drawn on screen, it must + be converted into a pixmap. It's better to do the conversion once + and for all here, rather than in \c paintEvent(). + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 17 + + In \c zoom(), we recompute \c curScale. Then we call + QWidget::update() to draw a scaled pixmap, and we ask the worker + thread to render a new image corresponding to the new \c curScale + value. + + \snippet threads/mandelbrot/mandelbrotwidget.cpp 18 + + \c scroll() is similar to \c zoom(), except that the affected + parameters are \c centerX and \c centerY. + + \section1 The main() Function + + The application's multithreaded nature has no impact on its \c + main() function, which is as simple as usual: + + \snippet threads/mandelbrot/main.cpp 0 +*/ diff --git a/examples/corelib/threads/doc/src/queuedcustomtype.qdoc b/examples/corelib/threads/doc/src/queuedcustomtype.qdoc new file mode 100644 index 0000000000..cca68b4513 --- /dev/null +++ b/examples/corelib/threads/doc/src/queuedcustomtype.qdoc @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example threads/queuedcustomtype + \title Queued Custom Type Example + \brief Demonstrates multi-thread programming using Qt + \ingroup qtconcurrent-mtexamples + + \brief The Queued Custom Type example shows how to send custom types between + threads with queued signals and slots. + + \image queuedcustomtype-example.png + + Contents: + + \tableofcontents + + \section1 Overview + + In the \l{Custom Type Example}, we showed how to integrate custom types with + the meta-object system, enabling them to be stored in QVariant objects, written + out in debugging information and used in signal-slot communication. + + In this example, we create a new value class, \c Block, and register it + with the meta-object system to enable us to send instances of it between + threads using queued signals and slots. + + \section1 The Block Class + + The \c Block class is similar to the \c Message class described in the + \l{Custom Type Example}. It provides the default constructor, copy + constructor and destructor in the public section of the class that the + meta-object system requires. It describes a colored rectangle. + + \snippet threads/queuedcustomtype/block.h custom type definition and meta-type declaration + + We will still need to register it with the meta-object system at + run-time by calling the qRegisterMetaType() template function before + we make any signal-slot connections that use this type. + Even though we do not intend to use the type with QVariant in this example, + it is good practice to also declare the new type with Q_DECLARE_METATYPE(). + + The implementation of the \c Block class is trivial, so we avoid quoting + it here. + + \section1 The Window Class + + We define a simple \c Window class with a public slot that accepts a + \c Block object. The rest of the class is concerned with managing the + user interface and handling images. + + \snippet threads/queuedcustomtype/window.h Window class definition + + The \c Window class also contains a worker thread, provided by a + \c RenderThread object. This will emit signals to send \c Block objects + to the window's \c addBlock(Block) slot. + + The parts of the \c Window class that are most relevant are the constructor + and the \c addBlock(Block) slot. + + The constructor creates a thread for rendering images, sets up a user + interface containing a label and two push buttons that are connected to + slots in the same class. + + \snippet threads/queuedcustomtype/window.cpp Window constructor start + \snippet threads/queuedcustomtype/window.cpp set up widgets and connections + \snippet threads/queuedcustomtype/window.cpp connecting signal with custom type + + In the last of these connections, we connect a signal in the + \c RenderThread object to the \c addBlock(Block) slot in the window. + + \dots + \snippet threads/queuedcustomtype/window.cpp Window constructor finish + + The rest of the constructor simply sets up the layout of the window. + + The \c addBlock(Block) slot receives blocks from the rendering thread via + the signal-slot connection set up in the constructor: + + \snippet threads/queuedcustomtype/window.cpp Adding blocks to the display + + We simply paint these onto the label as they arrive. + + \section1 The RenderThread Class + + The \c RenderThread class processes an image, creating \c Block objects + and using the \c sendBlock(Block) signal to send them to other components + in the example. + + \snippet threads/queuedcustomtype/renderthread.h RenderThread class definition + + The constructor and destructor are not quoted here. These take care of + setting up the thread's internal state and cleaning up when it is destroyed. + + Processing is started with the \c processImage() function, which calls the + \c RenderThread class's reimplementation of the QThread::run() function: + + \snippet threads/queuedcustomtype/renderthread.cpp processing the image (start) + + Ignoring the details of the way the image is processed, we see that the + signal containing a block is emitted in the usual way: + + \dots + \snippet threads/queuedcustomtype/renderthread.cpp processing the image (finish) + + Each signal that is emitted will be queued and delivered later to the + window's \c addBlock(Block) slot. + + \section1 Registering the Type + + In the example's \c{main()} function, we perform the registration of the + \c Block class as a custom type with the meta-object system by calling the + qRegisterMetaType() template function: + + \snippet threads/queuedcustomtype/main.cpp main function + + This call is placed here to ensure that the type is registered before any + signal-slot connections are made that use it. + + The rest of the \c{main()} function is concerned with setting a seed for + the pseudo-random number generator, creating and showing the window, and + setting a default image. See the source code for the implementation of the + \c createImage() function. + + \section1 Further Reading + + This example showed how a custom type can be registered with the + meta-object system so that it can be used with signal-slot connections + between threads. For ordinary communication involving direct signals and + slots, it is enough to simply declare the type in the way described in the + \l{Custom Type Example}. + + In practice, both the Q_DECLARE_METATYPE() macro and the qRegisterMetaType() + template function can be used to register custom types, but + qRegisterMetaType() is only required if you need to perform signal-slot + communication or need to create and destroy objects of the custom type + at run-time. + + More information on using custom types with Qt can be found in the + \l{Creating Custom Qt Types} document. +*/ diff --git a/examples/corelib/threads/doc/src/semaphores.qdoc b/examples/corelib/threads/doc/src/semaphores.qdoc new file mode 100644 index 0000000000..0b1a2e852e --- /dev/null +++ b/examples/corelib/threads/doc/src/semaphores.qdoc @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example threads/semaphores + \title Semaphores Example + \brief Demonstrates multi-thread programming using Qt + \ingroup qtconcurrent-mtexamples + + \brief The Semaphores example shows how to use QSemaphore to control + access to a circular buffer shared by a producer thread and a + consumer thread. + + The producer writes data to the buffer until it reaches the end + of the buffer, at which point it restarts from the beginning, + overwriting existing data. The consumer thread reads the data as + it is produced and writes it to standard error. + + Semaphores make it possible to have a higher level of concurrency + than mutexes. If accesses to the buffer were guarded by a QMutex, + the consumer thread couldn't access the buffer at the same time + as the producer thread. Yet, there is no harm in having both + threads working on \e{different parts} of the buffer at the same + time. + + The example comprises two classes: \c Producer and \c Consumer. + Both inherit from QThread. The circular buffer used for + communicating between these two classes and the semaphores that + protect it are global variables. + + An alternative to using QSemaphore to solve the producer-consumer + problem is to use QWaitCondition and QMutex. This is what the + \l{Wait Conditions Example} does. + + \section1 Global Variables + + Let's start by reviewing the circular buffer and the associated + semaphores: + + \snippet threads/semaphores/semaphores.cpp 0 + + \c DataSize is the amout of data that the producer will generate. + To keep the example as simple as possible, we make it a constant. + \c BufferSize is the size of the circular buffer. It is less than + \c DataSize, meaning that at some point the producer will reach + the end of the buffer and restart from the beginning. + + To synchronize the producer and the consumer, we need two + semaphores. The \c freeBytes semaphore controls the "free" area + of the buffer (the area that the producer hasn't filled with data + yet or that the consumer has already read). The \c usedBytes + semaphore controls the "used" area of the buffer (the area that + the producer has filled but that the consumer hasn't read yet). + + Together, the semaphores ensure that the producer is never more + than \c BufferSize bytes ahead of the consumer, and that the + consumer never reads data that the producer hasn't generated yet. + + The \c freeBytes semaphore is initialized with \c BufferSize, + because initially the entire buffer is empty. The \c usedBytes + semaphore is initialized to 0 (the default value if none is + specified). + + \section1 Producer Class + + Let's review the code for the \c Producer class: + + \snippet threads/semaphores/semaphores.cpp 1 + \snippet threads/semaphores/semaphores.cpp 2 + + The producer generates \c DataSize bytes of data. Before it + writes a byte to the circular buffer, it must acquire a "free" + byte using the \c freeBytes semaphore. The QSemaphore::acquire() + call might block if the consumer hasn't kept up the pace with the + producer. + + At the end, the producer releases a byte using the \c usedBytes + semaphore. The "free" byte has successfully been transformed into + a "used" byte, ready to be read by the consumer. + + \section1 Consumer Class + + Let's now turn to the \c Consumer class: + + \snippet threads/semaphores/semaphores.cpp 3 + \snippet threads/semaphores/semaphores.cpp 4 + + The code is very similar to the producer, except that this time + we acquire a "used" byte and release a "free" byte, instead of + the opposite. + + \section1 The main() Function + + In \c main(), we create the two threads and call QThread::wait() + to ensure that both threads get time to finish before we exit: + + \snippet threads/semaphores/semaphores.cpp 5 + \snippet threads/semaphores/semaphores.cpp 6 + + So what happens when we run the program? Initially, the producer + thread is the only one that can do anything; the consumer is + blocked waiting for the \c usedBytes semaphore to be released (its + initial \l{QSemaphore::available()}{available()} count is 0). + Once the producer has put one byte in the buffer, + \c{freeBytes.available()} is \c BufferSize - 1 and + \c{usedBytes.available()} is 1. At that point, two things can + happen: Either the consumer thread takes over and reads that + byte, or the consumer gets to produce a second byte. + + The producer-consumer model presented in this example makes it + possible to write highly concurrent multithreaded applications. + On a multiprocessor machine, the program is potentially up to + twice as fast as the equivalent mutex-based program, since the + two threads can be active at the same time on different parts of + the buffer. + + Be aware though that these benefits aren't always realized. + Acquiring and releasing a QSemaphore has a cost. In practice, it + would probably be worthwhile to divide the buffer into chunks and + to operate on chunks instead of individual bytes. The buffer size + is also a parameter that must be selected carefully, based on + experimentation. +*/ diff --git a/examples/corelib/threads/doc/src/waitconditions.qdoc b/examples/corelib/threads/doc/src/waitconditions.qdoc new file mode 100644 index 0000000000..aff6997b55 --- /dev/null +++ b/examples/corelib/threads/doc/src/waitconditions.qdoc @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example threads/waitconditions + \title Wait Conditions Example + \brief Demonstrates multi-thread programming using Qt + \ingroup qtconcurrent-mtexamples + + \brief The Wait Conditions example shows how to use QWaitCondition and + QMutex to control access to a circular buffer shared by a + producer thread and a consumer thread. + + The producer writes data to the buffer until it reaches the end + of the buffer, at which point it restarts from the beginning, + overwriting existing data. The consumer thread reads the data as + it is produced and writes it to standard error. + + Wait conditions make it possible to have a higher level of + concurrency than what is possible with mutexes alone. If accesses + to the buffer were simply guarded by a QMutex, the consumer + thread couldn't access the buffer at the same time as the + producer thread. Yet, there is no harm in having both threads + working on \e{different parts} of the buffer at the same time. + + The example comprises two classes: \c Producer and \c Consumer. + Both inherit from QThread. The circular buffer used for + communicating between these two classes and the synchronization + tools that protect it are global variables. + + An alternative to using QWaitCondition and QMutex to solve the + producer-consumer problem is to use QSemaphore. This is what the + \l{Semaphores Example} does. + + \section1 Global Variables + + Let's start by reviewing the circular buffer and the associated + synchronization tools: + + \snippet threads/waitconditions/waitconditions.cpp 0 + + \c DataSize is the amount of data that the producer will generate. + To keep the example as simple as possible, we make it a constant. + \c BufferSize is the size of the circular buffer. It is less than + \c DataSize, meaning that at some point the producer will reach + the end of the buffer and restart from the beginning. + + To synchronize the producer and the consumer, we need two wait + conditions and one mutex. The \c bufferNotEmpty condition is + signalled when the producer has generated some data, telling the + consumer that it can start reading it. The \c bufferNotFull + condition is signalled when the consumer has read some data, + telling the producer that it can generate more. The \c numUsedBytes + is the number of bytes in the buffer that contain data. + + Together, the wait conditions, the mutex, and the \c numUsedBytes + counter ensure that the producer is never more than \c BufferSize + bytes ahead of the consumer, and that the consumer never reads + data that the producer hasn't generated yet. + + \section1 Producer Class + + Let's review the code for the \c Producer class: + + \snippet threads/waitconditions/waitconditions.cpp 1 + \snippet threads/waitconditions/waitconditions.cpp 2 + + The producer generates \c DataSize bytes of data. Before it + writes a byte to the circular buffer, it must first check whether + the buffer is full (i.e., \c numUsedBytes equals \c BufferSize). + If the buffer is full, the thread waits on the \c bufferNotFull + condition. + + At the end, the producer increments \c numUsedBytes and signalls + that the condition \c bufferNotEmpty is true, since \c + numUsedBytes is necessarily greater than 0. + + We guard all accesses to the \c numUsedBytes variable with a + mutex. In addition, the QWaitCondition::wait() function accepts a + mutex as its argument. This mutex is unlocked before the thread + is put to sleep and locked when the thread wakes up. Furthermore, + the transition from the locked state to the wait state is atomic, + to prevent race conditions from occurring. + + \section1 Consumer Class + + Let's turn to the \c Consumer class: + + \snippet threads/waitconditions/waitconditions.cpp 3 + \snippet threads/waitconditions/waitconditions.cpp 4 + + The code is very similar to the producer. Before we read the + byte, we check whether the buffer is empty (\c numUsedBytes is 0) + instead of whether it's full and wait on the \c bufferNotEmpty + condition if it's empty. After we've read the byte, we decrement + \c numUsedBytes (instead of incrementing it), and we signal the + \c bufferNotFull condition (instead of the \c bufferNotEmpty + condition). + + \section1 The main() Function + + In \c main(), we create the two threads and call QThread::wait() + to ensure that both threads get time to finish before we exit: + + \snippet threads/waitconditions/waitconditions.cpp 5 + \snippet threads/waitconditions/waitconditions.cpp 6 + + So what happens when we run the program? Initially, the producer + thread is the only one that can do anything; the consumer is + blocked waiting for the \c bufferNotEmpty condition to be + signalled (\c numUsedBytes is 0). Once the producer has put one + byte in the buffer, \c numUsedBytes is \c BufferSize - 1 and the + \c bufferNotEmpty condition is signalled. At that point, two + things can happen: Either the consumer thread takes over and + reads that byte, or the consumer gets to produce a second byte. + + The producer-consumer model presented in this example makes it + possible to write highly concurrent multithreaded applications. + On a multiprocessor machine, the program is potentially up to + twice as fast as the equivalent mutex-based program, since the + two threads can be active at the same time on different parts of + the buffer. + + Be aware though that these benefits aren't always realized. + Locking and unlocking a QMutex has a cost. In practice, it would + probably be worthwhile to divide the buffer into chunks and to + operate on chunks instead of individual bytes. The buffer size is + also a parameter that must be selected carefully, based on + experimentation. +*/ diff --git a/examples/corelib/threads/mandelbrot/main.cpp b/examples/corelib/threads/mandelbrot/main.cpp new file mode 100644 index 0000000000..8334d94f9c --- /dev/null +++ b/examples/corelib/threads/mandelbrot/main.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mandelbrotwidget.h" + +#include <QApplication> + +//! [0] +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MandelbrotWidget widget; + widget.show(); + return app.exec(); +} +//! [0] diff --git a/examples/corelib/threads/mandelbrot/mandelbrot.pro b/examples/corelib/threads/mandelbrot/mandelbrot.pro new file mode 100644 index 0000000000..5a01a405f2 --- /dev/null +++ b/examples/corelib/threads/mandelbrot/mandelbrot.pro @@ -0,0 +1,13 @@ +QT += widgets + +HEADERS = mandelbrotwidget.h \ + renderthread.h +SOURCES = main.cpp \ + mandelbrotwidget.cpp \ + renderthread.cpp + +unix:!mac:!vxworks:!integrity:LIBS += -lm + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/corelib/threads/mandelbrot +INSTALLS += target diff --git a/examples/corelib/threads/mandelbrot/mandelbrotwidget.cpp b/examples/corelib/threads/mandelbrot/mandelbrotwidget.cpp new file mode 100644 index 0000000000..633d4a1ec5 --- /dev/null +++ b/examples/corelib/threads/mandelbrot/mandelbrotwidget.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QPainter> +#include <QKeyEvent> + +#include <math.h> + +#include "mandelbrotwidget.h" + + +//! [0] +const double DefaultCenterX = -0.637011f; +const double DefaultCenterY = -0.0395159f; +const double DefaultScale = 0.00403897f; + +const double ZoomInFactor = 0.8f; +const double ZoomOutFactor = 1 / ZoomInFactor; +const int ScrollStep = 20; +//! [0] + +//! [1] +MandelbrotWidget::MandelbrotWidget(QWidget *parent) + : QWidget(parent) +{ + centerX = DefaultCenterX; + centerY = DefaultCenterY; + pixmapScale = DefaultScale; + curScale = DefaultScale; + + connect(&thread, SIGNAL(renderedImage(QImage,double)), this, SLOT(updatePixmap(QImage,double))); + + setWindowTitle(tr("Mandelbrot")); +#ifndef QT_NO_CURSOR + setCursor(Qt::CrossCursor); +#endif + resize(550, 400); + +} +//! [1] + +//! [2] +void MandelbrotWidget::paintEvent(QPaintEvent * /* event */) +{ + QPainter painter(this); + painter.fillRect(rect(), Qt::black); + + if (pixmap.isNull()) { + painter.setPen(Qt::white); + painter.drawText(rect(), Qt::AlignCenter, tr("Rendering initial image, please wait...")); +//! [2] //! [3] + return; +//! [3] //! [4] + } +//! [4] + +//! [5] + if (curScale == pixmapScale) { +//! [5] //! [6] + painter.drawPixmap(pixmapOffset, pixmap); +//! [6] //! [7] + } else { +//! [7] //! [8] + double scaleFactor = pixmapScale / curScale; + int newWidth = int(pixmap.width() * scaleFactor); + int newHeight = int(pixmap.height() * scaleFactor); + int newX = pixmapOffset.x() + (pixmap.width() - newWidth) / 2; + int newY = pixmapOffset.y() + (pixmap.height() - newHeight) / 2; + + painter.save(); + painter.translate(newX, newY); + painter.scale(scaleFactor, scaleFactor); + QRectF exposed = painter.matrix().inverted().mapRect(rect()).adjusted(-1, -1, 1, 1); + painter.drawPixmap(exposed, pixmap, exposed); + painter.restore(); + } +//! [8] //! [9] + + QString text = tr("Use mouse wheel or the '+' and '-' keys to zoom. " + "Press and hold left mouse button to scroll."); + QFontMetrics metrics = painter.fontMetrics(); + int textWidth = metrics.width(text); + + painter.setPen(Qt::NoPen); + painter.setBrush(QColor(0, 0, 0, 127)); + painter.drawRect((width() - textWidth) / 2 - 5, 0, textWidth + 10, metrics.lineSpacing() + 5); + painter.setPen(Qt::white); + painter.drawText((width() - textWidth) / 2, metrics.leading() + metrics.ascent(), text); +} +//! [9] + +//! [10] +void MandelbrotWidget::resizeEvent(QResizeEvent * /* event */) +{ + thread.render(centerX, centerY, curScale, size()); +} +//! [10] + +//! [11] +void MandelbrotWidget::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Plus: + zoom(ZoomInFactor); + break; + case Qt::Key_Minus: + zoom(ZoomOutFactor); + break; + case Qt::Key_Left: + scroll(-ScrollStep, 0); + break; + case Qt::Key_Right: + scroll(+ScrollStep, 0); + break; + case Qt::Key_Down: + scroll(0, -ScrollStep); + break; + case Qt::Key_Up: + scroll(0, +ScrollStep); + break; + default: + QWidget::keyPressEvent(event); + } +} +//! [11] + +#ifndef QT_NO_WHEELEVENT +//! [12] +void MandelbrotWidget::wheelEvent(QWheelEvent *event) +{ + int numDegrees = event->delta() / 8; + double numSteps = numDegrees / 15.0f; + zoom(pow(ZoomInFactor, numSteps)); +} +//! [12] +#endif + +//! [13] +void MandelbrotWidget::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + lastDragPos = event->pos(); +} +//! [13] + +//! [14] +void MandelbrotWidget::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) { + pixmapOffset += event->pos() - lastDragPos; + lastDragPos = event->pos(); + update(); + } +} +//! [14] + +//! [15] +void MandelbrotWidget::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + pixmapOffset += event->pos() - lastDragPos; + lastDragPos = QPoint(); + + int deltaX = (width() - pixmap.width()) / 2 - pixmapOffset.x(); + int deltaY = (height() - pixmap.height()) / 2 - pixmapOffset.y(); + scroll(deltaX, deltaY); + } +} +//! [15] + +//! [16] +void MandelbrotWidget::updatePixmap(const QImage &image, double scaleFactor) +{ + if (!lastDragPos.isNull()) + return; + + pixmap = QPixmap::fromImage(image); + pixmapOffset = QPoint(); + lastDragPos = QPoint(); + pixmapScale = scaleFactor; + update(); +} +//! [16] + +//! [17] +void MandelbrotWidget::zoom(double zoomFactor) +{ + curScale *= zoomFactor; + update(); + thread.render(centerX, centerY, curScale, size()); +} +//! [17] + +//! [18] +void MandelbrotWidget::scroll(int deltaX, int deltaY) +{ + centerX += deltaX * curScale; + centerY += deltaY * curScale; + update(); + thread.render(centerX, centerY, curScale, size()); +} +//! [18] diff --git a/examples/corelib/threads/mandelbrot/mandelbrotwidget.h b/examples/corelib/threads/mandelbrot/mandelbrotwidget.h new file mode 100644 index 0000000000..183edf2e26 --- /dev/null +++ b/examples/corelib/threads/mandelbrot/mandelbrotwidget.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MANDELBROTWIDGET_H +#define MANDELBROTWIDGET_H + +#include <QPixmap> +#include <QWidget> +#include "renderthread.h" + + +//! [0] +class MandelbrotWidget : public QWidget +{ + Q_OBJECT + +public: + MandelbrotWidget(QWidget *parent = 0); + +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; + void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE; +#ifndef QT_NO_WHEELEVENT + void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; +#endif + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + +private slots: + void updatePixmap(const QImage &image, double scaleFactor); + void zoom(double zoomFactor); + +private: + void scroll(int deltaX, int deltaY); + + RenderThread thread; + QPixmap pixmap; + QPoint pixmapOffset; + QPoint lastDragPos; + double centerX; + double centerY; + double pixmapScale; + double curScale; +}; +//! [0] + +#endif // MANDELBROTWIDGET_H diff --git a/examples/corelib/threads/mandelbrot/renderthread.cpp b/examples/corelib/threads/mandelbrot/renderthread.cpp new file mode 100644 index 0000000000..5779c65c9c --- /dev/null +++ b/examples/corelib/threads/mandelbrot/renderthread.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderthread.h" + +#include <QtWidgets> + +#include <math.h> + +//! [0] +RenderThread::RenderThread(QObject *parent) + : QThread(parent) +{ + restart = false; + abort = false; + + for (int i = 0; i < ColormapSize; ++i) + colormap[i] = rgbFromWaveLength(380.0 + (i * 400.0 / ColormapSize)); +} +//! [0] + +//! [1] +RenderThread::~RenderThread() +{ + mutex.lock(); + abort = true; + condition.wakeOne(); + mutex.unlock(); + + wait(); +} +//! [1] + +//! [2] +void RenderThread::render(double centerX, double centerY, double scaleFactor, + QSize resultSize) +{ + QMutexLocker locker(&mutex); + + this->centerX = centerX; + this->centerY = centerY; + this->scaleFactor = scaleFactor; + this->resultSize = resultSize; + + if (!isRunning()) { + start(LowPriority); + } else { + restart = true; + condition.wakeOne(); + } +} +//! [2] + +//! [3] +void RenderThread::run() +{ + forever { + mutex.lock(); + QSize resultSize = this->resultSize; + double scaleFactor = this->scaleFactor; + double centerX = this->centerX; + double centerY = this->centerY; + mutex.unlock(); +//! [3] + +//! [4] + int halfWidth = resultSize.width() / 2; +//! [4] //! [5] + int halfHeight = resultSize.height() / 2; + QImage image(resultSize, QImage::Format_RGB32); + + const int NumPasses = 8; + int pass = 0; + while (pass < NumPasses) { + const int MaxIterations = (1 << (2 * pass + 6)) + 32; + const int Limit = 4; + bool allBlack = true; + + for (int y = -halfHeight; y < halfHeight; ++y) { + if (restart) + break; + if (abort) + return; + + uint *scanLine = + reinterpret_cast<uint *>(image.scanLine(y + halfHeight)); + double ay = centerY + (y * scaleFactor); + + for (int x = -halfWidth; x < halfWidth; ++x) { + double ax = centerX + (x * scaleFactor); + double a1 = ax; + double b1 = ay; + int numIterations = 0; + + do { + ++numIterations; + double a2 = (a1 * a1) - (b1 * b1) + ax; + double b2 = (2 * a1 * b1) + ay; + if ((a2 * a2) + (b2 * b2) > Limit) + break; + + ++numIterations; + a1 = (a2 * a2) - (b2 * b2) + ax; + b1 = (2 * a2 * b2) + ay; + if ((a1 * a1) + (b1 * b1) > Limit) + break; + } while (numIterations < MaxIterations); + + if (numIterations < MaxIterations) { + *scanLine++ = colormap[numIterations % ColormapSize]; + allBlack = false; + } else { + *scanLine++ = qRgb(0, 0, 0); + } + } + } + + if (allBlack && pass == 0) { + pass = 4; + } else { + if (!restart) + emit renderedImage(image, scaleFactor); +//! [5] //! [6] + ++pass; + } +//! [6] //! [7] + } +//! [7] + +//! [8] + mutex.lock(); +//! [8] //! [9] + if (!restart) + condition.wait(&mutex); + restart = false; + mutex.unlock(); + } +} +//! [9] + +//! [10] +uint RenderThread::rgbFromWaveLength(double wave) +{ + double r = 0.0; + double g = 0.0; + double b = 0.0; + + if (wave >= 380.0 && wave <= 440.0) { + r = -1.0 * (wave - 440.0) / (440.0 - 380.0); + b = 1.0; + } else if (wave >= 440.0 && wave <= 490.0) { + g = (wave - 440.0) / (490.0 - 440.0); + b = 1.0; + } else if (wave >= 490.0 && wave <= 510.0) { + g = 1.0; + b = -1.0 * (wave - 510.0) / (510.0 - 490.0); + } else if (wave >= 510.0 && wave <= 580.0) { + r = (wave - 510.0) / (580.0 - 510.0); + g = 1.0; + } else if (wave >= 580.0 && wave <= 645.0) { + r = 1.0; + g = -1.0 * (wave - 645.0) / (645.0 - 580.0); + } else if (wave >= 645.0 && wave <= 780.0) { + r = 1.0; + } + + double s = 1.0; + if (wave > 700.0) + s = 0.3 + 0.7 * (780.0 - wave) / (780.0 - 700.0); + else if (wave < 420.0) + s = 0.3 + 0.7 * (wave - 380.0) / (420.0 - 380.0); + + r = pow(r * s, 0.8); + g = pow(g * s, 0.8); + b = pow(b * s, 0.8); + return qRgb(int(r * 255), int(g * 255), int(b * 255)); +} +//! [10] diff --git a/examples/corelib/threads/mandelbrot/renderthread.h b/examples/corelib/threads/mandelbrot/renderthread.h new file mode 100644 index 0000000000..881870665f --- /dev/null +++ b/examples/corelib/threads/mandelbrot/renderthread.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RENDERTHREAD_H +#define RENDERTHREAD_H + +#include <QMutex> +#include <QSize> +#include <QThread> +#include <QWaitCondition> + +QT_BEGIN_NAMESPACE +class QImage; +QT_END_NAMESPACE + +//! [0] +class RenderThread : public QThread +{ + Q_OBJECT + +public: + RenderThread(QObject *parent = 0); + ~RenderThread(); + + void render(double centerX, double centerY, double scaleFactor, QSize resultSize); + +signals: + void renderedImage(const QImage &image, double scaleFactor); + +protected: + void run() Q_DECL_OVERRIDE; + +private: + uint rgbFromWaveLength(double wave); + + QMutex mutex; + QWaitCondition condition; + double centerX; + double centerY; + double scaleFactor; + QSize resultSize; + bool restart; + bool abort; + + enum { ColormapSize = 512 }; + uint colormap[ColormapSize]; +}; +//! [0] + +#endif // RENDERTHREAD_H diff --git a/examples/corelib/threads/queuedcustomtype/block.cpp b/examples/corelib/threads/queuedcustomtype/block.cpp new file mode 100644 index 0000000000..07bd58cd97 --- /dev/null +++ b/examples/corelib/threads/queuedcustomtype/block.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QColor> +#include <QRect> +#include "block.h" + +Block::Block() +{ +} + +Block::Block(const Block &other) +{ + m_rect = other.m_rect; + m_color = other.m_color; +} + +Block::~Block() +{ +} + +Block::Block(const QRect &rect, const QColor &color) +{ + m_rect = rect; + m_color = color; +} + +QColor Block::color() const +{ + return m_color; +} + +QRect Block::rect() const +{ + return m_rect; +} diff --git a/examples/corelib/threads/queuedcustomtype/block.h b/examples/corelib/threads/queuedcustomtype/block.h new file mode 100644 index 0000000000..f9930340e2 --- /dev/null +++ b/examples/corelib/threads/queuedcustomtype/block.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BLOCK_H +#define BLOCK_H + +#include <QColor> +#include <QDebug> +#include <QMetaType> +#include <QRect> + +//! [custom type definition and meta-type declaration] +class Block +{ +public: + Block(); + Block(const Block &other); + ~Block(); + + Block(const QRect &rect, const QColor &color); + + QColor color() const; + QRect rect() const; + +private: + QRect m_rect; + QColor m_color; +}; + +Q_DECLARE_METATYPE(Block); +//! [custom type definition and meta-type declaration] + +#endif diff --git a/examples/corelib/threads/queuedcustomtype/main.cpp b/examples/corelib/threads/queuedcustomtype/main.cpp new file mode 100644 index 0000000000..98933c66f3 --- /dev/null +++ b/examples/corelib/threads/queuedcustomtype/main.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QPainter> +#include <QTime> +#include "block.h" +#include "window.h" + +QImage createImage(int width, int height) +{ + QImage image(width, height, QImage::Format_RGB16); + QPainter painter; + QPen pen; + pen.setStyle(Qt::NoPen); + QBrush brush(Qt::blue); + + painter.begin(&image); + painter.fillRect(image.rect(), brush); + brush.setColor(Qt::white); + painter.setPen(pen); + painter.setBrush(brush); + + static const QPointF points1[3] = { + QPointF(4, 4), + QPointF(7, 4), + QPointF(5.5, 1) + }; + + static const QPointF points2[3] = { + QPointF(1, 4), + QPointF(7, 4), + QPointF(10, 10) + }; + + static const QPointF points3[3] = { + QPointF(4, 4), + QPointF(10, 4), + QPointF(1, 10) + }; + + painter.setWindow(0, 0, 10, 10); + + int x = 0; + int y = 0; + int starWidth = image.width()/3; + int starHeight = image.height()/3; + + QRect rect(x, y, starWidth, starHeight); + + for (int i = 0; i < 9; ++i) { + + painter.setViewport(rect); + painter.drawPolygon(points1, 3); + painter.drawPolygon(points2, 3); + painter.drawPolygon(points3, 3); + + if (i % 3 == 2) { + y = y + starHeight; + rect.moveTop(y); + + x = 0; + rect.moveLeft(x); + + } else { + x = x + starWidth; + rect.moveLeft(x); + } + } + + painter.end(); + return image; +} + +//! [main function] //! [main start] +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); +//! [main start] //! [register meta-type for queued communications] + qRegisterMetaType<Block>(); +//! [register meta-type for queued communications] + qsrand(QTime::currentTime().elapsed()); + + Window window; + window.show(); + + window.loadImage(createImage(256, 256)); +//! [main finish] + return app.exec(); +} +//! [main finish] //! [main function] diff --git a/examples/corelib/threads/queuedcustomtype/queuedcustomtype.pro b/examples/corelib/threads/queuedcustomtype/queuedcustomtype.pro new file mode 100644 index 0000000000..77421eb638 --- /dev/null +++ b/examples/corelib/threads/queuedcustomtype/queuedcustomtype.pro @@ -0,0 +1,14 @@ +HEADERS = block.h \ + renderthread.h \ + window.h +SOURCES = main.cpp \ + block.cpp \ + renderthread.cpp \ + window.cpp +QT += widgets + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/corelib/threads/mandelbrot +INSTALLS += target + + diff --git a/examples/corelib/threads/queuedcustomtype/renderthread.cpp b/examples/corelib/threads/queuedcustomtype/renderthread.cpp new file mode 100644 index 0000000000..50a638987f --- /dev/null +++ b/examples/corelib/threads/queuedcustomtype/renderthread.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderthread.h" + +RenderThread::RenderThread(QObject *parent) + : QThread(parent) +{ + m_abort = false; +} + +RenderThread::~RenderThread() +{ + mutex.lock(); + m_abort = true; + mutex.unlock(); + + wait(); +} + +//![processing the image (start)] +void RenderThread::processImage(const QImage &image) +{ + if (image.isNull()) + return; + + m_image = image; + m_abort = false; + start(); +} + +void RenderThread::run() +{ + int size = qMax(m_image.width()/20, m_image.height()/20); + for (int s = size; s > 0; --s) { + for (int c = 0; c < 400; ++c) { +//![processing the image (start)] + int x1 = qMax(0, (qrand() % m_image.width()) - s/2); + int x2 = qMin(x1 + s/2 + 1, m_image.width()); + int y1 = qMax(0, (qrand() % m_image.height()) - s/2); + int y2 = qMin(y1 + s/2 + 1, m_image.height()); + int n = 0; + int red = 0; + int green = 0; + int blue = 0; + for (int i = y1; i < y2; ++i) { + for (int j = x1; j < x2; ++j) { + QRgb pixel = m_image.pixel(j, i); + red += qRed(pixel); + green += qGreen(pixel); + blue += qBlue(pixel); + n += 1; + } + } +//![processing the image (finish)] + Block block(QRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), + QColor(red/n, green/n, blue/n)); + emit sendBlock(block); + if (m_abort) + return; + msleep(10); + } + } +} +//![processing the image (finish)] + +void RenderThread::stopProcess() +{ + mutex.lock(); + m_abort = true; + mutex.unlock(); +} diff --git a/examples/corelib/threads/queuedcustomtype/renderthread.h b/examples/corelib/threads/queuedcustomtype/renderthread.h new file mode 100644 index 0000000000..5630926d41 --- /dev/null +++ b/examples/corelib/threads/queuedcustomtype/renderthread.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RENDERTHREAD_H +#define RENDERTHREAD_H + +#include <QImage> +#include <QMutex> +#include <QThread> +#include "block.h" + +//! [RenderThread class definition] +class RenderThread : public QThread +{ + Q_OBJECT + +public: + RenderThread(QObject *parent = 0); + ~RenderThread(); + + void processImage(const QImage &image); + +signals: + void sendBlock(const Block &block); + +public slots: + void stopProcess(); + +protected: + void run(); + +private: + bool m_abort; + QImage m_image; + QMutex mutex; +}; +//! [RenderThread class definition] + +#endif diff --git a/examples/corelib/threads/queuedcustomtype/window.cpp b/examples/corelib/threads/queuedcustomtype/window.cpp new file mode 100644 index 0000000000..8afb8b6782 --- /dev/null +++ b/examples/corelib/threads/queuedcustomtype/window.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtWidgets> +#include "window.h" + +//! [Window constructor start] +Window::Window() +{ + thread = new RenderThread(); +//! [Window constructor start] //! [set up widgets and connections] + + label = new QLabel(); + label->setAlignment(Qt::AlignCenter); + + loadButton = new QPushButton(tr("&Load image...")); + resetButton = new QPushButton(tr("&Stop")); + resetButton->setEnabled(false); + + connect(loadButton, SIGNAL(clicked()), this, SLOT(loadImage())); + connect(resetButton, SIGNAL(clicked()), thread, SLOT(stopProcess())); + connect(thread, SIGNAL(finished()), this, SLOT(resetUi())); +//! [set up widgets and connections] //! [connecting signal with custom type] + connect(thread, SIGNAL(sendBlock(Block)), this, SLOT(addBlock(Block))); +//! [connecting signal with custom type] + + QHBoxLayout *buttonLayout = new QHBoxLayout(); + buttonLayout->addStretch(); + buttonLayout->addWidget(loadButton); + buttonLayout->addWidget(resetButton); + buttonLayout->addStretch(); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(label); + layout->addLayout(buttonLayout); + +//! [Window constructor finish] + setWindowTitle(tr("Queued Custom Type")); +} +//! [Window constructor finish] + +void Window::loadImage() +{ + QStringList formats; + foreach (QByteArray format, QImageReader::supportedImageFormats()) + if (format.toLower() == format) + formats.append("*." + format); + + QString newPath = QFileDialog::getOpenFileName(this, tr("Open Image"), + path, tr("Image files (%1)").arg(formats.join(' '))); + + if (newPath.isEmpty()) + return; + + QImage image(newPath); + if (!image.isNull()) { + loadImage(image); + path = newPath; + } +} + +void Window::loadImage(const QImage &image) +{ + QDesktopWidget desktop; + QImage useImage; + QRect space = desktop.availableGeometry(); + if (image.width() > 0.75*space.width() || image.height() > 0.75*space.height()) + useImage = image.scaled(0.75*space.width(), 0.75*space.height(), + Qt::KeepAspectRatio, Qt::SmoothTransformation); + else + useImage = image; + + pixmap = QPixmap(useImage.width(), useImage.height()); + pixmap.fill(qRgb(255, 255, 255)); + label->setPixmap(pixmap); + loadButton->setEnabled(false); + resetButton->setEnabled(true); + thread->processImage(useImage); +} + +//! [Adding blocks to the display] +void Window::addBlock(const Block &block) +{ + QColor color = block.color(); + color.setAlpha(64); + + QPainter painter; + painter.begin(&pixmap); + painter.fillRect(block.rect(), color); + painter.end(); + label->setPixmap(pixmap); +} +//! [Adding blocks to the display] + +void Window::resetUi() +{ + loadButton->setEnabled(true); + resetButton->setEnabled(false); +} diff --git a/examples/corelib/threads/queuedcustomtype/window.h b/examples/corelib/threads/queuedcustomtype/window.h new file mode 100644 index 0000000000..cb648f1be2 --- /dev/null +++ b/examples/corelib/threads/queuedcustomtype/window.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WINDOW_H +#define WINDOW_H + +#include <QWidget> +#include "renderthread.h" + +class QLabel; +class QPushButton; + +//! [Window class definition] +class Window : public QWidget +{ + Q_OBJECT + +public: + Window(); + void loadImage(const QImage &image); + +public slots: + void addBlock(const Block &block); + +private slots: + void loadImage(); + void resetUi(); + +private: + QLabel *label; + QPixmap pixmap; + QPushButton *loadButton; + QPushButton *resetButton; + QString path; + RenderThread *thread; +}; +//! [Window class definition] + +#endif diff --git a/examples/corelib/threads/semaphores/semaphores.cpp b/examples/corelib/threads/semaphores/semaphores.cpp new file mode 100644 index 0000000000..f519e5f323 --- /dev/null +++ b/examples/corelib/threads/semaphores/semaphores.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> + +#include <stdio.h> +#include <stdlib.h> + +//! [0] +const int DataSize = 100000; + +const int BufferSize = 8192; +char buffer[BufferSize]; + +QSemaphore freeBytes(BufferSize); +QSemaphore usedBytes; +//! [0] + +//! [1] +class Producer : public QThread +//! [1] //! [2] +{ +public: + void run() Q_DECL_OVERRIDE + { + qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); + for (int i = 0; i < DataSize; ++i) { + freeBytes.acquire(); + buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4]; + usedBytes.release(); + } + } +}; +//! [2] + +//! [3] +class Consumer : public QThread +//! [3] //! [4] +{ + Q_OBJECT +public: + void run() Q_DECL_OVERRIDE + { + for (int i = 0; i < DataSize; ++i) { + usedBytes.acquire(); + fprintf(stderr, "%c", buffer[i % BufferSize]); + freeBytes.release(); + } + fprintf(stderr, "\n"); + } + +signals: + void stringConsumed(const QString &text); + +protected: + bool finish; +}; +//! [4] + +//! [5] +int main(int argc, char *argv[]) +//! [5] //! [6] +{ + QCoreApplication app(argc, argv); + Producer producer; + Consumer consumer; + producer.start(); + consumer.start(); + producer.wait(); + consumer.wait(); + return 0; +} +//! [6] + +#include "semaphores.moc" diff --git a/examples/corelib/threads/semaphores/semaphores.pro b/examples/corelib/threads/semaphores/semaphores.pro new file mode 100644 index 0000000000..69154e57eb --- /dev/null +++ b/examples/corelib/threads/semaphores/semaphores.pro @@ -0,0 +1,9 @@ +SOURCES += semaphores.cpp +QT = core + +CONFIG -= app_bundle +CONFIG += console + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/corelib/threads/semaphores +INSTALLS += target diff --git a/examples/corelib/threads/threads.pro b/examples/corelib/threads/threads.pro new file mode 100644 index 0000000000..e47da84a06 --- /dev/null +++ b/examples/corelib/threads/threads.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs +CONFIG += no_docs_target + +SUBDIRS = semaphores \ + waitconditions + +qtHaveModule(widgets): SUBDIRS += mandelbrot diff --git a/examples/corelib/threads/waitconditions/waitconditions.cpp b/examples/corelib/threads/waitconditions/waitconditions.cpp new file mode 100644 index 0000000000..b0336f4c2b --- /dev/null +++ b/examples/corelib/threads/waitconditions/waitconditions.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> + +#include <stdio.h> +#include <stdlib.h> + +//! [0] +const int DataSize = 100000; + +const int BufferSize = 8192; +char buffer[BufferSize]; + +QWaitCondition bufferNotEmpty; +QWaitCondition bufferNotFull; +QMutex mutex; +int numUsedBytes = 0; +//! [0] + +//! [1] +class Producer : public QThread +//! [1] //! [2] +{ +public: + Producer(QObject *parent = NULL) : QThread(parent) + { + } + + void run() Q_DECL_OVERRIDE + { + qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); + + for (int i = 0; i < DataSize; ++i) { + mutex.lock(); + if (numUsedBytes == BufferSize) + bufferNotFull.wait(&mutex); + mutex.unlock(); + + buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4]; + + mutex.lock(); + ++numUsedBytes; + bufferNotEmpty.wakeAll(); + mutex.unlock(); + } + } +}; +//! [2] + +//! [3] +class Consumer : public QThread +//! [3] //! [4] +{ + Q_OBJECT +public: + Consumer(QObject *parent = NULL) : QThread(parent) + { + } + + void run() Q_DECL_OVERRIDE + { + for (int i = 0; i < DataSize; ++i) { + mutex.lock(); + if (numUsedBytes == 0) + bufferNotEmpty.wait(&mutex); + mutex.unlock(); + + fprintf(stderr, "%c", buffer[i % BufferSize]); + + mutex.lock(); + --numUsedBytes; + bufferNotFull.wakeAll(); + mutex.unlock(); + } + fprintf(stderr, "\n"); + } + +signals: + void stringConsumed(const QString &text); +}; +//! [4] + + +//! [5] +int main(int argc, char *argv[]) +//! [5] //! [6] +{ + QCoreApplication app(argc, argv); + Producer producer; + Consumer consumer; + producer.start(); + consumer.start(); + producer.wait(); + consumer.wait(); + return 0; +} +//! [6] + +#include "waitconditions.moc" diff --git a/examples/corelib/threads/waitconditions/waitconditions.pro b/examples/corelib/threads/waitconditions/waitconditions.pro new file mode 100644 index 0000000000..2dbe7df68a --- /dev/null +++ b/examples/corelib/threads/waitconditions/waitconditions.pro @@ -0,0 +1,9 @@ +QT = core +CONFIG -= moc app_bundle +CONFIG += console + +SOURCES += waitconditions.cpp + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/corelib/threads/waitconditions +INSTALLS += target |