In my program I need to download a file, and I came across this article:
http://www.java2s.com/Code/Cpp/Qt/DownloadfromURL.htm
This code does work but it doesn't fit into my program so I re-coded it. I haven't completed it all but I've got the basics coded. However, when I test it, it pops up with a send error report window.
So far this is my code:
QtDownload.h
#include <QObject>
#include <QString>
#include <QNetworkAccessManager>
#include <QNetworkReply>
class QtDownload : public QObject
{
Q_OBJECT
public:
explicit QtDownload();
~QtDownload();
void setTarget(const QString& t);
private:
QNetworkAccessManager manager;
QNetworkReply* reply;
QString target;
void connectSignalsAndSlots();
signals:
public slots:
void download();
void downloadFinished(QNetworkReply* data);
void downloadProgress(qint64 recieved, qint64 total);
};
QtDownload.cpp
#include "qtdownload.h"
#include <QUrl>
#include <QNetworkRequest>
#include <QFile>
QtDownload::QtDownload()
: QObject(0)
{
this->connectSignalsAndSlots();
}
QtDownload::~QtDownload()
{
if (reply != 0)
delete reply;
}
void QtDownload::connectSignalsAndSlots()
{
QObject::connect(&manager, SIGNAL(finished(QNetworkReply*)),this, SLOT(downloadFinished(QNetworkReply*)));
QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
}
void QtDownload::setTarget(const QString &t)
{
this->target = t;
}
void QtDownload::downloadFinished(QNetworkReply *data)
{
QFile localFile("downloadedfile");
if (!localFile.open(QIODevice::WriteOnly))
return;
localFile.write(data->readAll());
localFile.close();
delete data;
data = 0;
}
void QtDownload::download()
{
QUrl url = QUrl::fromEncoded(this->target.toLocal8Bit());
QNetworkRequest request(url);
this->reply = manager.get(request);
}
void QtDownload::downloadProgress(qint64 recieved, qint64 total)
{
}
main.cpp
#include "qtdownload.h"
#include <QTimer>
int main()
{
QtDownload dl;
dl.setTarget("http://www.java2s.com/Code/Cpp/Qt/DownloadfromURL.htm");
QTimer::singleShot(0, &dl, SLOT(download()));
}
As I said it's not completely finished but I want this part to be working before I move on.
I'm also new to Qt so any tips would be appreciated.
You're using uninitialized pointer, so it points out to nowhere. Initialize reply with NULL in your constructor.
You should connect reply after it was created (reply = manager.get(...)), not inside of your constructor.
QNetworkReply is never deleted by QNetworkManager as docs say:
Do not delete the reply object in the slot connected to this signal. Use deleteLater().
So you shouldn't call delete on QNetworkReply in finished slot.
In finished slot setting data to 0 will only set parameter value to 0, not your class member reply. It's an unneeded line of code. You should set your reply member to NULL instead.
Also you should consider writing to a file every time you get data chunk, as whole file will be buffered in memory in your current case. It may lead to huge memory usage of your software when file at pointed URL is big.
You need QCoreApplication to start the event loop for Qt4.
Something like this should work (not tested) :
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
QtDownload dl;
dl.setTarget("http://www.java2s.com/Code/Cpp/Qt/DownloadfromURL.htm");
dl.download();
QObject::connect(app, SIGNAL(aboutToQuit()), app, SLOT(quit()));
return app.exec();
}
edit :: new version
I found some problems :
You don't need the custom reply, also you never set it to 0 in your constructor, so if it was never used it will delete a random piece of memory in your ~QtDownload();
you were deleting data inside QtDownload::downloadFinished, which shouldn't be done, it is handled by Qt, so it was getting deleted twice.
because of #2, you were deleting reply 3 times.
Here's the modified version :
qtdownload.h :
#include <QObject>
#include <QString>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
class QtDownload : public QObject {
Q_OBJECT
public:
explicit QtDownload();
~QtDownload();
void setTarget(const QString& t);
private:
QNetworkAccessManager manager;
QString target;
signals:
void done();
public slots:
void download();
void downloadFinished(QNetworkReply* data);
void downloadProgress(qint64 recieved, qint64 total);
};
qtdownload.cpp :
#include "qtdownload.h"
#include <QCoreApplication>
#include <QUrl>
#include <QNetworkRequest>
#include <QFile>
#include <QDebug>
QtDownload::QtDownload() : QObject(0) {
QObject::connect(&manager, SIGNAL(finished(QNetworkReply*)),this, SLOT(downloadFinished(QNetworkReply*)));
}
QtDownload::~QtDownload() {
}
void QtDownload::setTarget(const QString &t) {
this->target = t;
}
void QtDownload::downloadFinished(QNetworkReply *data) {
QFile localFile("downloadedfile");
if (!localFile.open(QIODevice::WriteOnly))
return;
const QByteArray sdata = data->readAll();
localFile.write(sdata);
qDebug() << sdata;
localFile.close();
emit done();
}
void QtDownload::download() {
QUrl url = QUrl::fromEncoded(this->target.toLocal8Bit());
QNetworkRequest request(url);
QObject::connect(manager.get(request), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
}
void QtDownload::downloadProgress(qint64 recieved, qint64 total) {
qDebug() << recieved << total;
}
main.cpp :
#include <QtCore>
#include "qtdownload.h"
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
QtDownload dl;
dl.setTarget("http://localhost");
dl.download();
//quit when the download is done.
QObject::connect(&dl, SIGNAL(done()), &app, SLOT(quit()));
return app.exec();
}
As you asked for it, some general comments:
void QtDownload::downloadFinished(QNetworkReply *data)
{
QFile localFile("downloadedfile");
if (!localFile.open(QIODevice::WriteOnly))
return;
localFile.write(data->readAll());
localFile.close();
delete data;
data = 0;
}
You read all data in one chunk. Bad for big files. Better read it incrementally.
Deleting the argument data from a slot is dangerous. You don't know whether the network manager continues to use (or delete) the object "data" points to right after it emits the finished signal. Probably you don't even have to delete the reply, if its owned by the manager, something to check the documentation for.
If opening the files fails, data is not deleted. So whatever is correct, its inconsistent. Either you leak or you have the risk of double-deletion.
localFile.write(data->readAll()) is not guaranteed to write all data at once. that's why it has a return value, which you should check, to make sure everything is written. If it returns -1, you should handle the error.
if (reply != 0)
delete reply;
Omit the if. Deleting a null pointer is safe.
Related
I'm working on a simple wrapper for a IPC lib we are using.
I want to convert the events from this lib to calls on Qt slots.
Right now i have something like this:
void Caller::registerCallback(int id, QObject* reciever, const char* member)
{
_callbackMap[id] = std::make_pair(reciever, QString(member));
}
bool Caller::call(const SomeData data)
{
auto reciever = _callbackMap.value(data.id);
return QMetaObject::invokeMethod(reciever.first, reciever.second.toLocal8Bit(), Qt::QueuedConnection,
QGenericReturnArgument(),
Q_ARG(SomeData, data));
}
void Receiver::someCallback(SomeData data)
{
qDebug() << data.str;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Caller caller;
Receiver reciever;
caller.registerCallback(1, &reciever, "someCallback");
caller.call(SomeData({ "Hi", 1 }));
return a.exec();
}
struct SomeData {
QString str;
int id;
}; Q_DECLARE_METATYPE(SomeData);
This works quite well. But I don't like to register the callbacks as strings. I would prefer a compile time checking with a syntax like this:
caller.registerCallback(1, &reciever, &Reciever::someCallback);
I am aware of this implementation.
The slots I want to register always have exactly one argument and no return value.
I already found this request what could solve my problem but unfortunately this was never implemented.
Also this question doesn't help me as I'm not able to patch the moc we are using.
So is this really not possible with all the meta magic Qt is using?
EDIT:
I found a solution that works also when the Caller dose not know about the Receiver (what is actually what I need):
//Caller.h
class Caller : public QObject
{
Q_OBJECT
public:
Caller(QObject *parent = nullptr);
~Caller();
//void registerCallback(int id, QObject* reciever, const char *member);
template < class R, typename Func >
void inline registerCallback(int id, R reciever, Func callback)
{
using std::placeholders::_1;
registerCallbackImpl(id, reciever, std::bind(callback, reciever, _1));
};
bool call(const SomeData);
private:
QMap<int, std::pair<QObject *, std::function<void(SomeData)>> > _callbackMap;
void registerCallbackImpl(int id, QObject* reciever, std::function<void(SomeData)> callback);
};
//Caller.cpp
void Caller::registerCallbackImpl(int id, QObject* reciever, std::function<void(SomeData)> callback)
{
_callbackMap[id] = std::make_pair(reciever, callback);
}
bool Caller::call(const SomeData data)
{
auto reciever = _callbackMap.value(data.id).first;
auto fn = _callbackMap.value(data.id).second;
QMetaObject::invokeMethod(reciever, [reciever, fn, data]() {
std::invoke(fn, data);
fn(data);
}, Qt::QueuedConnection);
return true;
}
//main.cpp
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Caller caller;
Receiver reciever;
using std::placeholders::_1;
caller.registerCallback(2, &reciever, &Receiver::someCallback);
caller.call(SomeData({ "Hi2", 2 }));
return a.exec();
}
This soulution relies upon std::invoke and lambda.
Variant 1: use std::invoke directly instead of QMetaObject::invoke
Variant 2: use std::invoke inside a lambda, which is passed to QMetaObject::invoke
Variant 3: use MACRO instead of std::invoke in variant 2.
If you use QMetaObject::invoke you've got an option to choose connection type - Direct or Queued. In variant 1 the call is invoked immediately like in direct connection.
receiver.h
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
#include <QDebug>
struct SomeData {
QString str;
int id;
};
//Q_DECLARE_METATYPE(SomeData);
class Receiver : public QObject
{
Q_OBJECT
public:
explicit Receiver(QObject *parent = nullptr) : QObject(parent) {}
void doSmth(SomeData data) {
qDebug() << data.str;
}
signals:
};
#endif // RECEIVER_H
caller.h
#ifndef CALLER_H
#define CALLER_H
#include <QObject>
#include <QMap>
#include <utility>
#include <map>
#include "receiver.h"
#define CALL_MEMBER_FN(object,ptrToMember) ((object)->*(ptrToMember))
typedef void (Receiver::*callback)(SomeData);
class Caller : public QObject
{
Q_OBJECT
public:
explicit Caller(QObject *parent = nullptr) : QObject(parent) { }
void registerCallback(int id, Receiver* receiver, callback c)
{
auto pair = std::make_pair(receiver, c);
_callbackMap.emplace(id, pair);
}
bool call(const SomeData data)
{
auto &receiver = _callbackMap.at(data.id);
return QMetaObject::invokeMethod(receiver.first, [data, receiver] () {
// method 1
std::invoke(receiver.second, receiver.first, data);
// method 2 (better not to use a MACRO)
CALL_MEMBER_FN(receiver.first, receiver.second)(data);
}, Qt::QueuedConnection);
}
bool call_invoke(const SomeData data)
{
auto &receiver = _callbackMap.at(data.id);
std::invoke(receiver.second, receiver.first, data);
return true;
}
signals:
private:
std::map<int,std::pair<Receiver*,callback>> _callbackMap;
};
#endif // CALLER_H
main.cpp
#include <QCoreApplication>
#include "receiver.h"
#include "caller.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Caller caller;
Receiver reciever;
caller.registerCallback(1, &reciever, &Receiver::doSmth);
caller.registerCallback(2, &reciever, &Receiver::doSmth);
caller.call(SomeData({ "Hi", 1 }));
caller.call_invoke(SomeData({ "Hi2", 2 }));
return a.exec();
}
An alternative approach might be to use a suitable std::function to capture the callback and then make use of QTimer::singleShot with a zero timeout to invoke the callback in the correct context.
struct SomeData {
QString str;
int id;
};
class Caller {
public:
using task = std::function<void(SomeData)>;
void registerCallback (int id, QObject *receiver, task t)
{
_callbackMap[id] = std::make_pair(receiver, t);
}
bool call (SomeData data)
{
auto receiver = _callbackMap.value(data.id);
QTimer::singleShot(0, receiver.first, [=](){ receiver.second(data); });
return true;
}
private:
QMap<int, std::pair<QObject *, task>> _callbackMap;
};
class Receiver: public QObject {
public:
void someCallback (SomeData data)
{
qDebug() << data.str;
}
};
Then use as...
Caller caller;
Receiver receiver;
caller.registerCallback(1, &receiver, [&](SomeData d){ receiver.someCallback(d); });
caller.call(SomeData({ "Hi", 1 }));
I have a rather odd problem, using QDataStream, or at least it is odd to me, because I don't understand the behavior at all.
Of course my problem is from a big project, but I managed to reproduce that odd behavior with a minimal example, which I'll describe now.
I have two classes
a binary data reader Reader and
a binary data parser Parser.
The Reader reads data via a QTcpSocket, packs each received data chunk into a QByteArray and sends that array via Qt signal to Parser.
The Parser writes all received data chunks to its own QDataStream and shall parse packets from that stream afterwards.
The problem arises, when Parser writes the data from the received QByteArray to its QDataStream. The return value of QDataStream::writeRawData correctly returns the number of bytes written, but then QDataStream::atEnd returns true and QDataStream::device.bytesAvailable returns zero.
Why? Where is the data QDataStream::writeRawData claims to have written?
You can find the code at the and of this post.
Environment: Qt 5.9.1 (MSVC 2015, 32/64 bit) on Windows 7 Enterprise SP1 64 bit
Reader.h
#ifndef READER_H
#define READER_H
#include <QAbstractSocket>
#include <QByteArray>
#include <QDataStream>
#include <QHostAddress>
#include <QObject>
class Reader : public QObject
{
Q_OBJECT
public:
Reader(const QHostAddress ip, quint16 port);
virtual ~Reader();
signals:
void signalNewData(const QByteArray data);
private slots:
void slotOnReadyRead();
private:
QAbstractSocket *mSocket;
QDataStream mStream;
};
#endif // READER_H
Reader.cpp
#include "reader.h"
#include <QTcpSocket>
Reader::Reader(const QHostAddress ip, quint16 port)
: mSocket(new QTcpSocket(this))
, mStream()
{
mStream.setDevice(mSocket);
mStream.setVersion(QDataStream::Qt_5_9);
mStream.setByteOrder(QDataStream::LittleEndian);
connect(mSocket, &QIODevice::readyRead, this, &Reader::slotOnReadyRead);
mSocket->connectToHost(ip, port, QIODevice::ReadOnly);
}
Reader::~Reader()
{
mSocket->disconnectFromHost();
delete mSocket;
mSocket = nullptr;
}
void Reader::slotOnReadyRead()
{
mStream.startTransaction();
quint64 availableBytesForReading = mStream.device()->bytesAvailable();
QByteArray binaryDataBlock;
char *tmp = new char[availableBytesForReading];
mStream.readRawData(tmp, availableBytesForReading);
binaryDataBlock.append(tmp, availableBytesForReading);
delete[] tmp;
tmp = nullptr;
if (mStream.commitTransaction())
{
emit signalNewData(binaryDataBlock);
}
}
Parser.h
#ifndef PARSER_H
#define PARSER_H
#include <QByteArray>
#include <QDataStream>
#include <QObject>
class Parser : public QObject
{
Q_OBJECT
public:
Parser();
public slots:
void slotOnNewData(const QByteArray data);
private:
QDataStream mStream;
};
#endif // PARSER_H
Parser.cpp
#include "parser.h"
#include <QDebug>
Parser::Parser()
: mStream(new QByteArray(), QIODevice::ReadWrite)
{
mStream.setVersion(QDataStream::Qt_5_9);
mStream.setByteOrder(QDataStream::LittleEndian);
}
void Parser::slotOnNewData(const QByteArray data)
{
const char *tmp = data.constData();
int numberOfBytesWritten = mStream.writeRawData(tmp, data.length());
qDebug() << "numberOfBytesWritten:" << numberOfBytesWritten << endl;
qDebug() << "QDataStream::status():" << mStream.status() << endl;
qDebug() << "QDataStream::atEnd():" << mStream.atEnd() << endl;
qDebug() << "QDataStream::device.bytesAvailable():" << mStream.device()->bytesAvailable() << endl;
}
main.cpp
#include <QCoreApplication>
#include "reader.h"
#include "parser.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Reader *reader = new Reader(QHostAddress("<insert IP>"), <insertPort>);
Parser *parser = new Parser();
QObject::connect(&a, &QCoreApplication::aboutToQuit, reader, &QObject::deleteLater);
QObject::connect(&a, &QCoreApplication::aboutToQuit, parser, &QObject::deleteLater);
QObject::connect(reader, &Reader::signalNewData, parser, &Parser::slotOnNewData);
return a.exec();
}
delete.pro Yes, I called my minimal example project 'delete' :'D
QT += core network
QT -= gui
CONFIG += c++11
TARGET = delete
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp \
reader.cpp \
parser.cpp
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
HEADERS += \
reader.h \
parser.h
The problem is not that there is no data but that your current position is at the end of the data. Use device() to retrieve the QBuffer which it used to wrap your QByteArray and reset the position of that object.
mStream.device()->reset()
I am developing a DLL that is to be loaded dynamically. In this DLL I need to use some Qt networking components, like QLocalServer and QNetworkAccessManager. To use those, I need to have a Qt event loop running. I've read that QLocalServer could be used without an event loop, but that's not the case with QNetworkAccessManager AFAIK.
I've managed to create and exec a QCoreApplication. Instantiation and execution of the QCoreApplication is done in the same thread and I've made sure that the QCoreApplication is created before any other Qt class is used.
The DLL runs several other threads as well and when I emit signals from those threads, their connected slots are never called, except if connection type = Qt::DirectConnection. I need to avoid synchronous connections, so I need to use Qt::QueuedConnection, right?
The other threads I've mentioned are not QThread's, they are std::thread's or boost::thread's. Reason for it is that this is shared code that needs to run in non-Qt apps as well.
Signals are emitted in the following way: I instantiate a bridge object derived from QObject and with the Q_OBJECT set, so the moc compiler generates signal/slot code from it. From this bridge object, I register callback methods (using boost signals). When 'other' threads then call one of these callbacks, the bridge object then emits a signal, that is connected to a slot in the same class. The idea is that the slot is then executed from the Qt event loop so I can start using the Qt networking classes asynchronously. But the slots are never called. Why?
I've stripped my code to reproduce the problem without the DLL stuff.
main.cpp
#include "bridge.h"
#include "worker.h"
#include <QDebug>
#include <memory>
#include <iostream>
#include <string>
struct MyLibrary
{
public:
MyLibrary()
: myWorker_()
, myQtBridge_(myWorker_)
{
myQtBridge_.start();
myWorker_.start();
}
private:
MyWorker myWorker_;
MyQtBridge myQtBridge_;
};
static std::shared_ptr<MyLibrary> myLibrary;
extern "C" __declspec(dllexport) void __cdecl start(void)
{
try {
myLibrary.reset(new MyLibrary());
} catch (const std::exception& e) {
qCritical() << e.what();
}
}
extern "C" __declspec(dllexport) void __cdecl stop(void)
{
try {
myLibrary.reset();
} catch (const std::exception& e) {
qCritical() << e.what() << '\n';
}
}
// main() is only here to reproduce the problem.
// In a DLL build, the calling application would call the start() and stop()
// functions.
int main(int argc, char *argv[])
{
Q_UNUSED(argc);
Q_UNUSED(argv);
start();
for (;;) {
std::cerr << "Enter q to quit: ";
std::string input;
if (std::getline(std::cin, input) && input == "q") {
break;
}
}
stop();
}
bridge.h
#ifndef BRIDGE_H
#define BRIDGE_H
#include "worker.h"
#include "communicator.h"
#include "qapp.h"
// BOOST includes
#include <boost/bind.hpp>
// Qt includes
#include <QDebug>
class MyQtBridge
{
public:
explicit MyQtBridge(MyWorker& myWorker)
: myWorker_(myWorker) // copy reference to the worker
, coreApplication_() // instantiate QtCoreApplication and exec() it in a thread
, myCommunicator_() // instantiate my Qt communication module
{
myWorker_.onSignal1(boost::bind(&MyQtBridge::onSignal1Handler, this));
}
void start()
{
coreApplication_.start();
}
private:
void onSignal1Handler()
{
qDebug() << "MyQtBridge: calling myCommunicator_.signal1()";
myCommunicator_.signal1();
qDebug() << "MyQtBridge: called myCommunicator_.signal1()";
}
private:
MyWorker& myWorker_;
CoreApplication coreApplication_; // Must be created before MyCommunicator!
MyCommunicator myCommunicator_;
};
#endif // BRIDGE_H
worker.h
#ifndef WORKER_H
#define WORKER_H
// BOOST includes
#include <boost/signals2/signal.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
// STL includes
#include <memory>
// Qt includes
#include <QDebug>
// A dummy worker, just to reproduce the problem
// This code cannot have any dependencies to Qt ( except for QDebug now,... :-D )
class MyWorker
{
public:
typedef boost::signals2::signal<void ()> signal_1_type;
MyWorker()
{
}
// called from main thread
~MyWorker()
{
try {
if (thread_) {
thread_->interrupt();
thread_->join();
qDebug() << "MyWorker thread joined";
thread_.reset();
}
} catch (const std::exception& e) {
qCritical() << e.what();
}
}
boost::signals2::connection onSignal1(const signal_1_type::slot_type& subscriber)
{
return signal_1_.connect(subscriber);
}
void start()
{
if (!thread_) {
thread_.reset(new boost::thread(boost::bind(&MyWorker::run, this)));
qDebug() << "MyWorker thread created";
}
}
private:
void run()
{
for (;;) {
boost::this_thread::interruption_point();
boost::this_thread::sleep(boost::posix_time::seconds(3));
qDebug() << "MyWorker: calling signal_1_()";
signal_1_();
qDebug() << "MyWorker: called signal_1_()";
}
}
private:
std::shared_ptr<boost::thread> thread_;
signal_1_type signal_1_;
};
#endif // WORKER_H
qapp.h
#ifndef QAPP_H
#define QAPP_H
#include <QCoreApplication>
#include <QDebug>
#include <thread>
#include <mutex>
#include <condition_variable>
// Purpose of this class is to get a Qt event loop going.
// Instantiation of the QCoreApplication and calling it's exec() method
// are both done in the same thread (seems to be a requirement).
// The rest of this class is synchronization.
class CoreApplication
{
public:
CoreApplication()
: thread_(&CoreApplication::run, this)
{
// Wait until the QCoreApplication has been created
// This is needed before any other Qt objects are created.
std::unique_lock<std::mutex> lock(mutex_);
cv_app_created_.wait(lock);
}
CoreApplication(const CoreApplication&) = delete;
~CoreApplication()
{
QCoreApplication::instance()->quit();
thread_.join();
}
void start()
{
cv_started_.notify_all();
}
private:
void run()
{
int argc = 0;
char **argv = nullptr;
QCoreApplication app(argc, argv);
qDebug() << "QCoreApplication instantiated";
cv_app_created_.notify_all();
// Wait until we're started
{
std::unique_lock<std::mutex> lock(mutex_);
cv_started_.wait(lock);
}
// blocking call, should return when QCoreApplication::instance()->quit() is called
qDebug() << "CoreApplication:: calling QCoreApplication::exec()";
app.exec();
qDebug() << "CoreApplication:: called QCoreApplication::exec()";
}
private:
std::thread thread_;
std::mutex mutex_;
std::condition_variable cv_app_created_, cv_started_;
};
#endif // QAPP_H
communicator.h
#ifndef COMMUNICATOR_H
#define COMMUNICATOR_H
// Qt includes
#include <QObject>
// This would be the class that uses the Qt networking classes
// It would operate independently, reacting only to signals.
class MyCommunicator : public QObject
{
Q_OBJECT
public:
MyCommunicator();
~MyCommunicator();
// called from MyQtBridge::onSignal1Handler()
void signal1();
signals:
void sigSignal1();
private slots:
void slotSignal1();
};
#endif // COMMUNICATOR_H
communicator.cpp
#include "communicator.h"
// Qt includes
#include <QDebug>
MyCommunicator::MyCommunicator()
{
// Note: the reason for this local signal connection is that
// the signal sigSignal1() is emitted from a
// different thread. The Qt::QueuedConnection flag should make sure that
// the slot slotSignal1() is called in the QCoreApplication thread
auto rc = connect(
this, SIGNAL(sigSignal1())
, this, SLOT(slotSignal1())
, Qt::QueuedConnection
);
qDebug() << "MyCommunicator: connect: " << rc;
}
MyCommunicator::~MyCommunicator()
{
}
// called from MyQtBridge::onSignal1Handler()
void MyCommunicator::signal1()
{
qDebug() << "MyCommunicator: emitting sigSignal1()";
emit sigSignal1();
qDebug() << "MyCommunicator: emitted sigSignal1()";
}
void MyCommunicator::slotSignal1()
{
qDebug() << "MyCommunicator: slotSignal1(), yay!"; // NEVER CALLED!
}
Found the solution thanks to the helpful information provided by code_fodder.
I made the MyCommunicator a member of CoreApplication, instantiate it in the CoreApplication::run method with the QCoreApplication instance as parent and now it works!
I have two classes named IPCBase and DispatchData. Now I want to pass QDataStrean Object drom IPCBase to DispatchData. First I tried to send it directly using Connect Statement. But it is giving error like QDataStream object is not registered in QRegisterMatatype.
edit :: I have refered this link as well
When, where and why use namespace when registering custom types for Qt
So I have done something like
typedef QDataStream* myDataStrem;
Q_DECLARE_METATYPE(myDataStrem)
and then connect statement in another class(DispatchData)
connect(mpThrIPCReceiver, SIGNAL(dispatchReadData(const int&, myDataStrem)),
this, SLOT(onIPCDataReceived(const int&, myDataStrem)));
onIPCDataReceived Slot
void DispatchData::onIPCDataReceived(const int& msgType, myDataStrem dataReceived)
{
// dataReceived >> str1; Here it is giving error
// qDebug()<<"is"<<str1;
MemberFuncPointer f = mIPCCommandMapper.value(msgType);
(this->*f)(*dataReceived);
//This is function pointer which will rout it to respective function depending on the Message type.
and then it will come here
void DispatchData::onStartCountingCycle(QDataStream &dataReceived)
{
int data = 0;
dataReceived >> data; //Here it is crashing
//Giving error like
//pure virtual method called
//terminate called without an active exception
// I have debugged it and here dataReceived is becoming Readonly.
}
It seems like you're passing around a dangling pointer: the data stream seems to not exist anymore by the time the receiving thread gets to it. Even if you extended its lifetime in the source object, it's a bad idea to pass a raw pointer through signal-slot connections. If the source class might vanish while the receiver thread has a pending slot call, you'll still be using a dangling pointer at the receiver. You'd be best served by passing around a QSharedPointer or std::shared_ptr.
The following works, you can of course use any type in the shared pointer.
#include <QtCore>
#include <cstdio>
struct Class : public QObject {
Q_SIGNAL void source(QSharedPointer<QTextStream>);
Q_SLOT void destination(QSharedPointer<QTextStream> stream) {
*stream << "Hello" << endl;
}
Q_OBJECT
};
Q_DECLARE_METATYPE(QSharedPointer<QTextStream>)
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
Class c;
c.connect(&c, &Class::source, &c, &Class::destination, Qt::QueuedConnection);
auto out = QSharedPointer<QTextStream>(new QTextStream(stdout));
emit c.source(out);
QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
*out << "About to exec" << endl;
return app.exec();
}
#include "main.moc"
Output:
About to exec
Hello
On modern Qt (5.6 at least), you don't need to call qRegisterMetatype in this case.
The same using std::shared_ptr:
// https://github.com/KubaO/stackoverflown/tree/master/questions/datastream-pass-37850584
#include <QtCore>
#include <cstdio>
#include <memory>
struct Class : public QObject {
Q_SIGNAL void source(std::shared_ptr<QTextStream>);
Q_SLOT void destination(std::shared_ptr<QTextStream> stream) {
*stream << "Hello" << endl;
}
Q_OBJECT
};
Q_DECLARE_METATYPE(std::shared_ptr<QTextStream>)
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
Class c;
c.connect(&c, &Class::source, &c, &Class::destination, Qt::QueuedConnection);
auto out = std::make_shared<QTextStream>(stdout);
emit c.source(out);
QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
*out << "About to exec" << endl;
return app.exec();
}
#include "main.moc"
I tried to connect my app to OpenViBE through VRPN server. My app works well until I try to add code to connect my app to VRPN server.
My code looks like this:
MainWindow.c code:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtUiTools/QUiLoader>
#include <QFile>
#include <QMessageBox>
#include <QFileDialog>
#include <iostream>
using namespace std;
#include "vrpn_Analog.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
currentImage = 0;
labelSize = ui->label_2->size();
createActions();
openFileDialog();
}
void MainWindow::checkChannels()
{
vrpn_Analog_Remote *vrpnAnalog = new vrpn_Analog_Remote("Mouse0#localhost");
vrpnAnalog->register_change_handler( 0, handle_analog );
}
void VRPN_CALLBACK MainWindow::handle_analog( void* userData, const vrpn_ANALOGCB a )
{
int nbChannels = a.num_channel;
cout << "Analog : ";
for( int i=0; i < a.num_channel; i++ )
{
cout << a.channel[i] << " ";
}
cout << endl;
}
MainWindow.h code:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFileInfoList>
#include "vrpn_Analog.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void resizeEvent(QResizeEvent *);
private slots:
void openFileDialog();
private:
void checkChannels();
void VRPN_CALLBACK handle_analog( void* userData, const vrpn_ANALOGCB a );
};
#endif // MAINWINDOW_H
With this code, when I try to run my app I get:
error: C3867: 'MainWindow::handle_analog': function call missing argument list; use '&MainWindow::handle_analog' to create a pointer to member
I try to edit code by error advice, but I get another error:
error: C2664: 'vrpn_Analog_Remote::register_change_handler' : cannot convert parameter 2 from 'void (__stdcall MainWindow::* )(void *,const vrpn_ANALOGCB)' to 'vrpn_ANALOGCHANGEHANDLER'
There is no context in which this conversion is possible
I search around, but I don't find any usable solution.
Methods checkChannels and handle_analog I "copy" from this code, where all works fine:
#include <QtCore/QCoreApplication>
#include <iostream>
#include "vrpn_Analog.h"
void VRPN_CALLBACK vrpn_analog_callback(void* user_data, vrpn_ANALOGCB analog)
{
for (int i = 0; i < analog.num_channel; i++)
{
if (analog.channel[i] > 0)
{
std::cout << "Analog Channel : " << i << " / Analog Value : " << analog.channel[i] << std::endl;
}
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
/* flag used to stop the program execution */
bool running = true;
/* VRPN Analog object */
vrpn_Analog_Remote* VRPNAnalog;
/* Binding of the VRPN Analog to a callback */
VRPNAnalog = new vrpn_Analog_Remote("openvibe_vrpn_analog#localhost");
VRPNAnalog->register_change_handler(NULL, vrpn_analog_callback);
/* The main loop of the program, each VRPN object must be called in order to process data */
while (running)
{
VRPNAnalog->mainloop();
}
return 0;
return a.exec();
}
Where I'm doing mistake? Thanks for all replies.
I had a similar error in Visual Studio: "function call missing argument list; use '&className::functionName' to create a pointer to member"..
I was just missing the parenthesis when calling the getter, so className.get_variable_a()
The error message tells you that the argument you provided does not match vrpn_ANALOGCHANGEHANDLER. You didn't show the definition of that. I checked online and it suggested
typedef void (*vrpn_ANALOGCHANGEHANDLER)(void *userdata, const vrpn_ANALOGCB info);
so I'm going with that.
Your code attempts to pass a pointer-to-member-function, which cannot be converted to a pointer-to-function. This is because a pointer-to-member-function can only be called on an object, so it wouldn't know what object to use.
If you look at the code you are "copying off", you will see that vrpn_analog_callback is a free function. However in your code it is a member function. You need to change your code so that the callback is a free function (or a static member function).
If your intent is that the callback should call the member function on the same MainWindow object that you are registering the handler on, then do this:
// In MainWindow's class definition, add this:
static void VRPN_CALLBACK cb_handle_analog( void* userData, const vrpn_ANALOGCB a )
{
static_cast<MainWindow *>(userData)->handle_analog(NULL, a);
}
// In checkChannels()
vrpnAnalog->register_change_handler( this, cb_handle_analog );
You cannot directly call a non-static class method using this callback. This is because the method is expecting to be called with the class this pointer.
If you don't need any data from your class, then just make the method static. If you do need data from the class, you can make a static "stub" that takes the class pointer in the userData parameter and then calls the original method. Something like:
Declaration:
static void VRPN_CALLBACK handle_analog_stub( void* userData, const vrpn_ANALOGCB a );
Definition
void VRPN_CALLBACK MainWindow::handle_analog_stub( void* userData, const vrpn_ANALOGCB a )
{
MainWindow *mainWindow = static_cast<MainWindow*>(userData);
mainWindow->handle_analog(NULL, a);
}
Then when you call the function use:
vrpnAnalog->register_change_handler( this, handle_analog_stub );
(Updated to static_cast to pointer, thanks rpavlik)