QTimer::singleShot 在 qkeyevent 中不起作用
QTimer::singleShot not work in the qkeyevent
我创建了一个按键事件,如果我按下 "A" 键,它将执行功能 A()
。
在 A() 函数中,我将全局参数 "g" 递增 1,并创建一个 QTimer::singleShot
等待 2 秒并打印 "g" 的值。比如"g"的初始值为0,当我按两次"A"键时,"g"的输出值第一次应该是1,第二次应该是2。
但是,当我在2秒内按下按键时,我发现QTimer::singleShot
第一次不起作用,输出是"first:g=2,second:g=2" .为什么输出是 "first:g=2,second:g=2" 而不是 "first:g=1,second:g=2"?还有为什么 QTimer::singleShot
第一次不工作,它只是同时打印值,没有等待 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
}
如果我在 2 秒内按下按键,输出是“2, 2”而不是“1,2”。这意味着 QTimer::singleShot
不起作用。当我如此快速地按下键时,我该如何获得真正的价值。
a()
插槽在插槽运行时简单地输出 g
的当前值。如果在第一个单发实际 触发之前按下两个键, 将导致 g
增加 两次 键释放事件函数在第一次输出发生之前。
事实上,如果您全力以赴并在最初的两秒内按一个键 314159 次,您将看到大量 314159
值输出。
一种方法可能是将 g
的更新推迟到最后可能的时刻,例如:
int g = 0;
void MainWindow::keyReleaseEvent(QKeyEvent *event) {
qtimer1->singleShot(2000, this, SLOT(a()));
}
a() {
qdebug << (++g);
}
尽管如果某处有一些 other 代码依赖于 g
在原始点更新,这将不起作用。
不幸的是,singleShot
生成了一个 timeout
事件,其中没有附加信息。如果您需要额外信息(例如g
修改时的值),您可以创建自己的线程在键释放事件中,给它当前值作为成员存储,然后启动线程 - 然后它将根据需要休眠并打印其 stored 值而不是当前 g
.
下面是一些示例代码,展示了这一点。 MyTask
代表您的键释放事件函数,因为它启动一个单独的线程来管理时间和要打印的数据。每次它得到一个 "event" (在这种情况下这是一个简单的循环,但在你的情况下,它将响应一个被释放的键),它启动一个线程,将 [= 的当前值传递给它14=]。线程对象存储 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();
}
运行 此代码向您展示了会发生什么:
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
撇开线程笑话不谈,您可以在 lambda 中捕获 g
的当前值并提交 lambda 以供计时器执行。如果您坚持使用 Qt 4 或 C++11 之前的编译器,您可以显式排队要提交给方法的值。
这是一个完整的例子:
// https://github.com/KubaO/Whosebugn/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"
如果您仍然对为什么线程笑话是个笑话感到困惑:它取笑了 "if unsure, throw a thread at it" 那些不知道更好的圈子所拥护的 public 方法.
我创建了一个按键事件,如果我按下 "A" 键,它将执行功能 A()
。
在 A() 函数中,我将全局参数 "g" 递增 1,并创建一个 QTimer::singleShot
等待 2 秒并打印 "g" 的值。比如"g"的初始值为0,当我按两次"A"键时,"g"的输出值第一次应该是1,第二次应该是2。
但是,当我在2秒内按下按键时,我发现QTimer::singleShot
第一次不起作用,输出是"first:g=2,second:g=2" .为什么输出是 "first:g=2,second:g=2" 而不是 "first:g=1,second:g=2"?还有为什么 QTimer::singleShot
第一次不工作,它只是同时打印值,没有等待 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
}
如果我在 2 秒内按下按键,输出是“2, 2”而不是“1,2”。这意味着 QTimer::singleShot
不起作用。当我如此快速地按下键时,我该如何获得真正的价值。
a()
插槽在插槽运行时简单地输出 g
的当前值。如果在第一个单发实际 触发之前按下两个键, 将导致 g
增加 两次 键释放事件函数在第一次输出发生之前。
事实上,如果您全力以赴并在最初的两秒内按一个键 314159 次,您将看到大量 314159
值输出。
一种方法可能是将 g
的更新推迟到最后可能的时刻,例如:
int g = 0;
void MainWindow::keyReleaseEvent(QKeyEvent *event) {
qtimer1->singleShot(2000, this, SLOT(a()));
}
a() {
qdebug << (++g);
}
尽管如果某处有一些 other 代码依赖于 g
在原始点更新,这将不起作用。
不幸的是,singleShot
生成了一个 timeout
事件,其中没有附加信息。如果您需要额外信息(例如g
修改时的值),您可以创建自己的线程在键释放事件中,给它当前值作为成员存储,然后启动线程 - 然后它将根据需要休眠并打印其 stored 值而不是当前 g
.
下面是一些示例代码,展示了这一点。 MyTask
代表您的键释放事件函数,因为它启动一个单独的线程来管理时间和要打印的数据。每次它得到一个 "event" (在这种情况下这是一个简单的循环,但在你的情况下,它将响应一个被释放的键),它启动一个线程,将 [= 的当前值传递给它14=]。线程对象存储 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();
}
运行 此代码向您展示了会发生什么:
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
撇开线程笑话不谈,您可以在 lambda 中捕获 g
的当前值并提交 lambda 以供计时器执行。如果您坚持使用 Qt 4 或 C++11 之前的编译器,您可以显式排队要提交给方法的值。
这是一个完整的例子:
// https://github.com/KubaO/Whosebugn/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"
如果您仍然对为什么线程笑话是个笑话感到困惑:它取笑了 "if unsure, throw a thread at it" 那些不知道更好的圈子所拥护的 public 方法.