web-dev-qa-db-ja.com

Qt-2番目のスレッドでメインウィンドウを更新する

マルチスレッドの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->ui->horizontalLayout_4->addWidget(label);に到達できないことです

どうやってやるの?

18
abby

しかし問題は、ana-> ui-> horizo​​ntalLayout_4-> addWidget(label);に到達できないことです。

UIの変更をメインウィンドウのスロットに配置し、スレッドシグナルをそのスロットに接続します。メインスレッドだけがQtのUIにアクセスできると思います。したがって、GUI機能が必要な場合は、そこにある必要があり、他のスレッドからのみシグナルを送信できます。

わかりました、これは簡単な例です。ところで、あなたのシナリオは実際にQThreadを拡張する必要はありません-したがって、本当に必要な場合を除いて、拡張しない方が良いでしょう。そのため、この例では、代わりに通常のQThreadQObjectベースのワーカーを使用しますが、QThreadをサブクラス化しても、概念は同じです。

メイン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;
};
_

...そしてワーカーオブジェクト:

_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スロットに画像パスパラメータとともに渡され、新しいラベルがレイアウトに追加されます。

ワーカーオブジェクトは別のスレッドに移動できるようにするために親なしで作成されるため、スレッド破棄信号をworker deleteLater()スロットに接続します。

17
dtech

まず第一に、 "あなたはそれを間違っている" 。通常は、QObjectから派生したクラスを作成し、Qthreadからクラスを派生させる代わりに、そのクラスを新しいスレッドオブジェクトに移動します。

ここで、質問の詳細を説明するために、メインのGUIスレッドのui要素を別のスレッドから直接変更することはできません。 2番目のスレッドからメインスレッドのconnectsignalslotする必要があります。この信号/スロット接続を介して必要なデータを渡すことができますが、UI要素を直接変更することはできません(これは、アプリのフロントエンドをバックエンドから分離しておくつもりの場合、正直に言うとおそらく望まないことです) )。 Qtのシグナルとスロット ドキュメント をチェックアウトして、さらに多くの情報を入手してください。

4
g19fanatic

どうやってやるの?

あなたはすでにあなたがしているべきことに対する答えを持っていますが、なぜではないので、私は理由を追加します。

別のスレッドからGUI要素を変更しない理由は、GUI要素は通常 thread-safe ではないためです。これは、メインのGUIスレッドとワーカースレッドの両方がUIを更新する場合、いつ何が起こったかの順序を確認できないことを意味します。

データを読み取る場合、一般にこれは問題ない場合があります(条件のチェックなど)が、通常、これは当てはまりません。データを書き込む場合、これはほとんど常に、「ランダムに」発生する非常にストレスの多いバグの原因です。

別の回答では、優れた設計原則について述べています。GUIロジックを1つのスレッドに制限し、シグナルを発生させてそれとやり取りするだけでなく、競合状態の問題を取り除くだけでなく、コードを適切に区分化する必要があります。プレゼンテーションロジック(表示ビット)とデータ処理ロジックを明確に分離できるため、2つを簡単に維持できます。

この段階であなたは考えるかもしれません:一体、このスレッドビジネスはfarrrrrr作業が多すぎます!私はそれを避けます。これが悪い考えである理由を確認するには、単一のスレッドにファイルコピープログラムを実装し、単純な進行状況バーを使用して、コピーがどこまで進んでいるかを示します。大きなファイルで実行します。 Windowsでは、しばらくすると、アプリケーションが「白く」なり(またはXP灰色になると思います))、「応答しなくなります」これは、文字通り何が起こっているのかを示しています。

GUIアプリケーションは、ほとんどの場合、「1つの大きなループ」の処理とメッセージのディスパッチのバリエーションで内部的に動作します。たとえば、Windowsはこれらのメッセージへの応答時間を測定します。メッセージが応答を得るのに時間がかかりすぎる場合、Windowsはそれが死んでいると判断し、引き継ぎます。 これはGetMessage()に記載されています

かなりの作業のように見えるかもしれませんが、シグナル/スロット(イベント駆動モデル)は基本的に進むべき道です-これを考える別の方法は、スレッドが「イベント」を生成することは完全に許容できるということですUIも-進捗状況の更新など。

2
user257111