Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper usage of QProcess

Tags:

qt

qprocess

  • Platform: Qt 4.8.2, Win 7

Please consider the following logic flow:

1. App started
2. functionA() triggered
3. the app periodically capture some images to external files
4. end of functionA()
5. the app create a video from captured images, using ffmpeg as external program
6. [step 2 -> step 5] may be repeated.
7. App quit

To achieve the flow, I use QProcess to start external program for me to join the images, but I got confused with the proper usage pattern of QProcess. (I don't care about the console messages of ffmpeg, i determine the success of step 5 by checking if the video file is created.)

Attempt 1

void MyWidget::createAVI()
{
    checkAndDeleteAVI();
    process = new QProcess(this); // process_ defined as class member;
    process->start("ffmpeg -f images2 ....");
    process->waitForFinished(-1);  // (a)
    // (b)
}

At (a), I read the documentation the this call can freeze the main GUI, so should I call from QThread/QRunnable?

At (b), am I missed something here? as when I attempt to close the app, (step 7 in the flow), the app crashes, and I thought that the spawned QProcess is not properly released.

Attempt 2

I wrote a wrapper class of QProcess as follow:

Launcher.h

class Launcher : public QObject
{
    Q_OBJECT
public:
    /** constructor */
    explicit Launcher(QObject *parent = 0);
    /** destructor */
    ~Launcher() {
        if (started_ && process_->state() != QProcess::NotRunning)
            process_->kill();
    } // end_dtor(Launcher)
Q_SIGNALS:
    void feedbackLog(QString log);
public Q_SLOTS:
    void launch(QString program, QStringList argList);
private:
    QProcess * process_;
    bool started_;
private Q_SLOTS:
    void error(QProcess::ProcessError error);
    void finished(int exitCode, QProcess::ExitStatus status);
    void stateChanged(QProcess::ProcessState state);
}; // end_class(Launcher)

#endif // LAUNCHER_H

Launcher.cpp

#include "launcher.h"
#include <QCoreApplication>
#include <QtDebug>

Launcher::Launcher(QObject *parent) : QObject(parent), started_(false)
{
    process_ = new QProcess(this);
    connect(process_,
            SIGNAL(error(QProcess::ProcessError)),
            SLOT(error(QProcess::ProcessError)));
    connect(process_,
            SIGNAL(finished(int, QProcess::ExitStatus)),
            SLOT(finished(int, QProcess::ExitStatus)));
    connect(process_,
            SIGNAL(stateChanged(QProcess::ProcessState)),
            SLOT(stateChanged(QProcess::ProcessState)));
} // end_ctor(ExternalLauncher)

void Launcher::launch(QString program, QStringList argList)
{
    started_ = true;
    process_->start(program, argList);
    process_->waitForFinished(-1); // (c)
    Q_EMIT feedbackLog(process_->readAllStandardOutput());
    process_->close();
} // end Launcher::launch()

void Launcher::error(QProcess::ProcessError error)
{
    /* just feedback some text about the error */
} // end_slot(Launcher::error)

void Launcher::finished(int exitCode, QProcess::ExitStatus status)
{
    started_ = false;
    /* feedback some text about finished */
} // end_slot (Launcher::finished)

void Launcher::stateChanged(QProcess::ProcessState state)
{
    qDebug() << "Luancher::stateChanged" << state;
}

How I use the Launcher:

void MyWidget::createAVI()
{
    checkAndDeleteAVI();
    launcher_.launch("ffmpeg", "argsList"); // launcher_ defined as class member;
}

So, at (c), is it unnecessary to waitForFinished()? (as I read some info that I should not mix up waitForXXX() and signal/slot framework for QProcess)

Also, is there something I missed for the Launcher class, as I also experience app crash when using this approach.

Main question: In general, when to call QProcess::terminate() / QProcess::kill(), and when to delete the QProcess object?

Thanks

like image 382
YamHon.CHAN Avatar asked Nov 02 '22 22:11

YamHon.CHAN


1 Answers

You don't need to waitForFinished(), you'll receive the signal about it, so why wait? Instead, you might want to waitForStarted() in launch() to be sure that the process has started successfully. Of course in this case you'll need to change the way you use your Launcher - don't destroy it just after launch().

You don't need to terminate()/kill() the process if it has finished, only if you need to stop it prematurely. You may delete it when you receive finished() or error() signal using process_->deleteLater() (you can't just delete process_ while you are in the slot) or in your ~Launcher(), providing that it won't be called until the process is finished.

like image 156
Paul Avatar answered Nov 15 '22 11:11

Paul