Steve Lorimer
Steve Lorimer

Reputation: 28659

Any way to tell when forked/exec'd Qt application's main window is being displayed?

I have a launcher application which will fork and exec a Qt application when the user clicks on the relevant icon.

For visual feedback, I want to display a wait cursor from the time when the user selects an icon, until the requested app's main window is on-screen.

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();
}

The problem that I'm having is that in my launcher app, I get back the child process's pid from fork immediately, but it still takes some time for the child process to exec and for its main window to show up on-screen.

As such, my call to QApplication::restoreOverrideCursor() executes immediately, and there is no visual cue to the user that the application is still being launched.

Is there some way I can get signalled that the child is running?

Upvotes: 0

Views: 430

Answers (2)

Mohamed Hamzaoui
Mohamed Hamzaoui

Reputation: 345

I suggest you start a thread (with QtConcurrentRun and handled with QFutureWatcher) which is responsible for starting child process and waiting for the end displaying GUI. This mechanism can be done using a pipe in wich you send data from the place in child code where you believe that the GUI is displayed (catch showEvent() or the end of the MainWindow constrcutor...). The thread in parent process must sleep until receiving data on the pipe for waking up.

there is a lot of example of using pipe in linux. Also you can use Unix socket, shared memory or other IPC or maybe you can test with creatin a file in /tmp but is a bad idea.

Another simple idea is to proceed with QProcess and write from your new program some string on standard output to signal end of GUI display (to test...):

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());
    ...
}

Upvotes: 0

Thomas McGuire
Thomas McGuire

Reputation: 5466

I can see two ways to implement this:

  1. With explicit inter-process communication. The child app can tell the launcher app once its main window has been created, for example via a DBus call, standard output, shared memory or any other of the many IPC mechanisms available. There is a startup notification library for X11 that uses explicit IPC by passing X messages, used by KDE and GNOME.

  2. Depending on the OS and the used window manager in there, you might be able to ask the window manager for a list of opened windows and be notified when a new window is created. I don't think there is a Qt API for that, you'd need to use platform-specific code. For X11, you'd do something similar to the xwininfo program. Also, there is a LGPL-licensed KDE Frameworks API for this called KWindowSystem, which has a windowAdded signal. I haven't check on which platforms this is actually implemented, the docs suggest at least X11 and Mac OS.

Upvotes: 3

Related Questions