QWidget 外部 GUI 线程上的绘画问题

发布于 2024-08-06 15:24:36 字数 314 浏览 16 评论 0原文

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

对此有什么帮助吗?

I am developing an appliation in which I want to continuously receive images from remote host and display them on my screen. for this I am following the given strategy
1) I have a main QWidget object which contains the QImage on it (works fine)
2) Images received from remote host are painted on QImage object, this work is done in a worker thread using QPainter. (works fine)
3) but the problem is that the image is not updated on QWidget, unless I resize the widget, because the repaint event is called for QWidget... Now if i repaint the QWidget from the worker thread it gives error "QPixmap: It is not safe to use pixmaps outside the GUI thread".. and application crashes.

Any help regarding this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

找回味觉 2024-08-13 15:24:36

使用 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 线程,如 中所示曼德尔布罗示例

Emit a signal from the worker thread with a QueuedConnection

or post an update event (QPaintEvent) to the widget from the worker thread.

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

Don't forget to synchronize the access to the image.
As an alternative to the synchronization, you could also send the image to the gui thread, like in the Mandelbrot Example.

生生漫 2024-08-13 15:24:36

如果你想开发插件,qt 有一个很大的问题。如果主机应用程序是非qt应用程序(很多程序...),并且您想添加2或3个GUI插件,那么您就会遇到大麻烦(就像我一样)。

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

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

对于您的问题,这里就是答案。如果你只想创建一个插件,你可以使用 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();

我希望它有帮助

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

There is a big problem with qt if you would like to develop plugins. If the host application is non-qt application (lots of programs...), and you want to add 2 or 3 GUI plugin, you are in a big trouble (as i am).

The problem is that, there must be just 1 QApplication in one process. (Usually takes place in main)
If you write a plugin you cant afford to lock the host application with a QApplication.exec().

In this case you can create a QThread with QApplication and exec in the run() function.
It will work properly. But this one cant solve the original problem. Your second plugin cant has a QApplication ... because the host process has one. (giving Qapplication pointer into a shared memory is not an option ... because QWidget must create on GUI thread ... there is always one...)

And for your question here is the answer. If you want to create just one plugin, you can use QMetaObject::invokeMethod
this code set a pixmap to a label and updates gui.

QImage img;...
bool succ = QMetaObject::invokeMethod(mainWin, "DisplaySlot", Qt::QueuedConnection, Q_ARG(QImage, img));

and add a public slot: to your displayer window

void mainWinClass::DisplaySlot(QImage qim) {
(*(ui.label)).setPixmap(QPixmap::fromImage(qim));
(*(ui.label)).update();
}

I hope it helps.

If anyone know a solution to my problem ... described above (multiple gui plugin with qt in a host application) please write to me.

半寸时光 2024-08-13 15:24:36

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

GUI operations outside the main thread are not allowed in Qt. All GUI operations need to be done in the main thread, the thread where QApplication lives. Any GUI operation in another thread gives unpredictable results, i.e. crashes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文