GNU/Linux >> Linux の 問題 >  >> Linux

バックグラウンド ワーカー スレッドから Qt GUI を変更する

そのため、スレッド内からウィジェットを変更することはできません。そうしないと、次のようなエラーでアプリケーションがクラッシュします:

QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
Segmentation fault

これを回避するには、次のように、スレッド化された作業をクラスにカプセル化する必要があります。

class RunThread:public QThread{
  Q_OBJECT
 public:
  void run();

 signals:
  void resultReady(QString Input);
};

run() には、実行したいすべての作業が含まれています。

親クラスには、データを生成する呼び出し関数と QT ウィジェット更新関数があります:

class DevTab:public QWidget{
public:
  void ThreadedRunCommand();
  void DisplayData(QString Input);
...
}

次に、スレッドを呼び出すために、いくつかのスロットを接続します。

void DevTab::ThreadedRunCommand(){
  RunThread *workerThread = new RunThread();
  connect(workerThread, &RunThread::resultReady, this, &DevTab::UpdateScreen);
  connect(workerThread, &RunThread::finished, workerThread, &QObject::deleteLater);
  workerThread->start();  
}

接続関数は 4 つのパラメーターを取り、パラメーター 1 は原因クラス、パラメーター 2 はそのクラス内のシグナルです。パラメータ 3 はコールバック関数のクラス、パラメータ 4 はクラス内のコールバック関数です。

次に、子スレッドにデータを生成する関数を作成します:

void RunThread::run(){
  QString Output="Hello world";
  while(1){
    emit resultReady(Output);
    sleep(5);
  }
}

次に、親関数にウィジェットを更新するためのコールバックがあります:

void DevTab::UpdateScreen(QString Input){
  DevTab::OutputLogs->append(Input);
}

その後、実行すると、発行マクロがスレッドで呼び出されるたびに、親のウィジェットが更新されます。接続関数が適切に構成されている場合、発行されたパラメーターが自動的に取得され、コールバック関数の入力パラメーターに格納されます。

仕組み:

<オール>
  • クラスを初期化します
  • スレッドの終了で何が起こるかを処理するスロットをセットアップし、「返された」別名 emit をどうするか 通常の方法ではスレッドからデータを返すことができないため、ted データ
  • 次に、->start() でスレッドを実行します 呼び出し (QThread にハードコードされています)、QT はハードコードされた名前 .run() を探します クラスのメンバー関数
  • 毎回 emit resultReady マクロが子スレッドで呼び出され、QString データが共有データ領域に格納され、スレッド間で行き詰まる
  • QT は、resultReady がトリガーされたことを検出し、関数 UpdateScreen(QString ) に通知して、run() から発行された QString を親スレッドの実際の関数パラメーターとして受け入れるようにします。
  • これは、emit キーワードがトリガーされるたびに繰り返されます。
  • 基本的に connect() 関数は、子スレッドと親スレッドの間のインターフェースであり、データが行き来できるようにします。

    注: resultReady() を定義する必要はありません。 QT 内部に存在するマクロのようなものと考えてください。


    Qt で重要なことは、しなければならないことです。 メイン スレッドである GUI スレッドからのみ Qt GUI を操作します。

    そのため、これを行う適切な方法は 通知 することです ワーカーからのメイン スレッドであり、メイン スレッドのコードは実際にテキスト ボックス、進行状況バー、またはその他のものを更新します。

    これを行う最善の方法は、posix スレッドの代わりに QThread を使用し、Qt シグナル を使用することだと思います スレッド間の通信用。これは、thread_func の代わりとなるワーカーになります。 :

    class WorkerThread : public QThread {
        void run() {
            while(1) {
                 // ... hard work
                 // Now want to notify main thread:
                 emit progressChanged("Some info");
            }
        }
        // Define signal:
        signals:
        void progressChanged(QString info);
    };
    

    ウィジェットでスロットを定義します .h:のシグナルと同じプロトタイプを持つ

    class MyWidget : public QWidget {
        // Your gui code
    
        // Define slot:
        public slots:
        void onProgressChanged(QString info);
    };
    

    .cpp でこの関数を実装します:

    void MyWidget::onProgressChanged(QString info) {
        // Processing code
        textBox->setText("Latest info: " + info);
    }
    

    スレッドを生成したい場所 (ボタンのクリック時):

    void MyWidget::startWorkInAThread() {
        // Create an instance of your woker
        WorkerThread *workerThread = new WorkerThread;
        // Connect our signal and slot
        connect(workerThread, SIGNAL(progressChanged(QString)),
                              SLOT(onProgressChanged(QString)));
        // Setup callback for cleanup when it finishes
        connect(workerThread, SIGNAL(finished()),
                workerThread, SLOT(deleteLater()));
        // Run, Forest, run!
        workerThread->start(); // This invokes WorkerThread::run in a new thread
    }
    

    シグナルとスロットを接続した後、 emit progressChanged(...) でスロットを発行します ワーカー スレッドではメイン スレッドにメッセージが送信され、メイン スレッドはそのシグナルに接続されているスロットを呼び出します onProgressChanged

    追記私はまだコードをテストしていないので、どこか間違っている場合は自由に編集を提案してください


    Linux
    1. Ubuntu 20.04でデスクトップの背景を変更する-これを行う手順は?

    2. ループはバックグラウンドのサブシェルからの変数変換を無視しますか?

    3. 着信メールをText/plainからText/htmlに変更しますか?

    1. システムのデフォルトの背景画像を変更するにはどうすればよいですか?

    2. スレッド ID とスレッド ハンドル

    3. スレッド内からフォークしても安全ですか?

    1. ターミナルからGUIプログラムにキーストローク(f5)を送信する方法は?

    2. Debian – DebianからGuiを削除しますか?

    3. デーモン (つまりバックグラウンド) プロセスが USB キーボードからのキー押下を探すことは可能ですか?