什么方式告诉分叉/执行Qt应用程序的主窗口何时显示?

时间:2016-03-18 21:09:30

标签: c++ linux qt

我有一个启动器应用程序,当用户点击相关图标时,它将forkexec一个Qt应用程序。

对于视觉反馈,我想从用户选择图标的时间开始显示等待光标,直到所请求的应用程序的主窗口显示在屏幕上。

void RocketLauncher::onItemSelect(QListWidgetItem* item)
{
    QApplication::setOverrideCursor(Qt::WaitCursor);

    const AppConfig& app = getAppConfig(item);

    forkAndExec(app.cwd(), app.cmd(), app.args(), app.env());

    // TODO: wait until app's main window is being displayed

    QApplication::restoreOverrideCursor();
}

我遇到的问题是,在我的启动器应用中,我立即从fork返回子进程的pid,但子进程仍需要一些时间{ {1}}以及显示在屏幕上的主窗口。

因此,我对exec的调用立即执行,并且用户没有视觉提示应用程序仍在启动。

有什么方法可以让我发出孩子正在跑的信号吗?

2 个答案:

答案 0 :(得分:3)

我可以看到两种实现方法:

  1. 通过明确的进程间通信。子应用程序可以在创建主窗口后告诉启动器应用程序,例如通过DBus调用,标准输出,共享内存或任何其他可用的IPC机制。 X11有startup notification library通过传递X消息使用显式IPC,由KDE和GNOME使用。

  2. 根据操作系统和使用过的窗口管理器,您可以向窗口管理器询问打开的窗口列表,并在创建新窗口时收到通知。我不认为有Qt API,您需要使用特定于平台的代码。对于X11,您可以执行与xwininfo程序类似的操作。此外,还有一个LGPL许可的KDE Frameworks API,名为KWindowSystem,其windowAdded信号。我还没有检查实际实现的平台,文档建议至少使用X11和Mac OS。

答案 1 :(得分:0)

我建议您启动一个线程(使用QtConcurrentRun并使用QFutureWatcher处理),该线程负责启动子进程并等待结束显示GUI。这种机制可以使用管道来完成,你可以从子代码中的地方发送数据,你可以在那里显示GUI(catch showEvent()或MainWindow constrcutor的结尾......)。父进程中的线程必须休眠,直到在管道上接收数据才能唤醒。

在linux中有很多使用管道的例子。你也可以使用Unix套接字,共享内存或其他IPC,或者你可以在/ tmp中使用creatin测试一个文件,但这是一个坏主意。

另一个简单的想法是继续使用QProcess并从您的新程序中写入标准输出上的一些字符串,以指示GUI显示结束(以测试......):

bool Class::startExternalProgram(QString _file)
{
    QtConcurrent::run(this, &Class::starting, _file);
    return true;
}

bool Class::starting(QString file)
{
    QProcess *p = new QProcess;
    params << file;
    p->start("your program", params);
    if (!p->waitForStarted(5000)) {
        p->close();
        delete p;
        crashFinish(1, QProcess::CrashExit);
        return false;
    }
    else {
        processes.push_back(p);
        /*to handle crash*/
        QObject::connect(p, SIGNAL(finished(int,QProcess::ExitStatus)),
                         this, SLOT(crashFinish(int, QProcess::ExitStatus)));
        p->waitForReadyRead(600000);/*10mn*/
        ... /*handle timeout here*/
        return true;
    }
}

void Class::crashFinish(int, QProcess::ExitStatus) {
    auto p = qobject_cast<QProcess*>(QObject::sender());
    ...
}