Qt:如何在QTimer处于活动状态时延迟程序?
请参考下面的链接,因为我试图简化我遇到的问题,现在遇到了一个我无法解决的问题。Qt:如何在QTimer处于活动状态时延迟程序?
链接:Qt: How to use QTimer to print a message to a QTextBrowser every 10 seconds?
在张贴的链接上面我简单我想简单地说,我想按下一个按钮,并把它显示在一个QTextBrowser
每10秒的东西做任务。当时我在QTimer
工作时遇到问题,所以我认为如果我可以让QTimer
工作,那么我可以完成我的任务。
什么我真的试图做的是从文件中读取行,每2500线后,我想打印一个信息,然后等待10秒。
伪代码:
while(not at the end of the file)
{
read in 2500 lines
print a message
wait 10 seconds
}
QTimer
是好的,但它的什么,我想要它做的正好相反。而不是发送消息并等待10秒,首先他们等待10秒,超时然后发送消息。
所以要按照我想要的方式工作,我首先调用了printMessage()
SLOT,然后我制作了一个名为stopTimer()
的SLOT,它可以简单地停止定时器。所以在经过10秒后,它将简单地调用stopTimer(),然后继续处理输入文件。
走上真正的问题:
Qt不等待QTimer
完成它通过代码移动之前。我希望代码在读取接下来的2500行代码之前等待整整10秒钟。我发现QTimer
有一个返回bool值的isActive()
函数。
所以在我想要的10秒延迟完成的地方,我把下列:
while(timer->isActive());
我想通程序会留在这个循环持续10秒,然后退出QTimer
超时后因那么情况就会是错误的。问题在于它不会退出循环,因为无论在此循环中等待多长时间,定时器的状态都不会改变!我使用调试器进行了检查,并且无论经过的时间如何,isActive()
都保持为真。
我然后省略while(timer->isActive())
循环并在调试器中观察到的定时器。看起来,定时器并没有真正开始计时,直到退出while(不在文件末尾)。所以我相信由于while(timer->isActive())
循环嵌套在这里,所以它永远不会超时。我可能是错的,但这似乎正在发生。此外,令人讨厌的是QTimer
对象没有字段显示定时器处于活动状态时的已用时间。因此,我无法检查经过的时间以进一步调试。
有人请测试一下,或让我知道这个解决方法!
的东西,听起来很容易,这一直是最大的痛苦,我曾在最近的时间,但我一般不使用Qt所以它可能是我缺乏经验。
下面是从代码的摘录我目前冻结如上所述:
void Form::startBtn_pushed()
{
QTimer *timer = new QTimer(this);
QFile file(“file.txt”);
QTextStream stream(&file);
QString line;
int lineCount = 0;
connect(timer, SIGNAL(timeout()), this, SLOT(stopTimer()));
while(!(stream.atEnd())
{
line = stream.readLine();
lineCount++;
if(lineCount == 2500)
{
printMessage();
timer->start(10000);
while(timer->isActive()); //Wait for timer to timeout before continuing
}
}
}
很抱歉的长期职位,如果我可能已经和我在这里的代码中的任何语法错误。我的开发机器上没有互联网接入,所以我不得不在这里重新输入。
我已经找到了一个解决方案,比这里提出的要简单得多。
我意识到使用QTime对象而不是QTimer对象要容易得多。
基本上你开始一个QTime对象,然后你使用它的elapsed()函数来检查自启动以来经过了多少时间。
感谢大家花时间帮忙。我确实试图在我的代码中实现一些解决方案,但由于我不是Qt的专业人员而遇到了一些麻烦,而且他们比我想象的要复杂。最后,我发现这个解决方案是一个简单的解决方案,应该是一个简单的问题。
我当然从这个问题中学到了很多东西,我希望你们都做到了。
我给你的问题是为什么没有人建议从一开始就使用QTime?
再次感谢!
我的解决办法:
void Form::startBtn_pushed()
{
QTime *timer = new QTime();
QFile file(“file.txt”);
QTextStream stream(&file);
QString line;
int lineCount = 0;
connect(timer, SIGNAL(timeout()), this, SLOT(stopTimer()));
while(!(stream.atEnd())
{
line = stream.readLine();
lineCount++;
if(lineCount == 2500)
{
timer->start();
while(1)
{
QApplication::processEvents();
if(timer->elapsed() == 10000)
{
printMessage();
break;
}
}
}
}
}
QTimer有一个叫做timedout()的信号。如果你连接一个插槽,你可以将初始定时器设置为一个非常短的时间间隔(也许是1 MS)。当计时器到期时,在插槽内,您可以发送消息。在插槽末尾,将间隔设置为10秒,将单色设置为false,然后再次启动计时器。
如果您不想每次调用该插槽时都进行设置,则只需制作2个插槽即可。第一次,该插槽为其余的呼叫进行设置。它也从QTimer断开连接并连接第二个插槽。然后,事情继续愉快地前进。
编辑:
此外,意识到插槽被调用时,他们被称为事件线程上。因此,通过单击一个按钮并放置一个旋转块/忙碌循环 - 等待计时器到期,您可以保证不会进入此状态,因为您正在阻塞处理超时信号的线程。
你甚至可以设置超时时间为0,这是明确的,一旦记录为“超时作为控制返回到事件循环“。我经常使用它来异步调用插槽。它甚至有一个非常有用的静态工具方法singleShot(),所以我不必创建一个QTimer实例。 – 2011-01-29 07:01:34
@Sergey不错。谢谢。 – 2011-01-29 13:55:35
做一些类似while(timer.isActive())
不是一个好主意,因为它会导致您的应用程序消耗大约100%的CPU时间。它也会导致你的应用程序永远不会返回到执行定时器的实际代码的事件处理循环,这就是它冻结的原因。
如果你仍然想要使用这种方法,你应该在循环中调用QCoreApplication :: processEvents()。它会暂时将控制权交还给事件循环,所以会导致计时器超时。除了将timeout()连接到stopTimer()之外,您可以在启动它之前调用timer.setSingleShot(true),它会在第一次超时后自动停止。
请注意,当您在每次按钮按下时创建一个新的计时器时,您都有内存泄漏。他们当然是你形态的孩子,并且会被摧毁,但只有当形式被摧毁时。
如果您想要更优雅的方法,您可以创建一个单独的类来读取该文件。在构造函数中,你会打开你的文件和流,这应该是这个类中的字段。这个类还应该有一个readMore()插槽,它将读取2500行,然后放置一条消息并返回。如果它没有到达流的末尾,那么它会调用QTimer :: singleShot(10000,this,SLOT(readMore())),这将导致事件循环在10秒内再次调用readMore()。代码将看起来像这样(没有检查错误):
// myfilereader.h
class Form;
class MyFileReader: public QObject {
Q_OBJECT
public:
MyFileReader(const QString &fileName);
// this should be called after you create an instance of MyFileReader
void startReading() {readMore();}
private:
QFile file;
QTextStream stream;
private slots:
void readMore();
signals:
void message(); // this should be connected to printMessage() in the Form
void finished();
};
// myfilereader.cpp
MyFileReader::MyFileReader(const QString &fileName):
file(fileName),
stream(&file),
{
// open the file, possibly throwing an exception
// or setting some sort of "invalid" flag on failure
}
void MyFileReader::readMore()
{
QString line;
int lineCount = 0;
while(!(stream.atEnd())
{
line = stream.readLine();
lineCount++;
if(lineCount == 2500)
{
emit message();
break;
}
}
if (stream.atEnd())
emit finished();
else
QTimer::singleShot(10000, this, SLOT(readMore()));
}
这是一种更重量级的方法,但这是异步事件处理的价格。你也可以将所有这些东西放到Form类中,但我认为使用单独的类更好。
正如Daniel指出的那样,如果读取2500行需要很长时间,例如5秒钟,则在读取完成10秒后(即在开始15秒后)将打印该消息。如果您希望大约每10秒打印一次消息,则无论读取多长时间,都应该为该类添加一个QTimer timer
字段,将其timeout()信号连接到MyFileReader构造函数中的readMore()插槽,然后在startReading ()方法在调用readMore()之前调用timer.start()。现在,)readMore结束(这样做:
if (stream.atEnd()) {
timer.stop();
emit finished();
}
您需要在这种情况下,QTimer场,因为你无法取消QTimer :: singleShot()调用,但你需要,如果你这样做已经到达流的末尾,否则你的readMore()将会一次又一次地被调用,即使没有更多的东西需要读取。请注意,即使在这种情况下,如果读取2500行需要比这10秒钟更长的时间,仍然可能出现信息出现频率低于每10秒的频率。如果你想精确到10秒,你应该检查循环中的流逝时间,但我认为这是一种矫枉过正的情况,除非你期望读数非常缓慢。
稍微偏离主题,但如果你想要一个简单的方法,以避免内存泄漏,你也可以做到这一点在构造函数:
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
它将自动标记你的读者为删除当你emit finished()
,并且,一旦发生这种情况,只要控件返回到事件循环,阅读器就会被删除。也就是说,在连接到完成()信号的所有槽后返回。这种方法允许你只在堆上分配一个MyFileReader,然后丢弃指针而不用担心内存泄漏。
感谢您花时间为我做这个解决方案。这个想法听起来很棒,但最终我的头脑实施起来有些过头了。请参考我的解决方案,让我知道你的想法。再次感谢! – 2011-02-02 00:58:01
@Aaron,你到底还不清楚什么?我可以编辑我的答案来澄清。 – 2011-02-02 09:58:10
QTimer在Qt的事件循环中执行所有处理,因此您的代码必须返回到事件循环以使计时器超时。所以你想要做的是让startBtn_pushed
设置定时器,将一个插槽连接到定时器的timeout
信号,然后可能自己调用该插槽(以便立即调用该插槽)。这将是这个样子:
// timer, file, and stream are now instance variables (or maybe file and stream
// are broken out into their own class... up to you.
void Form::startBtn_pushed()
{
// timer has been allocated before (but not started)
file.open(“file.txt”);
stream.setDevice(&file);
connect(timer, SIGNAL(timeout()), this, SLOT(readAndPrint()));
timer->start(10000);
readAndPrint(); // respond to the button press immediately
}
void Form::readAndPrint()
{
int lineCount = 0;
while(!(stream.atEnd() && lineCount < 2500)
{
line = stream.readLine();
lineCount++;
if(lineCount == 2500)
{
printMessage();
}
}
}
QEventLoop l;
connect(timer, SIGNAL(timeout()), &l, SLOT(quit()));
l.exec(); // Waiting without freezing ui
结帐qtimer :: singleshot(对不起,太忙了完整的答案) – 2011-01-28 23:41:48