5

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

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

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

//! 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 返回主事件循环时安排一个绘制事件进行处理,并且不会导致立即重绘。

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

我使用 Qt 4.7.3,崩溃在 Windows 7 和 Linux 上重现。

4

4 回答 4

7

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

除了实现示例中给出的新 updatePixmap() 槽之外,您也可以直接连接小部件的update()槽。

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

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

于 2011-06-21T06:23:31.913 回答
5

问题是:规则“不允许在主线程之外进行 GUI 操作”是否适用于 QWidget::update()?这个操作属于“GUI操作”吗?

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

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

于 2011-06-23T17:22:24.650 回答
1

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

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

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

现在,您最好做正确的事情并删除陈旧的图像 - 如果在它前面的事件队列中有更新的图像,那么设置图像是没有意义的。这将是使用自定义小部件的唯一原因。有关详细信息,请参阅此答案

旁注(不适用于您的代码):如果您必须QWidget::update从小部件的实现外部调用,那么您做错了什么。如果您使用的是库存 Qt 小部件,则永远不需要这样做。如果您有自己的小部件并且需要其用户调用update它,那么您的设计就错了。这就是它的全部。

于 2015-09-29T13:41:48.463 回答
0

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

于 2019-02-12T22:48:52.943 回答