使用 lambda 函数了解 QProcess 信号的行为

Posted

技术标签:

【中文标题】使用 lambda 函数了解 QProcess 信号的行为【英文标题】:Understanding behaviour of QProcess signals with a lambda function 【发布时间】:2016-03-29 16:29:01 【问题描述】:

这是 Qt 5.4.0 中的一个问题。并已在 Qt 5.6.0 中修复


我有一个允许用户使用QProcess 启动进程的应用程序。 最初我想将QProcess::finished 信号连接到一个 lambda 函数,但由于它是一个重载函数,由于连接哪个函数不明确,似乎无法完成。

因此,我尝试了监控QProcess的状态变化。

void MainWindow::on_actionLaunchApplication_triggered()

    // launch the file open dialog for the user to select a file
    QString filePath = QFileDialog::getOpenFileName(this, "Select Application to Launch", "/Applications");

    if(filePath == "")
        return;

    QProcess* proc = new QProcess(this);
    // can't connect to QProcess::exited with lambda, due to its overloaded function, so will check state changed instead
    connect(proc, &QProcess::stateChanged, [filePath, proc, this](QProcess::ProcessState state)
        if(state == QProcess::NotRunning)
        
            qDebug << "Deleting proc";
            disconnect(proc, &QProcess::stateChanged, 0 , 0);
            proc->deleteLater();
        
    );
    proc->start(filePath);

通常这会按预期工作;选择的应用程序被执行,并且可以选择不同的应用程序以这种方式一个接一个地运行。退出这样的应用程序会导致执行删除 QProcess 的 tidyup 代码。

但是,如果已使用 QProcess 启动的应用程序退出,然后再次选择执行,则它无法启动,而是立即从 lambda 函数中对 deleteLater 的调用中删除该进程。

那么,发生了什么?考虑到每次都会创建一个新的QProcess,为什么每个应用程序都是第一次工作,但如果这样的应用程序退出并选择再次启动,它会立即被删除?

我完全清楚我可以在没有 lambda 函数或通过 SIGNAL 和 SLOT 宏的情况下连接到 QProcess::finished。这个问题是学术性的,我正在寻找对这里发生的事情的理解。


到目前为止,响应答案和 cmets,看起来这是一个 Qt 错误。连接到QProcess::finished 插槽会导致相同的问题,即仅在第一次启动应用程序。

// launch the file open dialog for the user to select a file
QString filePath = QFileDialog::getOpenFileName(this, "Select Application to Launch", "/Applications");

if(filePath == "")
    return;

QProcess* proc = new QProcess();
connect(proc, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [filePath, proc, this](int exitStatus) 
    Q_UNUSED(exitStatus);
    Log("Deleting proc for launched app");
    proc->deleteLater();
    proc->disconnect(proc, static_cast<void (QProcess::*)(int)>(&QProcess::finished), 0, 0);
);
proc->start(filePath);

【问题讨论】:

你能在start 之后测试proc 的状态吗?可能第二次不在同一个初始状态。 现在当您说QProcess::exited 时,您的意思是QProcess::finished,对吗?如果是这样,文档显示了如何使用函数指针语法连接到它的示例。 @thuga 是的,你是对的,它是 QProcess::finished。感谢您指出这一点。 @thuga,更新到 Qt 5.6.0 解决了这个问题。 这很有趣。想知道他们改变了什么。 【参考方案1】:

其实可以连接信号!您所要做的就是告诉编译器它应该选择哪个信号,因为它无法决定这一点。

这个问题有一个很好的答案:Qt5 overloaded Signals and Slots。

这不会解决您的奇怪删除行为问题,但也许问题会通过这种方式自行解决。

【讨论】:

谢谢@Felix,知道这真的很有用。有趣的是,它并没有解决问题,并且尝试第二次启动相同的应用程序无法启动。我开始认为这是 Qt 的内部问题。【参考方案2】:

finished 信号表示状态转换。但相反,您检查的是静态状态,而不是转换。

你应该保留一个与进程相关的属性来指示它正在运行或正在启动,然后只有在它停止运行或启动失败时才删除该进程。

void MainWindow::on_actionLaunchApplication_triggered()

  auto locations = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
  if (locations.isEmpty())
    locations << QString();

  auto filePath = QFileDialog::getOpenFileName(this, "Select Application to Launch", 
                                               locations.first());
  if (filePath.isEmpty())
    return;

  bool wasActive = false; // upon capture, it becomes a per-process field
  auto proc = new QProcess(this);
  connect(proc, &QProcess::stateChanged, [=](QProcess::ProcessState state) mutable 
    if (state == QProcess::Running) 
      qDebug() << "Process" << proc << "is running";
      wasActive = true;
    
    else if (state == QProcess::Starting) 
      qDebug() << "Process" << proc << "is starting";
      wasActive = true;
    
    else if (state == QProcess::NotRunning && wasActive) 
      qDebug() << "Will delete a formerly active process" << proc;
      proc->deleteLater();
    
    else /* if (state == QProcess::NotRunning) */
      qDebug() << "Ignoring a non-running process" << proc;
  );
  proc->start(filePath);

【讨论】:

谢谢 Kuba,我尝试过类似的方法,但无论是这样,你的代码也不起作用;同样的情况是,第二次启动应用程序会首先导致QProcess::NotRunning 的状态并退出。相反,第一次启动应用程序时,它会进入QProcess::Starting,然后是QProcess::Running,最后在退出时调用QProcess::NotRunning。因此,即使实例化了新的 QProcess,我仍然不知道为什么第二次状态会有所不同。 @TheDarkKnight 我不知道您是否使用 Qt 5.6,但如果您使用的是,它会发出 errorOccurred 信号吗? @thuga,我目前正在使用 5.4,但现在正在下载 5.6,并会报告。 问题在 Qt 5.6.0 中得到解决。谢谢大家的讨论。

以上是关于使用 lambda 函数了解 QProcess 信号的行为的主要内容,如果未能解决你的问题,请参考以下文章

了解 Lambda 函数的工作原理 [重复]

跟上 Java 8 – 了解 lambda

了解pandas中的lambda函数

lambda与函数式

了解 Lambda 闭包类型如何删除默认构造函数

了解 Python 中的嵌套 lambda 函数行为