我怎样才能等到 Qt 的 keyevents 中的每个进程完成?

Posted

技术标签:

【中文标题】我怎样才能等到 Qt 的 keyevents 中的每个进程完成?【英文标题】:How can i wait until the every process finished in keyevents in Qt? 【发布时间】:2017-08-28 06:57:20 【问题描述】:

我创建了一个按键事件,如果我按下按键“A”,它将执行功能 A()。A() 的过程将持续 2 秒。如果我像 4 次/2 秒那样快速按下键,我想等到每个过程完成。我测试发现,如果我在 4 次/2 秒内按键,它会首先在以后的按键事件中执行该过程。我如何才能等到每个过程在 keyevents 中完成?我必须尝试使用​​线程和互斥锁。但是有些不对劲。第一次用mutex,不知道怎么解决。

int g = 0;
void MainWindow::keyPressEvent(QKeyEvent *event)

 int keyCode = event->key();
 if(keyCode == Qt::Key_A) 
    qDebug() << "da";
    a->start();
 



void MyThread::run()// i try to block the second time process while press the key so quickly


  mutex->lock();
  ...//process:last for 2s
  g++;
  mutex->unlock();

【问题讨论】:

你的线程应该做什么?我不明白你为什么需要一个线程。只需维护已启动进程的计数器。进程完成时使用QProcess::finished 信号通知。当您收到此类信号时减少计数器。当计数器达到 0 时,您将知道所有进程都已完成。虽然我不明白这一切有什么意义。如果您稍微解释一下您的程序,将会很有帮助。 我已经更新了代码。例如我想在A()中2s处理后更新g的值。但是,如果我这么快按下键,它就没有更新 g 并接收新的键事件并再次从第一行执行 A()。所以我想设置一个互斥锁来防止新事件的发生,并确保它可以通过 A() 并更新 g。但是代码不工作 为什么要在启动进程两秒后更新g?为什么不能立即更新?您应该更详细地解释您要达到的目标,因为现在它没有任何意义。如果您想在进程运行时阻止某个键事件,只需在您的键事件中添加check the state of the process。 因为我已经设置了一个计时器,在更新之前使用 g 进行 2 秒的处理。所以我想把所有的keyevents保存在队列中,每2s一个一个地执行(等到进程完成再做下一步) 【参考方案1】:

如果我理解正确,您可以使用希望操作 A() 在按下按钮 A 时执行。但是,您在单独的线程中执行操作 A(),因为您不想阻塞 GUI 线程并冻结您的 UI。

如果您不想“跳过”按钮按下,解决方法很简单:

    创建一些名为context 的虚拟QObjectQObject* context = new QObject; 启动并运行名为threadQThreadQThread* thread = new QThread this ; thread-&gt;start(); ... 然后将context移动到thread object-&gt;moveToThread(thread)。 在MainWindow 类中创建信号,例如runA()。 将此信号与A() 操作连接:connect(this, &amp;MainWindow::runA, context, [] () A(); ); 每次按下按钮“A”时都会发出这个信号:emit runA();

每次发出信号时,都会将要执行的操作A() 的事件发布到`threads 事件循环。所有事件将按它们发布的顺序处理


#pragma once

#include <QThread>
#include <QDebug>
#include <QWidget>
#include <QKeyEvent>

class MainWindow : public QWidget

    Q_OBJECT
private:
    QThread* thread;
    QObject* context;

public:
    MainWindow()
        : thread new QThread ,
          context new QObject 
    
        context->moveToThread(thread);
        connect(this, &MainWindow::doAction, context, [this] () 
            Action();
        );
        thread->start();
    
    ~MainWindow() 
        context->deleteLater();
        thread->deleteLater();
    

signals:
    void doAction();

public:
    void keyPressEvent(QKeyEvent* event)
    
        int keyCode = event->key();
        if(keyCode == Qt::Key_A) 
            emit doAction();
        
    

    void Action() 
        qDebug() << "Action is being executed.";
        QThread::currentThread()->sleep(2); // imitate some long calculation
    
;

无论您多快按下按钮“A”。 “正在执行行动。”将以精确的 2 秒间隔和精确的按钮按下次数打印。

【讨论】:

感谢您的回答。但在我的情况下,我想将所有键事件保存在队列中,并每 2 秒一个一个地执行(等到进程完成再做下一步)。例如,我按键 2 次/秒,一个进程将持续 2 秒。所以在 2 秒内我按了 4 次键。我想将这4个事件保存在队列中,每2s一个一个执行。 用 A() 动作连接这个信号:connect(this, &MainWindow::runA, context, [] () A(); );无法工作 @MotoJack,好的,我知道第一部分不适合您。但是为什么“用A() 动作连接这个信号”不起作用呢? 我给了[] () A(); 只是作为一个占位符。请参阅我的测试工作示例。【参考方案2】:

一个快速的解决方案是在只有一个线程的线程池上使用QtConcurrent::run。这使您不必管理线程的生命周期,这是一种昂贵的资源 - 例如。它会在一段时间未使用后被丢弃以释放资源。

// https://github.com/KubaO/***n/tree/master/questions/single-job-lambda-45913311
#include <QtWidgets>
#include <QtConcurrent>

class LogWindow : public QPlainTextEdit 
   Q_OBJECT
   QThreadPool m_pool;
   int g = ; // can be accessed from the worker thread only
   void keyReleaseEvent(QKeyEvent * event) override 
      if (event->key() == Qt::Key_A)
         QtConcurrent::run(&m_pool, this, &LogWindow::method);
      QPlainTextEdit::keyReleaseEvent(event);
   
   /// This method must be thread-safe. It is never reentered.
   void method() 
      QThread::sleep(2); // block for two seconds
      g++;
      emit done(g);
   
   Q_SIGNAL void done(int);
public:
   LogWindow(QWidget * parent = ) : QPlainTextEditparent 
      appendPlainText("Press and release 'a' a few times.\n");
      m_pool.setMaxThreadCount(1);
      connect(this, &LogWindow::done, this, [this](int val)
         appendPlainText(QString::number(val));
      );
   
;

int main(int argc, char ** argv) 
   QApplication appargc, argv;
   LogWindow w;
   w.show();
   return app.exec();


#include "main.moc"

【讨论】:

【参考方案3】:

不清楚需要什么:

如果您想为每个按下的键启动一个线程,请使用QtConcurrent::run(A);,而A() 内没有任何互斥锁 如果您想为每个按下的键执行A(),但不是同时使用QtConcurrent::run(A);A() 内的互斥锁(就像您所做的那样)

【讨论】:

我已经更新了代码。例如我想在A()中经过2s处理后更新g的值。但是,如果我这么快按下键,它就没有更新 g 并接收新的键事件并再次从第一行执行 A()。所以我想设置一个互斥锁来防止新事件的到来,并确保它可以通过 A() 并更新 g。但是代码不起作用 这并没有提供问题的答案。您可以search for similar questions,或参考页面右侧的相关和链接问题找到答案。如果您有一个相关但不同的问题,ask a new question,并包含指向此问题的链接以帮助提供上下文。见:Ask questions, get answers, no distractions

以上是关于我怎样才能等到 Qt 的 keyevents 中的每个进程完成?的主要内容,如果未能解决你的问题,请参考以下文章

怎样模拟发送key event按键消息和touch event触摸消息?

怎样才能让qt中的combobox不自动补全输入的内容

Qt:我怎样才能减少我的 exe 的“重量”

迅速,我如何才能等到收到服务器响应后再继续?

Qt Creator 怎样设置才能实现静态编译

从 QML 生成 KeyEvent