QProcess -- working QIODevice::Unbuffered, or adjustable buffersize for faster access to stdout
-
dlonie, also redirecting stdout could be of some use
-
@template<class Elem = char, class Tr = std::char_traits<Elem>>
class StdRedirector : public std::basic_streambuf<Elem, Tr>
{
/**- Callback Function.
/
typedef void (pfncb)(const Elem, std::streamsize _Count, void pUsrData);
public:
/**- Constructor.
- @param a_Stream the stream to redirect
- @param a_Cb the callback function
- @param a_pUsrData user data passed to callback
/
StdRedirector(std::ostream& a_Stream, pfncb a_Cb, void a_pUsrData):
m_Stream(a_Stream), m_pCallbackFunction(a_Cb), m_pUserData(a_pUsrData)
{
//redirect stream
m_pBuffer = m_Stream.rdbuf(this);
};
/**
- Destructor.
- Restores the original stream.
*/
~StdRedirector()
{
m_Stream.rdbuf(m_pBuffer);
}
/**
- Override xsputn and make it forward data to the callback function.
/
std::streamsize xsputn(const Elem _Ptr, std::streamsize _Count)
{
m_pCallbackFunction(_Ptr, _Count, m_pUserData);
return _Count;
}
/**
- Override overflow and make it forward data to the callback function.
*/
typename Tr::int_type overflow(typename Tr::int_type v)
{
Elem ch = Tr::to_char_type(v);
m_pCallbackFunction(&ch, 1, m_pUserData);
return Tr::not_eof(v);
}
protected:
std::basic_ostream<Elem, Tr>& m_Stream;
std::streambuf* m_pBuffer;
pfncb m_pCallbackFunction;
void* m_pUserData;
};...
void outcallback(const char* ptr, std::streamsize count, void* pTextEdit)
{
(void) count;
QTextEdit* p = static_cast<QTextEdit*>(pTextEdit);
p->append(ptr);
}
...
QTextEdit *teStdCout = new QTextEdit(wParent);
m_stdRedirector = new StdRedirector<>(std::cout, outcallback, teStdCout);
@ - Callback Function.
-
bump.. i get the same behaviour. the signal readyReadStandardOutput () only appears to be emitted once a Qt buffer somewhere is filled. I know my process is writing frequent lines but get the output in chunks.
Is there any resolution since 2010?
-
I think the problem is caused by buffering in the external program. Most programs written in C will buffer unless their output stream is a tty. When it isn't, you have to fake it using something like unbuffer (on Unix/Linux - part of the expect package - http://expect.sourceforge.net - which IIUC works by faking tty behaviour).
-
In the QProcess external program, how are you outputting the text to be read in?
I had a similar issue with my QProcess. When I output to stdout, the readyRead() signal wouldn't emit until after it was already finished since I didn't have to output very much text. I found this link (http://www.lubby.org/ebooks/qtconsoleapp2/qtconsoleapp2.html) to be very helpful for outputting to stdout.
If you don't want to read the link, stdout (or the QTextStream, if you went that route) has a built-in buffer that needs to be flushed manually to trigger the readyRead() signal. If your output looks like this:
@QTextStream o(stdout);
o << "Line of text here";@then readyRead() won't trigger unless you do this thousands of times or the QProcess finishes. I've found two options that work, depending on your implementation:
@QTextStream o(stdout);
o << "Line of text here" << std::endl;@or
@QTextStream o(stdout);
o << "Line of text here\n";
o.flush();@Either way, you will trigger the stdout buffer to be flushed and readyRead() should trigger. As I understand it (I'm no expert), "std::endl" is the same as "\n" except that it also triggers a flush in addition to starting a new line. I prefer the second method so that I don't get extra newline characters floating around.
Hope that helps if anyone runs into this issue.
-
[quote author="Gerolf" date="1291544359"]Have you read the documentation? It states:
bq. bool QIODevice::canReadLine () const [virtual]
Returns true if a complete line of data can be read from the device; otherwise returns false.
Note that unbuffered devices, which have no way of determining what can be read, always return false.
This function is often called in conjunction with the readyRead() signal.I f you look at the code:
@
m_process->start(command, QProcess::Unbuffered | QProcess::ReadWrite);
@Your device is uinbuffered, so canReadLine always returns false....[/quote]
(Though this post quite old) I'd like to address a issue(or share info if possible) on this topic becuase I think I'm solving the similar problem described here.
QProcess has internal two ring buffers(one for stdout and the other for stderr) of which canReadLine is called and seem to be able to return 'true' even with QProcess's unbuffered flag(At least in my following code). see following QProcess::canReadLine()
@
bool QProcess::canReadLine() const
{
Q_D(const QProcess);
const QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError)
? &d->errorReadBuffer
: &d->outputReadBuffer;
return readBuffer->canReadLine() || QIODevice::canReadLine();
}
@I'm having no deep understand of QProcess and QRingBuffer's implementation.
Here's my code that sends one line of command to spawed process and get one line of response.
I could get true value from canReadLine and it seems to work as expected. But I'd appreciate if anyone points out any problem in this code nonetheless.@
QString program = QString::fromLocal8Bit("pcomCmd.exe");
m_process = new QProcess(this);
m_process->setReadChannel(QProcess::StandardOutput);
m_process->start(program, QStringList(), QIODevice::ReadWrite | QIODevice::Unbuffered);
if (m_process->waitForStarted()) {
connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT(handlePcommCmdOuput()));
} else {
killPcommCmd();
}
@and then call following function to send to command and get response.
@
void MainWindow::writeAndReadResponse(const QString& _cmd)
{
QString cmd = _cmd + QString("\n");
m_process->write(cmd.toLocal8Bit());m_log->appendPlainText(QString("cmd> ") + cmd);
const int WAIT_TIME = 1000000;
QElapsedTimer timer;
timer.start();
while (!timer.hasExpired(WAIT_TIME)) {
m_process->waitForReadyRead(WAIT_TIME-timer.elapsed());
if (m_process->canReadLine()) {
QByteArray arr = m_process->readAll();
m_log->appendPlainText(arr); // HERE I GOT TEXT LINE IN MY QPlainTextEdit widget.// do something according to arr
break;
}
}
}
@ -
An old thread, but for the record: I had a similar problem with a Python script outputting lines that didn't show up in the QProcess until the python scripot ended. The issue was buffered output in Python. When I start it with the -u flag, it did work.
-
Ran into the same problem. Was running external c code and the readReady() signal would only fire when the StdOut buffer was full enough or after some time eg. 30sek. So i was not recieving the Output in realtime.
Since i had access to the external code. I disabled buffering for the output.
[code]
setbuf(stdout, NULL);
printf("Testoutput \n");
[/code]after this modification the readReady() Signal fired everytime printf was used in external program.
regards
-
Same problem met - doing "hcitool -i hci0 lescan" which is quite interactive and does not seem to buffer stdout (e.g. I don't have this problem http://www.spinics.net/lists/linux-bluetooth/msg55523.html while redirecting to file) - I can read data from QProcess once per 4-10 seconds!
here's code:
void Tracer::start() { m_Process = new QProcess(); m_Process->setReadChannel(QProcess::StandardOutput); connect(m_Process, SIGNAL(readyReadStandardOutput()), SLOT(readTracer())); QString command = QString("stdbuf -oL hcitool -i hci0 lescan --duplicates"); m_Process->start(command, QProcess::Unbuffered | QProcess::ReadWrite); } void Tracer::readTracer() { if (m_Process) { for (int i=0; i<200; ++i) { m_Data.append(m_Process->readLine()); if (m_Data.isEmpty()) { if(i==0) {qDebug()<<"empty"<<QDateTime::currentDateTime().toString(QString("[hh:mm:ss]")).toUtf8(); return;} else {qDebug()<<"last line was "<<i; return;} } m_Data.clear(); m_Data.append(m_Process->readLine(18)); qDebug()<<m_Data<<QDateTime::currentDateTime().toString(QString("[hh:mm:ss:ms]")).toUtf8(); } m_Data.clear(); } }
I started with "hcitool -i hci0 lescan " and then came up to "stdbuf -oL hcitool -i hci0 lescan --duplicates" - no difference
-
looking through "hcitool lescan" source code here http://code.metager.de/source/xref/linux/bluetooth/bluez/tools/hcitool.c#print_advertising_devices
the output to stdout is made with
printf("%s %s\n", addr, name);
that's on line 2471.
If I write smth like
std::flush();
or rather fflush(stdout)
after this line - will this help? or maybesetbuf(stdout, NULL);
?
and then I should compile it somehow... -
fflush(stdout)
did its job! I receive lines in real-time, lots of times per second.
Well, of course it'd been oh so much better if QProcess could flush output of its child-process