QT Linux - QObject::connect & QHttp issue - qt

I have a problem. I'm writing a small application, which will fetch an image from a website and display it in a QT GUI application.
I use QHttp to do this. The code works if I execute it in main (before GUI is shown), but when I try to implement it, so that the code will run when I click on a button, it doesn't work.
Here's some of the code:
downloader.h - The class that's responsible for creating connection and saving image
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include <QObject>
#include <QHttp>
#include <QFile>
#include <QDebug>
#include <QDir>
class Downloader : public QObject
{
Q_OBJECT
public:
explicit Downloader(QObject *parent = 0);
void getImageFromWeb(QString host, QString append);
signals:
public slots:
void stateChanged(int state);
void responseHeaderReceived(const QHttpResponseHeader &resp);
void requestFinished(int id, bool error);
private:
QHttp *http;
};
#endif // DOWNLOADER_H
downloader.cpp - The implementation
The case switches are added for debugging
#include "downloader.h"
#include <QApplication>
Downloader::Downloader(QObject *parent) :
QObject(parent)
{
}
void Downloader::getImageFromWeb(QString host, QString append)
{
http = new QHttp(this);
connect(http, SIGNAL(stateChanged(int)), this, SLOT(stateChanged(int)));
qDebug() << "Connect 1";
connect(http, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), this, SLOT(responseHeaderReceived(QHttpResponseHeader)));
qDebug() << "Connect 2";
connect(http, SIGNAL(requestFinished(int,bool)), this, SLOT(requestFinished(int,bool)));
qDebug() << "Connect 3";
http->setHost(host);
http->get(append);
}
void Downloader::stateChanged(int state)
{
switch(state)
{
case 0:
qDebug() << "Unconnected";
break;
case 1:
qDebug() << "Hhost Lookup";
break;
case 2:
qDebug() << "Connection";
break;
case 3:
qDebug() << "Sending";
break;
case 4:
qDebug() << "Reading";
break;
case 5:
qDebug() << "Connect";
break;
case 6:
qDebug() << "Closing";
break;
}
}
void Downloader::responseHeaderReceived(const QHttpResponseHeader &resp)
{
qDebug() << "Size" << resp.contentLength();
qDebug() << "Type" << resp.contentType();
qDebug() << "Status" << resp.statusCode();
}
void Downloader::requestFinished(int id, bool error)
{
if(error)
{
qDebug() << "ERROR!";
}
else
{
qDebug() << "OK";
QFile *file = new QFile(QDir::currentPath() + "/image.png");
if(file->open(QFile::Append))
{
file->write(http->readAll());
file->flush();
file->close();
}
delete file;
}
}
main.cpp - The code above works correctly if it is implemented like this
#include "mainwindow.h"
#include <QApplication>
#include <downloader.h>
int main(int argc, char *argv[])
{
Downloader getImage;
getImage.getImageFromWeb("servlet.dmi.dk", "/byvejr/servlet/byvejr?by=8000&tabel=dag3_9");
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Instead of this, I would like the image to be fetched when I press a button in the program, so I tried this:
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "downloader.h"
#include <QApplication>
#include <QDir>
void MainWindow::on_pushButton_clicked()
{
Downloader getImage;
getImage.getImageFromWeb("www.dmi.dk/", "/uploads/tx_dmidatastore/webservice/k/d/_/n/g/femdgn_dk.png");
}
This doesn't work. From the debugger I get:
Connect 1
Connect 2
Connect 3
When it works (when it's implemented in main.cpp) the debugger gives me something like:
Connect 1
Connect 2
Connect 3
OK
Connection
Sending
Reading
Size 16282
Type "image/png"
Status 200
Connect
OK
So I guess this tells me that the connections are made, but nothing is being executed.
Any answer/suggestion is appreciated.
Thanks in advance!

Looks like your getImage object is being descoped/destructed before it can do anything. Try creating a Downloader object as a member of MainWindow instead of inside the on_pushButton_clicked() function.

Related

udp binded/connected but not datagram received

I am new to Qt, c++, recently I am trying to use UDP to receive data on my raspberrypi1 from another raspberrypi2 (multicast). I am able to bind both of them but I can't receive the data (nopendingdatagram). I wonder what I did wrong here. (As you might notice, the code below was taken from examples found online). Thank you in advanced for helping me.
// myudp.h
#ifndef MYUDP_H
#define MYUDP_H
#include <QObject>
#include <QUdpSocket>
class MyUDP : public QObject
{
Q_OBJECT
public:
explicit MyUDP(QObject *parent = 0);
//void HelloUDP();
signals:
public slots:
void readyRead();
private:
QUdpSocket *socket;
};
#endif // MYUDP_H
// myudp.cpp
#include "myudp.h"
MyUDP::MyUDP(QObject *parent) :
QObject(parent)
{
// create a QUDP socket
socket = new QUdpSocket(this);
bool result = socket->bind(QHostAddress("224.224.0.2"), 10002);
if(result)
{
qDebug() << "Socket Connected";
}
else
{
qDebug() << "Socket Not Connected";
}
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
}
void MyUDP::readyRead()
{
// when data comes in
bool data_pending = socket->hasPendingDatagrams();
qDebug() << data_pending;
if(data_pending)
{
QByteArray buffer;
buffer.resize(socket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
socket->readDatagram(buffer.data(), buffer.size(),
&sender, &senderPort);
qDebug() << "Message from: " << sender.toString();
qDebug() << "Message port: " << senderPort;
qDebug() << "Message: " << buffer;
}
else
{
qDebug() << "No data";
}
}
#include <QCoreApplication>
#include "myudp.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyUDP client;
client.readyRead();
return a.exec();
}
The result is as follows:
Socket Connected
false
No data

QNetworkAccessManager doesn't handle HTTP Status Code 308

I'm testing my code for compatibility with HTTP 3xx status codes (redirects).
I'm interested in codes 301, 302, 303, 307 and 308.
All of those work fine with my code, except 308.
My client testcase is Qt/C++ based, and my testing server is python-based. I'll post the code of both.
client.cpp:
#include <QGuiApplication>
#include <QObject>
#include <QByteArray>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QNetworkAccessManager webCtrl;
QObject::connect(&webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* reply) {
if(reply->error() != QNetworkReply::NoError) {
qDebug() << "got error";
}
QByteArray data = reply->readAll();
qDebug() << "got" << data.length() << "bytes";
});
QNetworkRequest request(QUrl("http://localhost:8080/not_working"));
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
webCtrl.get(request);
return app.exec();
}
test_server.py:
#!/usr/bin/env python
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import os
class MyHTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/working':
self.send_response(200)
self.send_header('Content-type','text-html')
self.end_headers()
self.wfile.write("hey")
elif self.path == '/not_working':
self.send_response(308)
self.send_header('Location','/working')
self.end_headers()
server_address = ('127.0.0.1', 8080)
httpd = HTTPServer(server_address, MyHTTPRequestHandler)
httpd.serve_forever()
I run the server, then while it's running, I run the client and I get got 0 bytes in the console. If I change the response from 308 to, say, 301, it works fine (prints got 3 bytes).
Any idea why?
Note: The redirect works fine in Chrome, so my server code is likely correct.
Note: It seems like it's documented as unsupported. From the docs:
This signal is emitted if the QNetworkRequest::FollowRedirectsAttribute was set in the request and the server responded with a 3xx status (specifically 301, 302, 303, 305 or 307 status code) with a valid url in the location header, indicating a HTTP redirect.
(emphasis mine)
I'd still like to know why, though.
For anyone who has the same problem, here's my FileDownloader class with support for 308.
filedownloader.cpp:
#include "filedownloader.h"
FileDownloader::FileDownloader(QUrl imageUrl, QObject *parent) :
QObject(parent)
{
m_imageUrl = imageUrl;
connect(
&m_webCtrl, SIGNAL (finished(QNetworkReply*)),
this, SLOT (onDownloaded_internal(QNetworkReply*))
);
QNetworkRequest request(imageUrl);
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
m_webCtrl.get(request);
}
FileDownloader::~FileDownloader() {
}
void FileDownloader::onDownloaded_internal(QNetworkReply* reply) {
if(reply->error() != QNetworkReply::NoError) {
qDebug() << "error " << reply->error();
emit error();
return;
}
if(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 308) {
handleRedirect308(reply);
return;
}
QByteArray data = reply->readAll();
reply->deleteLater();
emit downloaded(data);
deleteLater();
}
void FileDownloader::handleRedirect308(QNetworkReply *reply) {
QByteArray header = reply->rawHeader("location");
QUrl url(QString::fromUtf8(header));
url = m_imageUrl.resolved(url);
QNetworkRequest request(url);
qDebug() << "308 to " << url;
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
m_webCtrl.get(request);
}
filedownloader.h:
#ifndef FILEDOWNLOADER_H
#define FILEDOWNLOADER_H
#include <QObject>
#include <QByteArray>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
class FileDownloader : public QObject {
Q_OBJECT
public:
explicit FileDownloader(QUrl imageUrl, QObject *parent = 0);
virtual ~FileDownloader();
signals:
void downloaded(QByteArray const& data);
void error();
private slots:
void onDownloaded_internal(QNetworkReply* reply);
private:
void handleRedirect308(QNetworkReply* reply);
QNetworkAccessManager m_webCtrl;
QUrl m_imageUrl;
};
#endif // FILEDOWNLOADER_H

Qt QNetworkAccessManager & simple web service return

I have a simple web service with the following URL:
http://localhost:8080/WebSvc1/webresources/generic/data?ctype=Ping
This returns a simple XML data:
<CALL TYPE='Ping'><IP>10.0.0.10</IP></CALL>
I'm trying to write a Qt program to call this web service.
The code that makes the call is below:
QUrl qrl("http://localhost:8080/WebSvc1/webresources/generic/data?ctype=Ping");
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
printf ("Calling url: [%s]\n", qPrintable(url));
QNetworkReply *reply = 0;
reply = manager->get(QNetworkRequest(qrl));
qDebug() << reply->readAll();
I'm expecting/hoping the readAll will get the XML text data and print it (via qDebug).
Instead I see nothing and the program just hangs.
UPpdate, also have this:
void obj::replyFinished(QNetworkReply *reply)
{
qDebug() << reply->readAll();
}
I've included an example (forcing a synchronous request <-> reply exchange to easy the debugging process) that should work for you:
QUrl qrl("http://localhost:8080/WebSvc1/webresources/generic/data?ctype=Ping");
qDebug() << "Calling url: " << qrl.toString();
manager = new QNetworkAccessManager();
QNetworkReply* reply = manager->get(QNetworkRequest(qrl));
QEventLoop eventLoop;
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
eventLoop.exec();
if (reply->error() != QNetworkReply::NoError)
{
qDebug() << "Network error: " << reply->error();
}
else
{
qDebug() << reply->readAll();
}
Notice that the "emitter" of the finished signal is not the QNetworkAccessManager but the reply itself.
I think your error could be with your web service. I tried your code out (slightly modified) with httpbin.org, and was getting proper replies. Maybe take a look at your code with httpbin.org and then see if you can track down what's wrong with your service.
MainWindow.cpp
#include "MainWindow.hpp"
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QDebug>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent), manager(this) {
load();
}
void MainWindow::load() {
const QUrl url(QStringLiteral("http://httpbin.org/xml"));
QNetworkReply* reply = manager.get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
replyFinished(reply);
});
qDebug() << reply->readAll(); // The reply won't be ready by now, so
// testing here isn't very helpful.
}
void MainWindow::replyFinished(QNetworkReply* reply) {
qDebug() << reply->readAll();
reply->deleteLater();
}
MainWindow.hpp
#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP
#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkReply>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
public slots:
void load();
void replyFinished(QNetworkReply* reply);
private:
QNetworkAccessManager manager;
};
#endif // MAINWINDOW_HPP
main.cpp
#include "MainWindow.hpp"
#include <QApplication>
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
network.pro
QT += core gui widgets network
TARGET = network
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += c++11
SOURCES += \
main.cpp \
MainWindow.cpp
HEADERS += MainWindow.hpp

Qt SIGNAL and SLOT in hierarcy, not threads (yet)

OK, I'm a total beginner but I'm missing something here. Been all over the Qt documentation/examples and everything I can dig up through Google. All similar information is slightly different in context...
I'm just starting out with Qt SIGNALS and SLOTS, I'm successful with gui examples and within a class. Now I want to connect a SIGNAL in a child class with a SLOT in a sibling class with the Connect defined in the parent main. Ultimately my aim is to receive iamges in a class handling a QTcpSocket and emit the data as char* to be handled (saved or displayed) by another class.
For now I've just created the most basic version of the arrangement in a Console app as a learning exercise. I've got a sender class...
sender.h
#ifndef SENDER_H
#define SENDER_H
#include <QObject>
class sender : public QObject
{
Q_OBJECT
public:
sender(QObject *parent = 0);
~sender();
signals:
void output(int data);
public slots:
void test(int data);
private:
};
#endif // SENDER_H
sender.cpp
#include <iostream>
#include "sender.h"
sender::sender(QObject *parent)
: QObject(parent)
{
std::cout << "Created sender" << std::endl;
int stuff = 47;
std::cout << "stuff = " << stuff << std::endl;
connect(this, SIGNAL(output(int)), this, SLOT(test(int)));
emit output(stuff);
}
void sender::test(int data)
{
std::cout << "Got to test, data = " << data << std::endl;
}
sender::~sender()
{
std::cout << "Destroying sender" << std::endl;
}
...and a receiver class...
receiver.h
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
class receiver : public QObject
{
Q_OBJECT
public:
receiver(QObject *parent = 0);
~receiver();
public slots:
void input (int data);
private:
};
#endif // RECEIVER_H
receiver.cpp
#include <iostream>
#include "receiver.h"
receiver::receiver(QObject *parent)
: QObject(parent)
{
std::cout << "Created receiver" << std::endl;
}
void receiver::input(int data)
{
std::cout << "Got data as = " << data << std::endl;
}
receiver::~receiver()
{
std::cout << "Destroying receiver" << std::endl;
}
My main looks like this...
main.cpp
#include <QtCore/QCoreApplication>
#include <iostream>
#include "sender.h"
#include "receiver.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
receiver myReceiver;
sender mySender;
if (QObject::connect(&mySender, SIGNAL(output(int)),
&myReceiver, SLOT(input(int))))
{
std::cout << "Got here so connect returned true" << std::endl;
}
return a.exec();
}
I've added the cout outputs and the sender::test function to try and figure out what's happening.
For me, this compiles cleanly and runs without any warnings or errors but while the sender::test SLOT gets called the receiver::input SLOT doesn't. The test on the connect in main returns true and neither sender or receiver are destroyed prematurely. Console output is...
Created receiver
Created sender
stuff = 47
Got to test, data = 47
Got here so connect returned true
So the SIGNAL is emitted, the SIGNAL and SLOT parameters match, I've got the Q_OBJECT macros in both sender.h and receiver.h, and both inherit from and #include QObject.
What's wrong???
P.S. I'm running 4.8.3 and IDE is VS2010 with Qt plugin.
The answer is quite simple: you are sending signal before you've connected it to the receiver and after you've connected it to itself. So your output is absolutely correct.
Do all emits only after everything has been connected. Here the instantiation of sender happens before sender and receiver are connected, and that's where the emit was done.

Display and get the result of a QMessageBox from outside of a QObject

I am trying to display and get the result a message box from outside of a QObject class. I seem to be able to generate the dialog like this:
#include <iostream>
#include <QApplication>
#include <QtConcurrentRun>
#include <QMessageBox>
class DialogHandler : public QObject
{
Q_OBJECT
signals:
void MySignal();
public:
DialogHandler()
{
connect( this, SIGNAL( MySignal() ), this, SLOT(MySlot()) );
}
void EmitSignal()
{
emit MySignal();
}
public slots:
void MySlot()
{
QMessageBox* dialog = new QMessageBox;
dialog->setText("Test Text");
dialog->exec();
int result = dialog->result();
if(result)
{
std::cout << "ok" << std::endl;
}
else
{
std::cout << "invalid" << std::endl;
}
}
};
#include "main.moc" // For CMake's automoc
void MyFunction(DialogHandler* dialogHandler)
{
dialogHandler->EmitSignal();
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
DialogHandler* dialogHandler = new DialogHandler;
MyFunction(dialogHandler);
return app.exec();
}
To get the result back in MyFunction, it seems to work to do simply pass an object to fill with the result like this:
#include <iostream>
#include <QApplication>
#include <QtConcurrentRun>
#include <QMessageBox>
class DialogHandler : public QObject
{
Q_OBJECT
signals:
void MySignal(int* returnValue);
public:
DialogHandler()
{
connect( this, SIGNAL( MySignal(int*) ), this, SLOT(MySlot(int*)), Qt::BlockingQueuedConnection );
}
void EmitSignal(int* returnValue)
{
emit MySignal(returnValue);
}
public slots:
void MySlot(int* returnValue)
{
std::cout << "input: " << *returnValue << std::endl;
QMessageBox* dialog = new QMessageBox;
dialog->addButton(QMessageBox::Yes);
dialog->addButton(QMessageBox::No);
dialog->setText("Test Text");
dialog->exec();
int result = dialog->result();
if(result == QMessageBox::Yes)
{
*returnValue = 1;
}
else
{
*returnValue = 0;
}
}
};
#include "main.moc" // For CMake's automoc
void MyFunction(DialogHandler* dialogHandler)
{
int returnValue = -1;
dialogHandler->EmitSignal(&returnValue);
std::cout << "returnValue: " << returnValue << std::endl;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
DialogHandler* dialogHandler = new DialogHandler;
QtConcurrent::run(MyFunction, dialogHandler);
std::cout << "End" << std::endl;
return app.exec();
}
Does that seem reasonable? Is there a better way to do it?
This isn't possible quite like you have it, but with a bit of work it could be done. One option, of course, would be to convert your class to a QObject, at which point you could send signals. It doesn't help for the delay during exec, however. If that is necessary, you could have a messaging class that lives in the main UI thread, but can be called from other threads. The function called from other threads would need to lock, make a semaphore, and send an event to itself with the semaphore and message to be displayed. Then, in customEvent (which would be in the UI thread), you would create the message box, exec it, and trigger the semaphore after the message box is cleared.
Of course, things get a bit more complicated if you need to send information back the other way as well. Then you'll need a complete subsystem for your program, instead of just one basic class like I describe here.

Resources