QTimer和 QThread
让QTimer 跑在其他线程. 一般写法如下.
1. 在main thread中为worker thread指定定时器.
|
1
2
3
4
5
6
7
8
|
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的文档中
|
1
2
3
|
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 可以修改). 这里写"同一个线程" 似乎描述不太准确. 但大概就是这个意思.
|
1
|
connect(thread, SIGNAL(started()), timer,SLOT(start())); |
所以需要这样启动.
其实也可以这样取巧:
|
1
2
3
4
5
6
7
8
9
|
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.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class Worker :public QThread { Q_OBJECTpublic: 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窗口. 类似如下.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
class MyClass : public QMainWindow{ Q_OBJECTpublic: 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.
|
1
2
3
4
5
6
7
8
9
10
11
|
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;} |
|
1
2
3
4
5
6
7
|
class TestObject : public QObject { Q_OBJECTpublic: TestObject(QObject *parent) : QObject(parent) {} public slots : void onTimeout(){}}; |
看起来和平时用的一模一样. →_→
浙公网安备 33010602011771号