Using Google Test with a QNetworkAccessManager never emits finished - qt

I'm trying to create a test that will use a QNetworkAccessManager to talk with our RESTful api. All I want to do is grab a simple JSon object using a QNetworkAccessManager. My code looks like this:
Connection::Connection(QString const &username, QString const &password, QString const &url, QString const &api) :
_user(username), _pass(password), _url(url) {
_manager = new QNetworkAccessManager(this);
QObject::connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(receiveReply(QNetworkReply*)));
QNetworkRequest request;
request.setUrl(QUrl(_url.append(api)));
request.setRawHeader("Authorization", QString("Basic ").append(QString("%1:%2").arg(_user).arg(_pass)).toUtf8());
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
QNetworkReply *reply = _manager->get(request);
}
void Connection::Connection(QNetworkReply *reply) {
//do some validation of the reply...
_data = QJsonDocument::fromJson(reply->readAll());
}
QJsonDocument Connection::data() const {
return _data;
}
...
#include <gtest/gtest.h>
#include "Connection.h"
#include <QApplication>
TEST(ConnectionTest, creation) {
int argc = 0;
char *argv = 0;
QApplication a(argc, &argv);
Connection *connect = new Connection("user","abc123", https://mytest.com/", "api/");
connect->deleteLater();
while (connect->data().isEmpty()) {
//loop forever
}
}
Originally I was testing by having Connection also be a QWidget. The finished signal would only show if I called connection->show(); Then I have to close the widget manually and the test completes. However, this isn't really useful for automated unit testing.
If I use the above while loop to wait until _data has been set to something then nothing ever happens with the QNetworkAccessManager and it loops forever.
Is there some call that needs to happen before anything will move forward? Why was ->show() 'working?'

You're not spinning the event loop, so don't expect anything asynchronous to work at all.
You need to add a signal to the Connection class that indicates when it's done, and use that signal to quit the event loop:
TEST(ConnectionTest, creation) {
int argc = 0;
char *argv = 0;
QCoreApplication app(argc, &argv);
// No need to allocate the connection on the heap!
Connection connection("user", "abc123", "https://mytest.com/", "api/");
QObject::connect(&connection, &Connection::finished,
&app, &QCoreApplication::quit());
app.exec();
}
You could add the QApplication instance as a member of the ConnectionTest class.
There's also no need to double the number of heap allocations by having the QNetworkAccessManager * manager member. You can have it as a regular member:
class Connection : public QObject {
Q_OBJECT
QNetworkAccessManager manager;
Q_SLOT void receiveReply(QNetworkReply *) {
...
emit finished();
}
public:
explicit
Connection(const QString & user, const QString & password,
const QUrl & url, const QString & path,
QObject * parent = 0) : QObject(parent) { ... }
Q_SIGNAL void finished();
};

Related

How to display dynamically created images in QWebView?

I'm working with QtWebKit where I'm displaying dynamically created HTML content, and I need to display the images that I retrieve from the database. For example, if I need to display an image from the resource, I add this line to the content I use in QWebView::setHtml:
<img src="qrc:/images/image.jpg"/>
That works great, WebView automatically finds the resource and displays it. Now I need to substitute this image with the content that I retrieve from the database, and that doesn't have a file in the filesystem. How to do that?
Can I manage the qrc namespace adding the content dynamically? Is that possible to add my own QRC handler that would receive and serve the requests from WebView? If not QRC, is there any other protocol that I can use to provide the content to the images in WebView?
I have full control over what I'm adding to the WebView using the setHtml method.
Update: I would like to solve the same problem for QWebEngineView as well.
QtWebkit
In the case of QtWebkit you must intercept the request using a QNetworkAccessManager and resend a custom QNetworkReply.
#include <QtWebKitWidgets>
class CustomReply : public QNetworkReply{
public:
explicit CustomReply(const QByteArray & content, const QByteArray & contentType, const QUrl & url):
QNetworkReply(), m_content(content){
offset = 0;
setUrl(url);
open(ReadOnly | Unbuffered);
setHeader(QNetworkRequest::ContentTypeHeader, QVariant(contentType));
setHeader(QNetworkRequest::ContentLengthHeader, QVariant(m_content.size()));
QTimer::singleShot(0, this, &CustomReply::dispatch);
}
bool isSequential() const{
return true;
}
qint64 bytesAvailable() const{
return m_content.size() - offset + QIODevice::bytesAvailable();
}
public slots:
void abort(){
}
protected:
qint64 readData(char *data, qint64 maxSize){
if (offset < m_content.size()) {
qint64 number = qMin(maxSize, m_content.size() - offset);
::memcpy(data, m_content.constData() + offset, number);
offset += number;
return number;
} else
return -1;
}
private:
void dispatch(){
emit readyRead();
emit finished();
}
QByteArray m_content;
qint64 offset;
};
class NetworkAccessManager: public QNetworkAccessManager{
public:
using QNetworkAccessManager::QNetworkAccessManager;
protected:
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData){
qDebug() << request.url();
if (request.url() == QUrl("qrc:/images/image.jpg")){
QImage image(150, 150, QImage::Format_RGB32);
image.fill(QColor("salmon"));
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "JPEG");
return new CustomReply(ba, "image/jpeg", request.url());
}
return QNetworkAccessManager::createRequest(op, request, outgoingData);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWebView view;
view.resize(640, 480);
view.show();
view.page()->setNetworkAccessManager(new NetworkAccessManager);
QString html = R"(<img src="qrc:/images/image.jpg">)";
view.setHtml(html);
return a.exec();
}
QtWebEngine
In QtWebEngine you must implement a QWebEngineUrlSchemeHandler but you cannot use the qrc, http or https schemas:
#include <QtWebEngineWidgets>
#define SCHEMENAME "so"
class Handler : public QWebEngineUrlSchemeHandler{
public:
void requestStarted(QWebEngineUrlRequestJob *job){
if(job->requestUrl() == QUrl("so:/images/image.jpg")){
QImage image(150, 150, QImage::Format_RGB32);
image.fill(QColor("salmon"));
QBuffer *buffer = new QBuffer;
buffer->open(QIODevice::WriteOnly);
image.save(buffer, "JPEG");
buffer->seek(0);
buffer->close();
job->reply("image/jpeg", buffer);
}
}
static void registerUrlScheme(){
QWebEngineUrlScheme webUiScheme(SCHEMENAME);
webUiScheme.setFlags(QWebEngineUrlScheme::SecureScheme |
QWebEngineUrlScheme::LocalScheme |
QWebEngineUrlScheme::LocalAccessAllowed);
QWebEngineUrlScheme::registerScheme(webUiScheme);
}
};
int main(int argc, char *argv[])
{
Handler::registerUrlScheme();
QApplication a(argc, argv);
QWebEngineView view;
Handler handler;
view.page()->profile()->installUrlSchemeHandler(SCHEMENAME, &handler);
view.resize(640, 480);
view.show();
QString html = R"(<img src="so:/images/image.jpg">)";
view.setHtml(html);
return a.exec();
}

QT downloading large file error

When I try to downloading file up to 50mb example, no problem, but with a big files give the following error
void MainWindow::downloadFile() {
QNetworkRequest requests;
requests.setUrl(QUrl("https://urlToFile"));
QSslConfiguration configSsl = QSslConfiguration::defaultConfiguration();
configSsl.setProtocol(QSsl::AnyProtocol);
requests.setSslConfiguration(configSsl);
QNetworkAccessManager *manager5 = new QNetworkAccessManager(this);
QNetworkReply *reply5;
reply5 = manager5->get( requests );
connect(manager5, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*)));
}
void MainWindow::downloadFinished(QNetworkReply *data) {
QFile localFile("fileName");
if (!localFile.open(QIODevice::WriteOnly))
return;
localFile.write(data->readAll());
localFile.close();
}
In your code, You are holding the whole file in memory until the download process finishes (that is when QNetworkAccessManager::finished() signal gets emitted). Of course, this is not the best way to deal with large files.
Remember that QNetworkReply is a QIODevice. This means that you should use the readyRead() signal to save data chunks to the disk as soon as they are received from the network, in order to avoid holding the whole file in memory until the download is finished.
Here is a minimal complete example:
#include <QtNetwork>
int main(int argc, char* argv[]){
QCoreApplication a(argc, argv);
QNetworkAccessManager nam;
QFile file("downloadedFile.xxx");
if(!file.open(QIODevice::ReadWrite)) return 1;
QNetworkRequest request(QUrl("http://download_url/..."));
QNetworkReply* reply = nam.get(request);
QObject::connect(reply, &QNetworkReply::readyRead, [&]{
//this will be called every time a chunk of data is received
QByteArray data= reply->readAll();
qDebug() << "received data of size: " << data.size();
file.write(data);
});
//use the finished signal from the reply object to close the file
//and delete the reply object
QObject::connect(reply, &QNetworkReply::finished, [&]{
qDebug() << "finished downloading";
QByteArray data= reply->readAll();
file.write(data);
file.close();
reply->deleteLater();
a.quit();
});
return a.exec();
}
Update:
I have wrapped the whole thing in a class FileDownloader, which can be used to download a file using QNetworkAccessManager, Here is an example of using this class:
#include <QtWidgets>
#include <QtNetwork>
//downloads one file at a time, using a supplied QNetworkAccessManager object
class FileDownloader : public QObject{
Q_OBJECT
public:
explicit FileDownloader(QNetworkAccessManager* nam, QObject* parent= nullptr)
:QObject(parent),nam(nam)
{
}
~FileDownloader(){
//destructor cancels the ongoing dowload (if any)
if(networkReply){
a_abortDownload();
}
}
//call this function to start downloading a file from url to fileName
void startDownload(QUrl url, QString fileName){
if(networkReply) return;
destinationFile.setFileName(fileName);
if(!destinationFile.open(QIODevice::WriteOnly)) return;
emit goingBusy();
QNetworkRequest request(url);
networkReply= nam->get(request);
connect(networkReply, &QIODevice::readyRead, this, &FileDownloader::readData);
connect(networkReply, &QNetworkReply::downloadProgress,
this, &FileDownloader::downloadProgress);
connect(networkReply, &QNetworkReply::finished,
this, &FileDownloader::finishDownload);
}
//call this function to abort the ongoing download (if any)
void abortDownload(){
if(!networkReply) return;
a_abortDownload();
emit backReady();
}
//connect to the following signals to get information about the ongoing download
Q_SIGNAL void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
Q_SIGNAL void downloadSuccessful();
Q_SIGNAL void downloadError(QString errorString);
//the next two signals are used to indicate transitions between busy and
//ready states of the file downloader, they can be used to update the GUI
Q_SIGNAL void goingBusy();
Q_SIGNAL void backReady();
private:
Q_SLOT void readData(){
QByteArray data= networkReply->readAll();
destinationFile.write(data);
}
Q_SLOT void finishDownload(){
if(networkReply->error() != QNetworkReply::NoError){
//failed download
a_abortDownload();
emit downloadError(networkReply->errorString());
} else {
//successful download
QByteArray data= networkReply->readAll();
destinationFile.write(data);
destinationFile.close();
networkReply->deleteLater();
emit downloadSuccessful();
}
emit backReady();
}
//private function, cleans things up when the download is aborted
//(due to an error or user interaction)
void a_abortDownload(){
networkReply->abort();
networkReply->deleteLater();
destinationFile.close();
destinationFile.remove();
}
QNetworkAccessManager* nam;
QUrl downloadUrl;
QFile destinationFile;
QPointer<QNetworkReply> networkReply;
};
//A sample GUI application that uses the above class
class Widget : public QWidget{
Q_OBJECT
public:
explicit Widget(QWidget* parent= nullptr):QWidget(parent){
layout.addWidget(&lineEditUrl, 0, 0);
layout.addWidget(&buttonDownload, 0, 1);
layout.addWidget(&progressBar, 1, 0);
layout.addWidget(&buttonAbort, 1, 1);
layout.addWidget(&labelStatus, 2, 0, 1, 2);
lineEditUrl.setPlaceholderText("URL to download");
connect(&fileDownloader, &FileDownloader::downloadSuccessful,
this, &Widget::downloadFinished);
connect(&fileDownloader, &FileDownloader::downloadError,
this, &Widget::error);
connect(&fileDownloader, &FileDownloader::downloadProgress,
this, &Widget::updateProgress);
connect(&buttonDownload, &QPushButton::clicked,
this, &Widget::startDownload);
connect(&buttonAbort, &QPushButton::clicked,
this, &Widget::abortDownload);
showReady();
//use goingBusy() and backReady() from FileDownloader signals to update the GUI
connect(&fileDownloader, &FileDownloader::goingBusy, this, &Widget::showBusy);
connect(&fileDownloader, &FileDownloader::backReady, this, &Widget::showReady);
}
~Widget() = default;
Q_SLOT void startDownload(){
if(lineEditUrl.text().isEmpty()){
QMessageBox::critical(this, "Error", "Enter file Url", QMessageBox::Ok);
return;
}
QString fileName =
QFileDialog::getSaveFileName(this, "Destination File");
if(fileName.isEmpty()) return;
QUrl url= lineEditUrl.text();
fileDownloader.startDownload(url, fileName);
}
Q_SLOT void abortDownload(){
fileDownloader.abortDownload();
}
Q_SLOT void downloadFinished(){
labelStatus.setText("Download finished successfully");
}
Q_SLOT void error(QString errorString){
labelStatus.setText(errorString);
}
Q_SLOT void updateProgress(qint64 bytesReceived, qint64 bytesTotal){
progressBar.setRange(0, bytesTotal);
progressBar.setValue(bytesReceived);
}
private:
Q_SLOT void showBusy(){
buttonDownload.setEnabled(false);
lineEditUrl.setEnabled(false);
buttonAbort.setEnabled(true);
labelStatus.setText("Downloading. . .");
}
Q_SLOT void showReady(){
buttonDownload.setEnabled(true);
lineEditUrl.setEnabled(true);
buttonAbort.setEnabled(false);
progressBar.setRange(0,1);
progressBar.setValue(0);
}
QGridLayout layout{this};
QLineEdit lineEditUrl;
QPushButton buttonDownload{"Start Download"};
QProgressBar progressBar;
QPushButton buttonAbort{"Abort Download"};
QLabel labelStatus{"Idle"};
QNetworkAccessManager nam;
FileDownloader fileDownloader{&nam};
};
int main(int argc, char* argv[]){
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"

Qt Call Form in Main function after close a form

I am new to Qt. I trying convert my existing Gtk2 Project to Qt for Cross-Platform Support.
My Code as follows:
// main.cpp
int success = 0;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
success = 0;
Logon *l = new Logon();
l->show();
if(success == 1) // success flag set in Logon.cpp when connection created !
{
Menu *m = new m();
m->show();
}
return a.exec();
}
// logon.cpp
void Logon::OnOkBtnClicked(void)
{
if(!ValConnection())
return;
success = 1;
this->destroy();
}
While execute the code, the connection successfully created & the logon for disappears. But, Menu form doesn't appears.
Kindly help me to solve this.
Try to use SIGNAL/SLOT method with lambda function.
Logon .h side:
class Logon : public QObject
{
Q_OBJECT
<...>
signals:
void success(int scs);
public slots:
bool ValConnection();
}
Logon .cpp side:
// Where button creation
connect(button, &QPushButton::clicked, this, &Logon::ValConnection);
// Somewhere on ValConnection
if(true)
emit success(1);
Main side:
QObject::connect(l, &QLogon::success, [=] (int scs) {
if(scs == 1) {
Menu *m = new m();
m->show();
}
});

QNetworkAccessManager does not return any data

I have come across bug that I am not able to see myself. After studing QT and stack sites I wrote following code:
void RateOfExchangeGetter::run(){
mRunning = true;
mAccessManager = new QNetworkAccessManager();
//connect(mAccessManager, SIGNAL(finished(QNetworkReply*)),
// this, SLOT(replyFinished(QNetworkReply*)));
while(mRunning){
QNetworkReply *reply;
for(SiteParser *parser: mSiteParsers){
QNetworkRequest request(QUrl("https://www.google.pl/"));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
qDebug() << "here";
reply = mAccessManager->get(request);
parser->setQNetworkReply(reply);
connect(reply, SIGNAL(readyRead()), parser, SLOT(slotReadyRead()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
parser, SLOT(slotError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
parser, SLOT(slotSslErrors(QList<QSslError>)));
}
//for test only ->
this->sleep(10);
QByteArray array = reply->read(50);
qDebug() << array;
}
}
As good eye might have already noticed - this code is placed in class that inherits QThread.
For some reason (that I cannot find) I can't receive any data from get function (I know that it is asynchronous), no signals are transmitted, and also after waiting 10 second there are no data available in read. I had also tried to get data via finished signal from QNetworkAccessManager itself but also nothing appeared.
Thanks to anyone who might have a clue what is happening.
You don't have an event loop in your thread's run() method, so there's no chance of any networking code working.
You should put all of your code into a QObject, and move it to a generic thread. The default implementation of QThread::run() spins an event loop.
I also don't see much reason for there to be more than one parser. You can simply let the QNetworkReply accumulate the response in its internal buffer until the request is finished - you can then parse it all in one go, obviating the need for parser state. Use the finished slot instead of readyRead. The readyRead slot only makes sense for large requests.
Below is how it might be done. Note the arming mechanism to prevent races between the worker thread and the main thread. You're guaranteed that the finishedAllRequests signal will be emitted exactly once after a call to arm(). Without this mechanism, the worker thread might be able to process all requests before the connect has a chance to run, and no signal will reach the recipient, or you might get multiple signals as the requests are processed before the next one is added.
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QPointer>
#include <QSslError>
#include <QThread>
#include <QMetaMethod>
#include <QDebug>
class Parser : public QObject {
Q_OBJECT
bool m_armed;
QList<QNetworkReply*> m_replies;
QPointer<QNetworkAccessManager> m_manager;
QMetaMethod m_addRequestImpl, m_armImpl;
Q_SLOT void finished() {
QNetworkReply * reply = static_cast<QNetworkReply*>(sender());
Q_ASSERT(m_replies.contains(reply));
qDebug() << "reply" << reply << "is finished";
// ... use the data
m_replies.removeAll(reply);
if (m_armed && m_replies.isEmpty()) {
emit finishedAllRequests();
m_armed = false;
}
}
Q_SLOT void error(QNetworkReply::NetworkError) {
QNetworkReply * reply = static_cast<QNetworkReply*>(sender());
m_replies.removeAll(reply);
}
Q_SLOT void sslErrors(QList<QSslError>) {
QNetworkReply * reply = static_cast<QNetworkReply*>(sender());
m_replies.removeAll(reply);
}
Q_INVOKABLE void addRequestImpl(const QNetworkRequest & req) {
QNetworkReply * reply = m_manager->get(req);
connect(reply, SIGNAL(finished()), SLOT(finished()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
SLOT(error(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
SLOT(sslErrors(QList<QSslError>)));
m_replies << reply;
}
Q_INVOKABLE void armImpl() {
if (m_replies.isEmpty()) {
emit finishedAllRequests();
m_armed = false;
} else
m_armed = true;
}
static QMetaMethod method(const char * signature) {
return staticMetaObject.method(staticMetaObject.indexOfMethod(signature));
}
public:
// The API is fully thread-safe. The methods can be accessed from any thread.
explicit Parser(QNetworkAccessManager * nam, QObject * parent = 0) :
QObject(parent), m_armed(false), m_manager(nam),
m_addRequestImpl(method("addRequestImpl(QNetworkRequest)")),
m_armImpl(method("armImpl()"))
{}
void addRequest(const QNetworkRequest & req) {
m_addRequestImpl.invoke(this, Q_ARG(QNetworkRequest, req));
}
void arm() {
m_armImpl.invoke(this);
}
Q_SIGNAL void finishedAllRequests();
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread * thread = new QThread(&a);
thread->start();
QNetworkAccessManager mgr;
Parser parser(&mgr);
mgr.moveToThread(thread);
parser.moveToThread(thread);
for (int i = 0; i < 10; ++i) {
QNetworkRequest request(QUrl("https://www.google.pl/"));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
parser.addRequest(request);
}
thread->connect(&parser, SIGNAL(finishedAllRequests()), SLOT(quit()));
a.connect(thread, SIGNAL(finished()), SLOT(quit()));
parser.arm();
int rc = a.exec();
thread->wait();
delete thread; // Otherwise mgr's destruction would fail
return rc;
}
#include "main.moc"
Output:
reply QNetworkReplyHttpImpl(0x1011619e0) is finished
reply QNetworkReplyHttpImpl(0x101102260) is finished
reply QNetworkReplyHttpImpl(0x101041670) is finished
reply QNetworkReplyHttpImpl(0x1011023e0) is finished
reply QNetworkReplyHttpImpl(0x10102fa00) is finished
reply QNetworkReplyHttpImpl(0x101040090) is finished
reply QNetworkReplyHttpImpl(0x101163110) is finished
reply QNetworkReplyHttpImpl(0x10103af10) is finished
reply QNetworkReplyHttpImpl(0x10103e6b0) is finished
reply QNetworkReplyHttpImpl(0x101104c80) is finished

Cannot execute echo command in QProcess

I want to launch a SCPI command in my device using netcat utility under Ubuntu 10.04 LTS with Qt. My code looks like:
env = "echo TRIG | nc 192.168.1.100 23 -q1";
process1.execute(env);
process1.waitForFinished(1000);
This command does not return any data but simply triggers the data acquisition.
If using terminal with same "echo TRIG | nc 192.168.1.100 23 -q1" command, everything works fine.
From Qt, it does not work. The debug output is "TRIG | nc 10.0.3.250 23 -q1" ... so without an "echo". My device does not receive the TRIG command.
Could you please advise what I'm doing wrong?
Many thanks.
The code below shows a fairly complete, asynchronous implementation of this functionality. It demonstrates how it can be done without launching external processes, and how to leverage C++11 lambdas in Qt 5. For Qt 4, the slots from main() would need to live in their own QObject-derived class.
#include <QtWidgets>
#include <QtNetwork>
class SocketSignaler : public QObject
{
Q_OBJECT
Q_SLOT void stateChanged(QAbstractSocket::SocketState state) {
if (state == QAbstractSocket::UnconnectedState) emit unconnected();
else emit busy();
emit hasState(this->state());
}
public:
explicit SocketSignaler(QAbstractSocket * socket) : QObject(socket) {
connect(socket, &QAbstractSocket::stateChanged, this, &SocketSignaler::stateChanged);
connect(&(const QObject&)QObject(), &QObject::destroyed, this, // defer signal emission
[=]{ emit stateChanged(socket->state()); }, Qt::QueuedConnection);
}
Q_SIGNAL void busy();
Q_SIGNAL void unconnected();
Q_SIGNAL void hasState(const QString &);
QString state() const {
switch (static_cast<QAbstractSocket*>(parent())->state()) {
case QAbstractSocket::UnconnectedState: return "Disconnected";
case QAbstractSocket::HostLookupState: return "Looking up host";
case QAbstractSocket::ConnectingState: return "Connecting";
case QAbstractSocket::ConnectedState: return "Connected";
case QAbstractSocket::ClosingState: return "Closing";
default: return {};
}
}
};
class Ui : public QWidget {
Q_OBJECT
Q_PROPERTY(bool busy WRITE setBusy)
QVBoxLayout m_layout{this};
QFormLayout m_form;
QLineEdit m_target{"192.168.1.100"};
QLineEdit m_message{"TRIG"};
QLabel m_state;
QDialogButtonBox m_box;
QPushButton * const m_send = m_box.addButton("Send", QDialogButtonBox::AcceptRole);
QPushButton * const m_cancel = m_box.addButton(QDialogButtonBox::Cancel);
QMessageBox m_msgBox{this};
public:
Ui() {
m_form.addRow("Target Host", &m_target);
m_form.addRow("Command", &m_message);
m_layout.addLayout(&m_form);
m_layout.addWidget(&m_state);
m_layout.addWidget(&m_box);
m_msgBox.setIcon(QMessageBox::Critical);
connect(m_send, &QPushButton::clicked, this, &Ui::send);
connect(m_cancel, &QPushButton::clicked, this, &Ui::cancel);
}
void setState(const QString & text) { m_state.setText(text); }
QString target() const { return m_target.text(); }
QString message() const { return m_message.text(); }
void showError(const QString & text) {
m_msgBox.setText(text);
m_msgBox.show();
}
void setBusy(bool busy) {
m_send->setEnabled(!busy);
m_cancel->setEnabled(busy);
}
Q_SIGNAL void send();
Q_SIGNAL void cancel();
};
int main(int argc, char *argv[])
{
const int targetPort = 23;
QApplication app{argc, argv};
Ui ui;
ui.show();
QTcpSocket socket;
SocketSignaler socketSig{&socket};
QObject::connect(&socketSig, &SocketSignaler::hasState, &ui, &Ui::setState);
QStateMachine machine;
QState sReady{&machine};
QState sBusy{&machine};
sReady.assignProperty(&ui, "busy", false);
sBusy.assignProperty(&ui, "busy", true);
sReady.addTransition(&socketSig, &SocketSignaler::busy, &sBusy);
sBusy.addTransition(&socketSig, &SocketSignaler::unconnected, &sReady);
QObject::connect(&ui, &Ui::send, [&](){
socket.connectToHost(ui.target(), targetPort);
});
QObject::connect(&ui, &Ui::cancel, [&](){ socket.abort(); });
QObject::connect(&socket,
static_cast<void (QAbstractSocket::*)(QAbstractSocket::SocketError)>
(&QAbstractSocket::error), [&]()
{
ui.showError(socket.errorString());
});
QObject::connect(&socket, &QAbstractSocket::connected, [&](){
auto msg = ui.message().toLatin1();
msg.append('\n');
if (socket.write(msg) >= msg.size()) socket.close();
});
QObject::connect(&socket, &QAbstractSocket::bytesWritten, [&](){
if (!socket.bytesToWrite()) socket.close();
});
machine.setInitialState(&sReady);
machine.start();
return app.exec();
}
#include "main.moc"
You can't use the pipe command (|) with QProcess that way.
There are a few ways to tackle this: -
You can call the first command and retrieve its output before processing it either in Qt or with another call to QProcess.
Or, create a script that you call from QProcess and retrieve the output.
Finally, assuming you're using linux / OSX, you can call QProcess with /bin/bash and pass the command to that. For example: -
env = "/bin/bash \"echo TRIG | nc 192.168.1.100 23 -q1\"";
process1.execute(env);
You can probably find an equivalent to /bin/bash for windows, perhaps cmd.exe

Resources