Qt 5 and QProcess redirect stdout with signal/slot readyRead
Well i solved my problem.
If the process is started with startDetached() it will not receive the signals from readyRead(), readyReadStandardOutput() and readyReadStandardError().
So just starting it with start() solved the problem.
However i noticed that if i start and do the while loop and prints in main() it will read everything at once even if it ends with \n. So i started the while loop in a thread and that problem was also solved. 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();
}
QProcess saving to QTextEdit
It's crashing because you're deleting the sender object while inside a slot. Instead of delete process
, you should
process->deleteLater();
For logging purposes you should be using QPlainTextEdit
instead of a QTextEdit
. The former is faster. You're prematurely pessimizing by using the latter. Alas, even QPlainTextEdit
becomes abysmally slow if you're sending about 100 lines/s (at least on Qt 4.8). If you want a really fast log view, you'll need to use QListWidget
, with a caveat, or roll your own.
I have a complete example of how to send to and receive from a process in another answer.
QProcess problems, output of process
eI see one big problem.
Under windows you issue a commend pressing the Enter key. Writing
cmd.write("command");
cmd.write("\n");
just isn't enough you have to write
cmd.write("command");
cmd.write("\n\r");
Notice the trailing \r. Try this, it should work better, and by better I mean 7zip. I don't know if you'll get ipconfig to work properly.
Good luck and best regards
D
EDIT
Here is a working solution:
#include <QtCore/QCoreApplication>
#include <QtCore/QProcess>
#include <QtCore/QString>
#include <QtCore/QTextStream>
// Not clean, but fast
QProcess *g_process = NULL;
// Needed as a signal catcher
class ProcOut : public QObject
{
Q_OBJECT
public:
ProcOut (QObject *parent = NULL);
virtual ~ProcOut() {};
public slots:
void readyRead();
void finished();
};
ProcOut::ProcOut (QObject *parent /* = NULL */):
QObject(parent)
{}
void
ProcOut::readyRead()
{
if (!g_process)
return;
QTextStream out(stdout);
out << g_process->readAllStandardOutput() << endl;
}
void
ProcOut::finished()
{
QCoreApplication::exit (0);
}
int main (int argc, char **argv)
{
QCoreApplication *app = new QCoreApplication (argc, argv);
ProcOut *procOut = new ProcOut();
g_process = new QProcess();
QObject::connect (g_process, SIGNAL(readyReadStandardOutput()),
procOut, SLOT(readyRead()));
QObject::connect (g_process, SIGNAL(finished (int, QProcess::ExitStatus)),
procOut, SLOT(finished()));
g_process->start (QLatin1String ("cmd"));
g_process->waitForStarted();
g_process->write ("ipconfig\n\r");
// Or cmd won't quit
g_process->write ("exit\n\r");
int result = app->exec();
// Allright, process finished.
delete procOut;
procOut = NULL;
delete g_process;
g_process = NULL;
delete app;
app = NULL;
// Lets us see the results
system ("pause");
return result;
}
#include "main.moc"
Hope that helps. It worked everytime on my machine.
Start QProcess from within QDialog that is used as a progress monitor
Here is an example how you can do it (I use QWidget
but you can also use QDialog
or whatever). I don't use a separate thread because the UI doesn't need to be interactive. If you want to add buttons etc. then you should consider going for the good old QThread
running a QObject
model provided by Qt.
#!/usr/bin/python
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class MyQProcess(QWidget):
def __init__(self):
super(QWidget, self).__init__()
# Add the UI components (here we use a QTextEdit to display the stdout from the process)
layout = QVBoxLayout()
self.edit = QTextEdit()
self.edit.setWindowTitle("QTextEdit Standard Output Redirection")
layout.addWidget(self.edit)
self.setLayout(layout)
# Add the process and start it
self.process = QProcess()
self.setupProcess()
# Show the widget
self.show()
def setupProcess(self):
# Set the channels
self.process.setProcessChannelMode(QProcess.MergedChannels)
# Connect the signal readyReadStandardOutput to the slot of the widget
self.process.readyReadStandardOutput.connect(self.readStdOutput)
# Run the process with a given command
self.process.start("df -h")
def __del__(self):
# If QApplication is closed attempt to kill the process
self.process.terminate()
# Wait for Xms and then elevate the situation to terminate
if not self.process.waitForFinished(10000):
self.process.kill()
@pyqtSlot()
def readStdOutput(self):
# Every time the process has something to output we attach it to the QTextEdit
self.edit.append(QString(self.process.readAllStandardOutput()))
def main():
app = QApplication(sys.argv)
w = MyQProcess()
return app.exec_()
if __name__ == '__main__':
main()
Notice that the command I'm using (df -h
) runs once (it's a Linux command which displays the disk usage on your hard drives) and then it's over. You can replace it also with your Execute.exe
which can run indefinitely. I have tested it with htop
(a terminal-based advanced task manager), which once started doesn't stop unless the user wants it to or the system stops (crash, shutdown etc.).
Note that you have to ensure that the external process is stopped in a clean manner. This can be done inside __del__
(destructor) or another function invoked at the end of the life of a given widget. What I've done is basically send a SIGTERM
(terminate
) to the external process and once a given amount of time has passed but the process is still running I elevate the situation to SIGKILL
(kill
).
The code needs more work obviously but it should be enough to give you an idea how things work.
Here is the same version of the code above but with an extra thread. Note that I am redirecting the output from the external process to a slot in my worker. You don't have to do that unless you want to maybe work on that output. So you can skip this and connect your process signal to the slot in your widget that receives it and outputs its content. The processing of the output will be done again inside the separate thread so you can go the distance instead of freezing your UI (which will happen if you follow the
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class Worker(QObject):
sendOutput = pyqtSignal(QString)
def __init__(self):
super(Worker, self).__init__()
self.process = QProcess()
self.setupProcess()
def __del__(self):
self.process.terminate()
if not self.process.waitForFinished(10000):
self.process.kill()
def setupProcess(self):
self.process.setProcessChannelMode(QProcess.MergedChannels)
self.process.readyReadStandardOutput.connect(self.readStdOutput)
self.process.start("htop")
@pyqtSlot()
def readStdOutput(self):
output = QString(self.process.readAllStandardOutput())
# Do some extra processing of the output here if required
# ...
self.sendOutput.emit(output)
class MyQProcess(QWidget):
def __init__(self):
super(QWidget, self).__init__()
layout = QVBoxLayout()
self.edit = QTextEdit()
self.thread = QThread()
self.setupConnections()
self.edit.setWindowTitle("QTextEdit Standard Output Redirection")
layout.addWidget(self.edit)
self.setLayout(layout)
self.show()
def setupConnections(self):
self.worker = Worker()
self.thread.finished.connect(self.worker.deleteLater)
self.worker.sendOutput.connect(self.showOutput)
self.worker.moveToThread(self.thread)
self.thread.start()
def __del__(self):
if self.thread.isRunning():
self.thread.quit()
# Do some extra checking if thread has finished or not here if you want to
#Define Slot Here
@pyqtSlot(QString)
def showOutput(self, output):
#self.edit.clear()
self.edit.append(output)
def main():
app = QApplication(sys.argv)
w = MyQProcess()
return app.exec_()
if __name__ == '__main__':
main()
Further clarification:
As I've told @BrendanAbel in the comment section of his answer the issue with using slots with QThread
is that the slots have the same thread affinity (=the thread they belong to) as the QThread
instance itself, which is the same thread where the QThread
was created from. The only - I repeat the only - thing that runs in a separate thread when it comes to a QThread
is its event loop represented by QThread.run()
. If you look on the Internet you will find out that this way of doing things is discouraged (unless you really, really know that you have to subclass QThread
) because since the early versions of Qt 4 run()
was abstract and you had to subclass QThread
in order to use a QThread
. Later the abstract run()
got a concrete implementation hence the need of subclassing QThread
was removed. About thread-safety and signals what @BrendanAbel wrote is only partially true. It comes down to the connection type (default is AutoConnection
). If you manually specify the connection type you may actually render the signals thread-unsafe. Read more about this in the Qt documentation.
Qt Logging tool multithreading, calling signal and slot with variable number of arguments form another thread, mixing C and C++
Here are my nitpicks, arranged in the order of how I thought about it.
The declaration and definition of
r_printf
differ. Both must beEXPORT_C
.EXPORT_C
is likely to clash with some errant library code. Prefer a more unique name, likeLOG_H_EXPORT_C
.A signal can have zero or more recipients. The message allocated in
r_printf
can in principle leak or be multiply deleted. There's no reason to do manual memory management here, use a shared pointer or an implicitly shared data structure likeQByteArray
instead.For portability, you could use
qvsnprintf
.You're allocating a huge buffer. This is a tradeoff between allocation size and performance. You have an option of calling
qvsnprintf
with zero size to get the needed buffer size, then allocate a correctly-sized buffer, and callqvsnprintf
again. You'd need to profile this to make an informed choice. At the very least, don't allocate a page-sized buffer since on some platforms this pessimizes and allocates more than a page, at a 100% overhead. A 0xFE0 size would be a safer bet.Prefer
QString::asprintf
, and simply pass aQString
through the slots. This guarantees that the string will be converted from 8-bit to UTF-16 encoding only once.Since you're now emitting a container like
QString
orQByteArray
, you could factor out the log message source into a separate object. It could be connected to zero or more views, then.Do not reset the log text. Instead, use
QPlainText::appendPlainText
. This will avoid the need to re-parse the entire log every time you add to it.The
QPlainTextEdit
is abysmally slow and unsuitable for logging. You should use aQListView
or a custom widget instead.You may wish to keep the log scrolled to the bottom if it already is so. See this question for details.
Here's an example:
Log.h
#ifndef LOG_H
#define LOG_H
#ifdef __cplusplus
#include <QObject>
class Log : public QObject {
Q_OBJECT
public:
/// Indicates that a new message is available to be logged.
Q_SIGNAL void newMessage(const QString &);
/// Sends a new message signal from the global singleton. This method is thread-safe.
static void sendMessage(const QString &);
/// Returns a global singleton. This method is thread-safe.
static Log * instance();
};
#define LOG_H_EXPORT_C extern "C"
#else
#define LOG_H_EXPORT_C
#endif
LOG_H_EXPORT_C void r_printf(const char * format, ...);
#endif // LOG_H
Log.cpp
#include "Log.h"
#include <cstdarg>
Q_GLOBAL_STATIC(Log, log)
Log * Log::instance() { return log; }
void Log::sendMessage(const QString & msg) {
emit log->newMessage(msg);
}
LOG_H_EXPORT_C void r_printf(const char * format, ...) {
va_list argList;
va_start(argList, format);
auto msg = QString::vasprintf(format, argList);
va_end(argList);
Log::sendMessage(msg);
}
main.cpp
// https://github.com/KubaO/stackoverflown/tree/master/questions/simplelog-38793887
#include <QtWidgets>
#include <QtConcurrent>
#include "Log.h"
int main(int argc, char ** argv) {
using Q = QObject;
QApplication app{argc, argv};
QStringListModel model;
Q::connect(Log::instance(), &Log::newMessage, &model, [&](const QString & msg) {
auto row = model.rowCount();
model.insertRow(row);
model.setData(model.index(row), msg);
});
QWidget w;
QVBoxLayout layout{&w};
QListView view;
bool viewAtBottom = false;
QPushButton clear{"Clear"};
layout.addWidget(&view);
layout.addWidget(&clear);
Q::connect(&clear, &QPushButton::clicked,
&model, [&]{ model.setStringList(QStringList{}); });
view.setModel(&model);
view.setUniformItemSizes(true);
Q::connect(view.model(), &QAbstractItemModel::rowsAboutToBeInserted, &view, [&] {
auto bar = view.verticalScrollBar();
viewAtBottom = bar ? (bar->value() == bar->maximum()) : false;
});
Q::connect(view.model(), &QAbstractItemModel::rowsInserted,
&view, [&]{ if (viewAtBottom) view.scrollToBottom(); });
QtConcurrent::run([]{
auto delay = 10;
for (int ms = 0; ms <= 500; ms += delay) {
r_printf("%d ms", ms);
QThread::msleep(ms);
}
});
w.show();
return app.exec();
}
launching a program inside another program
In a C/C++ string literal, you must escape all backward slashes.
It's really bad to use the
waitForX()
functions in Qt. They block your GUI and make your application unresponsive. From a user experience point of view, it truly sucks. Don't do it.
You should code in asynchronous style, with signals and slots.
My other answer provides a rather complete example how asynchronous process communications might work. It uses QProcess
to launch itself.
Your original code could be modified as follows:
class Window : ... {
Q_OBJECT
Q_SLOT void launch() {
const QString program = "C:\\A2Q1-build-desktop\\debug\\A2Q1.exe";
QProcess *process = new QProcess(this);
connect(process, SIGNAL(finished(int)), SLOT(finished()));
connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(finished()));
process->start(program);
}
Q_SLOT void finished() {
QScopedPointer<Process> process = qobject_cast<QProcess*>(sender());
QString out = process->readAllStandardOutput();
// The string will be empty if the process failed to start
... /* process the process's output here */
// The scoped pointer will delete the process at the end
// of the current scope - right here.
}
...
}
How to communicate Qt applications two-way
It's very hard to explain in one answer all mistakes, so just look at code and ask if you still got problems.
Here is example of using QProcess as IPC.
This is your main process, that creates additional process and connects to its signals
MyApplicaiton.h
#ifndef MYAPPLICATION_H
#define MYAPPLICATION_H
#include <QApplication>
class InterProcess;
class MyApplication : public QApplication {
Q_OBJECT
public:
MyApplication(int &argc, char **argv);
signals:
void mainApplicationSignal();
private slots:
void onInterProcessSignal();
private:
InterProcess *mProcess;
};
#endif // MYAPPLICATION_H
MyApplicaiton.cpp
#include "MyApplication.h"
#include "InterProcess.h"
MyApplication::MyApplication(int &argc, char **argv) : QApplication(argc, argv) {
mProcess = new InterProcess(this);
connect(mProcess, SIGNAL(interProcessSignal()),
this, SLOT(onInterProcessSignal()));
mProcess->start();
}
void MyApplication::onInterProcessSignal() {}
This is example implementation of your interProcess class:
InterProcess.h
class InterProcess : public QProcess {
Q_OBJECT
public:
explicit InterProcess(QObject *parent = nullptr);
signals:
void interProcessSignal();
private slots:
void onMainApplicationSignal();
};
InterProcess.cpp
#include "InterProcess.h"
#include "MyApplication.h"
InterProcess::InterProcess(QObject *parent) : QProcess(parent) {
if(parent) {
auto myApp = qobject_cast<MyApplication *>(parent);
if(myApp) {
connect(myApp, SIGNAL(mainApplicationSignal()),
this, SLOT(onMainApplicationSignal()));
}
}
}
void InterProcess::onMainApplicationSignal() {}
How to redirect qDebug, qWarning, qCritical etc output?
You've to install a message handler using qInstallMessageHandler
function, and then, you can use QTextStream
to write the debug message to a file. Here is a sample example:
#include <QtGlobal>
#include <stdio.h>
#include <stdlib.h>
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtInfoMsg:
fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
abort();
}
}
int main(int argc, char **argv)
{
qInstallMessageHandler(myMessageOutput); // Install the handler
QApplication app(argc, argv);
...
return app.exec();
}
Taken from the doc of qInstallMessageHandler
(I only added the comments):
- QtMsgHandler qInstallMessageHandler ( QtMsgHandler handler )
In the above example, the function myMessageOutput
uses stderr
which you might want to replace with some other file stream, or completely re-write the function!
Once you write and install this function, all your qDebug
(as well as qWarning
, qCritical
etc) messages would be redirected to the file you're writing to in the handler.
Related Topics
C++ Type of Enclosing Class in Static Member Function
Visual Studio Code C++11 Extension Warning
Is There a Clean Way to Prevent Windows.H from Creating a Near & Far MACro
Brace Initialization for Inherited Pod
Large Negative Integer Literals
Why Are the Return Values of These Doubles -1.#Ind
How to Omit the Double-Braces for Std::Array in C++14
Undefined Reference to Mempcy@Glibc_2.14 When Compiling on Linux
In Which Access Control Context Are Concepts Evaluated
Why Does Sqrt() Work Fine on an Int Variable If It Is Not Defined for an Int
Does Boost::Bind() Copy Parameters by Reference or by Value
Msvcp140D.Dll Missing, Is There a Way Around
Character Pointers and Integer Pointers (++)
Why Does C++ Parameter Scope Affect Function Lookup Within a Namespace