summaryrefslogtreecommitdiffstats
path: root/examples/qtconcurrent/primecounter
diff options
context:
space:
mode:
Diffstat (limited to 'examples/qtconcurrent/primecounter')
-rw-r--r--examples/qtconcurrent/primecounter/CMakeLists.txt41
-rw-r--r--examples/qtconcurrent/primecounter/doc/images/primecounter.pngbin0 -> 9732 bytes
-rw-r--r--examples/qtconcurrent/primecounter/doc/src/qtconcurrent-primecounter.qdoc89
-rw-r--r--examples/qtconcurrent/primecounter/main.cpp18
-rw-r--r--examples/qtconcurrent/primecounter/primecounter.cpp139
-rw-r--r--examples/qtconcurrent/primecounter/primecounter.h49
-rw-r--r--examples/qtconcurrent/primecounter/primecounter.pro9
-rw-r--r--examples/qtconcurrent/primecounter/primecounter.ui177
8 files changed, 522 insertions, 0 deletions
diff --git a/examples/qtconcurrent/primecounter/CMakeLists.txt b/examples/qtconcurrent/primecounter/CMakeLists.txt
new file mode 100644
index 0000000000..c8ac5f20e8
--- /dev/null
+++ b/examples/qtconcurrent/primecounter/CMakeLists.txt
@@ -0,0 +1,41 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(primecounter LANGUAGES CXX)
+
+find_package(Qt6 REQUIRED COMPONENTS Concurrent Core Gui Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(primecounter
+ main.cpp
+ primecounter.ui
+ primecounter.cpp
+ primecounter.h
+)
+
+set_target_properties(primecounter PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(primecounter PRIVATE
+ Qt6::Concurrent
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Widgets
+)
+
+install(TARGETS primecounter
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_app_script(
+ TARGET primecounter
+ OUTPUT_SCRIPT deploy_script
+ NO_UNSUPPORTED_PLATFORM_ERROR
+)
+install(SCRIPT ${deploy_script})
diff --git a/examples/qtconcurrent/primecounter/doc/images/primecounter.png b/examples/qtconcurrent/primecounter/doc/images/primecounter.png
new file mode 100644
index 0000000000..d611a507b5
--- /dev/null
+++ b/examples/qtconcurrent/primecounter/doc/images/primecounter.png
Binary files differ
diff --git a/examples/qtconcurrent/primecounter/doc/src/qtconcurrent-primecounter.qdoc b/examples/qtconcurrent/primecounter/doc/src/qtconcurrent-primecounter.qdoc
new file mode 100644
index 0000000000..de6c94b0cf
--- /dev/null
+++ b/examples/qtconcurrent/primecounter/doc/src/qtconcurrent-primecounter.qdoc
@@ -0,0 +1,89 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example primecounter
+ \meta tags {widgets, threads}
+ \title Prime Counter
+ \ingroup qtconcurrentexamples
+ \examplecategory {Data Processing & I/O}
+ \brief Demonstrates how to monitor the progress of concurrent operations.
+
+ The following example demonstrates how to create an interactive and
+ non-blocking QtWidgets application using the QFutureWatcher class and the
+ \l {Concurrent Filter-Reduce} {filteredReduced} functions from
+ \l {Qt Concurrent}. With this example, the user can create a QList of
+ integers that can be resized. The list will be automatically filled with
+ natural numbers starting from 1 up to n. The program will then check for
+ prime numbers within the list and display the total count of prime numbers
+ found.
+
+ \image primecounter.png
+
+ \include examples-run.qdocinc
+
+ \section1 Setting up the connections
+
+ The \l {Qt Concurrent} library provides the
+ \l {Concurrent Filter-Reduce} {filteredReduced} functions, which can operate
+ in two modes:
+ \l {QtConcurrent::ReduceOption} {OrderedReduce and UnorderedReduce}. In
+ \c OrderedReduce mode, the reducing function is called in the order of the
+ original sequence, whereas in \c UnorderedReduce mode, the elements are
+ accessed randomly.
+
+ After configuring the UI with the desired elements, it is necessary to
+ connect them to the signals of the concurrent operations using the Qt
+ \l {Signals & Slots} mechanism. In this example, we use the QFutureWatcher
+ class to monitor the progress of the concurrent operations and provide the
+ signals required to implement the interactive GUI.
+
+ \dots
+ \snippet primecounter/primecounter.cpp 1
+ \dots
+
+ The QFutureWatcher class plays a vital role in this example as it provides
+ the signals required to update the UI in response to changes in the
+ concurrent operations.
+
+ \section1 Starting the concurrent operation
+
+ After connecting all the \l {Signals & Slots}, and when the user presses
+ the QPushButton, the \c {start()} function is called.
+
+ In the \c {start()} function, we call the
+ \l {Concurrent Filter-Reduce} {filteredReduced} function from Qt Concurrent
+ and set the future on the QFutureWatcher member. To ensure that this
+ operation runs truly concurrently, we specify a separate QThreadPool as the
+ first parameter. This approach also avoids any possible blocking in the
+ global thread pool. We pass the QList of integers as the container, a
+ static filter and reduce function, and finally the
+ \l {QtConcurrent::} {ReduceOption} flag.
+
+ \dots
+ \snippet primecounter/primecounter.cpp 2
+ \dots
+
+ Let's examine the filter and reduce functions. These functions are declared
+ static in this example since they do not depend on any member variable.
+ However, they could easily be specified as lambdas or member functions.
+
+ The filter function marks elements for subsequent reduction with the reduce
+ function. This implementation is a simple prime filter. As this function
+ takes a const reference as an argument, it allows thread-safe operation on
+ the container it operates on.
+
+ \dots
+ \snippet primecounter/primecounter.cpp 3
+ \dots
+
+ The reduce function takes a modifiable reference of the same type as the
+ container it operates on as its first parameter. The second parameter is the
+ previously filtered element from the filter function. In this example, we
+ count the number of primes.
+
+ \dots
+ \snippet primecounter/primecounter.cpp 4
+ \dots
+
+*/
diff --git a/examples/qtconcurrent/primecounter/main.cpp b/examples/qtconcurrent/primecounter/main.cpp
new file mode 100644
index 0000000000..acfd581341
--- /dev/null
+++ b/examples/qtconcurrent/primecounter/main.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets/qapplication.h>
+#include "primecounter.h"
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ app.setOrganizationName("QtProject");
+ app.setApplicationName(QApplication::translate("main", "Prime Counter"));
+
+ PrimeCounter dialog;
+ dialog.setWindowTitle(QApplication::translate("main", "Prime Counter"));
+ dialog.show();
+
+ return app.exec();
+}
diff --git a/examples/qtconcurrent/primecounter/primecounter.cpp b/examples/qtconcurrent/primecounter/primecounter.cpp
new file mode 100644
index 0000000000..01bde5d87f
--- /dev/null
+++ b/examples/qtconcurrent/primecounter/primecounter.cpp
@@ -0,0 +1,139 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "primecounter.h"
+#include "ui_primecounter.h"
+
+PrimeCounter::PrimeCounter(QWidget *parent)
+ : QDialog(parent), stepSize(100000), ui(setupUi())
+{
+ // Control the concurrent operation with the QFutureWatcher
+ //! [1]
+ connect(ui->pushButton, &QPushButton::clicked,
+ this, [this] { start(); });
+ connect(&watcher, &QFutureWatcher<Element>::finished,
+ this, [this] { finish(); });
+ connect(&watcher, &QFutureWatcher<Element>::progressRangeChanged,
+ ui->progressBar, &QProgressBar::setRange);
+ connect(&watcher, &QFutureWatcher<Element>::progressValueChanged,
+ ui->progressBar, &QProgressBar::setValue);
+ //! [1]
+}
+
+PrimeCounter::~PrimeCounter()
+{
+ watcher.cancel();
+ delete ui;
+}
+
+//! [3]
+bool PrimeCounter::filterFunction(const Element &element)
+{
+ // Filter for primes
+ if (element <= 1)
+ return false;
+ for (Element i = 2; i*i <= element; ++i) {
+ if (element % i == 0)
+ return false;
+ }
+ return true;
+}
+//! [3]
+
+//! [4]
+void PrimeCounter::reduceFunction(Element &out, const Element &value)
+{
+ // Count the amount of primes.
+ Q_UNUSED(value);
+ ++out;
+}
+//! [4]
+
+//! [2]
+void PrimeCounter::start()
+{
+ if (ui->pushButton->isChecked()) {
+ ui->comboBox->setEnabled(false);
+ ui->pushButton->setText(tr("Cancel"));
+ ui->labelResult->setText(tr("Calculating ..."));
+ ui->labelFilter->setText(tr("Selected Reduce Option: %1").arg(ui->comboBox->currentText()));
+ fillElementList(ui->horizontalSlider->value() * stepSize);
+
+ timer.start();
+ watcher.setFuture(
+ QtConcurrent::filteredReduced(
+ &pool,
+ elementList,
+ filterFunction,
+ reduceFunction,
+ currentReduceOpt | QtConcurrent::SequentialReduce));
+//! [2]
+ } else {
+ watcher.cancel();
+ ui->progressBar->setValue(0);
+ ui->comboBox->setEnabled(true);
+ ui->labelResult->setText(tr(""));
+ ui->pushButton->setText(tr("Start"));
+ ui->labelFilter->setText(tr("Operation Canceled"));
+ }
+}
+
+void PrimeCounter::finish()
+{
+ // The finished signal from the QFutureWatcher is also emitted when cancelling.
+ if (watcher.isCanceled())
+ return;
+
+ auto elapsedTime = timer.elapsed();
+ ui->progressBar->setValue(0);
+ ui->comboBox->setEnabled(true);
+ ui->pushButton->setChecked(false);
+ ui->pushButton->setText(tr("Start"));
+ ui->labelFilter->setText(
+ tr("Filter '%1' took %2 ms to calculate").arg(ui->comboBox->currentText())
+ .arg(elapsedTime));
+ ui->labelResult->setText(
+ tr("Found %1 primes in the range of elements").arg(watcher.result()));
+}
+
+void PrimeCounter::fillElementList(unsigned int count)
+{
+ // Fill elementList with values from [1, count] when starting the calculations.
+ auto prevSize = elementList.size();
+ if (prevSize == count)
+ return; // Nothing to do here.
+
+ auto startVal = elementList.empty() ? 1 : elementList.back() + 1;
+ elementList.resize(count);
+ if (elementList.begin() + prevSize < elementList.end())
+ std::iota(elementList.begin() + prevSize, elementList.end(), startVal);
+}
+
+Ui::PrimeCounter* PrimeCounter::setupUi()
+{
+ Ui::PrimeCounter *setupUI = new Ui::PrimeCounter;
+ setupUI->setupUi(this);
+ setModal(true);
+
+ // Set up the slider
+ connect(setupUI->horizontalSlider, &QSlider::valueChanged,
+ this, [setupUI, this] (const int &pos) {
+ setupUI->labelResult->setText("");
+ setupUI->labelSize->setText(tr("Elements in list: %1").arg(pos * stepSize));
+ });
+ setupUI->horizontalSlider->setValue(30);
+
+ // Set up the combo box
+ setupUI->comboBox->insertItem(0, tr("Unordered Reduce"), QtConcurrent::UnorderedReduce);
+ setupUI->comboBox->insertItem(1, tr("Ordered Reduce"), QtConcurrent::OrderedReduce);
+
+ auto comboBoxChange = [this, setupUI](int pos) {
+ currentReduceOpt = setupUI->comboBox->itemData(pos).value<QtConcurrent::ReduceOptions>();
+ setupUI->labelFilter->setText(tr("Selected Reduce Option: %1")
+ .arg(setupUI->comboBox->currentText()));
+ };
+ comboBoxChange(setupUI->comboBox->currentIndex());
+ connect(setupUI->comboBox, &QComboBox::currentIndexChanged, this, comboBoxChange);
+
+ return setupUI;
+}
diff --git a/examples/qtconcurrent/primecounter/primecounter.h b/examples/qtconcurrent/primecounter/primecounter.h
new file mode 100644
index 0000000000..5b6d442cc8
--- /dev/null
+++ b/examples/qtconcurrent/primecounter/primecounter.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PRIMECOUNTER_H
+#define PRIMECOUNTER_H
+
+#include <QtWidgets/qdialog.h>
+#include <QtCore/qfuturewatcher.h>
+#include <QtConcurrent/qtconcurrentfilter.h>
+#include <QtConcurrent/qtconcurrentreducekernel.h>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QProgressBar;
+namespace Ui {
+class PrimeCounter;
+}
+
+QT_END_NAMESPACE
+
+class PrimeCounter : public QDialog
+{
+ Q_OBJECT
+ using Element = unsigned long long;
+public:
+ explicit PrimeCounter(QWidget* parent = nullptr);
+ ~PrimeCounter() override;
+
+private:
+ static bool filterFunction(const Element &element);
+ static void reduceFunction(Element &out, const Element &value);
+ void fillElementList(unsigned int count);
+ Ui::PrimeCounter* setupUi();
+
+private slots:
+ void start();
+ void finish();
+
+private:
+ QList<Element> elementList;
+ QFutureWatcher<Element> watcher;
+ QtConcurrent::ReduceOptions currentReduceOpt;
+ QElapsedTimer timer;
+ QThreadPool pool;
+ unsigned int stepSize;
+ Ui::PrimeCounter *ui;
+};
+
+#endif //PRIMECOUNTER_H
diff --git a/examples/qtconcurrent/primecounter/primecounter.pro b/examples/qtconcurrent/primecounter/primecounter.pro
new file mode 100644
index 0000000000..bb5a740688
--- /dev/null
+++ b/examples/qtconcurrent/primecounter/primecounter.pro
@@ -0,0 +1,9 @@
+QT += concurrent widgets
+
+SOURCES += main.cpp primecounter.cpp
+HEADERS += primecounter.h
+
+target.path = $$[QT_INSTALL_EXAMPLES]/qtconcurrent/primecounter
+INSTALLS += target
+
+FORMS += primecounter.ui
diff --git a/examples/qtconcurrent/primecounter/primecounter.ui b/examples/qtconcurrent/primecounter/primecounter.ui
new file mode 100644
index 0000000000..625462e05e
--- /dev/null
+++ b/examples/qtconcurrent/primecounter/primecounter.ui
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PrimeCounter</class>
+ <widget class="QDialog" name="PrimeCounter">
+ <property name="windowModality">
+ <enum>Qt::WindowModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>454</width>
+ <height>320</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="leftMargin">
+ <number>20</number>
+ </property>
+ <property name="topMargin">
+ <number>20</number>
+ </property>
+ <property name="rightMargin">
+ <number>20</number>
+ </property>
+ <property name="bottomMargin">
+ <number>20</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="labelInfo">
+ <property name="font">
+ <font>
+ <pointsize>11</pointsize>
+ <bold>false</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Select a reducing option and measure the speed</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer1">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout2">
+ <property name="topMargin">
+ <number>10</number>
+ </property>
+ <property name="bottomMargin">
+ <number>10</number>
+ </property>
+ <item>
+ <widget class="QComboBox" name="comboBox"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout1">
+ <property name="topMargin">
+ <number>10</number>
+ </property>
+ <property name="bottomMargin">
+ <number>10</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="pushButton">
+ <property name="text">
+ <string>Start</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="progressBar">
+ <property name="value">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelFilter">
+ <property name="text">
+ <string>Filter Label</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout3">
+ <item>
+ <widget class="QSlider" name="horizontalSlider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelSize">
+ <property name="text">
+ <string>size</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ <property name="indent">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelResult">
+ <property name="text">
+ <string>Result Label</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>