Subclassing QIODevice: Wrapper for QUdpSocket - qt

I am trying to implement my own wrapper over QUdpSocket because of it is uncomfortable to use. I can use it, but anyway I need to implement some intermediate buffer for access to QDataStream operations. In additional:
I sublass QIODevice,
header (bit simplified):
class BerUdp : public QIODevice
{
Q_OBJECT
void startup();
public:
void setHostToWrite(const QHostAddress &address, quint16 port);
void setPortToRead(quint16 port);
qint64 bytesAvailable() const;
protected: // Reimplement:
qint64 readData(char *data, qint64 maxSize);
qint64 writeData(const char *data, qint64 maxSize);
private:
QUdpSocket* dev_write; // udp socket to write
QUdpSocket* dev_read; // udp socket to read
QBuffer m_buffer; // buffer to store received datagrams
};
.cpp file:
void BerUdp::startup()
{
m_buffer.open(QIODevice::ReadWrite);
open(QIODevice::ReadWrite);
}
void BerUdp::setHostToWrite(const QHostAddress &address, quint16 port)
{
dev_write->connectToHost(address, port);
dev_write->waitForConnected();
}
void BerUdp::setPortToRead(quint16 port)
{
dev_read->bind(port);
dev_read->open(QIODevice::ReadOnly);
bool ok = connect(dev_read, &QIODevice::readyRead,
this, &BerUdp::onReceive);
}
// Read new received data to my internal buffer
void BerUdp::onReceive()
{
bool av = dev_read->hasPendingDatagrams();
if (av)
{
int dSize = dev_read->pendingDatagramSize();
QByteArray dtg(dSize, 0);
dev_read->readDatagram(dtg.data(), dtg.size());
// write new data to the end
int buf_read_pos = m_buffer.pos();
m_buffer.seek(m_buffer.size());
m_buffer.write(dtg);
m_buffer.seek(buf_read_pos);
}
}
From the Qt documuntation on QIODevice::readData()
..When reimplementing this function it is important that this function
reads all the required data before returning. This is required in
order for QDataStream to be able to operate on the class. QDataStream
assumes all the requested information was read and therefore does not
retry reading if there was a problem...:
// Main read data function. There are only 4 bytes required, but maxSize == 0x4000 here:
qint64 BerUdp::readData(char *data, qint64 maxSize)
{
int n = m_buffer.read(data, maxSize);
// clear the data which has already read:
QByteArray& ba = m_buffer.buffer();
ba.remove(0, n); // m_buffer.size() == 0 after this
m_buffer.seek(0);
return n;
}
The problem is that after the first read I have empty buffer, and therefore my bytesAvailable() method returns 0:
qint64 BerUdp::bytesAvailable() const
{
qint64 my_size = m_buffer.size();
qint64 builtin_size = QIODevice::bytesAvailable();
return (my_size + builtin_size); // == 0 after 1st read
}
So I can not find out how many bytes are available, when use the class, for example:
BerUdp udp;
QDataStream stream;
stream.setDevice(&udp);
...
QIODevice* dev = stream.device(); // 19 bytes available here
if (dev->bytesAvailable() > 0) // == true
{
quint32 val;
stream >> val;
}
if (dev->bytesAvailable() > 0) // == false
{
//...
}
How can own wrapper of QUdpSocket be written correctly?
The idea of using intermediate buffer worked well, until I decided to move the logic to the separate QIODevice-derived class.

In the process of debugging with the Qt sources, it was found out that I should set the isSequential() property to true. Now my class works correct.
bool BerUdp::isSequential() const
{
return true;
}
Full class:
BerUdp.h
#pragma once
#include <QIODevice>
#include <QBuffer>
class QUdpSocket;
class QHostAddress;
class BerUdp : public QIODevice
{
Q_OBJECT
public:
BerUdp(QObject *parent = 0);
void startup();
void setHostToWrite(const QHostAddress &address, quint16 port);
void setPortToRead(quint16 port);
bool flush();
qint64 bytesAvailable() const;
bool waitForReadyRead(int msecs);
bool isSequential() const;
protected: // Main necessary reimplement
qint64 readData(char *data, qint64 maxSize);
qint64 writeData(const char *data, qint64 maxSize);
private slots:
void onReceive();
private:
void read_udp_datagram();
void write_new_data_to_buffer(QByteArray dtg);
private:
QUdpSocket* dev_write; // One udp socket to write
QUdpSocket* dev_read; // Another udp socket to read
// intermediate buffer to store received datagrams
// and to provide access to read- and QDataStream- operations
QBuffer m_buffer;
};
BerUdp.cpp
#include "BerUdp.h"
#include <QUdpSocket>
BerUdp::BerUdp(QObject *parent)
: QIODevice(parent)
{
startup();
}
// Initialization
void BerUdp::startup()
{
dev_write = new QUdpSocket(this);
dev_read = new QUdpSocket(this);
m_buffer.open(QIODevice::ReadWrite);
open(QIODevice::ReadWrite);
}
// Set a virtual connection to "host"
void BerUdp::setHostToWrite(const QHostAddress &address, quint16 port)
{
dev_write->connectToHost(address, port);
dev_write->waitForConnected();
}
// Bind a port for receive datagrams
void BerUdp::setPortToRead(quint16 port)
{
dev_read->bind(port);
dev_read->open(QIODevice::ReadOnly);
connect(dev_read, &QIODevice::readyRead,
this, &QIODevice::readyRead);
connect(dev_read, &QIODevice::readyRead,
this, &BerUdp::onReceive);
}
// Flush written data
bool BerUdp::flush()
{
return dev_write->flush();
}
// Returns the number of bytes that are available for reading.
// Subclasses that reimplement this function must call
// the base implementation in order to include the size of the buffer of QIODevice.
qint64 BerUdp::bytesAvailable() const
{
qint64 my_size = m_buffer.size();
qint64 builtin_size = QIODevice::bytesAvailable();
return (my_size + builtin_size);
}
bool BerUdp::waitForReadyRead(int msecs)
{
return dev_read->waitForReadyRead(msecs);
}
// Socket device should give sequential access
bool BerUdp::isSequential() const
{
return true;
}
// This function is called by QIODevice.
// It is main function for provide access to read data from QIODevice-derived class.
// (Should be reimplemented when creating a subclass of QIODevice).
qint64 BerUdp::readData(char *data, qint64 maxSize)
{
int n = m_buffer.read(data, maxSize);
// clear the data which has already been read
QByteArray& ba = m_buffer.buffer();
ba.remove(0, n);
m_buffer.seek(0);
return n;
}
// This function is called by QIODevice.
// It is main function for provide access to write data to QIODevice-derived class.
// (Should be reimplemented when creating a subclass of QIODevice).
qint64 BerUdp::writeData(const char *data, qint64 maxSize)
{
return dev_write->write(data, maxSize);
}
// Read new available datagram
void BerUdp::read_udp_datagram()
{
int dSize = dev_read->pendingDatagramSize();
QByteArray dtg(dSize, 0);
dev_read->readDatagram(dtg.data(), dtg.size());
write_new_data_to_buffer(dtg);
}
// Write received data to the end of internal intermediate buffer
void BerUdp::write_new_data_to_buffer(QByteArray dtg)
{
int buf_read_pos = m_buffer.pos();
m_buffer.seek(m_buffer.size());
m_buffer.write(dtg);
m_buffer.seek(buf_read_pos);
}
// Is called on readyRead signal
void BerUdp::onReceive()
{
bool available = dev_read->hasPendingDatagrams();
if (available)
{
read_udp_datagram();
}
}

Related

QtRemoteObjects replicas being called for every single replica signal

I built a minimal source/replica direct connection QtRo system:
https://codeshare.io/29xj4V
What I would like to achieve is uniquely identify the message coming from the source to be a reply to the one sent from each of the replicas.
If rep1 sends "hello source, I'm rep1", I'd like the source to reply back "hello rep1 this is source". I would like to achieve the same with 3 replicas running at a very high and different rate. The issue is on every signal to the source the source replies back to all 3 replicas; the way i'm filtering this is by sending the "id" of the replica in text format then append that id back with the message to check for it inside the replica itself, if it's a match then it accepts the message, otherwise it discards it. Although this seems to be working, there's a big overhead here. My question would be: is there a better way to handle that instead of sending an identifier back and forth? thanks
The replicas receive the same signals, there is no way to filter that data from the source side. So the filtering must be done on the client's side, for this you can use an attribute that is the identifier and send that data as a new parameter in the signal instead of sending it within the message. So although there is a lot of calls the cost will be minimal since the if statement does not consume much time.
remoteminimal.rep
class RemoteMinimal
{
SIGNAL(sendData(const QString &, const QString &));
SLOT(void printData(const QString &));
SLOT(void process(const QString &, const QString &));
SLOT(void triggerSendData());
};
minimalsource.h
#ifndef MINIMALSOURCE_H
#define MINIMALSOURCE_H
#include "rep_remoteminimal_source.h"
#include <QObject>
class MinimalSource : public RemoteMinimalSource
{
Q_OBJECT
public:
MinimalSource(QObject *parent = nullptr);
~MinimalSource() override;
public slots:
void printData(const QString& data) override;
void process(const QString& data, const QString & id) override;
void triggerSendData() override;
private:
int m_global_server;
};
#endif // MINIMALSOURCE_H
minimalsource.cpp
#include "minimalsource.h"
MinimalSource::MinimalSource(QObject* parent):
RemoteMinimalSource (parent), m_global_server(0)
{
}
MinimalSource::~MinimalSource(){}
void MinimalSource::printData(const QString& data)
{
Q_UNUSED(data);
}
void MinimalSource::process(const QString& data, const QString &id)
{
m_global_server++;
emit sendData(data+":"+QString::number(m_global_server), id);
}
void MinimalSource::triggerSendData()
{
}
objects.h
#ifndef OBJECTS_H
#define OBJECTS_H
#include <QObject>
#include "rep_remoteminimal_replica.h"
class QTimer;
class Objects: public QObject
{
Q_OBJECT
Q_PROPERTY(QString id READ id WRITE setId)
Q_PROPERTY(double rate READ rate WRITE setRate)
public:
explicit Objects(QSharedPointer<RemoteMinimalReplica> ptr);
~Objects();
double rate() const;
void setRate(double rate);
QString id() const;
void setId(const QString &id);
signals:
void sendData(const QString&, const QString &);
public slots:
void printData(const QString&, const QString &);
void process(const QString&);
void triggerSendData();
void run();
private:
int m_global_server;
QTimer *m_loop;
double m_rate;
QString m_id;
QSharedPointer<RemoteMinimalReplica> reptr;
};
#endif // OBJECTS_H
objects.cpp
#include "objects.h"
#include <QTimer>
#include <QDebug>
Objects::Objects(QSharedPointer<RemoteMinimalReplica> ptr): QObject (Q_NULLPTR),
m_global_server(0), reptr(ptr)
{
m_loop = new QTimer(this);
}
Objects::~Objects(){}
void Objects::printData(const QString& data, const QString& id)
{
if(id == m_id)
qDebug() << "[" << m_id << "]:" << data;
}
void Objects::process(const QString& data)
{
emit sendData(data+":processed", m_id);
}
void Objects::run()
{
connect(m_loop, &QTimer::timeout, this, &Objects::triggerSendData);
m_loop->setInterval(static_cast<int>(1000/m_rate));
m_loop->start();
}
QString Objects::id() const
{
return m_id;
}
void Objects::setId(const QString &id)
{
m_id = id;
}
double Objects::rate() const
{
return m_rate;
}
void Objects::setRate(double rate)
{
m_rate = rate;
}
void Objects::triggerSendData()
{
m_global_server++;
emit sendData(QString("test:%1:%2").arg(m_id).arg(m_global_server), m_id);
}
The complete example you find here

Qt - Create WriteOnly and ReadOnly socket

I have a socketDescriptor.
I need to two sockets. One of them just for ReadOnly and another one for WriteOnly.
Is it impossible? If so, how?
Qt's sockets don't support having more than one socket object per native socket, but you can approximate this by creating appropriate unidirectional interfaces.
In the example below, the interfaces don't perform any buffering, thus they enforce that there is only once of each kind of interface per socket. Once you use the interface, you shouldn't be using the socket directly.
// https://github.com/KubaO/stackoverflown/tree/master/questions/socket-split-43409221
#include <QtNetwork>
class SocketBase : public QIODevice {
Q_OBJECT
public:
explicit SocketBase(QAbstractSocket * parent) : QIODevice{parent} {
connect(parent, &QAbstractSocket::connected, this, &SocketBase::connected);
connect(parent, &QAbstractSocket::disconnected, this, &SocketBase::disconnected);
connect(parent, &QAbstractSocket::stateChanged, this, [this](QAbstractSocket::SocketState state){
emit stateChanged(state);
setOpenMode(m_dev->openMode());
});
connect(parent,
static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
this, [this](QAbstractSocket::SocketError error){
setErrorString(m_dev->errorString());
emit this->error(error);
});
setOpenMode(m_dev->openMode());
}
QAbstractSocket::SocketError error() const {
return m_dev->error();
}
QAbstractSocket::SocketState state() const {
return m_dev->state();
}
Q_SIGNAL void connected();
Q_SIGNAL void disconnected();
Q_SIGNAL void error(QAbstractSocket::SocketError);
Q_SIGNAL void stateChanged(QAbstractSocket::SocketState);
bool isSequential() const override { return true; }
protected:
QAbstractSocket * const m_dev = static_cast<QAbstractSocket*>(parent());
};
class ReadSocket : public SocketBase {
Q_OBJECT
public:
explicit ReadSocket(QAbstractSocket * parent) : SocketBase(parent) {
for (auto proxy : parent->findChildren<ReadSocket*>())
Q_ASSERT(proxy == this);
connect(m_dev, &QIODevice::readyRead, this, &QIODevice::readyRead);
}
bool atEnd() const override {
return QIODevice::atEnd() && m_dev->atEnd();
}
qint64 bytesAvailable() const override {
return m_dev->bytesAvailable();
}
bool canReadLine() const override {
return m_dev->canReadLine();
}
protected:
qint64 readData(char * data, qint64 maxLength) override {
return m_dev->read(data, maxLength);
}
qint64 readLineData(char *data, qint64 maxLength) override {
return m_dev->readLine(data, maxLength);
}
qint64 writeData(const char *, qint64) override {
return -1;
}
};
class WriteSocket : public SocketBase {
Q_OBJECT
public:
explicit WriteSocket(QAbstractSocket * parent) : SocketBase(parent) {
for (auto proxy : parent->findChildren<WriteSocket*>())
Q_ASSERT(proxy == this);
connect(m_dev, &QIODevice::bytesWritten, this, &QIODevice::bytesWritten);
}
qint64 bytesToWrite() const override {
return m_dev->bytesToWrite();
}
bool flush() {
return m_dev->flush();
}
protected:
qint64 readData(char *, qint64) override {
return -1;
}
qint64 writeData(const char * data, qint64 length) override {
return m_dev->write(data, length);
}
};
int main(int argc, char *argv[])
{
QCoreApplication app{argc, argv};
QHostAddress addr{"127.0.0.1"};
quint16 port{9341};
QTcpServer server;
if (! server.listen(addr, port)) qFatal("can't listen");
QObject::connect(&server, &QTcpServer::newConnection, &server, [&]{
auto s = server.nextPendingConnection();
QObject::connect(s, &QTcpSocket::readyRead, s, [s]{
s->write(s->readAll());
});
QObject::connect(s, &QTcpSocket::disconnected, s, &QObject::deleteLater);
});
const char data_[] = "dhfalksjdfhaklsdhfklasdfs";
auto const data = QByteArray::fromRawData(data_, sizeof(data_));
QTcpSocket client;
WriteSocket writer(&client);
ReadSocket reader(&client);
QObject::connect(&writer, &WriteSocket::connected, [&]{
writer.write(data);
});
QObject::connect(&reader, &ReadSocket::readyRead, [&]{
if (reader.bytesAvailable() >= data.size()) {
auto const read = reader.read(data.size());
Q_ASSERT(read == data);
qApp->quit();
}
});
client.connectToHost(addr, port);
return app.exec();
}
#include "main.moc"

QT signal error: "this" is unavailable for static member function

I'm working at a socket class for my application that will introduce me in QT framework. When I try to build I get this error: 'this' is unavailable for static member functions.
This is my class .h and .cpp
#pragma once
#include <QObject>
class QTcpSocket;
namespace Ps{
class InstSocket : public QObject
{
Q_OBJECT
public:
InstSocket(QObject *parent=0);
bool Connect();
bool isOpen();
void Disconnect();
//Geters
QString GetHostName() const {return m_hostName;}
quint16 GetPort() const {return m_port;}
//seters
void SetHostName(const QString& value);
void SetPort(quint16 value);
void SetLongWaitMs(int value){m_longWaitMs = value;}
void SetShortWaitMs(int value){m_shortWaitMs = value;}
void WriteData(const QString &data) const;
~InstSocket();
QString ReadData() const;
signals:
static void NotifyConnected();
static void NotifyDisconnected();
private slots:
void onConnected();
void onDisconnected();
private:
//this holds a reference to QtcpSocket
QTcpSocket& m_socket;
QString m_hostName;
quint16 m_port;
int m_shortWaitMs;
int m_longWaitMs;
explicit InstSocket(const InstSocket& rhs) = delete;
InstSocket& operator= (const InstSocket& rhs) = delete;
};
}
and the cpp:
#include "instsocket.h"
#include "QTcpSocket"
#include "QDebug"
#include "utils.h"
namespace Ps
{
InstSocket::InstSocket(QObject *parent) :
QObject(parent),
m_socket(*new QTcpSocket(this)),
m_hostName(""),
m_port(0),
m_shortWaitMs(0),
m_longWaitMs(0)
{
/* my signals are wired to the undelying socket signals, the signal connected is triggered, when a conection
* is established. This will be wired to onConnected and Disconnected slots*/
connect(&m_socket, &QTcpSocket::connected, this, &InstSocket::onConnected);
connect(&m_socket, &QTcpSocket::disconnected, this, &InstSocket::onDisconnected);
}
bool InstSocket::Connect()
{
qDebug() << "attempting to connect to "<< m_hostName << "on port" << m_port << "with wait time: "<<m_longWaitMs;
m_socket.connectToHost(m_hostName, m_port, QTcpSocket::ReadWrite);
return m_socket.waitForConnected(m_longWaitMs);
}
bool InstSocket::isOpen()
{
return m_socket.isOpen();
}
void InstSocket::Disconnect()
{
if(!isOpen()) return;
m_socket.disconnectFromHost();
}
void InstSocket::onConnected()
{
emit NotifyConnected();
}
void InstSocket::onDisconnected()
{
emit NotifyDisconnected();
}
void InstSocket::SetHostName(const QString &value)
{
m_hostName = value;
}
void InstSocket::SetPort(quint16 value)
{
m_port = value;
}
void InstSocket::WriteData(const QString &data) const
{
/*support for writeing to socket. The write metod of the socket will return the number of bites writen*/
int bytes_written = m_socket.write(qPrintable(data));
qDebug() << "Bytes written: "<<bytes_written;
}
QString InstSocket::ReadData() const
{
if(!m_socket.isReadable())
{
return "ERROR: Socket is unreadable.";
}
QString result;
//until the socket reports there is no data available
while(!m_socket.atEnd())
{
result.append(m_socket.readAll());
/*since typically a PC would be much faster at reading than an instrument might be at writing
* instrument must have a chance to queue up more data in case the message it's sending us is long.*/
m_socket.waitForReadyRead(m_shortWaitMs);
}
return result;
}
InstSocket::~InstSocket()
{
Utils::DestructorMsg(this);
}
}
and this is the error:
Qt Projects\build-Vfp-Desktop_Qt_5_7_0_MSVC2015_64bit-Debug\debug\moc_instsocket.cpp:-1: In static member function 'static void Ps::InstSocket::NotifyConnected()':
error: 'this' is unavailable for static member functions
QMetaObject::activate(this, &staticMetaObject, 0, Q_NULLPTR); In static member function 'static void Ps::InstSocket::NotifyDisconnected()':
error: 'this' is unavailable for static member functions
QMetaObject::activate(this, &staticMetaObject, 1, Q_NULLPTR);
When I clicked on them, QT creator took me to moc_instsocket.cpp (that is in build folder and poit to this:
// SIGNAL 0
void Ps::InstSocket::NotifyConnected()
{
QMetaObject::activate(this, &staticMetaObject, 0, Q_NULLPTR);
}
// SIGNAL 1
void Ps::InstSocket::NotifyDisconnected()
{
QMetaObject::activate(this, &staticMetaObject, 1, Q_NULLPTR);
}
I can't figure out what to do althought I checked all the code several times. There is no need to know about utils class since there are just some debug messages. Did anyone know how to fix it?
What is the meaning of static signal? In Qt signals and slots are used on object level.
signals:
static void NotifyConnected();
static void NotifyDisconnected();
All classes that inherit from QObject or one of its subclasses (e.g., QWidget) can contain signals and slots. Signals are emitted by objects when they change their state in a way that may be interesting to other objects. This is all the object does to communicate. It does not know or care whether anything is receiving the signals it emits. This is true information encapsulation, and ensures that the object can be used as a software component. Signal Slots documentation

cant read data from QTcpSocket

I use QTcpSocket to open a connection and receive the data. But I'm having issues reading data from then socket.
// header file
class PeerWireClient : public QTcpSocket
{
Q_OBJECT
public:
PeerWireClient(QObject *parent = 0);
private slots:
void readFromSocket();
private:
qint64 socketBytesAvailable() const { return QTcpSocket::bytesAvailable(); }
// Data waiting to be read/written
QByteArray incomingBuf;
QByteArray outgoingBuf;
};
// cpp file
PeerWireClient::PeerWireClient(QObject *parent) :
QTcpSocket(parent)
{
connect(this, SIGNAL(readyRead()), this, SLOT(readFromSocket()));
}
void PeerWireClient::readFromSocket(void)
{
qint64 oldsize, size;
qint64 readbytes = 0;
oldsize = incomingBuf.size();
size = socketBytesAvailable();
if (size > 0) {
incomingBuf.resize(oldsize + size);
readbytes = read((incomingBuf.data()+oldsize), size);
qDebug("%ld", readbytes); //
qDebug("data: %s", incomingBuf.constData());
}
}
If I try to send 281 bytes data from the other side then
the read function return 281 bytes but the is no data in buffer.
Debug:
281
data:
I dont know what I am doing wrong. How can I make this work?

How to process text streams with \r correctly? I'd like some line buffered way, using Qt

I'm using Qt and QProcess to read some data from other tools and printing them on my app. Think of it being a "terminal", for example.
I'm processing data using QProcess::canReadLine() and QProcess:readLine(), and that's wonderful. But some tools use \r to print progress bars on screen, and that's screwing with my parser. Since there is never some line to be read, my app just wait until the process finishes to print the last line: many lines glued together with \r instead of \n.
Anyways, is there someway to tell QProcess to use \r as linebreak also? I thought of implementing my QIODevice subclass, but I'd need to reimplement QProcess too, so that seems to be not the optimal approach.
I thought of using a middle buffer, and use this buffer to signal "hasLine" to my main program. I'd use QProcess::readyRead to populate the buffer, and then the buffer to populate my main app, but I'd like to just tell Qt that a \r is also OK as a linebreak. Is that possible?
I don't think it's possible to directly tell Qt to use '\r' as a linebreak. I thought that QTextStream could do that, but looking at its sources right now it seems to me that I was wrong.
One funny way of doing it would be to implement a custom QIODevice subclass that reads from another QIODevice and just replaces all '\r's with '\n's, delegating all other methods excep read() varieties to the original device. Then readLine() and QTextStream would work on the resulting stream just fine, I think. You'd have to deal somehow with the possible '\r\n' sequence, though. The upside is that you don't have to do any buffering in that class.
Something along these lines:
class CRFilter: public QIODevice {
Q_OBJECT
public:
CRFilter(QIODevice *device);
protected:
virtual qint64 readData(char *data, qint64 maxSize);
virtual qint64 writeData(const char *data, qint64 maxSize);
private:
QIODevice *device;
};
CRFilter::CRFilter(QIODevice *device):
device(device)
{
// delegate the readyRead() signal to this object
connect(device, SIGNAL(readyRead()), SIGNAL(readyRead()));
// and maybe other signals like bytesWritten() too...
}
qint64 CRFilter::readData(char *data, qint64 maxSize)
{
qint64 res = device->read(data, maxSize);
for (qint64 i = 0; i < res; i++) {
if (data[i] == '\r')
data[i] = '\n';
}
return res;
}
qint64 CRFilter::writeData(const char *data, qint64 maxSize)
{
return device->write(data, maxSize);
}
Then you just do this:
QProcess process; // use QProcess methods on this
CRFilter reader(&p); // use QIODevice methods on this
reader.open(QIODevice::ReadWrite); // need this to convince read()/write() methods to work
I hadn't actually tested it, so it probably needs some debugging to get it right. I also think it's a bit ugly, but can't think of any really elegant solution.
Since I'm not using polymorphism with this, no problem inheriting publicly and overriding some methods and signals:
QCLIProcess.h
#ifndef QCLIPROCESS_H
#define QCLIPROCESS_H
#include <QProcess>
class QCLIProcess : public QProcess
{
Q_OBJECT
public:
explicit QCLIProcess(QObject *parent = 0);
bool canReadLine() const;
QString readLine();
signals:
void readyRead();
private slots:
void processLine();
private:
QByteArray buffer;
QStringList lines;
};
#endif // QCLIPROCESS_H
QCLIProcess.cpp
#include "QCLIProcess.h"
#include <QtCore>
QCLIProcess::QCLIProcess(QObject *parent) :
QProcess(parent)
{
setReadChannelMode(QProcess::MergedChannels);
connect((QProcess *)this, SIGNAL(readyRead()), this, SLOT(processLine()));
}
void QCLIProcess::processLine(){
buffer.append(readAll());
int last = 0;
for(int i=0; i<buffer.size(); i++){
if (buffer.at(i) == '\n' || buffer.at(i) == '\r'){
QString line(buffer.mid(last, i-last));
line.append('\n');
if (!line.isEmpty()) lines << line;
last = i+1;
}
}
buffer.remove(0, last);
emit readyRead();
}
bool QCLIProcess::canReadLine() const {
return !lines.isEmpty();
}
QString QCLIProcess::readLine(){
QString line;
if (!lines.isEmpty()){
line = lines.at(0);
lines.removeFirst();
}
return line;
}
UPDATE:
I ended encapsulating the QProcess in a new class, rather than deriving it. This way I could control which signals and which slots I want to expose.
QLineBufferedCRFilteredProcess.h
#ifndef QCLIPROCESS_H
#define QCLIPROCESS_H
#include <QProcess>
class QLineBufferedCRFilteredProcess : public QObject
{
Q_OBJECT
public:
explicit QLineBufferedCRFilteredProcess(QObject *parent = 0);
bool canReadLine() const;
QString readLine();
void start(const QString &program, const QStringList &arguments);
void close();
signals:
void readyRead();
void finished(int exitCode, QProcess::ExitStatus exitStatus);
private slots:
void processLine();
private:
QProcess process;
QByteArray buffer;
QStringList lines;
};
#endif // QCLIPROCESS_H
QLineBufferedCRFilteredProcess.cpp
#include "QLineBufferedCRFilteredProcess.h"
#include
QLineBufferedCRFilteredProcess::QLineBufferedCRFilteredProcess(QObject *parent) :
QObject(parent)
{
process.setReadChannelMode(QProcess::MergedChannels);
connect(&process, SIGNAL(readyRead()), SLOT(processLine()));
connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus)));
}
void QLineBufferedCRFilteredProcess::processLine()
{
buffer.append(process.readAll());
int last = 0;
for(int i=0; i<buffer.size(); i++){
if (buffer.at(i) == '\n' || buffer.at(i) == '\r'){
QString line(buffer.mid(last, i-last));
line.append('\n');
if (!line.isEmpty()) lines << line;
last = i+1;
}
}
buffer.remove(0, last);
emit readyRead();
}
bool QLineBufferedCRFilteredProcess::canReadLine() const
{
return !lines.isEmpty();
}
QString QLineBufferedCRFilteredProcess::readLine()
{
QString line;
if (!lines.isEmpty()){
line = lines.at(0);
lines.removeFirst();
}
return line;
}
void QLineBufferedCRFilteredProcess::start(const QString &program, const QStringList &arguments)
{
process.start(program, arguments);
}
void QLineBufferedCRFilteredProcess::close()
{
process.close();
}

Resources