Qt signal slot button - c++

I need to change my text in QLabel every time when I clicked in order to my info updates.
When I change the value of A, the value of B must change, too.
I have two button that can change values in two QLabel (value of A, value of B).
main.cpp:
Counter A, B;
QObject::connect(&A, &Counter::changeValue, &B, &Counter::setValue);
QObject::connect(&A, &Counter::changeValue, &B, &Counter::Increment);
QObject::connect(&A, &Counter::changeValue, &B, &Counter::Decrement );
QObject::connect(Add, &QPushButton::clicked, &A, &Counter::clickedAdd(QLabel* obj));
QObject::connect(Sub, &QPushButton::clicked, &B, &Counter::clickedSub(QLabel* obj));
class Counter: public QObject{
private:
int count;
public slots:
int Increment () {
count++;
emit changeValue(count);
}
int Decrement () {
count--;
emit changeValue(count);
}
void clickedAdd(QLabel* obj){
int new_count = Increment();
obj_label->setText(QString::number(new_count));_
}
void clickedSub(QLabel* obj){
int new_count = Deccrement();
obj_label->setText(QString::number(new_count));_
}
void setValue(int new_count){
m_count = new_count;
emit changeValue(new_count);
}
public signals:
void changeValue(int);
How can I change my text in two QLabel's? Because in this way it stays const - 0....
When I try to connect:
QObject::connect(Add, &QPushButton::clicked, &A, &Counter::clickedAdd(QLabel* obj));
It writes an error:
Call to non-static member function without an object argument.
But I pass the function an argument - QLabel*.

Assuming OP wants an application with
two counters which can be incremented and decremented
a GUI
displaying the counter values
buttons to increment/decrement the counters interactively.
From this requirement, I would derive the structure of the program:
a class for the Counter (as already exposed by OP)
a GUI.
For the latter, I often saw classes as well in numerous sample codes but I believe: for such a minimal GUI / application, this could even all be done in main() directly.
testQCounter.cc:
#include <iostream>
#include <string>
// Qt header:
#include <QtWidgets>
// OPs Counter Class
class Counter : public QObject {
Q_OBJECT
private:
int count = 0;
public slots:
int Increment() {
count++;
emit changeValue(count);
return count;
}
int Decrement() {
count--;
emit changeValue(count);
return count;
}
int getValue() const { return count; }
void setValue(int new_count) {
count = new_count;
emit changeValue(new_count);
}
signals:
void changeValue(int);
};
#include "testQCounter.moc"
void setLabelValue(QLabel& qLbl, int value)
{
qLbl.setText(QString::number(value));
}
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup data
Counter a, b;
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("Counter Sample");
QGridLayout qGrid;
QLabel qLblATitle("Counter A");
qGrid.addWidget(&qLblATitle, 0, 0);
QPushButton qBtnIncA("+");
qGrid.addWidget(&qBtnIncA, 1, 0);
QLabel qLblA;
qLblA.setAlignment(Qt::AlignRight);
qLblA.setFrameStyle(QLabel::Box);
qGrid.addWidget(&qLblA, 2, 0);
QPushButton qBtnDecA("-");
qGrid.addWidget(&qBtnDecA, 3, 0);
QLabel qLblBTitle("Counter B");
qGrid.addWidget(&qLblBTitle, 0, 1);
QPushButton qBtnIncB("+");
qGrid.addWidget(&qBtnIncB, 1, 1);
QLabel qLblB("");
qLblB.setAlignment(Qt::AlignRight);
qLblB.setFrameStyle(QLabel::Box);
qGrid.addWidget(&qLblB, 2, 1);
QPushButton qBtnDecB("-");
qGrid.addWidget(&qBtnDecB, 3, 1);
qWinMain.setLayout(&qGrid);
qWinMain.show();
setLabelValue(qLblA, a.getValue());
setLabelValue(qLblB, b.getValue());
// install signal handlers
// connect clicked signal of buttons to counter a
QObject::connect(&qBtnDecA, &QPushButton::clicked, &a, &Counter::Decrement);
QObject::connect(&qBtnIncA, &QPushButton::clicked, &a, &Counter::Increment);
// connect changeValue signal of counter a to a function
QObject::connect(&a, &Counter::changeValue,
[&](int value) { setLabelValue(qLblA, value); });
// connect clicked signal of buttons to counter b
QObject::connect(&qBtnDecB, &QPushButton::clicked, &b, &Counter::Decrement);
QObject::connect(&qBtnIncB, &QPushButton::clicked, &b, &Counter::Increment);
// connect changeValue signal of counter b to b function
QObject::connect(&b, &Counter::changeValue,
[&](int value) { setLabelValue(qLblB, value); });
// runtime loop
return app.exec();
}
Output:
Qt Version: 5.15.1
Notes:
Concerning the update of the counter value label:
The signature of QLabel::setText() (the potential slot) is void QLabel::setText(const QString&).
The signature of the signal to connect is void Counter::changeValue(int).
Obviously, these signatures are not compatible.
For convenience, I introduced a function
void setLabelValue(QLabel& qLbl, int value)
{
qLbl.setText(QString::number(value));
}
but this doesn't fix the incompatibility because the function still has another parameter QLabel& which is not in the emitted signal.
This is a very usual case and the very usual solution is to bind the resp. QLabel reference to the signal.
It can be done most easily using a lambda.
When I first saw lambdas in C++, I found the syntax non-intuitive and somehow scaring.
However, after having read the doc. and tutorials I got used to it, and today, I couldn't imagine to live without.
I must admit before I learnt about lambdas I had to fiddle with bind() and hide() (in gtkmm with sigc++). This was a real nightmare…
Counter defines a signal. (I fixed the wrong syntax of OP.)
To make this linking properly, I had to add some things:
Q_OBJECT
#include "testQCounter.moc"
support for the Qt moc in my build script.
My build script is a Visual Studio project which I prepared with a CMake script.
I had to extend my CMakeLists.txt for moc (as I usually build without moc).
The CMakeLists.txt used to build the build script for testQCounter.cc:
project(QCounter)
cmake_minimum_required(VERSION 3.10.0)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Qt5 COMPONENTS Widgets REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories("${CMAKE_SOURCE_DIR}")
add_executable(testQCounter testQCounter.cc)
target_link_libraries(testQCounter Qt5::Widgets)
For my production, I would use separate classes for the GUI stuff as well, of course.
So, considering that the sample has two counters with nearly identical GUIs, it may make sense to introduce a counter widget – CounterEdit.
testQCounter2.cc:
#include <iostream>
#include <string>
// Qt header:
#include <QtWidgets>
// OPs Counter Class
class Counter : public QObject {
Q_OBJECT
private:
int count = 0;
public slots:
int Increment() {
count++;
emit changeValue(count);
return count;
}
int Decrement() {
count--;
emit changeValue(count);
return count;
}
int value() const { return count; }
void setValue(int new_count) {
count = new_count;
emit changeValue(new_count);
}
signals:
void changeValue(int);
};
#include "testQCounter2.moc"
class CounterEdit : public QWidget {
private:
Counter* pCounter = nullptr;
QVBoxLayout qVBox;
QLabel qLblTitle;
QPushButton qBtnInc;
QLabel qLblValue;
QPushButton qBtnDec;
QMetaObject::Connection connectionInc;
QMetaObject::Connection connectionDec;
QMetaObject::Connection connectionValue;
public:
CounterEdit(const QString& title, QWidget* pQParent = nullptr) :
QWidget(pQParent),
qLblTitle(title),
qBtnInc("+"),
qLblValue(""),
qBtnDec("-")
{
qLblTitle.setAlignment(Qt::AlignCenter);
qVBox.addWidget(&qLblTitle);
qVBox.addWidget(&qBtnInc);
qLblValue.setAlignment(Qt::AlignRight);
qLblValue.setFrameStyle(QLabel::Box);
qVBox.addWidget(&qLblValue);
qVBox.addWidget(&qBtnDec);
setLayout(&qVBox);
}
virtual ~CounterEdit()
{
QObject::disconnect(connectionInc);
QObject::disconnect(connectionDec);
QObject::disconnect(connectionValue);
}
CounterEdit(const CounterEdit&) = delete;
CounterEdit& operator=(const CounterEdit&) = delete;
Counter* counter() { return pCounter; }
const Counter* counter() const { return pCounter; }
void updateValue();
void updatevalue(int) { updateValue(); }
void setCounter(Counter* pCounter);
};
void CounterEdit::updateValue()
{
if (pCounter) {
qLblValue.setText(QString::number(pCounter->value()));
} else {
qLblValue.setText(QString());
}
}
void CounterEdit::setCounter(Counter* pCounter)
{
QObject::disconnect(connectionInc);
QObject::disconnect(connectionDec);
QObject::disconnect(connectionValue);
this->pCounter = pCounter;
if (pCounter) {
qLblValue.setText(QString::number(pCounter->value()));
connectionInc
= QObject::connect(&qBtnInc, &QPushButton::clicked, pCounter, &Counter::Increment);
connectionDec
= QObject::connect(&qBtnDec, &QPushButton::clicked, pCounter, &Counter::Decrement);
connectionValue
= QObject::connect(pCounter, &Counter::changeValue, this, &CounterEdit::updateValue);
}
}
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup data
Counter a, b;
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("Counter Sample");
QHBoxLayout qHBox;
CounterEdit editA("Counter A:");
qHBox.addWidget(&editA);
CounterEdit editB("Counter B:");
qHBox.addWidget(&editB);
qWinMain.setLayout(&qHBox);
qWinMain.show();
editA.setCounter(&a);
editB.setCounter(&b);
// runtime loop
return app.exec();
}
Output:
Qt Version: 5.15.1
Notes:
As data model and GUI are not anymore hardwired by design, I changed the management of the signal-slot connections a bit:
The connections are done on demand (in CounterEdit::setCounter()).
Connections are disconnected when not anymore needed.
It's not strictly necessary to store the connections like I did in the sample.
In Qt, a connection may be disconnected as well by providing signal and slot like in connect().
I don't like this for two reasons:
I'm paranoid.
This won't work for lambdas.
While the actual member function for update (CounterEdit::updateValue()) is parameter-less, I provided a second flavor (CounterEdit::updateValue(int)) which is just a wrapper.
However, this 2nd flavor has an identical signature like Counter::changeValue() and, hence, can be used as slot without an adapter.

Related

Functionpointer based syntax for QMetaObject invokeMethod

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 }));

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"

Qt C++ connect signal with non-void signature to lambda

I want to connect a signal with non-void signature to a lambda function.
My code looks like the following
QTimeLine *a = new QTimeLine(DURATION, this);
connect(a, &QTimeLine::valueChanged, [a,this](qreal r) mutable { this->setMaximumHeight(r);});
in a way similar to the SIGNAL-SLOT approach:
connect(a, SIGNAL(valueChanged(qreal),this,SLOT(doStuff(qreal)));
My connect-to-lambda compiles, but it won't change this->height().
What did I get wrong? How should I write the lambda so that it takes the qreal from valueChanged?
I read the related documentation, but I couldn't find useful examples.
****EDIT****
In fact it works, I was getting the QTimeLine settings wrong. And yes, I don't need to capture a.
I was trying to animate a custom insertRow() method of a QTableWidget.
I also made the lambda change the height of the table row instead of the contained widget's. For reference, here's the working snippet:
QTimeLine *a = new QTimeLine(DURATION,this);
connect(a,&QTimeLine::valueChanged,[this](qreal r) mutable {
this->list->setRowHeight(0,r * ROW::HEIGHT);
});
a->start();
Thanks a lot for the quick replies anyway.
Should just work. Here is a complete SSCCE that demonstrates it working. Check what you are doing different in principles.
main.cpp
#include <QTimeLine>
#include <QObject>
#include <QDebug>
#include <QCoreApplication>
class Foo
{
void setMaximumHeight(int h) {height = h; qDebug() << "Height:" << height;}
public:
void doStuff() { QObject::connect(&timeLine, &QTimeLine::valueChanged, [this](qreal r) mutable { setMaximumHeight(r);}); timeLine.start(); }
int maximumHeight() const { return height; }
int height{0};
int DURATION{100};
QTimeLine timeLine{DURATION};
};
int main(int argc, char **argv)
{
QCoreApplication application(argc, argv);
Foo foo;
foo.doStuff();
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
CONFIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && ./main

QML QSettings and QML - Cannot save/load settings

What I do wrong ? I always have in debug console-"error! cannot load data". Can anyone point out an error? Maybe I create bad "settings" ? It's a permission program problem?
//main.cpp
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include <QQmlContext>
#include <QSettings>
#include "settings.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/kon/main.qml"));
Settings* settings = new Settings();
viewer.rootContext()->setContextProperty("settings", settings);
viewer.showExpanded();
return app.exec();
}
//settings.h
#ifndef SETTINGS_H
#define SETTINGS_H
#include <QObject>
#include <QSettings>
class Settings : public QSettings {
Q_OBJECT
public:
Settings(QObject *parent = 0);
virtual ~Settings();
Q_INVOKABLE
void setValue(const QString &key, const QVariant &value);
Q_INVOKABLE
void setValueIfNotSet(const QString &key, const QVariant &value);
Q_INVOKABLE
QVariant value(const QString &key, const QVariant &defaultValue);
Q_INVOKABLE
bool boolValue(const QString &key, const bool defaultValue);
Q_INVOKABLE
void initToDefaults();
signals:
void settingChanged(const QString& key);
};
#endif // SETTINGS_H
//settings.cpp
#include "settings.h"
Settings::Settings(QObject* parent) :
QSettings(parent) {
}
Settings::~Settings() {
}
QVariant Settings::value(const QString &key, const QVariant &defaultValue = QVariant()) {
return QSettings::value(key, defaultValue);
}
bool Settings::boolValue(const QString &key, bool defaultValue) {
return QSettings::value(key, defaultValue).toBool();
}
void Settings::setValue(const QString &key, const QVariant &value) {
// change the setting and emit a changed signal
// (we are not checking if the value really changed before emitting for simplicity)
QSettings::setValue(key, value);
emit settingChanged(key);
}
void Settings::setValueIfNotSet(const QString &key, const QVariant &value) {
// change the setting and emit a changed signal
if( !QSettings::contains(key) ) {
QSettings::setValue(key, value);
// (we are not checking if the value really changed before emitting for simplicity)
emit settingChanged(key);
}
}
void Settings::initToDefaults() {
setValueIfNotSet("test", true);
}
And in QML i use this class like that:
Button
{
id:button1
nazwa: "Set value"
onClicked: settings.setValue("1","adskmmads")
}
Button
{
id:button2
onClicked: console.log(settings.value("1","error! cannot load data"))
nazwa: "Load value and show"
}
QSettings constuctor needs at least 2 strings organization name and application name
QSettings::QSettings(const QString & organization, const QString & application = QString(), QObject * parent = 0);
In your case just use this code below in your main()
(1)If you use QSettings from many places in your application, you might want to specify the organization name and the application name using QCoreApplication::setOrganizationName() and QCoreApplication::setApplicationName(), and then use the default QSettings constructor:
QCoreApplication::setOrganizationName("MySoft");
QCoreApplication::setOrganizationDomain("mysoft.com");
QCoreApplication::setApplicationName("Star Runner");
...
QSettings settings; //default constructor
(1)reference: QSettings

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.