讓QTimer 跑在其他線程. 一般寫法如下.
1. 在main thread中為worker thread指定定時器.
QThread* thread = new QThread(this); thread->start(); QTimer *timer = new QTimer(0); timer->setInterval(100); timer->moveToThread(thread); connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()), Qt::DirectConnection); connect(thread, SIGNAL(started()), timer,SLOT(start()));
需要注意幾個地方.
1) QTimer 不能指定parent, 否則 會出現警告 " QObject::moveToThread: Cannot move objects with a parent"
因為moveToThread 無法移動有parent的object.
2) QTimer 需要用moveToThread 來改變線程相關性. 這樣emit signal的時候才會在worker線程.
3) connect timeout時, 需要附加參數Qt::DirectConnection,
根據Qt的文檔中
Qt::AutoConnection 0 (default) If the signal is emitted from a different thread than the receiving object, the signal is queued, behaving as Qt::QueuedConnection. Otherwise, the slot is invoked directly, behaving as Qt::DirectConnection. The type of connection is determined when the signal is emitted. Qt::DirectConnection 1 The slot is invoked immediately, when the signal is emitted. Qt::QueuedConnection 2 The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
connect默認參數為AutoConnection, 所以當slot的object是main線程時, 會自動post 事件到main線程. 指定DirectConnection 才會直接調用slot. 即在worker線程中處理.
4) 如果直接 timer->start(); 的話, 會有警告: QObject::startTimer: Timers can only be used with threads started with QThread
timer 只能在同一個線程中創建和啟動. (使用moveToThread 可以修改). 這里寫"同一個線程" 似乎描述不太准確. 但大概就是這個意思.
connect(thread, SIGNAL(started()), timer,SLOT(start()));
所以需要這樣啟動.
其實也可以這樣取巧:
QThread* thread = new QThread(this); thread->start(); QTimer *timer = new QTimer(0); timer->setInterval(100); connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()), Qt::DirectConnection); //connect(thread, SIGNAL(started()), timer,SLOT(start())); timer->start(); timer->moveToThread(thread);
5) 因為timer 沒有指定parent, 所以不會自動銷毀.
2. 在worker線程中啟動QTimer.
class Worker :public QThread { Q_OBJECT public: Worker(MyClass *parent); virtual ~Worker(){} void run(); MyClass *timerReceiver; }; Worker::Worker(MyClass *parent) : QThread(parent) { timerReceiver = parent; } void Worker::run() { QTimer *timer = new QTimer(this); timer->setInterval(100); connect(timer, SIGNAL(timeout()), timerReceiver, SLOT(onTimeout()), Qt::DirectConnection); timer->start(); exec(); return; }
MyClass 是一個UI窗口. 類似如下.
class MyClass : public QMainWindow { Q_OBJECT public: MyClass(QWidget *parent = 0); ~MyClass(); public slots: void onTimeout(); private: Ui::MyClassClass ui; }; MyClass::MyClass(QWidget *parent) : QMainWindow(parent) { ui.setupUi(this); Worker *thread = new Worker(this); //QThread* thread = new QThread(this); thread->start(); } MyClass::~MyClass() { } void MyClass::onTimeout() { }
so. 可以看到. 如果timer是在worker 線程中創建的話. 即不需要moveToThread來修改線程相關性.
timer->start()也可以直接調用.
但仍然需要指定 Qt::DirectConnection. 因為timerReceiver 是在main thread中.
如果timerReceiver 也在worker線程中創建, 則不需要指定 Qt::DirectConnection.
void Worker::run() { TestObject *timerReceiver = new TestObject(this); QTimer *timer = new QTimer(this); timer->setInterval(100); connect(timer, SIGNAL(timeout()), timerReceiver, SLOT(onTimeout())); timer->start(); exec(); return; }
class TestObject : public QObject { Q_OBJECT public: TestObject(QObject *parent) : QObject(parent) {} public slots : void onTimeout(){} };
看起來和平時用的一模一樣. →_→