How to pass QList from QML to C++/Qt? - c++

I'm trying to pass QList of integer from QML to C++ code, but somehow my approach is not working. With below approach am getting following error:
left of '->setParentItem' must point to class/struct/union/generic type
type is 'int *'
Any inputs to trouble shoot the issue is highly appreciated
Below is my code snippet
Header file
Q_PROPERTY(QDeclarativeListProperty<int> enableKey READ enableKey)
QDeclarativeListProperty<int> enableKey(); //function declaration
QList<int> m_enableKeys;
cpp file
QDeclarativeListProperty<int> KeyboardContainer::enableKey()
{
return QDeclarativeListProperty<int>(this, 0, &KeyboardContainer::append_list);
}
void KeyboardContainer::append_list(QDeclarativeListProperty<int> *list, int *key)
{
int *ptrKey = qobject_cast<int *>(list->object);
if (ptrKey) {
key->setParentItem(ptrKey);
ptrKey->m_enableKeys.append(key);
}
}

You CAN'T use QDeclarativeListProperty (or QQmlListProperty in Qt5) with any other type than QObject derived ones. So int or QString will NEVER work.
If you need to exchange a QStringList or a QList or anything that is an array of one of the basic types supported by QML, the easiest way to do it is to use QVariant on the C++ side, like this :
#include <QObject>
#include <QList>
#include <QVariant>
class KeyboardContainer : public QObject {
Q_OBJECT
Q_PROPERTY(QVariant enableKey READ enableKey
WRITE setEnableKey
NOTIFY enableKeyChanged)
public:
// Your getter method must match the same return type :
QVariant enableKey() const {
return QVariant::fromValue(m_enableKey);
}
public slots:
// Your setter must put back the data from the QVariant to the QList<int>
void setEnableKey (QVariant arg) {
m_enableKey.clear();
foreach (QVariant item, arg.toList()) {
bool ok = false;
int key = item.toInt(&ok);
if (ok) {
m_enableKey.append(key);
}
}
emit enableKeyChanged ();
}
signals:
// you must have a signal named <property>Changed
void enableKeyChanged();
private:
// the private member can be QList<int> for convenience
QList<int> m_enableKey;
};
On the QML side, simply affect a JS array of Number, the QML engine will automatically convert it to QVariant to make it comprehensible to Qt :
KeyboardContainer.enableKeys = [12,48,26,49,10,3];
That's all !

Related

Cannot convert const object in Return

I'm not entirely sure what I'm doing incorrectly here. I have a class which contains a constant pointer to another class object. However I'm getting an error about not being able to convert the const (class object). What am I doing wrong? Is my code setup incorrect in the what I'm trying to do?
Error message: cannot convert 'const AppProfile' to 'AppProfile*' in return
I initially had this in my header file class AppProfile and i changed it to #include "appprofile.h" which helped remove another error.
I later will call run() which executes run on my AppProfile object.
header file
#ifndef APPITEM_H
#define APPITEM_H
#include <QObject>
#include <QUrl>
#include <QDir>
#include "appprofile.h"
class AppItem : public QObject
{
Q_OBJECT
public:
explicit AppItem(QObject *parent = nullptr);
explicit AppItem(const AppProfile &profile,
QObject *parent);
/// App Profile
AppProfile *profile() const;
signals:
public slots:
void run();
private:
const AppProfile m_profile;
};
#endif // APPITEM_H
cpp file
#include "appitem.h"
#include "appprofile.h"
AppItem::AppItem(QObject *parent) :
QObject(parent)
{
}
AppItem::AppItem(const AppProfile &profile,
QObject *parent) :
QObject(parent),
m_profile(profile)
{
}
QString AppItem::name() const
{
return m_name;
}
void AppItem::run()
{
AppProfile *profile = profile();
profile->run();
}
AppProfile *AppItem::profile() const
{
return m_profile;
}
UPDATE:
Follow up question reguarding the answer givens given...
To simply explain my intentions, I'm parsing a json file that contains data used to create the parent object AppItem. When this item is constructed, it takes in it's construct an AppProfile object. This object is only ever created once, at the time in which AppItem is created.
Knowing that, how would you suggest i move forward editing the original questions code relating to AppProfile. Assuming that's enough information. I appreciate you help. This is what the code looks like that I would use to create an AppItem
AppProfile *profile = new AppProfile();
AppItem *appItem = new AppItem(profile);
For starters either there is a typo in your code or the function is defined incorrectly
AppProfile *AppItem::profile() const
{
return m_profile;
}
Within the class the data member m_profile is not a pointer.
//...
private:
const AppProfile m_profile;
};
So if the declaration of the data member is valid then the function should look like
const AppProfile *AppItem::profile() const
{
return &m_profile;
}
Or if the data member declaration should look like
//...
private:
const AppProfile *m_profile;
};
then in any case the function shall return a pointer to constant data.
const AppProfile *AppItem::profile() const
{
return m_profile;
}
That is the error message implicitly says that there is a typo in your code
cannot convert 'const AppProfile' to 'AppProfile*' in return
And if you will update the typo in any case you may not discard the qualifier const for the pointer.

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*.

Compiler errors on setter and getter functions in QT

In the midst of making a test program I've come across a problem that has me boggled. I have googled and looked on here and haven't found an answer to the problem so thought I might just ask.
The issue: I get compiler errors on a getter and a setter function I made in QT. Basically I need to be able to set and get a QList object.
What am I overlooking? I think I might be doing something wrong with QList but I can't see what.
SliderArray.h
#ifndef SLIDERARRAY_H
#define SLIDERARRAY_H
#include <QWidget>
#include <QList>
#include <QSlider>
class SliderArray : public QWidget
{
Q_OBJECT
public:
explicit SliderArray(int sliders, QWidget *parent = 0);
~SliderArray();
//getter function for slider data
QList<int> GetSliderData();
//setter function for slider data
void SetSliderData(QList<int>);
private:
QList<int> integerList1; //Qlist Object to hold data
SliderArray.cpp
SliderArray::GetSliderData()
{
return integerList1;
}
SliderArray::SetSliderData(QList<int> datalist)
{
integerList1 = datalist;
}
these are the errors I get when I try to compile:
...\sliderarray.cpp:24: error: prototype for 'intSliderArray::GetSliderData()' does not match any in class 'SliderArray'
SliderArray::GetSliderData()
^
...\sliderarray.h:16: error: candidate is: QList<int> SliderArray::GetSliderData()
QList<int> GetSliderData();
^
...\sliderarray.cpp:29: error: prototype for 'int SliderArray::SetSliderData(QList<int>)' does not match any in class 'SliderArray'
SliderArray::SetSliderData(QList<int> datalist)
^
...\sliderarray.h:19: error: candidate is: void SliderArray::SetSliderData(QList<int>)
void SetSliderData(QList<int>);
^
In your cpp file you should have:
QList<int> SliderArray::GetSliderData()
{
return integerList1;
}
void SliderArray::SetSliderData(QList<int> datalist)
{
integerList1 = datalist;
}
This has nothing to do with Qt, you're just missing the return types.

Passing Q_GADGET as signal parameter from C++ to QML

Can't get a property of a C++ object inside a QML code.
Object is passed as a parameter to the signal.
Expected that in QML, the property text of the Record object can be extracted. And the value should be abc. QML sees the object as QVariant(Record), and its property text as undefined.
Record is a value-type like QPoint, so it uses Q_GADGET declaration.
hpp:
#ifndef LISTENP_HPP_
#define LISTENP_HPP_
#include <QObject>
#include "Record.hpp"
class ListenP: public QObject
{
Q_OBJECT
public:
ListenP();
virtual ~ListenP();
void emitGotRecord();
signals:
void gotRecord(Record r);
};
#endif /* LISTENP_HPP_ */
cpp:
#include "ListenP.hpp"
ListenP::ListenP() :
QObject()
{
}
ListenP::~ListenP()
{
}
void ListenP::emitGotRecord()
{
emit gotRecord(Record("abc"));
}
hpp for Record:
#ifndef RECORD_HPP_
#define RECORD_HPP_
#include <QObject>
#include <QMetaType>
class Record
{
Q_GADGET
Q_PROPERTY(QString text READ text WRITE setText)
public:
Record(const QString& text = "");
~Record();
QString text() const
{
return m_text;
}
void setText(const QString& text)
{
m_text = text;
}
private:
QString m_text;
};
Q_DECLARE_METATYPE(Record)
#endif /* RECORD_HPP_ */
cpp for Record:
#include "Record.hpp"
Record::Record(const QString& text) :
m_text(text)
{
}
Record::~Record()
{
}
namespace
{
const int RecordMetaTypeId = qMetaTypeId<Record>();
}
QML piece:
Connections {
target: listenPModel
onGotRecord: {
console.log(r)
console.log(r.text)
}
}
main piece:
QGuiApplication app(argc, argv);
auto listenP = std::make_shared<ListenP>();
QQuickView view;
view.rootContext()->setContextProperty("listenPModel", &*listenP);
view.setSource(QStringLiteral("src/qml/main.qml"));
view.show();
QtConcurrent::run([=]
{
QThread::sleep(3);
listenP->emitGotRecord();
});
return app.exec();
Log shows:
qml: QVariant(Record)
qml: undefined
The release notes for Qt 5.5 says for the new features:
Qt Core
You can now have Q_PROPERTY and Q_INVOKABLE within a Q_GADGET, and there is a way to query the QMetaObject of such gadget using the QMetaType system
Indeed, compiling and running your example with Qt 5.4 gives the same result as yours whereas with Qt 5.5 I got Record correctly recognised, i.e. I got as a result:
qml: Record(abc)
qml: abc
Also, as stated in the Q_DECLARE_METATYPE documentation, the type passed to the macro - Record in this case, should provide (1) a public default constructor, (2) a public copy constructor and (3) a public destructor. Since Record is a very simple class, there's no need to provide a copy constructor as the default one is sufficient.

QMetaMethods for regular methods missing?

I'm new in QT, and I'm just testing out the MOC.
For a given class:
class Counter : public QObject
{
Q_OBJECT
int m_value;
public:
Counter() {m_value = 0;}
~Counter() {}
int value() {return m_value;}
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
};
I want to get a list of all methods in a class, but seem to only be getting a list of signals and slots, although the documentation says it should be all methods? Here's my code:
#include <QCoreApplication>
#include <QObject>
#include <QMetaMethod>
#include <iostream>
using std::cout;
using std::endl;
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
const QMetaObject cntmo = Counter::staticMetaObject;
for(int i = 0; i != cntmo.methodCount(); ++i)
{
QMetaMethod qmm(cntmo.method(i));
cout << qmm.signature() << endl;
}
return app.exec();
}
Please beware this is my best c/p, perhaps I forgot to include some headers.
My output:
destroyed(QObject*)
destroyed()
deleteLater()
_q_reregisterTimers(void*)
valueChanged(int)
setValue(int)
Does anyone know why this is happening? Does qt not recognise
int value() {return m_value;}
as a valid method? If so, is there a macro I've forgotten or something like that?
P.S. I'm using 4.6.2
UPDATE
I forgot the implementation of the setValue method, not that it makes too much a difference to my actual question.
void Counter::setValue(int value)
{
if(value != m_value)
{
m_value = value;
emit valueChanged(value);
}
}
As far as I remember you can't access all methods of a QObject subclass through the QMetaObject it provides access only for the signals, slots and INVOCABLE methods:
class MyClass: public QObject {
Q_OBJECT
public:
Q_INVOCABLE int someMethod(const QString &someParam);
};
Maybe it's also provide access to Q_PROPERTY getters and setters. Read articles about Qt object model and meta object system more carefully.
Quotation from QMetaObject class description (http://doc.trolltech.com/4.6/qmetaobject.html#details):
"method() and methodCount() provide information about a class's meta-methods (signals, slots and other invokable member functions)."
There is no information about normal C++ methods access. And actually it's good since reflective techniques are slow.