在QWidget外部GUI线程上绘制问题

时间:2009-10-02 07:31:07

标签: c++ linux qt qwidget qimage

我正在开发一个应用程序,我想继续从远程主机接收图像并将其显示在我的屏幕上。为此我遵循给定的策略 1)我有一个主要的QWidget对象,其中包含QImage(工作正常) 2)从远程主机接收的图像绘制在QImage对象上,这项工作是在使用QPainter的工作线程中完成的。 (工作良好) 3)但问题是QWidget上没有更新图像,除非我调整窗口小部件,因为为QWidget调用了重绘事件...现在,如果我从工作线程重新绘制QWidget,它会给出错误“QPixmap:它是在GUI线程之外使用pixmaps是不安全的“..和应用程序崩溃。

对此有何帮助?

3 个答案:

答案 0 :(得分:9)

使用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;
};

不要忘记同步对图像的访问。
作为同步的替代方法,您还可以将图像发送到gui线程,就像在Mandelbrot Example中一样。

答案 1 :(得分:1)

Qt中不允许主线程外的GUI操作。所有GUI操作都需要在主线程中完成,即QApplication所在的主线程。另一个线程中的任何GUI操作都会产生不可预测的结果,即崩溃。

答案 2 :(得分:1)

如果你想开发插件,qt有一个大问题。如果主机应用程序是不Qt应用程序(很多程序...),并且要添加2或3 GUI插件,你是在一个大麻烦(因为我)。

问题是,在一个进程中必须只有1个QApplication。 (通常发生在主要) 如果您编写插件,则无法使用QApplication.exec()锁定主机应用程序。

在这种情况下,您可以在run()函数中使用QApplication和exec创建QThread。 它会正常工作。但这一个无法解决原来的问题。你的第二个插件不能有QApplication ...因为主机进程有一个。 (将Qapplication指针放入共享内存不是一个选项......因为QWidget必须在GUI线程上创建......总有一个......)

对于你的问题,这里是答案。如果您只想创建一个插件,可以使用QMetaObject :: invokeMethod 此代码将pixmap设置为标签并更新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(); }

我希望它有所帮助。

如果有人知道我的问题的解决方案......如上所述(在主机应用程序中使用qt的多个gui插件),请写信给我。