0

I'm reading this answer about creating a custom QQuickPaintedItem in C++ How to paint sequential image in efficient way in QQuickPaintedItem

and I want to write code that is not Qt-dependent. That is, I don't want to rely on QThread. I just want to call CameraView::updateImage(const QImage& image) from a std::thread, not q QThread. Is it possible?

I thougth of simply creating a thread, passing the CameraView instance to it, and calling it from the thread. However, this is not thread safe.

How can I call CameraView::updateImage(const QImage& image) in a thread safe manner?

Guerlando OCs
  • 1,886
  • 9
  • 61
  • 150

1 Answers1

2

You can, of course, use the C++ Standard Library classes in your code. For example, use std::mutex to update image as follows:

void CameraView::updateImage(const QImage& image) {
    std::unique_lock<std::mutex> lock(mutex); // std::mutex object is located elsewhere
    // ... the rest of the code as before ...
}

You can also leave CameraView code unchanged and call the updateImage function from external code. Just make sure that the image is updated using the same mutex in all threads:

void functionInAnotherThread(CameraView& cameraView) {
    std::unique_lock<std::mutex> lock(mutex); // std::mutex object is located elsewhere

    // ... the rest of the code ...

    cameraView.updateImage(image);

    // ... the rest of the code ...
}

Let's try to implement this approach. Let's change the header file a bit:

class CameraView : public QQuickPaintedItem {
    Q_OBJECT
    Q_DISABLE_COPY(CameraView)

public:
    CameraView(QQuickItem* parent = nullptr);

public slots:
    void updateImage(const QImage&);
    void scheduleUpdate();

protected:
    QImage image_;
};

Now, write the definitions of the methods:

void CameraView::updateImage(const QImage& image) {
    image_ = image.copy(); // Does deep copy of image data.
}

void CameraView::scheduleUpdate() {
    update();
}

void CameraView::paint(QPainter* painter) {
    painter->drawImage(this->boundingRect(), image_);
}

Finally, we write the function of updating the picture and scheduling redrawing:

void functionInAnotherThread(CameraView& cameraView) {
    std::unique_lock<std::mutex> lock(mutex); // std::mutex object is located elsewhere

    cameraView.updateImage(image);

    lock.unlock(); // Unlock the mutex as we have already updated the image.

    QMetaObject::invokeMethod(&cameraView, "scheduleUpdate",
        Qt::QueuedConnection); // Call slot through the queued connection.
}

With Qt::QueuedConnection, the slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread. Thus, we can schedule the widget to be redrawn from another thread. Try using other types of connections if this does not work.

Grandbrain
  • 409
  • 3
  • 11
  • `cameraView.updateImage(image)` can't be called from another thread because updateImage calls `update` on the qt object. However if I take `update` off from the call and call `update` from the main thread, it works. However, it isn't clear how to do it. – Guerlando OCs Feb 19 '20 at 02:28
  • Now I have added a code example demonstrating the redrawing of a widget from another thread. Try to implement it yourself. – Grandbrain Feb 19 '20 at 05:21