I am trying to capture output of my Qt application but without success.
(I am calling an external lib that outputs to console, and I want to show this in my UI. Its NOT a QProcess, its a calss instance in my own process).
The problem: my lambda slot when writing to std::cout never gets called.
Here is my code distilled to a small testable application.
This is my latest attempt, and so far I am getting the "furthest" with it meaning, that the std::cout output is successfully redirected to the QFile.
What I don't understand however, is why the QTextStream connected to this file is not being triggered?
#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <iostream>
#include <QTimer>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QFile file("output.txt");
file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate);
// Redirect stdout to the QFile
std::freopen(file.fileName().toStdString().c_str(), "w", stdout);
QTextStream stream(&file);
std::string output; //to spy on in debugger
//this lambda never gets called!?
QObject::connect(&file, &QFile::readyRead, [&stream,&output] {
// Read from the QTextStream whenever new data is available
output += stream.readLine().toStdString();
qApp->quit();
});
//This succefully writes to the QFile on disk
QTimer::singleShot(100, [] {
std::cout << "This will be written to output.txt" << std::endl<<std::flush;
});
app.exec();
// Close the stream
std::fclose(stdout);
file.close();
}
My previous attempts are listed below, and are still not answered, so if you know how to fix any of the approaches, I will be really thankful!
Note at the end of the code, I am outputting directly to std::cout and to the QTextStream.
All of these outputs are seen on the console, including the output to stream - which means, that QTextStream object is correctly initialized with stdout.
Any ideas why the lambda slot is not getting called?
(I am aware of the recursion that will happen in the lambda due to outputting to stdout in it. But as this is just a test code its ok - the problem is that the lambda is not getting called at all)
#include <QCoreApplication>
#include <iostream>
#include <QTextStream>
#include <QTimer>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QTextStream stream(stdout);
QObject::connect(stream.device(), &QIODevice::bytesWritten, [&stream](qint64 bytes) {
// This lambda function is called when new data is available to be read
std::cout<<"in lambda"<<std::endl;
});
QTimer::singleShot(100, [&stream]{
std::cout << "Hello, world!" << std::endl<<std::flush;
std::cout << "This is some output." << std::endl<<std::flush;
stream<< "Stream output\n";
stream.flush();
});
return app.exec();
}
After some more thought, it occurred to me to try listening on stdout with QSocketNotifier.
With this approach, the notifier slot gets triggered, however, two things:
The notifier slot gets called before the QTimer slot - repeatedly like in an endless loop.
I get nothing from the stream which is really confusing me - I am being notified when stdout is being written to (even though nothing is writing to it, yet) and am I getting nothing when trying to read that data? (the fact that I get nothing is not that surprising since I didn't write anything (yet) to stdout it's more the fact it is being triggered all the time)
Anyone knows how to explain this?
Here is the code:
#include <QCoreApplication>
#include <iostream>
#include <QTextStream>
#include <QTimer>
#include <QSocketNotifier>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QTextStream stream(stdout);
int stdoutFd = fileno(stdout);
std::string output; //allows me to spy the content in the debuger
QSocketNotifier notifier(stdoutFd, QSocketNotifier::Write );
QObject::connect(¬ifier, &QSocketNotifier::activated, [&stream, &output] {
auto s = stream.readAll();
output += s.toStdString();
});
QTimer::singleShot(100, [&stream]{
std::cout << "This is some output." << std::endl<<std::flush;
stream << "Stream output\n";
});
return app.exec();
}
Many thanks in advance!
I have managed to achieve what I wanted, not very elegant, but it works.
I am putting my solution here for others that might also encounter this problem.
On one hand I like this solution since its basically pure C++ and does not require Qt.
On the other, it requires polling, which is quite "clunky".
Thus, I am leaving my OP open, with the hope that a more elegant solution will be offered, and I am curious as to why my Qt solutions didn't work.
This solution uses std::stringstream and std::streambuf.
I am putting here in a simple example main(), you can then adapt it to what ever application you have as it is really simple.
https://onlinegdb.com/AixVsxUyy
#include <sstream>
#include <iostream>
int main(int argc, char *argv[]) {
//Redirect stdout to streambuf - this "grabs" the stdout
std::stringstream buffer;
std::streambuf *streamBuff = std::cout.rdbuf(buffer.rdbuf());
std::cout << "this is redirected to streambuf" << std::endl;
//Read our buffer
std::string text = buffer.str();
// Release stdout back to original buffer
std::cout.rdbuf(streamBuff);
std::cout << "Grabbed text:" << text << std::endl;
std::exit(0);
}
Since this is synchronous, I resorted to polling buffer.str().
If anyone knows how can I be notified when streambuff has new data, please share.
Note:
When polling, if between the calls no new data was written to stdout, the same string will be read again as the buffer is still intact.
If you want to clear the buffer when you read it call:
buffer.clear();
buffer.str("");
I hope this helps someone!
Related
i am making a Tail Log application. It is executing perfectly but the output window is not showing any results.
Below is the code:
LogTail.cpp
#include "logtail.h"
#include <QApplication>
#include <QTextEdit>
#include<QDebug>
//start id=constructor
LogTail::LogTail(QString fn) {
//if (fn == QString()) {
// fn = "C:/Users/arpit.k/OneDrive - Accord Software & Systems Pvt. Ltd/Documents/QT/LogTail3/sample.log";
// }
connect (this, SIGNAL(readyReadStandardOutput()),
this, SLOT(logOutput())); /* When there is input ready, call this slot.*/
QStringList argv;
argv << "-f" << fn; /* tail -f filename */
start("tail", argv); /* Returns immediately, and now there is a child process running, "attached"
to this process. When this process exits, the child tail process will also terminate. */
}
LogTail::~LogTail() {
terminate(); /* Attempts to terminate this process. */
}
//end
//start id=logOutput
// tail sends its output to stdout.
void LogTail::logOutput() { /* Slot called whenever there is input to read. */
QByteArray bytes = readAllStandardOutput();
QStringList lines = QString(bytes).split("\n");
foreach (QString line, lines) {
//qDebug() << line;
emit logString(line);
}
}
//end
main.cpp
#include "logtail.h"
#include <QTextEdit>
#include <QApplication>
int main (int argc, char* argv[]) {
QApplication app(argc, argv);
QStringList al = app.arguments();
QTextEdit textEdit;
textEdit.setWindowTitle("Debug");
textEdit.setWindowTitle("logtail demo");
QString filename = "sample.log";
if (al.size() > 1) filename = al[1];
LogTail tail(filename); /* Create object, starts process too. */
tail.connect (&tail, SIGNAL(logString(const QString&)),
&textEdit, SLOT(append(const QString&)));
textEdit.show();
return app.exec();
}
Note: sample.log file contains some log data
Output window doesn't show any logs:
(https://i.stack.imgur.com/ENhqs.png)
I need it to display recent 10 Log lines!
Assuming you are subclassing from QProcess (you should post a complete minimal example in order to get an accurate answer).
You are starting the process in the constructor of LogTail, 'tail' will probably output some data or some events that will trigger readyReadStandardOutput before you have the chance to connect the signal in the main function.
readyReadStandardOutput will be triggered only once as you never cleared the input buffer (you never had the chance to read the available data).
So in order to make your example work you should call logOutput after you start the process.
Anyway I will suggest you to implement differently, I would call start from outside or at least outside the constructor, that way your object will be more scalable.
I am working on a Qt project with a team. I have two functions — one retrives the numerical coordinates of a place, the other downloads the map of the place — that I want to merge in one wrapper class, so that my teammates can call it easily.
#include <QCoreApplication>
#include <QFile>
#include <QHttpMultiPart>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <iostream>
class OpenStreetMapWrapper: public QObject{
Q_OBJECT
public:
OpenStreetMapWrapper(QObject *parent=nullptr):QObject(parent){
connect(&manager, &QNetworkAccessManager::finished, this, &OpenStreetMapWrapper::handle_finished);
}
void download(const std::string ®ion, const std::string &department, const QFile& outfile){
QNetworkRequest request;
QUrl url = QUrl(QString::fromStdString("https://download.openstreetmap.fr/extracts/europe/france/" + region + "/" + department + ".osm.pbf"));
request.setUrl(url);
request.setAttribute(QNetworkRequest::User, outfile.fileName());
manager.get(request);
}
void searchCSV(QFile& file, QFile& outfile){
QNetworkRequest request(QUrl("https://api-adresse.data.gouv.fr/search/csv/")); // Free API provided by the French government
request.setAttribute(QNetworkRequest::User, outfile.fileName());
QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart postpart;
postpart.setHeader(QNetworkRequest::ContentDispositionHeader,
QString("form-data; name=%1; filename=%2")
.arg("data", file.fileName()));
postpart.setBodyDevice(&file);
multipart->append(postpart);
file.setParent(multipart);
manager.post(request, multipart);
}
private:
QNetworkAccessManager manager;
void handle_finished(QNetworkReply *reply){
if(reply->error() == QNetworkReply::NoError){
QByteArray read = reply->readAll();
std::cout << read.toStdString() << std::endl; // For debugging
QString filename = reply->request().attribute(QNetworkRequest::User).toString();
QFile out(filename);
if(out.open(QIODevice::WriteOnly)){
out.write(read);
out.close();
}
}
else{
qDebug() << reply->error() << reply->errorString();
}
reply->deleteLater();
// QCoreApplication::quit(); This is done somewhere else?
}
};
#include <main.moc>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
OpenStreetMapWrapper A;
QFile file("./search.csv");
file.open(QIODevice::ReadWrite);
QFile outfile("./output.csv");
outfile.open(QIODevice::ReadWrite);
// Search
A.searchCSV(file, outfile); // 1st call works
A.searchCSV(file, outfile); // 2nd call -> makes both calls fail.
// Downloader
std::string region = "corse";
std::string department = "haute_corse";
return a.exec();
}
The problem with the code above, is that when for example searchCSV is called it displays the output as needed, but if it is called twice in the code, there is no output at all. After some debugging I think the problem is that the manager and handle_finished are not connected properly, because the execution never reaches there. Is there a simple way to solve this issue? Ideally, there is just one class instant, and any method can be called any number of times.
I don't know much about Qt, but it looks like you're trying to read from file twice and my guess is that when it reaches the end of the file after the first call to A.searchCSV it is done and you can't read from it anymore - unless you reposition the QFile to the beginning of the file.
Possible solution:
A.searchCSV(file, outfile);
file.unsetError(); // possibly needed
file.seek(0); // rewind to the start of the file
A.searchCSV(file, outfile);
The two QFile (input, output) are shared between two asynchronous calls (searchCSV) that might give an undefined behavior. The input file (stream) contents will be load and push only after the connection was made (like curl does).
You should:
Make searchCSV a blocking function (wait until handle_finished() done), input file pointer should be reinitialized before an other call.
OR: Use separated input/output QFile instances
I have 2 bash scripts
One script not requiring user input, it displays info
The other requires some user input, and displays some info too
Problem:
When creating a QProcess, I am unable to differentiate if the QProcess is finished or is hanging for user input.
I was hoping for some function e.g. QProcess::atEnd that returns true if the script is finished, but it returns true even if the QProcess is hanging for user input (from script)
More Info:
Script requiring user input : name-please.sh
Script not requiring input : hello.sh
name-please.sh
#!/bin/bash
echo "I am Mr Robot"
echo "what is your name:"
read n
if [[ ! -z "$n" ]];
then
echo "please to meet you"
exit 0;
else
echo "ok, bye..."
exit 1;
fi
hello.sh
#!/bin/bash
echo "I am Mr Robot"
using mReadyReadStandardOutput(), I read the script output. The issue exists around the fact that I need to read the output, determine if the output contains a line that requests user info (line from the script) and respond to that.
see code with pointer to line
Code:
#include <QCoreApplication>
#include <QLoggingCategory>
#include <QTextStream>
#include <QProcess>
#include <QString>
#include <QVariant>
#include <QDebug>
#include <QObject>
#include <QEventLoop>
class m_Proc : public QProcess
{
Q_OBJECT
public:
int exitCode = -1;
QString _out, _input;
m_Proc(QString input = QString(), QObject *parent = 0);
~m_Proc() {}
public slots:
void myReadyReadStandardOutput();
void myFinished(int i);
};
m_Proc::m_Proc(QString input, QObject *parent)
{
connect(this,SIGNAL(readyReadStandardOutput()),
this,SLOT(myReadyReadStandardOutput()));
connect(this, SIGNAL(finished(int)),
this, SLOT(myFinished(int)));
}
void m_Proc::myReadyReadStandardOutput() {
// Note we need to add \n (it's like pressing enter key)
_out = this->readAllStandardOutput();
if (!_input.isEmpty()) {
/*
* check if line matches that of the script,
* where the user is required to enter their name
*/
if (_out.contains("what is your name")) { <<< ---- LINE WITH ISSUE
this->write(QString(_input + QString("\n")).toLatin1());
_out = this->readAllStandardOutput();
}
}
else
qDebug() << "Input Emptry (should not be reached!) !";
}
void m_Proc::myFinished(int i){
exitCode = i;
qDebug() << "exit code = " << QString::number(exitCode);
qDebug() << _out;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
m_Proc *myProcess1 = new m_Proc(QString("text"));
// QString program = "/home/dev/script";
myProcess1->start("/home/dev/script", QStringList());// << "one" << "two");
// myProcess1->start("/bin/sh", QStringList() << "-c" << "ls" << "-la");
a.exec();
}
#include "main.moc"
Any advice to bypass this issue?
Three things.
According to the documentation for QProcess::atEnd(), this returns true only if the process is not running (QProcess::NotRunning). So...are you sure the process isn't truly done when you call atEnd()? I don't see it anywhere in the code you've posted, so I can't tell.
Your myReadyReadStandardOutput() signal is overwriting the m_Proc::_out member variable each time new data is available. Depending on how writes to standard output are buffered on your system (and by Qt), you may be reading partial lines into _out. Thus the _out.contains() call may return false, because you've got something like at is your in the line, rather than the full what is your name:.
You write the member string _input to the process's standard input, and then immediately try to read again from the standard output. This will most likely not return any data that you didn't already have in _out. Qt only really does actual I/O with the underlying device when control returns to the main event loop. From the documentation for QIODevice:
Certain subclasses of QIODevice, such as QTcpSocket and QProcess, are asynchronous. This means that I/O functions such as write() or read() always return immediately, while communication with the device itself may happen when control goes back to the event loop.
My suggestion for solving your issue would be to modify the myReadyReadStandardOutput signal. For asynchronous devices (such as sockets and sub-processes), data may arrive in arbitrary-sized chunks. This is point 2 I made above. The usual method for dealing with this is to put something like the following at the top of the signal:
if (!canReadLine())
return;
So you bail out of the signal and return to the main event loop if a full line of data is not available. If that returns true, you can do something like:
_out = readLine();
/* Process an entire line, e.g., check if it matches the request for name */
So the signal is designed to operate only on full lines of data. This is useful for terminals or network protocols that are line-oriented. In this case, you can call the QString::contains() method to check if this is the line requesting a name, and, if so, write it. But you must then return to the event loop in order for the data to be written, so you'd process the next line (please to meet you) in the next call to the signal in which a full line is available.
I have been bussy for the last five hours with this problem so I hope someone can help me out. In my C++ program (which I develop in QTcreator on lubuntu) I want to run airodump-ng in the child process of my program. The output of airodump-ng should be directed to the STDOUT of the parent proces. This works with many other programs but strangly enough not with airodump-ng. There is simply no output in the console. This, or my Linux crashes, I get logged out and when I log back in all my programs are closed. Does anybody know why?
#include <QCoreApplication>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <fstream>
#include <sys/wait.h>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//execl("/usr/sbin/airodump-ng", "airodump-ng", (char*)0 );
//dup2(1, 2); //pipe stderr to stdout
pid_t pidAirodump;
pid_t pidAircrack;
int pip[2];
if (pipe(pip) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
pidAirodump = fork();
if(pidAirodump > 0)//parent
{
pidAircrack = fork();
if(pidAircrack == 0)//pidAircrack
{
close(pip[0]);
dup2(pip[1], 2);
cout << "test" << endl;
//execl("/usr/sbin/arp", "arp", (char*)0 );
execl("/usr/sbin/airodump-ng", "airodump-ng ", "mon0", (char*)0 );
exit(0);
}
}
else//pidAirodump
{
exit(0);
}
wait(NULL);
return a.exec();
}
There's a few oddities in your program. But let's start at the question - you should distinguish between execl not working and the program you're trying to execute is misbehaving. You should not be able to crash linux from a user space program without special privilieges. The code snippet you've posted should not do that (what airpodump-ng does is another question).
If execl fails it will return and set errno, I suggest that you examine that after execl instead of just exiting.
Now for the oddities:
The first fork? Why do you do that? You basically forks and exits the child right away. You fork again and let the parent wait - this should trigger on the fact that the first child has terminated rather immediately.
The pipe, why do you do that if you wan't to keep the standard out? Instead you dup standard err to the write end of the pipe, but you doesn't seem to do anything with the read end.
I'm writing a Qt (5.3) program which has a joystick test UI in it, but I need a separate thread for an infinite while loop looking for joystick axis/button value changes through SDL. That part of the code is working fine as I can have the thread qDebug() messages and it seems to work. But from the main window, when I try to open the test joystick UI, the program crashes. I've had the test joystick UI running separation WITHOUT the JoystickThread thread and it seems to open up fine.
The error messages are inconsistent though - some times, I just get
The program has unexpectedly finished.
/home/narendran/QtWorkspace/build-LinkControl-Desktop-Debug/LinkControl crashed
This has shown up once:
QXcbWindow: Unhandled client message: "_GTK_LOAD_ICONTHEMES"
And a few other times:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
star: ../../src/xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
I found that this was common if XInitThreads(); is not run in the main function, but even with it on there, it crashes with the same error(s).
main.cpp
#include <qsplashscreen.h>
#include "linkcontrol.h"
#include "configure.h"
#include <unistd.h>
#include <QApplication>
#include <QPixmap>
#include <QStyle>
#include <QDesktopWidget>
#include "linkports.h"
#include "joystickthread.h"
#include <X11/Xlib.h>
int main(int argc, char *argv[])
{
XInitThreads();
QApplication a(argc, argv);
QPixmap pix(":splash.png");
QSplashScreen splash(pix);
splash.show();
a.processEvents();
JoystickThread jsThread;
jsThread.start();
LinkControl linkcontrol;
usleep(1000000);
splash.finish(&linkcontrol);
usleep(100000);
linkcontrol.show();
linkcontrol.setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter,linkcontrol.size(),a.desktop()->availableGeometry()));
return a.exec();
}
The actual thread is in joystickthread.cpp
#include "joystickthread.h"
#include "global.h"
#include "unistd.h"
/* JoystickThread::JoystickThread(int _interval)
{
this->interval_us = _interval;
} */
void JoystickThread::run()
{
while(1)
{
if(joystick->connected)
{
joystick->updateJSData();
// Check for changed values
for(int i=0; i<joystick->axis.count(); i++)
{
if(joystick->axis.value(i) != joystick->axis_last[i])
{
joystick->axisUpdateEmit(i);
// qDebug() << "AXIS: " << i << "\tVALUE: " << joystick->axis.value(i);
}
joystick->axis_last[i] = joystick->axis.value(i);
}
for(int i=0; i<joystick->button.count(); i++)
{
if(joystick->button.value(i) != joystick->button_last[i])
{
joystick->btnUpdateEmit(i);
// qDebug() << "BUTTON: " << i << "\tVALUE: " << joystick->button.value(i);
}
joystick->button_last[i] = joystick->button.value(i);
}
}
usleep(2500);
}
}
The function that causes the program to crash is in linkcontrol.cpp
void LinkControl::on_actionJoystick_Test_triggered()
{
qDebug() << "STARTING CHECK";
if(!js_test->initialized) {
qDebug() << "NOT INIT";
js_test = new TestJoystick();
js_test->initialized = true;
qDebug() << "FININSH INIT";
}
if(joystick->connected) {
qDebug() << "SHOWING UI";
js_test->show();
} else {
QMessageBox::critical(this, tr("No Joystick Connected!"), tr("Please connect a joystick first..."));
}
}
Where js_test is declared as a TestJoystick object in the linkcontrol.h file
public:
explicit LinkControl(QWidget *parent = 0);
QSlider *portSliders[16];
QLineEdit *setVals[16];
SerialTerminal *ser_term;
TestJoystick *js_test;
~LinkControl();
Thank you very much! Please let me know if you need anymore information.
QThreads are a little tricky to get used to initially, and have their share of gotchas.
You should construct and connect appropriate items at the top of your run function.
If you do it other places, you need to make sure that you don't use Qt::AutoConnection, but instead use Qt:QueuedConnection.
http://qt-project.org/doc/qt-5/qt.html#ConnectionType-enum
Certain elements are only accessible from the "GUI" thread or the main thread of the program. This is the thread that has QApplication::exec(); ran on. It has an event loop that propagates messages around.
Look at the Application output for runtime errors that Qt will tell you about.
When crossing thread boundaries, be sure to use signals and slots.
And if you are accessing a member of your thread class from outside that thread, be sure to use thread synchronization, practices, such as prefacing all access to these members with QMutexLocker locker(m_mutex);.
http://qt-project.org/doc/qt-5/threads.html
And as implied by the title "GUI thread", it is the only thread that is allowed to do certain things such as drawing QPixmaps and accessing certain parts of QWidgets.
Hope that helps.