记录一个跨线程启动QTimer的问题。问题是个小问题,但是不注意的话就不是知道为啥咱的计时器它咋就坏了呢。
先看个示例代码:(为简洁 部分省略[手动狗头])
//*.h
//主窗口类
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
void startTimer();
public slots:
void slotTimeout();
private:
Ui::Dialog *ui;
QTimer timer;
};
//工作线程
class WorkThread : public QThread
{
Q_OBJECT
public:
WorkThread(QObject* parent);
protected:
virtual void run() override;
};
///
//*.cpp
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
qDebug() << "Main:" << QThread::currentThreadId();
timer.setSingleShot(true);
connect(&timer, &QTimer::timeout,this, &Dialog::slotTimeout);
startTimer();
WorkThread* th = new WorkThread(this);
th->start();
}
void Dialog::startTimer()
{
qDebug() << __FUNCTION__ << QThread::currentThreadId();
timer.start(1000);
}
void Dialog::slotTimeout()
{
qDebug() << __FUNCTION__;
}
WorkThread::WorkThread(QObject *parent):QThread(parent)
{}
void WorkThread::run()
{
qDebug() << "Thread id:" << QThread::currentThreadId();
QThread::sleep(3);
qobject_cast<Dialog*>(parent())->startTimer();
QThread::exec();
}
这一小段干了什么呢?计时器设置成单发,主窗口类Dialog启动了一次计时器;接着启动了一个线程,线程也启动了一次计时器,只不过让线程休息3秒后才启动。看结果:
从结果上看,线程启动的QTimer似乎失败了 Qt直接给了错误消息,告诉我们不能从另一个线程中启动计时器。赶紧看看文档
上面巴拉巴拉的就不看了,直接看重点,我们必须要在他所在线程中启动停止。人家都说这么明白了,改吧。
//第一 startTimer改成槽函数
public slots:
void startTimer();
void slotTimeout();
//第二 调用用invokeMethod
//qobject_cast<Dialog*>(parent())->startTimer();
QMetaObject::invokeMethod(parent(),"startTimer",Qt::QueuedConnection);
用invokeMethod就省去了再定义信号,关联信息这两步,非常方便。
再看结果,没毛病了。从线程中调用的startTimer 线程id也成了主线程id。