簡體   English   中英

Qt - 用第二個線程更新主窗口

[英]Qt - updating main window with second thread

我有一個多線程的 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

我的線程.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->ui->horizontalLayout_4->addWidget(label);

我怎樣才能做到這一點?

但問題是,我無法到達 ana->ui->horizo​​ntalLayout_4->addWidget(label);

將您的 UI 修改放在主窗口的一個插槽中,並將線程信號連接到該插槽,很有可能它會起作用。 我認為只有主線程可以訪問 Qt 中的 UI。 因此,如果您想要 GUI 功能,它必須存在,並且只能從其他線程發出信號。

好的,這是一個簡單的例子。 順便說一句,你的場景並不真正需要擴展QThread - 所以你最好不要這樣做,除非你真的必須這樣做。 這就是為什么在這個例子中我將使用一個普通的QThread和一個基於QObject的工作QObject ,但是如果你子類化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 &);
};

工作對象被創建並移動到另一個線程,然后連接到創建標簽的插槽,然后調用它的newLabel方法,它只是一個包裝器,用於發出requestNewLabel信號並將路徑傳遞給圖像。 然后將信號從工作對象/線程與圖像路徑參數一起傳遞到主 UI 插槽,並將新標簽添加到布局中。

由於工作者對象是在沒有父對象的情況下創建的,以便能夠將其移動到另一個線程,因此我們還將線程銷毀信號連接到工作者deleteLater()槽。

首先, “你做錯了” 通常你想創建一個從 QObject 派生的類並將該類移動到一個新的線程對象,而不是從 Qthread 派生你的類

現在要了解問題的具體細節,您無法從單獨的線程直接修改主 GUI 線程的 ui 元素。 您必須connect來自第二個線程的signal connect到主線程中的slot 您可以通過此信號/插槽連接傳遞您需要的任何數據,但您無法直接修改 ui 元素(老實說,如果您打算將應用程序的前端與后端分開,您可能不希望這樣做)。 查看 Qt 的信號和插槽文檔以獲取更多信息

我怎樣才能做到這一點?

你已經得到了你應該做什么的答案,但沒有一個為什么,所以我要添加一個為什么。

您不從另一個線程修改 GUI 元素的原因是因為 GUI 元素通常不是線程安全的 這意味着如果您的主 GUI 線程和您的工作線程都更新了 UI,則您無法確定發生的順序。

對於通常讀取數據,這有時可能很好(例如檢查條件),但通常您不希望出現這種情況。 對於寫入數據,這幾乎總是“隨機”發生的非常非常有壓力的錯誤的來源。

另一個答案提到了良好的設計原則 - 不僅將您的 GUI 邏輯限制為一個線程並觸發信號與之對話可以消除您的競爭條件問題,而且還迫使您很好地划分代碼。 表示邏輯(顯示位)和數據處理邏輯然后可以完全分離,這使得維護兩者更容易。

在這個階段你可能會想:哎呀,這個線程業務真是太多工作了! 我只會避免這種情況。 要了解為什么這是一個壞主意,請在單個線程中實現一個文件復制程序,並帶有一個簡單的進度條,告訴您復制的進度。 在一個大文件上運行它。 在 Windows 上,一段時間后,應用程序將“變白”(或者在 XP 上我認為它變灰)並且“沒有響應”。 這就是正在發生的事情。

GUI 應用程序內部主要工作在“一個大循環”處理和調度消息的變化上。 例如,Windows 測量對這些消息的響應時間。 如果一條消息需要很長時間才能得到響應,Windows 會認為它已死,並接管。 這記錄在 GetMessage() 中

因此,雖然看起來工作量很大,但信號/插槽(事件驅動模型)基本上是要走的路-另一種思考方式是,您的線程為以下內容生成“事件”是完全可以接受的UI也是 - 例如進度更新等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM