首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从非GUI线程使用QWidget::update()

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

Stack Overflow用户
提问于 2011-06-21 13:50:54
回答 4查看 9K关注 0票数 5

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

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

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

代码语言:javascript
复制
//! 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();
}

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

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

我使用的是Qt 4.7.3,在Windows7和Linux上崩溃重现。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-06-24 01:22:25

的问题是:“不允许主线程外的图形用户界面操作”的规则是否适用于QWidget::update()?这个操作属于"GUI操作“吗?

是。更新属于GUI操作。根据documentation,所有的QWidget和派生类只能由主线程使用。这是通用的,特定的函数可能声明它们是线程安全的,但在本例中update()并非如此,因此从其他线程调用是不安全的。

信号/槽机制之所以有效,是因为Qt将(除非另行通知)使用事件来允许一个线程中的槽被另一个线程中的信号触发。如果你使用信号/槽,并告诉Qt不要做特殊的线程处理,崩溃就会再次出现。

票数 5
EN

Stack Overflow用户

发布于 2011-06-21 14:23:32

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

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

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

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

票数 7
EN

Stack Overflow用户

发布于 2015-09-29 21:41:48

要从任何线程更新像素图,只需在主线程中执行相关代码即可。这会处理所有的锁定和其他所有事情。现代多线程编程应该很简单:如果不是这样,您可能太努力了:)

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

代码语言:javascript
复制
/// 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,那么您的设计就错了。这就是全部内容。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6420917

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档