QDataStream empty after call of QDataStream::writeRawData - c++

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()

Related

Qt slots not executed in a multi-threaded DLL

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!

QDataStream is becoming Readonly

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"

How to convert enum to QString?

I am trying to use the Qt reflection for converting enum to QString.
Here is the part of code:
class ModelApple
{
Q_GADGET
Q_ENUMS(AppleType)
public:
enum AppleType {
Big,
Small
}
}
And here is i trying to do:
convertEnumToQString(ModelApple::Big)
Return "Big"
Is this possible?
If you have any idea about convertEnumToQString, please share it
You need to use Q_ENUM macro, which registers an enum type with the meta-object system.
enum AppleType {
Big,
Small
};
Q_ENUM(AppleType)
And now you can use the QMetaEnum class to access meta-data about an enumerator.
QMetaEnum metaEnum = QMetaEnum::fromType<ModelApple::AppleType>();
qDebug() << metaEnum.valueToKey(ModelApple::Big);
Here is a generic template for such utility:
template<typename QEnum>
std::string QtEnumToString (const QEnum value)
{
return std::string(QMetaEnum::fromType<QEnum>().valueToKey(value));
}
Much more elegant way found (Qt 5.9), just one single line, with the help of mighty QVariant.
turns enum into string:
QString theBig = QVariant::fromValue(ModelApple::Big).toString();
Perhaps you don't need QMetaEnum anymore.
Sample code here:
ModelApple (no need to claim Q_DECLARE_METATYE)
class ModelApple : public QObject
{
Q_OBJECT
public:
enum AppleType {
Big,
Small
};
Q_ENUM(AppleType)
explicit ModelApple(QObject *parent = nullptr);
};
And I create a widget application, calling QVaraint function there :
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <modelapple.h>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QString s = QVariant::fromValue(ModelApple::Big).toString();
qDebug() << s;
}
MainWindow::~MainWindow()
{
delete ui;
}
You can see that i try to output the string on console , which really did:
And sorry for reverse casting , i tried successfully in some project , but some how this time i met compiling error. So i decide to remove it from my answer.
The following should get you going:
QString convertEnumToQString(ModelApple::AppleType type) {
const QMetaObject metaObject = ModelApple::staticMetaObject;
int enumIndex = metaObject.indexOfEnumerator("AppleType");
if(enumIndex == -1) {
/* The enum does not contain the specified enum */
return "";
}
QMetaEnum en = metaObject.enumerator(enumIndex);
return QString(en.valueToKey(type));
}
For the global Enum declaring use this in any header file:
namespace YourNamespace {
Q_NAMESPACE
enum YourEnum: int {
EnumValue1,
EnumValue2
};
Q_ENUM_NS(YourEnum)
}
and this where you want to get Enum description:
QMetaEnum metaEnum = QMetaEnum::fromType<YourEnum>();
qDebug() << "Enum description: " << metaEnum.name() << "::" << metaEnum.valueToKey(YourEnum::EnumValue2);
How about:
QString convertEnumToQString(ModelApple::AppleType type)
{
const QMetaObject &mo = ModelApple::staticMetaObject;
int index = mo.indexOfEnumerator("AppleType");
QMetaEnum metaEnum = mo.enumerator(index);
return metaEnum.valueToKey(type);
}
UPDATED: For Qt 5.5, see this answer
I faced the same problem and this is how i solved it. This is especially for Qt 4.8
QString string = enumToString(ModelApple::Big);
QString ModelApple::enumToString(AppleType apple)
{
int index = metaObject()->indexOfEnumerator("AppleType");
QMetaEnum metaEnum = metaObject()->enumerator(index);
return metaEnum.valueToKey(apple);
}

Function call missing argument list to create pointer

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)

Downloading File in Qt From URL

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.