c++qtsignals-slotsqtimer

QTimer::singleShot not work in the qkeyevent


I create a keyevent which if I push key "A" it will do the function A().

In the A() function, I increment global parameter "g" by 1 and create a QTimer::singleShot to wait 2 seconds and print the value of "g". For example, the initial value of "g" is 0. When I push Key "A" twice, the output value of "g" should be 1 in the first time and 2 in the second time.

However, when I push the key in 2 seconds, I found that the QTimer::singleShot not work in the first time and the output is "first:g=2,second:g=2". Why the output is "first:g=2,second:g=2" not "first:g=1,second:g=2"? Also why the QTimer::singleShot not work in the first time,it just print the values in the same time, did not wait 2s.

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

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

If I push the key in 2s,the output are "2, 2" not "1,2". It means that QTimer::singleShot not work. How can i do to get the true values when i push the key so quickly.


Solution

  • The a() slot simply outputs the current value of g at the time the slot runs. If you press two keys before the first one-shot is actually fired, that will cause g to have been incremented twice by the key release event function before the first output has happened.

    In fact, if you go whole hog and press a key a hundred times within that initial two seconds, what you will see is a whole lot of 100 values output.

    One possible approach may be to defer the updating of g until the last possible moment, such as with:

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

    although this won't work if there's some other piece of code somewhere that relies on g being updated at the original point.

    Unfortunately, singleShot generates a timeout event that carries no additional information. If you need extra information (such as the value of g at the time it was modified), you can create your own thread in the key release event, give it the current value to be stored as a member, then start the thread - it will then sleep for as long as necessary and print its stored value rather than the current g.


    Here's some example code that shows this in action. The MyTask represents your key release event function in that it starts an individual thread to manage the timing and data to print. Each time it gets an "event" (which is generated by a simple loop in this case but, in yours, it will be in response to a key being released), it kicks off a thread, passing it the current value of g. The thread object stores that g for later use.

    And, once the thread has waited a suitable time, it prints out that stored value of g, regardless of what the main task has done to the real g in the meantime.

    #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();
    }
    

    Running this code shows you what happens. Even though the main thread is updating g many times before the worker threads start using it, the value used in the worker threads is what you expect (the value that existed when the worker thread was created):

    10:49:48 MyTask, g <- 1
    10:49:49 MyTask, g <- 2
    10:49:50 MyTask, g <- 3
    10:49:51 MyTask, g <- 4
    10:49:52 MyTask, g <- 5
    
    10:49:54 MyThread, g is 1
    10:49:54 MyTask, thread #1 finished
    
    10:49:55 MyThread, g is 2
    10:49:55 MyTask, thread #2 finished
    
    10:49:56 MyThread, g is 3
    10:49:56 MyTask, thread #3 finished
    
    10:49:57 MyThread, g is 4
    10:49:57 MyTask, thread #4 finished
    
    10:49:58 MyThread, g is 5
    10:49:58 MyTask, thread #5 finished