Store QList<Custom> in QVariant - c++

I have a class defined as this:
cdataentry.h:
#ifndef CDATAENTRY_H
#define CDATAENTRY_H
#include <QObject>
#include <QString>
#include <QVariant>
#include <QtOpcUa>
#include <QMetaType>
#include <cnodetype.h>
#include <cdatastatus.h>
/**
* #brief A class providing data and methods to describe a single OPCUA ua
* node in the user input table.
*/
class CDataEntry : public QObject
{
Q_OBJECT
public:
CDataEntry(const QString& np, QObject* parent = nullptr);
~CDataEntry();
QString nodePath() const;
private:
/**
* #brief Obsolute path to the node on the MDE server
*/
const QString m_nodePath;
};
Q_DECLARE_METATYPE(CDataEntry); // to be able to store it in QVariant.
#endif // CDATAENTRY_H
I am trying to store a QList<CDataEntry> object in a QVariant. For that end, I have provided the Q_DECLARE_METATYPE(CDataEntry);
The problem is that the code doesnt compile, what I get is:
error: no matching function for call to 'QVariant::QVariant(QList<CDataEntry>&)'
What am I missing here?

You need to add default constructor, copy constructor and copy/assignment operator to your QObject subclass.
Like this:
CDataEntry& operator=(const CDataEntry&){}
CDataEntry(QObject* parent = nullptr):QObject(parent){}
CDataEntry(const CDataEntry&){}
//...
CDataEntry(const QString& np, QObject* parent = nullptr)
after that you can use it in QVariant like that:
CDataEntry test;
QList<CDataEntry> list;
list.append(test);
QVariant var = QVariant::fromValue<QList<CDataEntry>>( list );
auto t = var.value<QList<CDataEntry>>();
qDebug() << t.first().nodePath();

Related

Issue with QNetworkAccessManager Singleton Injection

I'm writing a RSS Feed Reader in Qt. My approach is to save every Feed in an object of the class Feed. The download is handled inside the Feed class, therefore I need to make sure that every object of Feed can access the (one) NetworkAccessManager. To manage this (and to make sure the NetworkAccessManager has only one instance) I want to make a Singleton injection. The problem is, with my current code I get the following error message
...main.cpp:-1: Fehler: undefined reference to `NetworkMgr::qnam'
Following is the code of which I think it is the most relevant for the problem, if you need anything more let me know.
I try to get the pointer on the instance of the NetworkAccessManager directly inside the constructor of the Feed class and then safe it in a private variable.
#ifndef FEED_H
#define FEED_H
#include <QObject>
#include <QGuiApplication>
#include <QtQuick>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QPixmap>
#include <iostream>
#include <QDomDocument>
#include "NetworkMgr.h"
using namespace std;
class Feed : public QObject {
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged)
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
public:
Feed(QObject* parent = NULL) : QObject(parent){
m_nMgr = NetworkMgr::getInstance();
connect(m_nMgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(parse(QNetworkReply*)));
m_active = false;
};
QString name();
void setName(QString);
QString url();
void setUrl(QString);
int id();
void setId(int);
bool active();
void setActive(bool);
public slots:
void get();
private:
QString m_name;
QString m_url;
int m_id;
bool m_active;
QNetworkAccessManager* m_nMgr;
private slots:
void parse(QNetworkReply* reply);
signals:
void nameChanged();
void urlChanged();
void idChanged();
void activeChanged();
};
#endif
This is my Singleton class:
#ifndef NETWORKMGR_H
#define NETWORKMGR_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
class NetworkMgr : public QObject {
Q_OBJECT
NetworkMgr(QObject* parent = NULL) : QObject(parent){};
~NetworkMgr(){
delete qnam;
}
static QNetworkAccessManager* qnam;
public:
static QNetworkAccessManager* getInstance(){
if(qnam == NULL){
qnam = new QNetworkAccessManager();
}
return qnam;
}
NetworkMgr(NetworkMgr const& copy) = delete;
NetworkMgr& operator=(NetworkMgr const& copy) = delete;
};
//QNetworkAccessManager* NetworkMgr::qnam = NULL;
#endif
As you can see, I also tried to initialize qnam to nullpointer, but then I get the error:
...Feed.h:17: Fehler: multiple definition of NetworkMgr::qnam' debug/main.o: In function Feed::~Feed()':
...Feed.h:17: multiple definition of `NetworkMgr::qnam'
And this is how I tried to call it as a test from main.cpp:
Feed feed;
feed.setUrl("https://www.deskmodder.de/blog/feed/");
feed.get();
I tried getting the instance of NetworkAccessManager outside the constructor as well, but the error stays the same.
I would really appreciate it, if anybody here has an idea what I could try to get rid of this error.. Before I take a completely different approach, I'd like to try it this way.
Thanks in advance to anyone contributing to fix my problem!

How to pass container with custom objects from C++ to QML?

I know something about C++ but I'm pretty new to QML.
I want to pass multiple custom C++ objects in a container to QML but I am having trouble doing so.
The code provided is narrowed down to the basics.
I can communicate from and to QML in a single object which I register with setContextProperty, this works fine. But if I try to do so with a QHash, then I get an error:
‘QVariant::QVariant(void*)’ is private within this context‘
Maybe you can help me out or give me a direction?
Thanks a bunch.
Update:
Thanks derM, here is my try at it:
I have added: Q_DECLARE_METATYPE(MyData); at the end of the header file. I have changed the container to a QVariantMap.
If I try: QVariant qvtest1(test1);
I get the error:
no matching function for call to ‘QVariant::QVariant(MyData&)’
However this works:
QVariant qvtest1, qvtest2;
qvtest1.setValue(test1);
qvtest2.setValue(test2);
But again I get an error with: setContextProperty("mymap", &mymap);
error:
calling a private constructor of class 'QVariant'
Code is adjusted accordingly.
Update 2
Thanks eyllanesc, your approach is working!
However I now get confronted with related issues in QML.
It seems that I can not acces all the QMap functions from QML.
For example:
var test_data = mymap["three"] // works fine
var test_data2 = mymap.find("two").value() // results in: Property 'find' of object [object Object] is not a function
Same problem:
var tmp1 = mydata_qml_object // object was created before
mymap["four"] = tmp1 // works fine
mymap.insert("four", tmp1) // Property 'insert' of object [object Object] is not a function
I am using Qt 5.11.1
Is this a bug or am I missing something?
C++ code
mydata.hpp:
#ifndef MYDATA_HPP
#define MYDATA_HPP
#include <QObject>
#include <QString>
class MyData : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ get_name WRITE set_name NOTIFY nameChanged)
public:
explicit MyData(QObject *parent = nullptr);
MyData(QString name);
MyData(const MyData &other);
MyData(MyData &&other) = delete;
MyData &operator=(const MyData &other);
MyData operator=(MyData &&other) = delete;
~MyData() override = default;
signals:
void nameChanged();
public slots:
void set_name(const QString &name);
QString get_name();
private:
QString _name;
};
Q_DECLARE_METATYPE(MyData);
#endif // MYDATA_HPP
mydata.cpp:
#include "mydata.hpp"
MyData::MyData(QObject *parent)
: QObject(parent)
{
}
MyData::MyData(QString name)
: _name(name)
{
}
MyData::MyData(const MyData &other)
{
_name = other._name;
}
MyData &MyData::operator=(const MyData &other)
{
if (this != &other)
{
_name = other._name;
return *this;
}
}
void MyData::set_name(const QString &name)
{
_name = name;
}
QString MyData::get_name()
{
return _name;
}
main.cpp:
#include <mydata.hpp>
#include <QGuiApplication>
#include <QMap>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQuickView>
#include <iostream>
int main(int argc, char *argv[])
{
MyData test1("Hi");
MyData test2("Hello");
QMap<QString, QVariant> mymap; // QVariantMap
QVariant qvtest1(test1); // error: no matching function for call to ‘QVariant::QVariant(MyData&)’
//working:
QVariant qvtest1, qvtest2;
qvtest1.setValue(test1);
qvtest2.setValue(test2);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
auto *engine = new QQmlEngine;
QQuickView view;
QQmlContext *ctxt = view.rootContext();
// this is working:
qmlRegisterType<MyData>("MyData", 1, 0, "MyData");
engine->rootContext()->setContextProperty("test1", &test1);
// this produces an error: calling a private constructor of class 'QVariant'
engine->rootContext()->setContextProperty("mymap", &mymap);
QQmlComponent component(engine, QUrl("qrc:/main.qml"));
QQmlEngine::setObjectOwnership(engine, QQmlEngine::CppOwnership);
QObject *object = component.create();
return app.exec();
}
According to the docs, the classes that inherit from QObject can not have a copy constructor or assignment operator:
No Copy Constructor or Assignment Operator
QObject has neither a copy constructor nor an assignment operator.
This is by design. Actually, they are declared, but in a private
section with the macro Q_DISABLE_COPY(). In fact, all Qt classes
derived from QObject (direct or indirect) use this macro to declare
their copy constructor and assignment operator to be private. The
reasoning is found in the discussion on Identity vs Value on the Qt
Object Model page.
The main consequence is that you should use pointers to QObject (or to
your QObject subclass) where you might otherwise be tempted to use
your QObject subclass as a value. For example, without a copy
constructor, you can't use a subclass of QObject as the value to be
stored in one of the container classes. You must store pointers.
On the other hand, if you want a class that inherits from QObject to support the QMetaType to use it as QVariant, you must pass it to the pointer because, as noted, the QObjects do not have a copy constructor, but the pointers are copyable.
//mydata.h
#ifndef MYDATA_H
#define MYDATA_H
#include <QObject>
class MyData : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ get_name WRITE set_name NOTIFY nameChanged)
public:
explicit MyData(QObject *parent = nullptr);
MyData(const QString & name);
~MyData() override = default;
signals:
void nameChanged();
public slots:
void set_name(const QString &name);
QString get_name();
private:
QString _name;
};
Q_DECLARE_METATYPE(MyData*)
#endif // MYDATA_H
//mydata.cpp
#include "mydata.h"
MyData::MyData(QObject *parent) : QObject(parent)
{}
MyData::MyData(const QString & name)
: _name(name){}
void MyData::set_name(const QString &name)
{
if(_name == name) return;
_name = name;
emit nameChanged();
}
QString MyData::get_name()
{return _name;}
You point out that setValue works, have you bought that it works ?, besides this method is used for the types that do not support QVariant so they will probably accept any type of data, what must be done is to pass the pointer:
MyData test1("Hi");
MyData test2("Hello");
QMap<QString, QVariant> mymap;
QVariant qvtest1 = QVariant::fromValue(&test1);
QVariant qvtest_1, qvtest_2;
qvtest_1.setValue(&test1);
qvtest_2.setValue(&test2);
On the other hand is setContextProperty, this accepts a QVariant or a pointer to QObject, and in your first case you pass QObject, the correct thing is to pass the pointer, in the second case you pass a QVariant so there are no problems as it can be copied.
engine->rootContext()->setContextProperty("test1", &test1);
engine->rootContext()->setContextProperty("mymap", mymap);
In conclusion you must pass an object copyable to setContextProperty.
qmlRegisterType only records that a type can be transmitted through the signals, but this does not guarantee that it works, this is a necessary condition but not enough since it is not copyable, so you must use MyData*.

Qt C++ use method of creating class in created objects

I`m still stuck.
I have a GUI-class that creates an Object. From a method of this new Object I want to use methods of the creating class
I have a Gui(PBV) with line_Edits and a Combobox. In this Combobox I can choose between different Geometries. Geo_1, Geo_2,… that inherit from Geometry. According to the entries in the combobox I want to create different Objects (Geo_1,Geo_2,…) that then set the lineEdits of the creating class according to the needs of the Geo_n-Object.
I don`want to do that with signal-slot
I asked different questions on that but I can`t get further.
I somehow feel this is kind of recursive... Is there a solution fort hat?
Here is my code:
PBV.h:
#include "../Geometry/Geo_1.h"
class PBV : public Tab
{
Q_OBJECT
public:
explicit PBV (QWidget *parent = 0);
~ PBV ();
virtual void printContent( QStringList *const qsl);
private:
Geo_1 *GEO_1;
Geometry *GEO;
}
PBV.cpp:
…
Geo_1 *GEO_1;
GEO_1 = new Geo_1(this);
GEO_1->set_LNE_default();
…
.
Geo_1.h:
#ifndef GEO_1_H
#define GEO_1_H
#include "Geometry.h"
#include "../Tabs/PBV.h"
class Geo_1: public Geometry
{
Q_OBJECT
public:
Geo_1 (QObject *_parent = 0);
virtual void set_LNE_default();
};
#endif // GEO_1_H
.
Geo_1.cpp:
#include "Geometry.h"
#include <QDebug>
#include "Geo_1.h"
#include "../Tabs/PBV.h"
Geo_1::Geo_1(QObject*_parent)
: Geometry(_parent) {}
void Geo_1::set_LNE_default()
{
// here I want to use printContent
}
.
Geometry.h:
#ifndef GEOMETRY_H
#define GEOMETRY_H
#include <QObject>
class Geometry : public QObject
{
Q_OBJECT
public:
Geometry(QObject *_parent=0);
~Geometry();
virtual void set_LNE_default();
};
#endif // GEOMETRY_H
.
Geometry.cpp:
#include "Geometry.h"
#include <QDebug>
Geometry::Geometry(QObject *_parent)
: QObject(_parent) {}
Geometry::~Geometry(){}
void Geometry::set_LNE_default() { }
When PBV allocates a Geo_1 instance, it is already passing a pointer to itself in the constructor:
GEO_1 = new Geo_1(this); // "this" is your PBV instance
Why not save the pointer to use later?
Geo_1::Geo_1(PBV*_parent) : Geometry((QObject*)_parent)
{
this->pbv = _parent; // Save reference to parent
}
Then you can do:
void Geo_1::set_LNE_default()
{
// here I want to use printContent
this->pbv->printContent(...); // this->pbv is your saved ptr to the PBV that created this instance
}
EDIT
Additonally, you might run into the problem that you will have to cross-include the two headers (PBV and Geo_1), to fix this, remove the include for Geo_1 from PBV.h and forward-declare Geo_1. Just like this:
class Geo_1;
Before declaring your PBV class.

c++ Is it possible to inherit a class, but not what that class inherits? (qt)

I have four classes at the moment. Client, ChatWindow, FunctionCall and MainWindow. What I ultimatly would want to do is not have FunctionCall class and have a virtual inheritance of Client in ChatWindow and MainWindow, but QT, or more specifically QObject doesn't allow this.
The reason I thought a virtual class would be good is to not create two different instances of a class, but rather have ChatWindow and MainWindow share the variables.
I've made a FunctionCall class that inherits Client, and I've created virtual inheritance between ChatWindow and MainWindow with FunctionCall
ChatWindow.h
#ifndef CHATWINDOW_H
#define CHATWINDOW_H
#include <QWidget>
#include "functioncall.h"
namespace Ui {
class ChatWindow;
}
class ChatWindow : public QMainWindow, public virtual FunctionCall
{
Q_OBJECT
public:
explicit ChatWindow(QWidget *parent = 0);
~ChatWindow();
private slots:
void on_sendButton_clicked();
private:
Ui::ChatWindow *ui;
};
#endif // CHATWINDOW_H
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "functioncall.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow, public virtual FunctionCall
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_connectButton_clicked();
private:
Ui::MainWindow *ui;
protected:
void something();
};
#endif // MAINWINDOW_H
FunctionCall.h
#ifndef FUNCTIONCALL_H
#define FUNCTIONCALL_H
#include "client.h"
class FunctionCall : public Client
{
public:
FunctionCall();
};
#endif // FUNCTIONCALL_H
Client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <QApplication>
#include <QWidget>
#include <QDialog>
#include <QObject>
#include <QLabel>
#include <QComboBox>
#include <QLineEdit>
#include <QPushButton>
#include <QDialogButtonBox>
#include <QTcpSocket>
#include <QString>
#include <QTcpServer>
#include <QStringList>
#include <QNetworkSession>
#include <QDataStream>
#include <QGridLayout>
#include <QMainWindow>
class Client : public QDialog
{
Q_OBJECT
public:
Client(QWidget *parent = 0);
public slots:
void read();
void displayError(QAbstractSocket::SocketError socketError);
void sessionOpened();
void connectedSocket();
void disconnectedSocket();
void pushToSocket(
quint8 registerForm,
QString registerUsername,
QString registerPassword,
QString username,
QString text
);
QString readFromSocket();
void saveToFile(std::string fileName, QString text);
public:
QTcpSocket *tcpSocket;
quint16 blockSize;
QNetworkSession *networkSession;
QTcpServer *tcpServer;
struct HeaderFile {
quint8 registerForm = 2;
QString registerUsername;
QString registerPassword;
QString username;
QString text;
};
public:
QStringList *hostCombo;
void send(QString username, QString text);
void loginRegisterConnect(QString host, int port, QString username, QString password);
friend QDataStream & operator<<(QDataStream& str, const HeaderFile & data) {
str << data.registerForm << data.registerUsername << data.registerPassword << data.username << data.text;
return str;
}
friend QDataStream & operator>>(QDataStream& str, HeaderFile & data) {
str >> data.registerForm >> data.registerUsername >> data.registerPassword >> data.username >> data.text;
return str;
}
};
#endif // CLIENT_H
Problem is I'm getting an error, probably because the Client class inherits QDialog.
I was wondering if it was possible only to inherit from Client, and not what Client also inherits, basically I want to use the functions in the Client class. But nothing it inherits from QDialog.
It doesn't compile here is the error:
C:\\main.cpp:9: error: C2385: ambiguous access of 'show'
could be the 'show' in base 'QWidget'
or could be the 'show' in base 'QWidget'
Solved my issue:
I basically made a singleton of the Client class, and created instances of that.
No, because that would violate the type equivalency (Liskov Substitution Principle). Basically it means that since you inherit from Client every FunctionCall object will also be a Client object and since every Client object has to be a QDialog object it follows that the FunctionCall object has to be a QDialog object.
Also what you've seem to be victim of here is that you use multiple inheritance and the same (non-virtual) base appears twice in the inheritance tree. You probably should think twice or thrice about this: is this really the right design? is this really what you want? Note that the different occations of QWidget in the inheritance tree are different (sub) objects.

Subclassing QString to add more functionality?

Trying to add functionality to QString but get build errors ?? And if I am missing things??
#ifndef CSTRING_H
#define CSTRING_H
#include <QString>
#include <QStringList>
#include <QObject>
class CString : public QString, public QObject
{
Q_OBJECT
public:
explicit CString(QObject *parent = 0);
QStringList Find(QString qstrSearch);//all occurances
signals:
public slots:
};
#endif // CSTRING_H
#include "cstring.h"
CString::CString(QObject *parent) :
QString(parent) //ERROR IS POINTING TO HERE
{
}
QStringList Find(QString qstrSearch)//all occurances
{//indexOf, contains
QStringList qstrList;
return qstrList;
}
Don't derive classes form QString since it wasn't designed with polymorphy in mind (note that it has no virtual methods, in particular no virtual destructors) If you want to provide new utility functions, just use free functions - you may want to put them in a namespace:
namespace CString {
QStringList find(const QString &search);
}
QString(parent) Qstring does not have a constructor taking a QObject-parent as parameter. Therefor the compiler tries to cast your QObject to closest Matching constructor, which probably would be QString ( QChar ch )
You should use composition instead of inheritance here, because QString is not designed for subclassing. You can get a lot of troubles if you will subclass it.
Do something like this:
class CString : public QObject //if you're really need this class to be QObject, that's not always a good idea
{
Q_OBJECT
public:
explicit CString(QObject *parent = 0) :
QObject(parent),
mString() //QString have no constructors with parameter QObject*...
{
}
private:
QString mString;
}
Of course, implementation should be in cpp file, it's just a short example