Qt learning notes - Examples/Qt-6.2.0/corelib/threads/queuedcustomtype

//In the custom type example, we showed how to integrate custom types with the meta object system
//Enable them to be stored in the QVariant object, write debugging information, and use for signal slot communication
//In this example, we create a new value class Block
//And register it with the meta object system
//Enables us to send its instances between threads using queued signals and slots.

//The Block Class
//The Block class is similar to the Message class described in the custom type example
//It provides a default constructor in the public part of the classes required by the meta object system
//Copy constructors and destructors. It describes a colored rectangle.

    class Block
    {
    public:
        Block();
        Block(const Block &other);
        Block(const QRect &rect,const QColor &color);

        QColor color() const;
        QRect rect() const;

    private:
        QRect m_rect;
        QColor m_color;
    };

    Q_DECLARE_METATYPE(Block);

//We still need to call qRegisterMetaType() at runtime
//The template function registers it in the meta object system
//Then we can make any connection using this type of signal slot.
//Although we are not going to use a type with QVariant in this example
//But it's best to use Q_DECLARE_METATYPE() declares a new type.
//The implementation of the Block class is trivial, so we avoid referencing it here.

//The Window Class
//We define a simple Window class with a common slot that accepts Block objects.
//The rest of this class is related to managing the user interface and working with images.

    class Window : public QWidget
    {
        Q_OBJECT
    public:
        Window(QWidget *parent = nullptr);
        void loadImage(const QImage &image);
    public slots:
        void addBlock(const Block &block);
    public slots:
        void loadImage();
        void resetUi();
    private:
        QLabel *label;
        QPixmap pixmap;
        QPushButton *loadButton;
        QPushButton *resetButton;
        QString path;
        RenderThread *thread;
    };

//The Window class also contains a worker thread
//Provided by the RenderThread object
//This signals the Block object to the addBlock(Block) slot of the window.
//The Window class also contains a worker thread provided by the RenderThread object.
//This signals the Block object to the addBlock(Block) slot of the window.
//The most relevant parts of the Window class are the constructor and the addBlock(Block) slot.
//Constructor creates a thread for rendering an image
//Set up a user interface with a label and two buttons connected to slots in the same class.

    Window::Window(QWidget *parent)
        :QWidget(parent),thread(new RenderThread(this))
    {
        label = new QLabel(this);
        label->setAlignment(Qt::AlignCenter);

        loadButton = new QPushButton(tr("&Load image ..."),this);
        resetButton = new QPushButton(tr("&Stop"),this);
        resetButton->setEnable(false);

        connect(loadButton,&QPushButton::clicked,
                this,QOverload<>::of(&Window::loadImage));
        connect(resetButton,&QPushButton::clicked,
                thread,&RenderThread::stopProcess);
        connect(thread,&RenderThread::finished,
                this,&Window::resetUi);
        connect(thread,&RenderThread::sendBlock,
                this,&Window::addBlock);
    }

//In the last connection
//We connect the signal in the RenderThread object to the addBlock(Block) slot in the window

//The rest of the constructor just sets the layout of the window.
//adBlock(Block) slot receiver receives blocks from the rendering thread through the signal slot connection set in the constructor:

    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);
    }

//We just put them on the label when they arrive.

//RenderThread class
//The RenderThread class processes images
//Create Block objects and send them to other components in the example using sendBlock(Block) signals.

    class RenderThread : public QThread 
    {
        Q_OBJECT
    public:
        RenderThread(QObject *parent = nullptr);
        ~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;
    };

//There are no references to constructors and destructors.
//They are responsible for setting the internal state of a thread and cleaning it up when it is destroyed.
//Processing starts with the processImage() function
//It calls the RenderThread class to re implement the QThread::run() function:

    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)
            {
                //Ignore details of image processing methods
                //We see that signals containing blocks are transmitted in the usual way
                //Ignore details of image processing methods
                //We see that signals containing blocks are transmitted in the usual way:
//....
                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);
        }
    }

//Each signal emitted will be queued and later transmitted to the windows adBlock(Block) slot.
//Registering the Type
//In the main() function of the example
//We register the Block class as a custom type of the meta object system by calling the qRegisterMetaType() template function:

    int main(int argc,char *argv[])
    {
        QApplication app(argc,argv);
        qRegisterMetaType<Block>();

        Window window;
        window.show();

        window.loadImage(createImage(256, 256));
        return app.exe();
    }

//This call is placed here to ensure that the type is registered before any signal slot connection using it.
//The rest of the main() function is about seeding the pseudo-random number generator
//Create and display windows and set default images
//See the implementation source code of the createImage() function.
//Further Reading
//This example shows how to register a custom type in the meta object system
//So that it can be used with signals and slot connections between threads
//For general communications involving direct signals and slots
//It is sufficient to declare types as described in the custom type example.
//In fact, Q_DECLARE_METATYPE() macro and qRegisterMetaType()
//Template functions can be used to register custom types
//However, qRegisterMetaType() only needs to communicate with the signal slot or create and destroy self
//You only need to type the type at run time when defining an object.
//For more information about using custom types in Qt, see the documentation on creating custom Qt types

main.cpp

#include <QApplication>
#include <QPainter>
#include <QTime>
#include "block.h"
#include "window.h"

//Qt provides four classes for processing image data: QImage, QPixmap, QBitmap and QPicture.
//QImage is designed and optimized for I/O and direct pixel access and operation
//QPixmap is designed and optimized to display images on the screen.
//QBitmap is just a convenience class that inherits QPixmap and ensures a depth of 1.
//Finally, the QPicture class is a drawing device that records and replays QPainter commands.
//Because QImage is a subclass of QPaintDevice,
//So you can use QPainter to draw directly on the image.
//When using QPainter on QImage, you can perform drawing in a thread other than the current GUI thread.
//The QImage class supports several image formats described by the Format enumeration.
//These include monochrome, 8-bit, 32-bit, and alpha blend images,
//These images are available in all versions of Qt 4.x.
//QImage provides a set of functions that can be used to obtain various information about images. There are also several functions to convert images.
//QImage objects can be passed by value because the QImage class uses implicit data sharing.
//QImage objects can also be streamed and compared.
//Note: if you want to load QImage objects in the static build of Qt, see the plug-in HowTo.
//Warning: QImage:: format is not supported_ Indexed8 format painting on QImage.
//Reading and writing image files
//QImage provides several methods to load image files:
//You can load the file when the QImage object is constructed, or you can load the file later using the load() or loadFromData() functions.
//QImage also provides a static fromData() function
//Construct a QImage from the given data.
//When loading an image, the file name can refer to the actual file on disk
//It can also refer to one of the embedded resources of the application
//More about how to embed images and other resource files in your application's executable
//Just call the save() function to save the QImage object.
//A complete list of supported file formats is available through QImageReader::supportedImageFormats()
//And QImageWriter::supportedImageFormats() functions.
//New file formats can be added as plug-ins.
//By default, Qt supports the following formats:

QImage createImage(int width, int height)
{
    QImage image(width, height, QImage::Format_RGB16);
    //QPainter provides highly optimized functions to complete the work required by most drawing GUI programs.
    //It can draw everything from simple lines to complex shapes such as pies and chords.
    //It can also draw aligned text and pixel maps.
    //Typically, it is drawn in a "natural" coordinate system, but it can also perform view and world transformations.
    //QPainter can operate on any object that inherits the QPaintDevice class.
    //The common use of QPainter is in widget drawing events:
    //Construct and customize painters, such as setting up pens or brushes.
    //Then draw. Remember to destroy the QPainter object after drawing. For example:
    ```cpp
        void SimpleExampleWidget::paintEvent(QPaintEvent *)
        {
            QPainter painter(this);
            painter.setPen(Qt::blue);
            painter.setFont(QFont("Arial",30));
            painter.drawText(rect(), Qt::AlignCenter,"Qt");
        }
    ```
    //The core function of QPainter is drawing
    //However, this class also provides several functions
    //Allows you to customize the settings of QPainter and its rendering quality
    //And other clip enabled features.
    //In addition, you can control how different shapes are merged by specifying the painter's synthesis mode.
    //The core function of QPainter is drawing
    //However, this class also provides several functions
    //Allows you to customize the settings of QPainter and its rendering quality
    //And other clip - enabled features
    //In addition, you can control how different shapes are combined by specifying the painter's composition mode.
    //The isActive() function indicates whether the painter is active
    //The painter is activated by the begin() function and the constructor that accepts the QPaintDevice parameter
    //The end() function and the destructor deactivate it.
    //Together with QPaintDevice and QPaintEngine classes
    //QPainter forms the basis of Qt painting system.
    //QPainter is a class used to perform drawing operations.
    //QPaintDevice represents a device that can be drawn using QPainter.
    //QPaintEngine provides interfaces for painters to draw different types of devices.
    //If the painter is active, device() returns the painting device that the painter paints
    //paintEngine() returns the painting engine that the artist is currently working on. For more information, see drawing system.
    //Sometimes I want others to paint on an unusual QPaintDevice
    //QPainter supports a static function to do this, setRedirected().

    QPainter painter;
    QPen pen;
    pen.setStyle(Qt::NoPen);
    QBrush brush(Qt::blue);

    //Start drawing the drawing device, and return true if successful; Otherwise, false is returned.
    //Note that when begin() is called, all painter settings (setPen(), setBrush(), and so on) are reset to the default values.
    //Possible errors are serious problems, such as:
    ```cpp
        painter->begin(0); //impossible - paint device cannot be 0
        
        QPixmap image(0,0);
        painter->begin(&image); //impossible - image.isNull() == true

        painter->begin(myWidget);
        painter2->begin(myWidget); //impossible - only one painter at a time        
    ```
    painter.begin(&image);
    //void QPainter::fillRect(const QRectF &rectangle, const QBrush &brush)
    //Fills the given rectangle with the specified brush.
    //Alternatively, you can specify QColor instead of QBrush;
    //The QBrush constructor (with the QColor parameter) will automatically create a pure pattern brush.
    painter.fillRect(image.rect(), brush);
    brush.setColor(Qt::white);
    painter.setPen(pen);
    painter.setBrush(brush);

    //A point is specified by the x and y coordinates and can be accessed using the x() and y() functions.
    //For accuracy, use a finite floating point number to specify the coordinates of the point.
    //If x and y are both set to 0.0, the isNull() function returns true.
    //You can set (or change) the coordinates using the setX() and setY() functions
    //Or use the rx() and ry() functions that return coordinate references (allowing direct operations).
    //Given a point p, the following statements are equivalent:
    ```cpp
        QPointF p;
        p.setX(p.x() + 1.0);
        p += QPointF(1.0,0.0);
        p.rx()++;
    ```
    //QPointF objects can also be used as vectors:
    //Addition and subtraction are defined the same as vectors (each component is added separately).
    //QPointF objects can also be divided by or multiplied by int or qreal.
    //In addition, the QPointF class provides a way to convert a QPointF object to QPointF
    //Object Constructors 
    //And the corresponding toPoint() function, which returns a QPoint copy of the point.
    //Finally, QPointF objects can be streamed and compared.

    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)
    };
    //Painting equipment is an abstraction of two-dimensional space
    //You can draw with QPainter.
    //The origin of its default coordinate system is located in the upper left corner.
    //X increases to the right and Y increases downward. The unit is one pixel.
    //The drawing function of QPaintDevice is currently composed of QWidget, QImage
    //Implementation of subclasses QPixmap, QPicture and QPrinter.
    //To implement support for the new backend
    //You must derive from QPaintDevice and re implement the virtual paintEngine() function
    //To tell QPainter which drawing engine should be used to draw on this particular device.
    //Note that you must also create the appropriate drawing engine to draw on the device
    //That is, derive from QPaintEngine and re implement its virtual function.
    //Warning: Qt requires the existence of a QGuiApplication object before creating any drawing devices.
    //Draw device access window system resources
    //These resources are not initialized until the application object is created.
    //The QPaintDevice class provides several functions that return various device indicators
    //The depth() function returns its bit depth (the number of bit planes).
    //The height() function returns its height in default coordinate system units, such as the pixels of QPixmap and QWidget
    //Highmm () returns the height of the device in millimeters.
    //Similarly, the width() and widthMM() functions return the width of the device in default coordinate system units and millimeters, respectively.
    //Alternatively, the protected metric() function can be used to retrieve metric information by specifying the required PaintDeviceMetric as a parameter.
    //The logicalDpiX() and logicalDpiY() functions return the horizontal and vertical resolutions of the device in dots per inch.
    //The physicalDpiX() and physicalDpiY() functions also return the resolution of the device in dots per inch
    //Note, however, that if the logical and physical resolutions are different
    
    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]

    Window window;
    window.show();

    window.loadImage(createImage(1024, 520));
//! [main finish]
    return app.exec();
}
//! [main finish] //! [main function]

window.h

#ifndef WINDOW_H
#define WINDOW_H

#include <QWidget>
#include "renderthread.h"

QT_BEGIN_NAMESPACE
class QLabel;
class QPushButton;
QT_END_NAMESPACE

//! [Window class definition]
class Window : public QWidget
{
    Q_OBJECT

public:
    Window(QWidget *parent = nullptr);
    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
#include "window.h"
#include <QtWidgets>

//! [Window constructor start]
Window::Window(QWidget *parent)
    : QWidget(parent), thread(new RenderThread(this))
{
//! [Window constructor start] //! [set up widgets and connections]

    label = new QLabel(this);
    label->setAlignment(Qt::AlignCenter);

    loadButton = new QPushButton(tr("&Load image..."), this);
    resetButton = new QPushButton(tr("&Stop"), this);
    resetButton->setEnabled(false);

    connect(loadButton, &QPushButton::clicked,
            this, QOverload<>::of(&Window::loadImage));
    connect(resetButton, &QPushButton::clicked,
            thread, &RenderThread::stopProcess);
    connect(thread, &RenderThread::finished,
            this, &Window::resetUi);
//! [set up widgets and connections] //! [connecting signal with custom type]
    connect(thread, &RenderThread::sendBlock,
            this, &Window::addBlock);
//! [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;
    const QList<QByteArray> supportedFormats = QImageReader::supportedImageFormats();
    for (const QByteArray &format : supportedFormats)
        if (format.toLower() == format)
            formats.append(QLatin1String("*.") + QString::fromLatin1(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)
{
    QImage useImage;
    QRect space = QGuiApplication::primaryScreen()->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);
}

renderthread.h

#ifndef RENDERTHREAD_H
#define RENDERTHREAD_H

#include <QImage>
#include <QMutex>
#include <QThread>
#include "block.h"
//A control thread in the QThread object manager
//QThreads starts executing in run()
//By default, run() starts the event loop by calling exec() and runs the Qt event loop within the thread.
//You can use them by moving work objects to threads using QObject::moveToThread().
//Note: if QObject has no thread Association (that is, if thread() returns zero), or
//If it is in a thread that does not run an event loop, it cannot receive queued signals or published events.
//By default, QObject exists in the thread that created it.
//You can use thread() to query the thread Association of an object and use moveToThread() to make changes.
/*
    class Worker : public QObject
    {
        Q_OBJECT
    public slots:
        void doWork(const QString &parameter)
        {
            QString result;
            emit resultReady(result);
        }
    signals:
        void resultReady(const QString &result);
    };

    class Controller : public QObject
    {
        Q_OBJECT
        QThread workerThread;
    public:
        Controller()
        {
            Worker *worker = new Worker;
            worker->moveToThread(&workThread);
            connect(&workerThread,&QThread::finished,worker,&QObject::deleteLater);
            connect(this,&Controller::operate,worker,&Worker::doWork);
            connect(worker,&Worker::resultReady,this,&Controller::handleResults);
            workerThread.start();
        }
        ~Controller()
        {
            workerThread.quit();
            workerThread.wait();
        }
    public slots:
        void handleResults(const QString &);
    signals:
        void operate(const QString& );
    };
*/
//The code in the Worker slot will be executed in a separate thread.
//However, you are free to connect the Worker's slot to any object
//Any signal in any thread.
//Because of a mechanism called queued connection, it is safe to connect signals and slots across different threads.
//The code in the Worker slot will be executed in a separate thread
//However, you are free to connect the Worker's slot to any object
//Any signal in any thread.
//Because of a mechanism called queued connection, it is safe to connect signals and slots across different threads.
//Another way to make code run in a separate thread is to inherit QThread and re implement run(). for example
/*
    class WorkerThread : public QThread
    {
        Q_OBJECT
        void run() override
        {
            QString result;
            //here is the expensive or blocking operation
            emit resultReady(result);
        }
        signals:
            void resultReady(const QString &s);
    };

    void MyObject::startWorkInAThread()
    {
        WorkerThread *workerThread = new WorkerThread(this);
        connect(workerThread, &WorkerThread::resultReady,this,&MyObject::handleResults);
        connect(workerThread, &WorkerThread::finished,workerThread, &Object::deleteLater);
        workerThread->start();
    }
*/

//In this example, the thread will exit after the run function returns.
//No event loop will run in the thread unless you call exec().
//It is important to remember that the QThread instance exists in the old thread that instantiated it
//Instead of a new thread that calls run().
//This means that all QThread queuing slots and called methods will be executed in the old thread.
//Therefore, the developer who wants to call the slot in the new thread must use the working object method.
//The new slot should not be implemented directly in the subclass QThread.
//Unlike queued slots or calling methods,
//The method called directly on the QThread object will be executed in the thread calling the method.
//When inheriting QThread, remember that the constructor executes in the old thread and run() executes in the new thread.
//If a member variable is accessed from two functions, it is accessed from two different threads.
//Check whether it is safe to do so.
//Note: you must be careful when interacting with objects across different threads.
//As a general rule, unless otherwise stated in the document
//Otherwise, the function can only be called from the thread that created the QThread object itself (for example, setPriority()).
//For more information, see synchronizing threads.

//Management thread
//QThread signals you when the thread starts () and finishes ()
//Alternatively, you can use is Finished() and is Running() to query the status of threads.
//You can stop the thread by calling exit() or quit().
//In extreme cases, you might want to force the thread () executing to terminate.
//However, it is dangerous and frustrating to do so.
//Please read the documentation for terminate() and setTerminationEnabled() for details.
//Starting with Qt 4.8, you can connect the finished() signal to
//QObject::deleteLater() to release the object in the thread that just ended.
//Use wait() to block the calling thread until another thread completes execution (or until the specified time has elapsed)
//QThread also provides static and platform independent sleep functions: sleep()
//msleep() and usleep() allow full second, millisecond, and microsecond resolutions, respectively. These functions are exposed in Qt 5.0.
//Note: in general, the wait() and sleep() functions should not be necessary
//Because Qt is an event driven framework.
//Instead of wait(), consider listening for the finished() signal.
//Consider using QTimer instead of the sleep() function.

//The static functions currentThreadId() and currentThread() return the identifier of the thread currently executing
//The former returns the platform specific ID of the thread; The latter returns a QThread pointer.
//To select the name of your thread (for example, identified by the command ps -L on Linux)
//You can call setObjectName() before starting the thread.
//If you do not call setObjectName()
//The name specified for your thread will be the class name of the runtime type of your thread object
//(for example, RenderThread in the Mandelbrot example, because this is a subclass of QThread)
//Note that this does not currently apply to releases on Windows.
//To select the name of your thread (for example, identified by the command ps -L on Linux)
//You can call setObjectName() before starting the thread.
//If you do not call setObjectName(), the name specified for your thread will be the class name of the runtime type of your thread object


//! [RenderThread class definition]
class RenderThread : public QThread
{
    Q_OBJECT

public:
    RenderThread(QObject *parent = nullptr);
    ~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

renderthread.cpp

#include "renderthread.h"

#include <QRandomGenerator>

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, QRandomGenerator::global()->bounded(m_image.width()) - s/2);
            int x2 = qMin(x1 + s/2 + 1, m_image.width());
            int y1 = qMax(0, QRandomGenerator::global()->bounded(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();
}

block.h

#ifndef BLOCK_H
#define BLOCK_H

#include <QColor>
#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

block.cpp

#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;
}

Tags: Qt

Posted on Sat, 30 Oct 2021 13:50:42 -0400 by GB_001