Qt 5 and QProcess redirect stdout with readyRead signal / slot

This problem bothers me because it should work, but unfortunately it is not. What I'm trying to achieve is to read the standard output of a specific process and make another process process it, i.e. Print it out.

The process creating the output is as follows:

#include <stdio.h> #include <stdlib.h> #include <iostream> int main() { for (int i = 0; i < 100; i++) { printf("yes %d\n",i); fflush(stdout); sleep(1); } return 0; } 

The process starts in another application as follows:

 #include <QProcess> ... QProcess * process = new QProcess; SomeClass * someClass = new SomeClass(process); connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyRead())); process->start("../Test/Test",QStringList()); if (!process->waitForStarted(4000)) { qDebug() << "Process did not start."; } ... void SomeClass::onReadyRead() { qDebug() << "Reading:" << process->readAllStdOutput(); } 

My expected result:

 Reading: yes 0 Reading: yes 1 ... Reading: yes 99 

However, I am not getting any output. And when I use QCoreApplication, I get all the output, but not through the signal / slot, but directly in the console.

I do not understand, because it works in another application using Qt 4.8.

My question is, is anyone experiencing the same problem or does anyone know how I can get the expected behavior?

+4
source share
3 answers

Well, I solved my problem.

If a process is started using startDetached (), it will not receive signals from readyRead (), readyReadStandardOutput (), and readyReadStandardError ().

So, just starting from start () the problem is solved.

However, I noticed that if I start and execute a while loop and print to main (), it will read everything at once, even if it ends with \ n. So I started the while loop in the thread, and this problem was also resolved. Everything prints as expected.

 #include <QThread> class Thread : public QThread { Q_OBJECT public: explicit Thread(QObject *parent = 0) : QThread(parent) {} protected: void run() { for (int i = 0; i < 100; i++) { std::cout << "yes" << i << std::endl; msleep(200); } exit(0); } }; int main(int argc, char ** argv) { QCoreApplication app(argc,argv); Thread * t = new Thread(); t->start(); return app.exec(); } 

TestP main.cpp

 #include <QProcess> #include <iostream> class Controller : public QObject { Q_OBJECT private: QProcess * process; public: Controller(QObject *parent = 0) : QObject(parent), process(new QProcess) {} void init(const QString &program) { connect(process,SIGNAL(readyRead()),this,SLOT(readStdOut())); connect(process,SIGNAL(started()),this,SLOT(onStarted())); connect(process,SIGNAL(finished(int)),this,SLOT(onFinished(int))); process->start(program); } private slots: void readStdOut() { std::cout << "YES " << QString(process->readAllStandardOutput()).toUtf8().constData() << std::endl; } void onStarted(){ std::cout << "Process started" << std::endl; } void onFinished(int) { std::cout << "Process finished: " << signal << std::endl; } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Controller c; c.init("../Test/Test"); return a.exec(); } 
-2
source

Your problem in the answer you provide is a misunderstanding of how reading works. It just returns all the data you have, whether there is line endings or not. By creating a thread and sleeping between the lines, you effectively send the interprocess process data in lines of line size, since the channel is reset when you have been waiting for a long time.

So, your answer, while working, is actually not how it should be done. You should use readLine() to chop the input into lines. Below is an example with the following qualities:

  • Just one executable file :)
  • Only Qt apis is used. This reduces memory consumption during operation.
  • Both processes are fully completed.
  • The amount of code is as minimal as possible.

 // https://github.com/KubaO/stackoverflown/tree/master/questions/process-17856897 #include <QtCore> QTextStream out{stdout}; class Slave : public QObject { QBasicTimer m_timer; int m_iter = 0; void timerEvent(QTimerEvent * ev) override { if (ev->timerId() == m_timer.timerId()) { out << "iteration " << m_iter++ << endl; if (m_iter > 35) qApp->quit(); } } public: Slave(QObject *parent = nullptr) : QObject(parent) { m_timer.start(100, this); } }; class Master : public QObject { Q_OBJECT QProcess m_proc{this}; Q_SLOT void read() { while (m_proc.canReadLine()) { out << "read: " << m_proc.readLine(); out.flush(); // endl implicitly flushes, so we must do the same } } Q_SLOT void started() { out << "started" << endl; } Q_SLOT void finished() { out << "finished" << endl; qApp->quit(); } public: Master(QObject *parent = nullptr) : QObject(parent) { connect(&m_proc, SIGNAL(readyRead()), SLOT(read())); connect(&m_proc, SIGNAL(started()), SLOT(started())); connect(&m_proc, SIGNAL(finished(int)), SLOT(finished())); m_proc.start(qApp->applicationFilePath(), {"dummy"}); } }; int main(int argc, char *argv[]) { QCoreApplication app{argc, argv}; if (app.arguments().length() > 1) new Slave{&app}; // called with an argument, this is the slave process else new Master{&app}; // no arguments, this is the master return app.exec(); } #include "main.moc" 
+3
source

Based on the code you posted, you connect to the class slot with this: -

 connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyReadStdOutput())); 

But the class function is declared as follows: -

 void SomeClass::onReadyRead(); 

If you expect onReadyRead to be called, you should call it in SLOT, not onReadyReadStdOutput. So change your connection to: -

  connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyRead())); 
0
source

Source: https://habr.com/ru/post/1493424/


All Articles