问:如何在QTimer处于活动状态时延迟程序?

Aar*_*lar 1 qt qt4 timer

当我试图简化我遇到的问题时,请参考以下链接,现在遇到了一个我无法解决的问题.

链接:Qt:如何使用QTimer每10秒向QTextBrowser打印一条消息?

在上面链接的帖子中,我简化了我想要做的任务,只是说我想按一个按钮并让它在QTextBrowser每10秒显示一次.我当时难以QTimer上班,所以我想如果我能QTimer上班,那么我就能完成任务.

我真正想做的是从文件中读取行,每2500行后我想打印一条消息然后等待10秒.

伪代码:

while(not at the end of the file)
{
   read in 2500 lines 

   print a message

   wait 10 seconds
}
Run Code Online (Sandbox Code Playgroud)

QTimer很好,但它与我想要的相反.而不是发送消息并等待10秒,首先他们等待10秒,超时,然后发送消息.

所以为了让它以我想要的方式工作,我首先调用了printMessage()SLOT然后我做了一个SLOT stopTimer(),它只是停止了计时器.所以在10秒过后,它将简单地调用stopTimer()然后继续处理输入文件.

在真正的问题上:

QTimer在通过代码之前,Qt不会等待a 完成.我希望代码在读取下一行2500行代码之前等待整整10秒.我发现它QTimer有一个isActive()返回bool值的函数.

因此,在我希望完成10秒延迟的地方,我将以下内容:

while(timer->isActive());
Run Code Online (Sandbox Code Playgroud)

我认为程序将在此循环中保持10秒钟,然后在QTimer超时后退出,因为条件将为假.问题是它不会退出循环,因为计时器的状态永远不会改变,无论它在这个循环中等待多长时间!我检查了调试器并且isActive( )无论经过的时间如何都保持为真.

然后我省略了while(timer->isActive())循环并在调试器中观察了计时器.看起来计时器实际上并没有开始计时,直到它退出时(不在文件的末尾).所以我相信,因为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
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

很抱歉这篇文章很长,如果我的代码可能出现任何语法错误.我的开发机器上没有互联网访问权限,所以我不得不在这里重新输入.

Ser*_*nov 7

做一些事情while(timer.isActive())根本不是一个好主意,因为它会导致你的应用程序消耗大约100%的CPU时间.它还会导致应用程序永远不会返回到执行计时器的实际代码的事件处理循环,这就是它冻结的原因.

如果您仍想使用此方法,则应在循环中调用QCoreApplication :: processEvents().它会暂时将控制权传递回事件循环,因此会导致计时器超时.您可以在启动之前调用timer.setSingleShot(true),而不是将timeout()连接到stopTimer(),这将导致它在第一次超时后自动停止.

请注意,在每次按下按钮时创建新计时器时,会出现内存泄漏.当然,他们是你的形式的孩子,将被摧毁,但只有当形式被摧毁.

如果您想要更优雅的方法,可以创建一个单独的类来读取该文件.在构造函数中,您将打开文件和流,这应该是此类中的字段.这个类还应该有一个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()));
}
Run Code Online (Sandbox Code Playgroud)

这是一种更重量级的方法,但这是异步事件处理的代价.您也可以将所有这些内容放入Form类中,但我认为使用单独的类更好.

正如丹尼尔指出的那样,如果读取2500行需要很长时间,比如5秒,则在读取完成后10秒钟后,即启动后15秒,将打印该消息.如果你希望无论读取多长时间,大约每10秒打印一次消息,你应该QTimer timer在类中添加一个字段,将它的timeout()信号连接到MyFileReader构造函数中的readMore()槽,然后在startReading中( )方法在调用readMore()之前调用timer.start().现在,在readMore()结束时执行以下操作:

    if (stream.atEnd()) {
       timer.stop();
       emit finished();
    }
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您需要一个QTimer字段,因为您无法取消QTimer :: singleShot()调用,但如果您已到达流的末尾,则需要执行此操作,否则您的readMore()将继续被调用一次又一次,即使没有什么可读的.请注意,即使在这种情况下,如果读取2500行的时间超过这10秒,则仍然可以比每10秒更频繁地显示该消息.如果你只想要10秒,你应该检查循环中的经过时间,但我认为这是一种矫枉过正,除非你期望读数非常慢.

略微偏离主题,但如果您想要一种简单的方法来避免内存泄漏,您也可以在构造函数中执行此操作:

connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
Run Code Online (Sandbox Code Playgroud)

当您emit finished()发生这种情况时,它会自动标记您的阅读器,一旦发生这种情况,一旦控件返回到事件循环,阅读器就会被删除.也就是说,在连接到finished()信号的所有插槽返回之后.这种方法允许您只在堆上分配MyFileReader,然后丢弃指针而不必担心内存泄漏.