web-dev-qa-db-ja.com

QThreadでQTimerを起動する

特定のスレッドでQTimerを起動しようとしています。ただし、タイマーは実行されていないようで、何も印刷されません。タイマー、スロット、またはスレッドに関係がありますか?

main.cpp

    #include "MyThread.h"
    #include <iostream>
    using namespace std;

    int main(int argc, char *argv[]) {
        MyThread t;
        t.start();
        while(1);
    }

MyThread.h

    #ifndef MYTHREAD_H
    #define MYTHREAD_H

    #include <QTimer>
    #include <QThread>
    #include <iostream>

    class MyThread : public QThread {
        Q_OBJECT
    public:
        MyThread();
    public slots:
        void doIt();
    protected:
        void run();
    };

    #endif  /* MYTHREAD_H */

MyThread.cpp

    #include "MyThread.h"

    using namespace std;

    MyThread::MyThread() {
        moveToThread(this);
    }

    void MyThread::run() {
        QTimer* timer = new QTimer(this);
        timer->setInterval(1);
        timer->connect(timer, SIGNAL(timeout()), this, SLOT(doIt()));
        timer->start();
    }

    void MyThread::doIt(){
        cout << "it works";
    }
15
Tudor Pascu

私がコメントしたように(リンクの詳細情報)、あなたはそれを間違っています:

  1. スレッドデータを保持しているオブジェクトを別のオブジェクトと混合しています(doIt()を担当)。それらは分離されるべきです。
  2. あなたの場合、QThreadをサブクラス化する必要はありません。さらに悪いことに、あなたはrunメソッドを、何をしているかを考慮せずにオーバーライドしています。

コードのこの部分で十分です

_QThread* somethread = new QThread(this);
QTimer* timer = new QTimer(0); //parent must be null
timer->setInterval(1);
timer->moveToThread(somethread);
//connect what you want
somethread->start();
_

これで(Qtバージョン> = 4.7)デフォルトでQThreadrun()メソッドでイベントループを開始します。スレッド内で実行するには、オブジェクトを移動するだけです。 ドキュメントを読む...

24
UmNyobe
m_thread = new QThread(this);
QTimer* timer = new QTimer(0); // _not_ this!
timer->setInterval(1);
timer->moveToThread(m_thread);
// Use a direct connection to whoever does the work in order
// to make sure that doIt() is called from m_thread.
worker->connect(timer, SIGNAL(timeout()), SLOT(doIt()), Qt::DirectConnection);
// Make sure the timer gets started from m_thread.
timer->connect(m_thread, SIGNAL(started()), SLOT(start()));
m_thread->start();
12

QTimerは、イベントループがあるスレッドでのみ機能します。

http://qt-project.org/doc/qt-4.8/QTimer.html

マルチスレッドアプリケーションでは、イベントループを持つ任意のスレッドでQTimerを使用できます。非GUIスレッドからイベントループを開始するには、QThread :: exec()を使用します。 Qtはタイマーのスレッドアフィニティを使用して、timeout()シグナルを送信するスレッドを決定します。このため、そのスレッドでタイマーを開始および停止する必要があります。別のスレッドからタイマーを開始することはできません。

9
Brian Roach

あなたは放出シグナルを使用し、放出されたスロット関数内でタイマーを開始することができます

main.cpp

#include "MyThread.h"
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {
    MyThread t;
    t.start();
    while(1);
}

MyThread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QTimer>
#include <QThread>
#include <iostream>

class MyThread : public QThread {
    Q_OBJECT
public:
    MyThread();
    QTimer *mTimer;
signals:
   start_timer();
public slots:
    void doIt();
    void slot_timer_start();
protected:
    void run();
};

#endif  /* MYTHREAD_H */

MyThread.cpp

#include "MyThread.h"

using namespace std;

MyThread::MyThread() {
    mTimer = new QTimer(this);
    connect(this,SIGNAL(start_timer()),this, SLOT(slot_timer_start()));
    connect(mTimer,SIGNAL(timeout()),this,SLOT(doIt()));

}

void MyThread::run() {
    emit(start_timer());
    exec();
}

void MyThread::doIt(){
    cout << "it works";
}
void MyThread::slot_timer_start(){
    mTimer->start(1000);
}
1
pathaleswar

ラムダ関数内でタイマーを呼び出す例を作成しました:

#include <QCoreApplication>
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QDebug>
#include <memory>

int main(int argc, char** argv)
{
    QCoreApplication app(argc, argv);

    QThread* thread = new QThread(&app);
    QObject::connect(thread, &QThread::started, [=]()
    {
        qInfo() << "Thread started";
        QTimer* timer1 = new QTimer(thread);
        timer1->setInterval(100);
        QObject::connect(timer1, &QTimer::timeout, [=]()
        {
            qInfo() << "Timer1 " << QThread::currentThreadId();
        });
        timer1->start();
    });
    thread->start();

    QTimer timer2(&app);
    QObject::connect(&timer2, &QTimer::timeout, [=]()
    {
        qInfo() << "Timer2 " << QThread::currentThreadId();
    });
    timer2.setInterval(100);
    timer2.start();

    return app.exec();
}
0
Fernando

タイマーを使用するには、イベントループが必要です。これが私のコードで同じ問題を解決した方法です:

MyThread::MyThread() {
}

void MyThread::run() {
    QTimer* timer = new QTimer(this);
    timer->setInterval(1);
    timer->connect(timer, SIGNAL(timeout()), this, SLOT(doIt()));
    timer->start();

    /* Here: */
    exec();             // Starts Qt event loop and stays there
   // Which means you can't have a while(true) loop inside doIt()
   // which instead will get called every 1 ms by your init code above.
}

void MyThread::doIt(){
    cout << "it works";
}

他の投稿者の誰も言及しなかったドキュメントの関連部分はここにあります:

int QCoreApplication :: exec()

メインイベントループに入り、exit()が呼び出されるまで待機します。 exit()に設定された値を返します(exit()がquit()経由で呼び出された場合は0)。イベント処理を開始するには、この関数を呼び出す必要があります。メインイベントループは、ウィンドウシステムからイベントを受け取り、それらをアプリケーションウィジェットにディスパッチします。アプリケーションにアイドル処理を実行させる(つまり、保留中のイベントがない場合は常に特別な関数を実行する)には、タイムアウト0のQTimerを使用します。より高度なアイドル処理スキーマは、processEvents()を使用して実現できます。