CybeX
CybeX

Reputation: 2396

Custom QProcess object to wait for output before returning

note: the title does not convey the question exactly, feel free to edit as is necessary

  1. Info on classes and descriptions
  2. Problem
  3. Code examples, etc
  4. TL;DR

1. Info:

I have created a custom QProcess class called m_Proc.

#include "misc_serv"

using namespace misc_serv;

class GEN_SERVSHARED_EXPORT m_Proc : public QProcess
{
    Q_OBJECT

public:
    int exitCode = -1;
    QString _out;
    s_i_combo si;
    QList<s_i_combo> sch_in_List;

    m_Proc(QList<s_i_combo> i_s_list, QObject *parent = 0);
    m_Proc(s_i_combo i_s_obj, QObject *parent);
    m_Proc(QObject *parent);

    void setProcessBlocks(s_i_combo _si);
    void setProcessBlocks(QList<s_i_combo> _si_list);
    ~m_Proc() {}

private:
    s_i_combo getSearchInput();
    void initConnectors();

public slots:
    void myReadyReadStandardOutput();
    void myFinished(int i);

signals:
    void finishedSignal();
};

The purpose of this class is to use the signals emitted from the QProcess parent, which will allow me to read*() from and write() to the QProcess.

When the final output is reached, the QProcess::finished(int), is connected to a method to assign the final output and exit code of the QProcess

a signal is then emitted from m_Proc which will notify the parent class The signal named (quite creatively) finishedSignal

m_Proc is handled by another parent class named gen_serv.

#include "m_proc.h"

class GEN_SERVSHARED_EXPORT gen_serv : public QObject
{
    Q_OBJECT

private:
    QObject *parent;
    QProcess *p;
    int MSEC = 1000;
    m_Proc *m;

    int exitCode = -1;
    QString _proc_out;

    void init_connector();

private slots:
    void getResults();

public:
    gen_serv(QObject *_parent = new QObject());
    virtual ~gen_serv();
    void runCommand(const QString _prog);
    void runCommand(const QString _prog, const QStringList args);
    void runCommand(const QString _prog, const QString _sSearch, const QString _sInput);
    void runCommand(const QString _prog, const QStringList args, const QString _sSearch, const QString _sInput);
    void runCommand(const QString _prog, s_i_combo siCombo);
    void runCommand(const QString _prog, const QStringList args, s_i_combo siCombo);
    void runCommand(const QString _prog, QList<s_i_combo> siList);
    void runCommand(const QString _prog, const QStringList args, QList<s_i_combo> siList);

    QString getRunCommand_Output();
    int getRunCommand_ExitCode();

    QProcess* createProcess(QString cmd/*, int t_sec = 30*/);

};

gen_serv has various "constructor methods" for m_Proc. These constructor methods calls runCommand. These constructors with their parameters allow various processing capabilities ranging from a basic program execution to a detailed I/O process.

2. The Problem:

In my qt console application, I create a gen_serv object, and use this to "runCommands", e.g. :

//main.cpp

gen_serv *gen_proc
gen_proc = new gen_serv();
gen_proc->runCommand("sh", QStringList()
                         << "-c"
                         << "echo sleeping; sleep 2; echo hello...");
qDebug() << gen_proc->_proc_out;

based on how my m_Proc works, it will should return: "hello..."

the command to be run above is the same as (to my understanding):

$ sh -c "echo sleeping; sleep 2; echo hello..."
sleeping
hello...

BUT: it returns an empty QString, and:

qDebug() << QString::number(gen_proc->exitCode);

outputs : "-1"

3. Extra code examples, etc - This led me to believe:

the issue does not lie in m_Proc, but in my parent class gen_serv.

Currently, after calling void gen_serv::runCommand(), I run the QObject::connect() as shown below (getResults() just gets the _proc_out and exitCode and assigns them to gen_serv for accessor methods):

QObject::connect(m, SIGNAL(finishedSignal()), this, SLOT(getResults()));

However this is executed, and runCommand() returns to main.cpp where qDebug() immediately calls the gen_proc->_proc_out and qDebug() will output:

_gen_proc->_proc_out = ""
_gen_proc->exitcode = -1

4. TL;DR

What I need:

I need to devise a method which will run after/or replacing the connect (in gen_serv).

This method will wait until the signal is emitted, so the runCommand() , which calls the QProcess.start(), will not return until the finishedSignal from m_Proc is emitted, thus guaranteeing a valid output/exit code value.

Any suggestions?

Upvotes: 0

Views: 698

Answers (1)

CybeX
CybeX

Reputation: 2396

I have found a solution, I cannot tell if it is a good one or not, but it works.

Possible Solution: (using QProcess::wait*()'s)

In the method (gen_serv::init_connector()) where I call the QObject::connect(), I changed/added a few extra lines.

void gen_serv::init_connector(){
    if (m->waitForStarted()) {
        QObject::connect(m, SIGNAL(finishedSignal()), this, SLOT(getResults()));
        if (!m->waitForFinished())
            qDebug() << "program is hanging, check m_Proc description for possible hang issues...: \n" << m->errorString();
    }
    else{
        if (m->errorString().contains("No such file or directory"))
            qDebug() << "check program and arguments - something seems wrong. Cannot find something... : \n" << m->errorString();
        else
            qDebug() << m->errorString();
    }
}

The solution is pretty straight forward, but nonetheless, I will provide a short description:

  • Firstly, one checks if the process has actually started. If not, then display an appropriate message with the error string.

note: I noticed that an error string like:

"execp: No such file or directory usually implies that either the program is not found

i.e. incorrect path or there are surplus arguments or the application does not take any arguments

  • If the QProcess is started, then connect() the signal and slot. Followed by the waitForFinished().

Reason for the waitForFinished() is this method waits for the QProcess::finished(int) signal.

This allows the application to wait until the process has completed (which is what I require).

Hope this helps someone

Upvotes: 1

Related Questions