What are the alternatives to QProcess::startDetached() and QProcess::start() to start an external application as they are marked deprecated in qt5?
Only some overloads of the start() and startDetached() method are deprecated like:
void start(const QString &command, QIODevice::OpenMode mode = ReadWrite)
bool startDetached(const QString &command)
But the others are still available:
void QProcess::start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode = ReadWrite)
void QProcess::start(QIODevice::OpenMode mode = ReadWrite)
bool QProcess::startDetached(qint64 *pid = nullptr)
bool QProcess::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory = QString(), qint64 *pid = nullptr)
Related
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
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();
}
}
Qt Creator's wizards and configuration settings supports variable expansion, including evaluation of JavaScript expressions, e.g. the C++ class wizard file template contains:
%{JS: Cpp.openNamespaces('%{Class}')}
Cpp seems to be a global object. Where in the Qt Creator sources are those defined, and what's available?
These are not documented anywhere.
The JavaScript variable expansion is performed by the JsExpander in the core plugin. The expander can register QObject instances and expose them as properties of the global JS object.
To find all of those global objects, search for registerQObjectForJs method invocations. See the github search results for this method.
As of Qt Creator 4.2.1, and until at least 4.6, the following are the only objects registered:
Util - exposing Internal::UtilsJsExtension,
Cpp - exposing CppTools::Internal::CppToolsJsExtension,
Modeling - exposing ModelEditor::Internal::JsExtension,
QtSupport - exposing QtSupport::CodeGenerator,
Vcs - exposing VcsBase::Internal::VcsJsExtension.
The method parameter types are mapped to JavaScript types by QJSEngine. E.g. to obtain the Qt includes, one could have the following substitution:
%{JS: QtSupport.qtIncludes([ '%{Base}' ], [ '%{Base}' ])}
given the signature
QString qtIncludes(const QStringList &qt4, const QStringList &qt5)
The method list follows.
Util
QString toNativeSeparators(const QString &in) const;
QString fromNativeSeparators(const QString &in) const;
QString baseName(const QString &in) const;
QString fileName(const QString &in) const;
QString completeBaseName(const QString &in) const;
QString suffix(const QString &in) const;
QString completeSuffix(const QString &in) const;
QString path(const QString &in) const;
QString absoluteFilePath(const QString &in) const;
QString relativeFilePath(const QString &path, const QString &base) const;
// File checks:
bool exists(const QString &in) const;
bool isDirectory(const QString &in) const;
bool isFile(const QString &in) const;
// MimeDB:
QString preferredSuffix(const QString &mimetype) const;
// Generate filename:
QString fileName(const QString &path,
const QString &extension) const;
// Generate temporary file:
QString mktemp(const QString &pattern) const;
// Generate a ascii-only string:
QString asciify(const QString &input) const;
Cpp
// Generate header guard:
QString headerGuard(const QString &in) const;
// Fix the filename casing as configured in C++/File Naming:
QString fileName(const QString &path, const QString &extension) const;
// Work with classes:
QStringList namespaces(const QString &klass) const;
QString className(const QString &klass) const;
QString classToFileName(const QString &klass,
const QString &extension) const;
QString classToHeaderGuard(const QString &klass, const QString &extension) const;
QString openNamespaces(const QString &klass) const;
QString closeNamespaces(const QString &klass) const;
Modeling
QString fileNameToElementName(const QString &file);
QString elementNameToFileName(const QString &element);
QtSupport
// Ui file related:
// Change the class name in a UI XML form
QString changeUiClassName(const QString &uiXml, const QString &newUiClassName);
QString uiClassName(const QString &uiXml);
// Generic Qt:
QString qtIncludes(const QStringList &qt4, const QStringList &qt5);
Vcs
bool isConfigured(const QString &vcsId) const;
QString displayName(const QString &vcsId) const;
It is also useful to note that several JS variables exposed via the macroexpander see:
https://github.com/qt-creator/qt-creator/blob/master/src/plugins/coreplugin/coreplugin.cpp#L169
I have class called List to print a list
class List : public QAbstractListModel
{
Q_OBJECT
Q_ENUMS(Roles)
public:
enum Roles {
address = Qt::UserRole + 1,
name
};
DeviceList(QObject *parent = 0);
void addrows(const Manager &client);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList< Manager > m_client;
};
And another class Manager as
class Manager : public QObject
{
Q_OBJECT
Q_PROPERTY(List* List READ getList CONSTANT)
public:
Manager(const QString &address, const QString &name);
QString address() const;
QString name() const;
virtual List* getList() = 0;
private:
QString m_address;
QString m_name;
};
Now i am trying to addrows in manager.cpp as
void List::addrows(const Manager &client)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_client << client; // **i am getting error here**
endInsertRows();
}
My intention is to implement getlist in manager.cpp file
List* Manager :: getList()
{
List* list = new List();
list->addrows(Manager("street1","John"));
list->addrows(Manager("street2:","Tim"));
list->addrows(Manager("street3","Roberrt"));
return list;
}
In order to use the operator << you have to override the operator = in the class Manager. Instead of doing that use List and handle list elements as pointer.
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();
}