Qt 5和QProcess使用signal / slot readyRead重定向stdout
这个问题困扰我,因为它应该工作,但遗憾的是它没有。 我试图实现的是读取某个过程的标准输出并使另一个过程处理它,即将其打印出来。
生成输出的过程如下所示:
#include #include #include int main() { for (int i = 0; i < 100; i++) { printf("yes %d\n",i); fflush(stdout); sleep(1); } return 0; }
该过程在另一个应用程序中启动,如下所示:
#include ... 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:" <readAllStdOutput(); }
我的预期输出是:
Reading: yes 0 Reading: yes 1 ... Reading: yes 99
但是我根本没有输出。 当我使用QCoreApplication时,我得到所有输出,但不是通过信号/插槽,而是直接在控制台中。
我不明白,因为它适用于使用Qt 4.8的另一个应用程序。
我的问题是,是否有人遇到同样的问题或有谁知道我如何能得到预期的行为?
您提供的答案中的问题在于误解了阅读的工作原理。 它只返回你在那里得到的任何数据,无论是否有行结尾。 通过生成一个线程并在线之间hibernate,您可以有效地以行大小的块发送进程间数据,因为当您等待足够长时间时管道会被刷新。
所以,你的答案,在工作时,并不是真的应该怎么做。 您需要使用readLine()
将传入的数据切割成行。 以下是具有以下品质的示例:
- 只有一个可执行文件:)
- 仅使用Qt apis。 这减少了运行时内存消耗。
- 两个过程都干净地终止。
- 代码量尽可能小。
// https://github.com/KubaO/stackoverflown/tree/master/questions/process-17856897 #include 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"
根据您发布的代码,您将使用以下代码连接到类槽: –
connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyReadStdOutput()));
但是类中的函数声明如下: –
void SomeClass::onReadyRead();
如果您希望调用onReadyRead,那么您应该在SLOT中调用它,而不是onReadyReadStdOutput。 因此,请将您的连接更改为: –
connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyRead()));
好吧,我解决了我的问题。
如果使用startDetached()启动进程,它将不会从readyRead() , readyReadStandardOutput()和readyReadStandardError()接收信号。
所以刚用start()启动就解决了这个问题。
但是我注意到,如果我开始并执行while循环并在main()中打印,它将立即读取所有内容,即使它以\ n结尾。 所以我在一个线程中启动了while循环,这个问题也解决了。 一切都按预期打印。
#include 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 #include 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(); }