QProcess::execute capture output



  • Hi,

    When I start bash through a QProcess, I use QProcess::execute to execute the commands. The QProcess is through the readyRead-signal connected to a method that appends the output in a QPlainRexrEdtit. The documentation says of QProcess::execute:

    Starts the program program with the arguments arguments in a new process, waits for it to finish, and then returns the exit code of the process. Any data the new process writes to the console is forwarded to the calling process.

    However, the output of the new process is written tot the Application output of QtCreator or to the terminal if the application is started from there. How can I capture the output of the child process?

    setProcessChannelMode and setInputChannelMode did not work for this.


  • Lifetime Qt Champion

    Hi,

    Isn't rather readyReadStandardOutput what you are looking for ?



  • @SGaist

    I made a simple example.

    // in constructor
    connect(&process, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput()));
    
    // slot connected to a QAction
    void Widget::runProcess()
    {
        QString sh = qgetenv("SHELL");
        process.setProgram(sh);
        process.setArguments(QStringList() << "-i");
        process.start();
    
        process.write("ls"); // first test
        // process.execute("ls"); // second test
    
        process.closeWriteChannel();
    }
    
    // slot connected to QProcess::finished
    void Widget::readOutput()
    {
        plainTextEdit->appendPlainText("readOutput");
        plainTextEdit->appendPlainText(process.readAllStandardError());
        plainTextEdit->appendPlainText(process.readAllStandardOutput());
    }
    

    In the first test QProcess::readAllStandardOutput() gives me the string "readOutput" and the listing of the current working directory in the plainTextEdit.

    For the second test I changed the following in Widget::runProcess():

        // process.write("ls"); // first test
        process.execute("ls"); // second test
    

    In the second test readyReadStandardOutput() is not emitted and the listing of the current working directory is show in the Application Output pane in QtCreator.

    I expected the output forwarded to the defined QProcess process, but it seems to be forwarded to the main application. Any ideas how I can get it in the calling QProcess process?


  • Moderators

    @Jan-Willem execute() is a static method! It is not related to your QProcess instance which you connected to your slot.
    You should replace

    process.setProgram(sh);
    

    with

    process.setProgram("ls");
    


  • @jsulm thanks for the clarification. The I have interpreted the use of execute() wrong.
    I know

    process.setProgram("ls");
    

    works. But I was trying to create a bash-session because of the variables defined in .bashrc.
    The output of the QProcess seems to be only available after the QProcess is finished. Consider this:

    process.setProgram("/bin/bash");
    process.start();
    process.write("pwd\n");
    process.write("cd ..\n");
    process.write("pwd");
    

    This gives the desired result, but the output is received only when the QProcess is finished. And then the bash-session is closed, so the result of cd is not there anymore.
    You see my problem? I can't seem to find a way around it.


  • Lifetime Qt Champion

    What exactly do you want to do with your serie of commands ?


  • Moderators

    @Jan-Willem Maybe you should do

    process.write("cd ..\n");
    process.write("pwd");
    

    after you got the output of the first pwd? Because currently you're flooding the shell with commands without waiting for them to finish.



  • @SGaist I'm experimenting with building a simple terminal window. The series of commands are only for testing. It could be any command for that matter.
    @jsulm Thanks, I suspected that. But the output never comes, unless I close the writingchannel or what for the process to finish.

    If I write:

    process.write("pwd\n");
    process.write("ls\n");
    process.write("pwd\n");
    

    it works as expected. The output after each step is shown. cmd does not generate output, but the result is also not shown on the last call of pwd.

    When I create a custom object through which the process is started and then connect it to a Q(Plain)TextEdit, then everything seems to work. I'm trying to understand the reason why.


  • Lifetime Qt Champion

    Do you have something like Konsole in mind ?



  • Yes, but much more simple.
    I have looked into that code and from a derived project QTermWidget.

    It seems I now have a working solution, but I try to understand why this works.



  • I have started from scratch to see what my problem actually was, but I wasn't able to reproduce it.
    I probably got lost somewhere in my code.

    Well, below is the working code for the curious.

    @SGaist @jsulm Thanks for your help!

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
        QPlainTextEdit *plainTextEdit = new QPlainTextEdit(this);
        plainTextEdit->setReadOnly(true);
    
        QLineEdit *lineEdit = new QLineEdit(this);
        lineEdit->setClearButtonEnabled(true);
    
        QVBoxLayout *layout = new QVBoxLayout;
        layout->addWidget(plainTextEdit);
        layout->addWidget(lineEdit);
        setLayout(layout);
    
        connect(&process, &QProcess::readyReadStandardOutput, [=](){ plainTextEdit->appendPlainText(process.readAllStandardOutput()); });
        connect(&process, &QProcess::readyReadStandardError, [=](){ plainTextEdit->appendPlainText(process.readAllStandardError()); });
        connect(&process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &Widget::close);
        connect(lineEdit, &QLineEdit::returnPressed, [=](){ process.write(lineEdit->text().toLatin1() + "\n"); lineEdit->clear(); });
    
        process.start("sh");
    }
    
    Widget::~Widget()
    {
        process.close();
    }
    

  • Lifetime Qt Champion

    If you have a recent enough version of Qt, you can use qOverload in place of static_cast. It will do the same but will be more readable.



  • @SGaist Good one! Though I had to use the C++11 version since C++14 is still in Debian Sid.

    // C++11
    connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &MainWindow::close);
    

    For those who want to know, this is the C++14-version:

    // C++14
    connect(process, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, &MainWindow::close);
    

    Since the actual problem of this thread is solved, I will mark this thread as solved tomorrow. In the mean time comments on the above code can be made.


Log in to reply