【问题标题】:Qt - updating main window with second threadQt - 用第二个线程更新主窗口
【发布时间】:2013-05-06 06:15:56
【问题描述】:

我有一个多线程的 qt 应用程序。当我在 mainwindow.cpp 中做一些进程时,同时,我想从其他线程更新 mainwindow.ui。

我有 mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include "mainwindow.h"

class mythread : public QThread
{
    public:
        void run();
        mythread( MainWindow* ana );
     MainWindow* ana;
private:

};

#endif // MYTHREAD_H

mythread.cpp

mythread::mythread(MainWindow* a)
{
    cout << "thread created" << endl;
        ana = a;
}

void mythread::run()
{
    QPixmap i1 (":/notes/pic/4mdodiyez.jpg");
    QLabel *label = new QLabel();
    label->setPixmap(i1);
    ana->ui->horizontalLayout_4->addWidget(label);


}

但问题是,我无法到达ana-&gt;ui-&gt;horizontalLayout_4-&gt;addWidget(label);

我该怎么做?

【问题讨论】:

  • 如果你和我一样,距离截止日期还有 10 分钟,这里有一个更骇人听闻的解决方案:在主窗口中添加一个虚拟按钮(宽度和高度 0),只要你需要更新工作人员的 ui 在工作人员中发出一个 click () 事件,并覆盖该按钮的单击处理程序以进行更新。

标签: c++ multithreading qt


【解决方案1】:

首先,"you're doing it wrong"。通常你想创建一个从 QObject 派生的类并将该类移动到一个新的线程对象,而不是从 Qthread 派生你的类

现在要了解您的问题的具体细节,您无法从单独的线程直接修改主 GUI 线程的 ui 元素。您必须将第二个线程中的connectsignal 连接到主线程中的slot。您可以通过此信号/插槽连接传递所需的任何数据,但您无法直接修改 ui 元素(老实说,如果您打算将应用程序的前端与后端分开,您可能不希望这样做)。查看 Qt 的信号和插槽 documentation 了解更多信息

【讨论】:

  • 有没有办法从工作线程更新 mainwindow.ui?因为我真的需要它。
  • 您将更新插槽中的 ui 元素,该元素连接到来自不同线程的信号
  • 啊那个臭名昭著的“你做错了”博客文章。我强烈反对他。见woboq.com/blog/qthread-you-were-not-doing-so-wrong.html
  • 扩展 QThread 并没有错。它甚至被用于 Qt 的示例中。
【解决方案2】:

但问题是,我无法到达 ana->ui->horizo​​ntalLayout_4->addWidget(label);

将您的 UI 修改放在主窗口的插槽中,并将线程信号连接到该插槽,它可能会起作用。我认为只有主线程可以访问 Qt 中的 UI。因此,如果您想要 GUI 功能,它必须存在,并且只能从其他线程发出信号。

好的,这是一个简单的例子。顺便说一句,您的方案实际上并不需要扩展 QThread - 所以最好不要这样做,除非您真的必须这样做。这就是为什么在这个例子中我将使用一个普通的QThread 和一个基于QObject 的worker,但是如果你继承QThread,这个概念是一样的:

主界面:

class MainUI : public QWidget
{
    Q_OBJECT

public:
    explicit MainUI(QWidget *parent = 0): QWidget(parent) {
        layout = new QHBoxLayout(this);
        setLayout(layout);
        QThread *thread = new QThread(this);
        GUIUpdater *updater = new GUIUpdater();
        updater->moveToThread(thread);
        connect(updater, SIGNAL(requestNewLabel(QString)), this, SLOT(createLabel(QString)));
        connect(thread, SIGNAL(destroyed()), updater, SLOT(deleteLater()));

        updater->newLabel("h:/test.png");
    }

public slots:
    void createLabel(const QString &imgSource) {
        QPixmap i1(imgSource);
        QLabel *label = new QLabel(this);
        label->setPixmap(i1);
        layout->addWidget(label);
    }

private:
    QHBoxLayout *layout;
};

...和工作对象:

class GUIUpdater : public QObject {
    Q_OBJECT

public:
    explicit GUIUpdater(QObject *parent = 0) : QObject(parent) {}    
    void newLabel(const QString &image) { emit requestNewLabel(image); }

signals:    
    void requestNewLabel(const QString &);
};

worker对象被创建并移动到另一个线程,然后连接到创建标签的槽,然后调用它的newLabel方法,这只是一个包装器来发出requestNewLabel信号并将路径传递给图片。然后,信号从工作对象/线程与图像路径参数一起传递到主 UI 插槽,并将新标签添加到布局中。

由于工作对象是在没有父对象的情况下创建的,以便能够将其移动到另一个线程,我们还将线程销毁信号连接到工作人员deleteLater() 插槽。

【讨论】:

  • 你能举一个连接的例子吗?我无法决定连接的元素。
  • @abby - 我添加了一个快速示例如何实现您的需求。
  • 我试过了,但我没有加载图像,而是在 newLabelText() 中进行了一些计算(必须修改 newLabel(),requestNewLabel()),看看这是否真的解决了冻结的常见问题在进行大量后台工作时使用线程时的 UI(正如作者建议他/她想要做的那样)。事实上,它确实冻结了整个事情(我放了一个 LCD 数字控件和一个按钮,用于计数并在 LCD 上显示结果,只是为了检查它是否确实冻结了)。这也意味着如果我们加载一个非常大的图像,这可能会导致 UI 阻塞。
  • 我猜你的 GUIUpdater 被泄露了,除非你添加 this 作为父级。
【解决方案3】:

我该怎么做?

你已经得到了你应该做什么的答案,但不知道为什么,所以我要添加一个为什么。

您不从另一个线程修改 GUI 元素的原因是因为 GUI 元素通常不是 thread-safe。这意味着如果您的主 GUI 线程和您的工作线程都更新 UI,您无法确定发生的顺序。

对于读取数据,这有时可能很好(例如检查条件),但通常您不希望这种情况发生。对于写入数据,这几乎总是“随机”发生的非常非常紧张的错误的来源。

另一个答案提到了良好的设计原则 - 不仅将您的 GUI 逻辑限制在一个线程上并触发信号与它交谈,从而摆脱了您的竞争条件问题,而且还迫使您很好地划分您的代码。然后可以将表示逻辑(显示位)和数据处理逻辑清晰地分离出来,这使得维护两者变得更加容易。

在这个阶段你可能会想:哎呀,这个线程业务是farrrrr太多的工作!我会避免这种情况。要了解为什么这是一个坏主意,请在单个线程中实现一个文件复制程序,并使用一个简单的进度条告诉您复制的距离。在一个大文件上运行它。在 Windows 上,一段时间后,应用程序将“变白”(或者在 XP 上,我认为它变灰)并且“没有响应”。这就是正在发生的事情。

GUI 应用程序内部主要处理“一个大循环”处理和调度消息的变体。例如,Windows 测量对这些消息的响应时间。如果一条消息需要很长时间才能得到响应,Windows 就会确定它已死,并接管。 This is documented in GetMessage()

因此,虽然看起来工作量很大,但 Signals/Slots(一种事件驱动模型)基本上是可行的方法——另一种思考方式是,您的线程生成“完全可以接受”事件”也适用于 UI - 例如进度更新等。

【讨论】:

    猜你喜欢
    • 2018-11-11
    • 2018-09-10
    • 2012-12-21
    • 2017-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-01
    • 1970-01-01
    相关资源
    最近更新 更多