【问题标题】:How can i wait until the every process finished in keyevents in Qt?我怎样才能等到 Qt 的 keyevents 中的每个进程完成?
【发布时间】: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一个一个地执行(等到进程完成再做下一步)

标签: c++ qt signals-slots


【解决方案1】:

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

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

  1. 创建一些名为context 的虚拟QObject
    QObject* context = new QObject;
  2. 启动并运行名为threadQThread
    QThread* thread = new QThread{ this }; thread-&gt;start(); ...
  3. 然后将context移动到thread object-&gt;moveToThread(thread)
  4. MainWindow 类中创建信号,例如runA()
  5. 将此信号与A() 操作连接:
    connect(this, &amp;MainWindow::runA, context, [] () { A(); });
  6. 每次按下按钮“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/stackoverflown/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 = {}) : QPlainTextEdit{parent} {
      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 app{argc, 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
    猜你喜欢
    • 1970-01-01
    • 2011-12-14
    • 2016-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多