以线程安全的方式调用"QQuickPaintedItem::updateImage(const QImage&image)"(no QThread)

Calling `QQuickPaintedItem::updateImage(const QImage& image)` in a thread safe way (no QThread)

本文关键字:QImage image 线程 QThread no const 安全 调用 QQuickPaintedItem updateImage 方式      更新时间:2023-10-16

我正在阅读这个关于在C++中创建自定义QQuickPaintedItem的答案 如何在QQuickPaintedItem 中以有效的方式绘制顺序图像

我想编写不依赖于Qt的代码。也就是说,我不想依赖QThread。我只想从std::thread打电话给CameraView::updateImage(const QImage& image),而不是 qQThread。可能吗?

我只是简单地创建一个线程,将CameraView实例传递给它,然后从线程调用它。但是,这不是线程安全的。

如何以线程安全的方式调用CameraView::updateImage(const QImage& image)

当然,您可以在代码中使用C++标准库类。例如,使用std::mutex更新映像,如下所示:

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 ...
}

还可以保持CameraView代码不变,并从外部代码调用updateImage函数。只需确保在所有线程中使用相同的互斥锁更新映像:

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 ...
}

让我们尝试实现此方法。让我们稍微更改一下头文件:

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

现在,编写方法的定义:

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

最后,我们编写更新图片和调度重绘的函数:

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.
}

使用Qt::QueuedConnection,当控制返回到接收线程的事件循环时,时隙被调用。该插槽在接收器的线程中执行。因此,我们可以安排从另一个线程重绘小部件。如果这不起作用,请尝试使用其他类型的连接。