C++ Qt - 用第二个线程更新主窗口

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16501284/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:25:06  来源:igfitidea点击:

Qt - updating main window with second thread

c++multithreadingqt

提问by abby

i have an multithreaded qt application. when i am doing some processes in mainwindow.cpp, at the same time, i want to update mainwindow.ui from other thread.

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

i have mythread.h

我有 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

我的线程.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);


}

but the problem is that, i cannot reach the ana->ui->horizontalLayout_4->addWidget(label);

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

how can i do that?

我怎样才能做到这一点?

采纳答案by dtech

but the problem is that, i cannot reach the ana->ui->horizontalLayout_4->addWidget(label);

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

Put your UI modifications in a slot in your main window, and connect a thread signal to that slot, chances are it will work. I think only the main thread has access to the UI in Qt. Thus if you want GUI functionality, it must be there, and can be only signaled from other threads.

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

OK, here is a simple example. BTW, your scenario doesn't really require to extend QThread- so you are better off not doing it, unless you really have to. That is why in this example I will use a normal QThreadwith a QObjectbased worker instead, but the concept is the same if you subclass QThread:

好的,这是一个简单的例子。顺便说一句,您的方案并不真正需要扩展QThread- 所以你最好不要这样做,除非你真的必须这样做。这就是为什么在这个例子中,我将使用一个QThread带有QObject基础工作器的法线,但如果你子类化,概念是相同的QThread

The main UI:

主界面:

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

... and the worker object:

...和工人对象:

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 &);
};

The worker object is created and moved to another thread, then connected to the slot that creates the labels, then its newLabelmethod is invoked, which is just a wrapper to emit the requestNewLabelsignal and pass the path to the image. The signal is then passed from the worker object/thread to the main UI slot along with the image path parameter and a new label is added to the layout.

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

Since the worker object is created without parent in order to be able to move it to another thread, we also connect the thread destroyed signal to the worker deleteLater()slot.

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

回答by g19fanatic

First and foremost, "you're doing it wrong". Normally you want to create a class derived from a QObject and move that class to a new thread object instead of deriving your class from a Qthread

首先,“你做错了”。通常你想创建一个从 QObject 派生的类并将该类移动到一个新的线程对象,而不是从 Qthread 派生你的类

Now to get onto the specifics of your question, you're not able to directly modify the ui elements of your main GUI thread from a separate thread. You have to connecta signalfrom your 2nd thread to a slotin your main thread. You can pass any data that you need through this signal/slot connection but you're unable to directly modify the ui element (which in all honestly you probably do not want to if you intend to keep the frontend of your app separate from the backend). Checkout Qt's signal and slot documentationfor a whole lot more information

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

回答by g19fanatic

how can i do that?

我怎样才能做到这一点?

You've already got the answers to what you should be doing, but not a why, so I'm going to add a why.

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

The reason you don't modify GUI elements from another thread is because GUI elements are usually not thread-safe. This means that if both your main GUI thread and your worker thread update the UI, you cannot be certain of the order of what happened when.

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

For reading data generally this can sometimes be fine (e.g. checking a condition) but generally you do not want this to be case. For writing data, this is almost always the source of very, very stressful bugs which occur "at random".

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

Another answer has remarked on good design principles - not only does constraining your GUI logic to one thread and firing signals to talk to it get rid of your race condition issues, but it also forces you to compartmentalize your code nicely. Presentation logic (the display bit) and data processing logic can then be cleanly separated out, which makes maintaining the two much easier.

另一个答案提到了良好的设计原则 - 不仅将您的 GUI 逻辑限制为一个线程并触发信号与之对话可以消除您的竞争条件问题,而且还迫使您很好地划分代码。表示逻辑(显示位)和数据处理逻辑然后可以完全分离,这使得维护两者更容易。

At this stage you might think: heck, this threads business is farrrrrrtoo much work! I'll just avoid that. To see why this is a bad idea, implement a file copy program in a single thread with a simple progress bar telling you how far along the copy is. Run it on a large file. On Windows, after a while, the application will "go white" (or on XP I think it goes gray) and will be "not responding". This is very literally what is happening.

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

GUI applications internally mostly work on the variation of "one big loop" processing and dispatching messages. Windows, for example, measures response time to those messages. If a message takes too long to get a response, Windows then decides it is dead, and takes over. This is documented in GetMessage().

GUI 应用程序内部主要工作在“一个大循环”处理和分发消息的变化上。例如,Windows 测量对这些消息的响应时间。如果一条消息需要很长时间才能得到响应,Windows 会认为它已死,并接管。这记录在 GetMessage() 中

So whilst it may seem like quite a bit of work, Signals/Slots (an event-driven model) is basically the way to go - another way to think of this is that it is totally acceptable for your threads to generate "events" for the UI too - such as progress updates and the like.

因此,虽然看起来工作量很大,但信号/插槽(事件驱动模型)基本上是要走的路-另一种思考方式是,您的线程为以下内容生成“事件”是完全可以接受的UI也是 - 例如进度更新等。