QWidget在GUI线程外绘制的问题

4
我正在开发一个应用程序,希望能够持续从远程主机接收图像并在屏幕上显示它们。为此,我遵循以下策略: 1)我有一个包含QImage的主QWidget对象(运行良好) 2)从远程主机接收到的图像被绘制在QImage对象上,这项工作是在一个工作线程中使用QPainter完成的。(运行良好) 3)但问题在于,除非调整小部件大小,否则QWidget上的图像不会更新,因为repaint事件仅对QWidget调用...现在,如果我从工作线程重新绘制QWidget,它会出现错误“QPixmap:在GUI线程之外使用像素映射是不安全的”,导致应用程序崩溃。
有关此事的任何帮助?

这是来自 https://stackoverflow.com/questions/71873020/how-to-udpate-gui-from-another-thread-qt 的同一个问题吗? - James Smith
3个回答

10

在工作线程中使用QueuedConnection发射信号,或者从工作线程向小部件发布更新事件(QPaintEvent)。

//--------------Send Queued signal---------------------
class WorkerThread : public QThread
{
    //...
signals:
    void updateImage();

protected:
    void run()
    {
        // construct QImage
        //...
        emit updateImage();
    }
    //...
};

//...
widgetThatPaintsImage->connect(
    workerThread, 
    SIGNAL(updateImage()), 
    SLOT(update()),
    Qt::QueuedConnection);
//...

//--------------postEvent Example-----------------------
class WorkerThread : public QThread
{
    //...
protected:
    void run()
    {
        //construct image
        if(widgetThatPaintsImage)
        {
            QCoreApplication::postEvent(
                widgetThatPaintsImage, 
                new QPaintEvent(widgetThatPaintsImage->rect()));
        }
        //... 
    }

private:
    QPointer<QWidget> widgetThatPaintsImage;
};
不要忘记同步访问图像。
作为同步的替代方案,你也可以像Mandelbrot示例中那样将图像发送到GUI线程。

2
如果你想开发插件,那么使用qt存在一个大问题。如果宿主应用程序是非qt应用程序(很多程序...),而且你想要添加2或3个GUI插件,你会遇到麻烦的情况(就像我一样)。
问题在于,一个进程中必须只有一个QApplication。(通常在main函数中)如果你编写插件,又不能用QApplication.exec()锁定宿主应用程序。
在这种情况下,你可以在run()函数中创建一个带有QApplication和exec的QThread。它将正常工作。但是这个方法无法解决原来的问题。因为宿主进程已经有了一个QApplication,所以你的第二个插件不能有一个QApplication...因为GUI线程必须由QWidget创建...并且总有一个...
至于你的问题,答案如下。如果你只想创建一个插件,可以使用QMetaObject::invokeMethod。下面的代码将一个图像设置为标签并更新GUI。
QImage img;... bool succ = QMetaObject::invokeMethod(mainWin, "DisplaySlot", Qt::QueuedConnection, Q_ARG(QImage, img));
然后在你的显示器窗口中添加一个公共槽:
void mainWinClass::DisplaySlot(QImage qim) { (*(ui.label)).setPixmap(QPixmap::fromImage(qim)); (*(ui.label)).update(); }
希望这能帮到你。
如果有人知道如何解决我上述的问题(在一个宿主应用程序中使用多个GUI插件),请写信给我。

1

在Qt中,不允许在主线程之外进行GUI操作。所有的GUI操作都需要在主线程中完成,也就是QApplication所在的线程。在其他线程中进行任何GUI操作都会导致不可预测的结果,例如崩溃。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接