我正在开发使用Qt 4.6开发的应用程序。
我想创建一个在单独线程中计数的自定义计时器。但是,我希望这个计时器能够向主线程发送信号。
我将QThread子类化,但它似乎不起作用。
这是Timer.h:
#ifndef TIMER_H
#define TIMER_H
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QTimer>
class Timer : public QThread
{
Q_OBJECT
public:
explicit Timer(QObject *parent = 0);
~Timer();
// true if the timer is active
bool isCounting();
// start the timer with a number of seconds
void startCounting(int value = 300);
void stopCounting();
// the number of seconds to reach
int maximum();
// the current value of the timer
int value();
// elapsed time since the timer has started
int elapsedTime();
signals:
// sent when the timer finishes to count
void timeout();
// an event is emited at each second when the timer is active
void top(int remainingSeconds);
protected:
// launch the thread
//virtual void run();
private slots:
// decrements the remaining time at each second and emits top()
void timerEvent();
private:
QTimer* _timer;
// remaining time
int _left;
// number of seconds at timer startup
int _maximum;
};
#endif // TIMER_H
和Timer.cpp:
#include "Timer.h"
Timer::Timer(QObject *parent) :
QThread(parent)
{
_timer = new QTimer(this);
_maximum = 0;
_left = 0;
connect(_timer, SIGNAL(timeout()), this, SLOT(timerEvent()));
}
Timer::~Timer()
{
delete _timer;
}
bool Timer::isCounting()
{
// test if timer still active
return _timer->isActive();
}
void Timer::startCounting(int value)
{
qDebug() << QString("Start timer for %1 secs").arg(QString::number(value));
if(_left != 0 || _timer->isActive())
{
_timer->stop();
}
_maximum = value;
_left = value;
// emit the first top
emit top(_left);
// start the timer: 1000 msecs
_timer->start(1000);
// start the thread
start();
}
void Timer::stopCounting()
{
qDebug() << QString("Stopping timer at %1 secs => %2 secs remaining.").arg(QString::number(elapsedTime()), QString::number(_left));
// stop timer
_timer->stop();
_left = 0;
_maximum = 0;
// kill thread
terminate();
}
int Timer::maximum()
{
return _maximum;
}
int Timer::value()
{
return _left;
}
void Timer::timerEvent()
{
qDebug() << "Timer event";
if(--_left == 0)
{
// stop timer
_timer->stop();
// emit end of timer
emit timeout();
// stop thread
terminate();
}
else
{
// emit a signal at each second
emit top(_left);
}
}
int Timer::elapsedTime()
{
return (_maximum - _left);
}
修改
我意识到我试图移动到另一个线程的对象实际上是一个单例。这可能会导致问题(see here)。
答案 0 :(得分:12)
在这种特殊情况下,您不需要继承QThread。一般来说,除非你确定它是你需要的,否则不要继承QThread。
以下是如何在线程中设置worker和timer并启动它的快速示例:
工人阶级:
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0) : QObject(parent) {}
signals:
void doSomething();
public slots:
void trigger() {
emit doSomething();
}
};
的main.cpp
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MainThreadObject o;
QThread *thread = new QThread;
Worker w;
QTimer timer;
timer.setInterval(1000);
timer.moveToThread(thread);
w.moveToThread(thread);
QObject::connect(thread, SIGNAL(started()), &timer, SLOT(start()));
QObject::connect(&w, SIGNAL(doSomething()), &o, SLOT(doSomething()));
QObject::connect(&timer, SIGNAL(timeout()), &w, SLOT(trigger()));
thread->start();
return a.exec();
}
因此,我们有MainThreadObject
表示生成在主线程中的QObject。我们创建了定时器和Worker
对象,它只用于包装信号和插槽,以避免需要子类化QThread。设置定时器并将它和worker移动到新线程,线程started()
信号连接到定时器start()
槽,worker doSomething()
信号连接到主线程对象doSomething()
槽,最后定时器timeout()
信号连接到工作线程trigger()
。然后启动线程,在事件循环中启动整个链。
结果,每隔一秒调用MainThreadObject::doSomething()
,从辅助线程发出信号。
答案 1 :(得分:1)
尝试
QMetaObject::invokeMethod(&timer, "start", Qt::QueuedConnection); //timer->start()
如果你想立即启动计时器
或
QMetaObject::invokeMethod(&timer, "start", Qt::QueuedConnection , Q_ARG(int, 1000 )); //timer->start(200)
如果你想在1000秒后启动计时器
在非GUI线程(QThread或Pthread回调)
中答案 2 :(得分:0)
首先,如果你是QThread的子类,你必须实现run()方法,否则就没有意义,你可以从QObject继承。
其次,您的QTimer必须驻留在运行事件循环的线程中。没有事件循环,就不能传输Qt排队信号。您可以通过在线程的run方法中调用exec()来启动事件循环:
void Timer::run() {
exec();
}
答案 3 :(得分:0)
可能的原因可能是,您的计时器对象不在具有事件循环的线程中。触发信号需要事件循环。
但是,我建议你不要采用这种方法。计时器在不同平台上使用不同的机制,并且您的代码可能在不同平台上的行为不如预期。