QTimer :: singleShot在qkeyevent中不起作用

时间:2017-08-28 01:00:50

标签: c++ qt signals-slots qtimer

我创建了一个keyevent,如果我按下“A”键,它将执行函数A()

在A()函数中,我将全局参数“g”递增1并创建QTimer::singleShot等待2秒并打印“g”的值。例如,“g”的初始值为0.当我按下“A”键两次时,“g”的输出值应该是第一次为1而第二次为2。

但是,当我在2秒内按下键时,我发现QTimer::singleShot在第一次不起作用,输出“第一次:g = 2,第二次:g = 2”< / strong>即可。为什么输出“第一:g = 2,第二:g = 2”不是“第一:g = 1,第二:g = 2”?同样为什么QTimer::singleShot第一次不起作用,它只是在同一时间打印值,没等2秒。

int g = 0;
void MainWindow::keyReleaseEvent(QKeyEvent *event) {
  Qt::Key_A: 
    g++;
    qtimer1->singleShot(2000, this, SLOT(a()));
}

//slots
a() {
  qdebug << g;//print value
}

如果我按2秒键,输出为“2,2”而不是“1,2”。这意味着QTimer::singleShot不起作用。当我如此快速地按下键时,我该如何才能获得真正的价值。

2 个答案:

答案 0 :(得分:2)

a()插槽只在插槽运行时输出g的当前值。如果在实际触发的第一个单击之前按两个键,将导致g通过键释放事件函数增加两次 > 之前第一个输出已经发生。

事实上,如果你在最初的两秒钟内完全按下并按键314159次,你会看到输出的314159值很多。

一种方法可能是将g的更新推迟到最后一刻,例如:

int g = 0;
void MainWindow::keyReleaseEvent(QKeyEvent *event) {
    qtimer1->singleShot(2000, this, SLOT(a()));
}
a() {
  qdebug << (++g);
}

虽然如果某些其他代码片段依赖于g在原始位置更新,这将无效。

不幸的是,singleShot生成的timeout事件没有附加信息。如果您需要额外信息(例如修改时g的值),您可以在密钥发布事件中创建自己的线程,给它当前值作为成员存储,然后启动线程 - 然后它将根据需要休眠并打印其存储的值而不是当前的g

这是一些显示此操作的示例代码。 MyTask代表您的密钥发布事件功能,因为它启动一个单独的线程来管理要打印的时序和数据。每次它获得一个“事件”(在这种情况下这是一个简单的循环,但在你的情况下,它将响应一个密钥被释放),它启动一个线程,传递它的当前值{{1} }。线程对象存储g以供以后使用。

并且,一旦线程等待了一段合适的时间,它就会打印出g的存储值,而不管主要任务在此期间对真实g做了什么。

g

运行此代码可显示发生的情况:

#include <QtCore>
#include <iostream>

class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread(int useGVal): m_gVal(useGVal) {}
public slots:
    void run()
    {
        QThread::msleep(6000);
        std::cout << QTime::currentTime().toString().toStdString()
            << " MyThread, g is " << m_gVal << std::endl;
    }
private:
    int m_gVal;
};

class MyTask : public QObject
{
    Q_OBJECT
public:
    MyTask(QObject *parent = 0) : QObject(parent) {}
public slots:
    void run()
    {
        MyThread *x[5];
        for (size_t i = 0; i < sizeof(x) / sizeof(*x); ++i)
        {
            std::cout << QTime::currentTime().toString().toStdString()
                << " MyTask, g <- " << ++g << std::endl;
            x[i] = new MyThread(g);
            x[i]->start();
            QThread::msleep(1000);
        }
        for (int i = 0; i < 5; ++i)
        {
            x[i]->wait();
            std::cout << QTime::currentTime().toString().toStdString()
                << " MyTask, thread #" << (i + 1) << " finished"
                << std::endl;
        }
        emit finished();
    }
signals:
    void finished();
private:
    int g = 0;
};

#include "main.moc"

int main(int argc, char *argv[])
{
    QCoreApplication appl(argc, argv);
    MyTask *task = new MyTask(&appl);
    QObject::connect(task, SIGNAL(finished()), &appl, SLOT(quit()));
    QTimer::singleShot(0, task, SLOT(run()));

    return appl.exec();
}

答案 1 :(得分:0)

除了线程外,您可以捕获lambda中g的当前值并提交lambda以供计时器执行。如果您坚持使用Qt 4或C ++ 11之前的编译器,则可以显式排列要提交给方法的值。

这是一个完整的例子:

// https://github.com/KubaO/stackoverflown/tree/master/questions/timer-lambda-notadevil-45910623
#include <QtWidgets>

class LogWindow : public QPlainTextEdit {
   Q_OBJECT
   int g = {};
#if __cplusplus < 201103L
   // C++98
   QQueue<int> logQueue;
#endif
   void keyReleaseEvent(QKeyEvent * event) override {
      if (event->key() == Qt::Key_A) {
         g++;
#if __cplusplus >= 201402L
         // C++14
         QTimer::singleShot(2000, this, [this, val=g]{ log(val); });
#elif __cplusplus >= 201103L
         // C++11
         int val = g;
         QTimer::singleShot(2000, this, [=]{ log(val); });
#else
         // C++98
         logQueue.enqueue(g);
         QTimer::singleShot(2000, this, SLOT(log()));
#endif
      }
      QPlainTextEdit::keyReleaseEvent(event);
   }
   void log(int value) {
      appendPlainText(QString::number(value));
   }
   Q_SLOT void log() { // becasue MOC doesn't define __cplusplus :(
#if __cplusplus < 201103L
      // C++98
      log(logQueue.dequeue());
#endif
   }
};

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   LogWindow w;
   w.appendPlainText("Press and release 'a' a few times.\n");
   w.show();
   return app.exec();
}

#include "main.moc"

如果你仍然对为什么线程玩笑是一个笑话感到困惑:它会取笑&#34;如果不确定,可以在它上面写一个帖子&#34;公众的不了解任何更好的圈子所支持的方法。