Qt: QTimer和QThread

讓QTimer 跑在其餘線程. 通常寫法以下. oop

1. 在main thread中爲worker thread指定定時器. post

	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()));

  

須要注意幾個地方.ui

1) QTimer 不能指定parent, 不然 會出現警告 " QObject::moveToThread: Cannot move objects with a parent"this

   由於moveToThread 沒法移動有parent的object. spa

2) QTimer 須要用moveToThread 來改變線程相關性. 這樣emit signal的時候纔會在worker線程. 線程

3) connect timeout時, 須要附加參數Qt::DirectConnection,  blog

 根據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 QThreadget

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(){}
};

  

看起來和平時用的如出一轍.  →_→

相關文章
相關標籤/搜索