从非GUI线程使用QWidget :: update()

时间:2011-06-21 05:50:54

标签: qt thread-safety libvlc

有时候我的应用程序在QWidget :: update()中崩溃,并且在非GUI线程中执行。

我正在开发一个应用程序,从远程主机接收视频帧并在QWidget上显示它们。

为此,我使用了libVLC库,它为我提供了一个解码图像。我在libVLC回调中接收图像,该图像在单独的libVLC线程中执行。在这个回调中,我正在尝试执行QWidget :: update()方法。有时应用程序崩溃,并且callstack在这个方法中的某个地方。这是我的回调代码:

//! Called when a video frame is ready to be displayed, according to the vlc clock. 
//! \c picture is the return value from lockCB().

void VideoWidget::displayCB(void* picture)
{
    QImage* image = reinterpret_cast<QImage*>(picture);

    onScreenPixmapMutex_.lock();
    onScreenPixmap_ = QImage(*image);
    onScreenPixmap_.detach();
    onScreenPixmapMutex_.unlock();

    delete image;

    update();
}

我知道Qt中不允许主线程外的GUI操作。但根据文档QWidget :: update()只是在Qt返回主事件循环时调度一个paint事件进行处理,并且不会立即重新绘制。

问题是:QWidget :: update()的规则是“不允许主线程外的GUI操作”吗?此操作是否属于“GUI操作”?

我使用Qt 4.7.3,在Windows 7和Linux上发生崩溃。

4 个答案:

答案 0 :(得分:6)

查看Mandelbrot Example。在该示例中,工作线程正在生成图像并将其传递给具有信号/槽机制的渲染小部件。使用相同的方法!

您可以使用示例中给出的新的updatePixmap()插槽,而不是实现 也可以直接连接小部件的update ()插槽。

从您的代码中我可以看到您有一个互斥锁来提供并发访问。因此,直接使用更新槽应该很容易。

两种方法仍然使用信号/插槽机制,因为在Qt中不允许主线程外部的GUI操作。

答案 1 :(得分:5)

  

问题是:是规则“GUI   主线程外的操作是   不允许“适用于   QWidget的:: update()方法?这个操作   属于“GUI操作”?

是。更新属于GUI操作。根据{{​​3}},所有QWidget和派生类只能由主线程使用。这是通用的,并且特定的函数可以声明它们是线程安全的,但是在这种情况下update()没有,因此从其他线程调用是不安全的。

信号/插槽机制有效,因为Qt将(除非另有说明)使用事件来允许一个线程中的插槽被另一个线程中的信号触发。如果您使用信号/插槽并告诉Qt不要进行特殊线程处理,则会再次出现崩溃。

答案 2 :(得分:1)

任何线程更新像素映射所需要做的就是在主线程中执行相关代码。这将处理所有锁定和其他一切。现代多线程编程应该很容易:如果不是,你可能会过于努力:)

例如,假设您使用QLabel来显示图像(为什么要重新发明自己的小部件?!):

/// This function is thread-safe. It can be invoked from any thread.
void setImageOn(const QImage & image, QLabel * label) {
  auto set = [image, label]{
    label->setPixmap(QPixmap::fromImage(image));
  };
  if (label->thread() == QThread::currentThread())
    set();
  else {
    QObject sig;
    sig.connect(&sig, &QObject::destroyed, label, set);
  }
}

现在,您可以做正确的事情并删除过时的图像 - 如果事件队列中有更新的图像,那么设置图像是没有意义的。这将是使用自定义小部件的唯一原因。有关详细信息,请参阅this answer

附注(不适用于您的代码):如果您必须从小部件实现的外部调用QWidget::update,那么您正在做一些非常错误的事情。如果您正在使用库存Qt小部件,则永远不需要这样做。如果您有自己的小部件,并且需要其用户在其上调用update,那么您的设计是错误的。这就是它的全部内容。

答案 3 :(得分:0)

如果您不想使用信号/插槽机制(也可以使用),请查看我的答案here