让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_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窗口. 类似如下.
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_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.
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_OBJECT
public
:
TestObject(QObject *parent) : QObject(parent) {}
public
slots :
void
onTimeout(){}
};
看起来和平时用的一模一样. →_→