I couldn't use custom type in Q_PROPERTY MEMBER.
header.h
class Custom {
Q_GADGET
Q_PROPERTY(int mode MEMBER mode STORED true)
public:
Custom();
int mode;
};
class Test {
Q_GADGET
Q_PROPERTY(QString ip MEMBER ip STORED true)
Q_PROPERTY(Custom discovery MEMBER discovery STORED true)
public:
Test();
QString ip;
Custom discovery;
};
Q_DECLARE_METATYPE(Custom)
Q_DECLARE_METATYPE(Test)
main.cpp
#include "header.h"
Test::Test() { qRegisterMetaType<Test>("Test"); }
Custom::Custom() { qRegisterMetaType<Custom>("Custom"); }
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
Test reg;
return a.exec();
}
when I compile the code I get below error.
/testing/build-test-Desktop_Qt_5_12_4_GCC_64bit-Debug/moc_CustomeTypes.cpp:175: error: no match for ‘operator!=’ (operand types are ‘Custom’ and ‘Custom’)
moc_CustomeTypes.cpp:175:31: error: no match for ‘operator!=’ (operand types are ‘Custom’ and ‘Custom’)
if (_t->discovery != *reinterpret_cast< Custom*>(_v)) {
~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
is it mandatory to provide operator!= for Custom class?
Just to mention, if I change my Test class as below it compiles.
class Test {
Q_GADGET
Q_PROPERTY(QString ip MEMBER ip STORED true)
Q_PROPERTY(Custom discovery READ discovery WRITE setDiscovery STORED true)
public:
Test();
Custom discovery() const { return m_discovery; }
void setDiscovery(const Custom& c) { m_discovery = c; }
QString ip;
Custom m_discovery;
};
Why Can't I use MEMBER for custom type?
Related
The common part (crating a lib) of my project currently holds a interface Class of the type:
class CanBeAddedToGroup{
public:
void addToGroup(Group& )=0;
}
Now i also wantred to use the programm on a Class containing data in a QVariant, so i started it off simple:
class DataContainingClass: public CanBeAddedToGroup{
QMap<QString,QVarient> data;
public:
void addToGroup(Group& ){
QMap<QString,QVarient>::itterator itter = data.begin();
QMap<QString,QVarient>::itterator end= data.end();
for(;itter !=end;itter ++){
<Handle Data>
}
}
};
}
Now one of the datatypes addedt to the list (outside the lib) is of the type:
class DataClass: public QObject, public CanBeAddedToGroup{
void addToGroup(Group& );
}
Q_DECLARE_METATYPE(DataClass)
And it is added to the map using "QVariant::fromValue(", now i need a way in the "DataContainingClass" to check if the Data is derived from a QObject, so i know static_cast(variant.data()) is valid.
Then i could try to dynamic_cast the Object pointer to CanBeAddedToGroup, and call it.
--- EDIT ---:
The Problem IS NOT: Having a QObject and check if it inherits another QObject, it is not even checking if a Class inherits from another one, it is to know if the data i have actually IS a QObject.
Minimal Example:
Header File:
#include <QObject>
#include <QDebug>
class DmbClass: public QObject{
Q_OBJECT
public:
DmbClass(){TST="RealOne";}
DmbClass(const DmbClass&d){TST="Clone";}
QString TST;
};
Q_DECLARE_METATYPE(DmbClass)
class Tst{
public:
virtual void tstFunct()=0;
};
class CanClass: public QObject, public Tst{
Q_OBJECT
public:
CanClass(){TST="RealOne";}
CanClass(const CanClass&d){TST="Clone";}
QString TST;
virtual void tstFunct() override{
qDebug()<<"tst";
}
};
Q_DECLARE_METATYPE(CanClass)
class DmbGadget{
Q_GADGET
public:
DmbGadget(){TST="RealOne";}
DmbGadget(const DmbGadget&d){TST="Clone";}
QString TST;
};
Q_DECLARE_METATYPE(DmbGadget)
C File:
// QObject in QVariant
DmbClass dC;
QVariant varC=QVariant::fromValue(dC);
const void* vPC = varC.data();
DmbClass dCc = varC.value<DmbClass>();
QObject* objC = (QObject*)varC.data();
QObject* schouldWork = objC->parent();
Tst* schouldBeNull = dynamic_cast<Tst*>(objC);
// Object using correct base class in QVariant
CanClass dT;
QVariant varT=QVariant::fromValue(dT);
const void* vPT = varT.data();
CanClass dTc = varC.value<CanClass>();
QObject* objT = (QObject*)varT.data();
QObject* schouldWork2 = objT->parent();
Tst* schouldNotNull = dynamic_cast<Tst*>(objT);
schouldNotNull->tstFunct();
// Q_Gadget in QVariant
DmbGadget dG;
QVariant varG=QVariant::fromValue(dG);
const void* vPG = varG.data();
DmbGadget dGg = varG.value<DmbGadget>();
QObject* objD = (QObject*)varG.data();
//QObject* schouldSegFault = objD->parent();
// base value in QVariant
QVariant var4=4;
const void* vP4 = var4.data();
QObject* obj4 = (QObject*)var4.data();
//QObject* schouldSegFault2 = obj4 ->parent();
I need a way to distinguisch cases 1&2 from 3&4 ("schouldSegFault"), without using something only defined outside of the lib.
I Already Tryed:
int tst4 = qRegisterMetaType<CanClass>("CanClass");
QMetaType help2(tst4);
But help2 has a MetaObject of 0, so i cant check for the inheriance from QObject.
Edit/for who added "Proper way to check QObject derived class type in Qt" ther was te issue in my programm that the class inherits from another QObjectclass so i cant chack for inheriance of my interface (even when defined as Q_Gadget) using inherits, since it would only be true for the first element.
PS: For everyone trying to call functions on a QVariant containing a Object rather than a pointer might be interested in this approach:
How to support comparisons for QVariant objects containing a custom type? / https://pastebin.com/tNLa0jSa
While having a global registry for types is what i wished i could avoid for the case.
Just try to use QVariant::value and see if the value in a QVariant can be converted to your target class. Here's a minimal example:
#include <QObject>
#include <QVariant>
#include <QVariantMap>
#include <QDebug>
class MyQObjectClass : public QObject {
Q_OBJECT
public:
explicit MyQObjectClass(QObject *parent = nullptr) : QObject(parent) {}
void greet() { qDebug() << "I am a MyQObjectClass!"; }
};
Q_DECLARE_METATYPE(MyQObjectClass*)
int main(int, char *[])
{
MyQObjectClass obj;
QVariantMap map;
map.insert("foo", QString("Hello World"));
map.insert("bar", QVariant::fromValue(&obj));
QVariantMap::iterator iter = map.begin();
QVariantMap::iterator end= map.end();
for(;iter !=end;iter ++) {
auto value = iter.value();
// Try to convert to MyQObjectClass*
auto obj = value.value<MyQObjectClass*>();
if (obj != nullptr) {
qDebug() << iter.key() << "is an instance of MyQObjectClass";
obj->greet();
continue;
}
qDebug() << iter.key() << "is not an instance of MyQObjectClass";
}
}
#include "main.moc"
Running it should yield the following output on the console:
"bar" is an instance of MyQObjectClass
I am a MyQObjectClass!
"foo" is not an instance of MyQObjectClass
The important parts:
Make sure the class you want to store in a QVariant derives from QObject and has the Q_OBJECT macro.
When iterating over the map, use QVariant::value() and try to convert the contained value to your target class. In the example, I use QVariant::value<MyQObjectClass*>() - according to the documentation, this either returns the contained instance of MyQObjectClass* if the value can be converted to it or - which is the case if the QVariant contains either basic values or gadgets - a default constructed value. In the case of a pointer this would be a null pointer, so just check if the value returned is null. That's it.
Never work on Qvariant::data() directly.
Update
Just as a remark: The Qobject class declared the copy constructor and assignment operators as private:
From the official documentation:
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.
Hence, you cannot copy around instances of QObject (and consequentially you cannot store them in QVariant). Instead, you pass around pointers to QObject instances.
Update #2
If your interface class cannot derive directly from QObject, you might consider using Qt's plugin mechanism instead. Here's the above example slightly edited to fit this approach:
#include <QObject>
#include <QVariant>
#include <QVariantMap>
#include <QDebug>
class MyInterfaceClass {
public:
MyInterfaceClass() {}
virtual ~MyInterfaceClass() {}
virtual void greet() = 0;
};
#define MyInterfaceClass_IID "org.example.MyInterfaceClass"
Q_DECLARE_INTERFACE(MyInterfaceClass, MyInterfaceClass_IID)
class MyConcreteClass : public QObject, public MyInterfaceClass {
Q_OBJECT
Q_INTERFACES(MyInterfaceClass)
public:
MyConcreteClass(QObject *parent = nullptr) : QObject(parent) {}
void greet() override { qDebug() << "I am a MyInterfaceClass!"; }
};
int main(int, char *[])
{
MyConcreteClass obj;
QVariantMap map;
map.insert("foo", QString("Hello World"));
map.insert("bar", QVariant::fromValue(&obj));
QVariantMap::iterator iter = map.begin();
QVariantMap::iterator end= map.end();
for(;iter !=end;iter ++) {
auto value = iter.value();
// Try to convert to QObject*:
auto obj = value.value<QObject*>();
if (obj != nullptr) {
// Try if we can cast to our interface class:
auto ifc = qobject_cast<MyInterfaceClass*>(obj);
if (ifc != nullptr) {
qDebug() << iter.key() << "is an instance of MyInterfaceClass";
ifc->greet();
}
continue;
}
qDebug() << iter.key() << "is not an instance of MyInterfaceClass";
}
}
#include "main.moc"
You need to:
Define your interface class and register it with Qt using the Q_DECLARE_INTERFACE macro.
Declare your concrete classes, deriving from QObject and your interface class. In addition, you need to tell Qt about the interface part using the Q_INTERFACES macro.
When checking the values in your map, first try to convert to a QObject* via QVariant::value(). If this succeeds, you can try to qobject_cast to your interface class.
Your design is completely broken: QObjects cannot be used as unrestricted values. They cannot be copied nor moved, and your implementations of copy constructors are hiding this fundamental fact.
i'm trying to learn qt.this is my first example that I'm practicing.but i have this error:C:\Qt2\Qt5.2.1\Tools\QtCreator\bin\recognize_signal_slot\main.cpp:19: error: undefined reference to `Counter::valueChanged(int)'
I don't know what I should do..someone told me you should put your class in header file.but I couldn't understand what he said.can anyone tell me step by step.thank you so much.
here is my code in main.cpp :
#include <QCoreApplication>
#include <QObject>
class Counter : public QObject
{
int m_value;
public:
int value() const { return m_value; }
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
};
void Counter::setValue(int value)
{
if (value != m_value)
{
m_value = value;
emit valueChanged(value);
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Counter d, b;
QObject::connect(&d, SIGNAL(valueChanged(int)),
&b, SLOT(setValue(int)));
d.setValue(12); // a.value() == 12, b.value() == 12
return a.exec();
}
[What's Wrong?]
You signals & slots are not invoked by Meta-Object-Compiler (MOC).
Suggested reading: Why Does Qt Use Moc for Signals and Slots?.
[Solution]
Step 1. Add Q_OBJECT macro to the QObject derivatives that use signals & slots.
class Counter : public QObject
{
Q_OBJECT // <-----HERE
int m_value;
public:
int value() const { return m_value; }
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
};
Step 2. move your class declaration to counter.h and implementation to counter.cpp. Since MOC searches header files that contain Q_OBJECT, it's better to keep your QObject classes and main well separate, even for a small test project.
Step 3. Clean all ---> run qmake ---> rebuild (qmake will automatically call MOC to translate signals & slots syntax into compilable C++ code)
I know that I can use QMetaType to create an object without parameters.
Another possible option is to use QMetaObject and call newInstance. But I need to get QMetaObject from something.
I tried to use QMetaType::metaObjectForType, but it always returns null pointer (but QMetaType is able to create the object).
QMetaObject const* metaObject = QMetaType::metaObjectForType(id); // return null pointer
QObject* object = (QObject*)QMetaType::create(id); // create the object
QMetaObject const* metaObject = object->metaObject(); // return not-null pointer
UPDATE:
I think the question is why metaObjectForType does not work for me.
The class is registered with qRegisterMetaType, also Q_DECLARE_METATYPE and Q_OBJECT are applied.
First of all, to pass parameters to methods, you needs some kind of reflection framework beyond plain C++. With Qt, the obivous choice is Qt Meta Object system with it's QMetaObject, though then you must derive your classes for QObject. After that, you need to do two things:
1. make constructor invokable
Signals and slots are invokable by default, but any other method you want to invoke through the meta object system needs to be explicitly marked as such. Example myqobject.h:
#ifndef MYQOBJECT_H
#define MYQOBJECT_H
#include <QObject>
class MyQObject : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE MyQObject(QObject *parent = 0); // tested with empty constructor in .cpp
};
#endif // MYQOBJECT_H
2. create your own mapping from class name string to the QMetaObject
QMetaType doc says: "any class or struct that has a public default constructor, a public copy constructor, and a public destructor can be registered." This rules out QObject, because they can't have copy constructors. You need to create your own mapping from name to the meta object. An example shown in this main.cpp:
#include <QCoreApplication>
#include <QtCore>
#include "myqobject.h"
// a global map for mapping strings to QMetaObjects,
// you need header file like this if you want to access it from other .cpp files:
//
// #include <QHash>
// #include <QString>
// class QMetaObject; // forward declaration, enough when only pointer is needed
// extern QHash<QString, const QMetaObject*> metaObjs;
//
QHash<QString, const QMetaObject*> metaObjs;
// optional: create a macro to avoid typing class name twice,
// #c surrounds macro argument with double quotes converting it to string
#define METAOBJS_INSERT(c) (metaObjs.insert(#c, &c::staticMetaObject))
int main()
{
METAOBJS_INSERT(MyQObject);
const QMetaObject *meta = metaObjs["MyQObject"];
qDebug() << "Class name from staticMetaObject: " << meta->className();
QObject *o = meta->newInstance(); // add constructor arguments as needed
MyQObject *mo = qobject_cast<MyQObject*>(o);
if (mo) qDebug() << "Class name from object instance: " << mo->metaObject()->className();
else qDebug() << "Instance creation failed or created wrong class!";
return 0;
}
If you do not want to use QObject, then you need to come up with some similar (probably lighter-weight and without separate compiler step) mechanism of your own.
I had the same problem.
The solution is in two steps:
call qRegisterMetaType() with a pointer type:
qRegisterMetaType<MyClass*>(). this will give you a valid
QMetaObject with call of QMetaType::metaObjectForType(id);
make your constructor Q_INVOKABLE. this will enable a valid call to
QMetaObject::newInstance
And also be sure that your class is derived from QObject and have a Q_OBJECT macro in it.
class A : public QObject
{
Q_OBJECT
public:
A(const A&) {}; // dummy copy contructor that do nothing to disable error message
Q_INVOKABLE A(int test_value = 99999) : TestValue(test_value) {};
int TestValue;
};
Q_DECLARE_METATYPE(A);
int main(int argc, char *argv[])
{
qRegisterMetaType<A>(); //you do not need this
qRegisterMetaType<A*>();//the real registration
int type_id_for_A = QMetaType::type("A"); // valid type id (in my case = 403)
const QMetaObject *meta_object_for_A = QMetaType::metaObjectForType(type_id_for_A); // returns NULL
int type_id_for_A_ptr = QMetaType::type("A*"); // valid type id (in my case = 404)
const QMetaObject *meta_object_for_A_tr = QMetaType::metaObjectForType(type_id_for_A_ptr); // returns NOT NULL
A* A_obj= dynamic_cast<A*>(meta_object_for_A_tr->newInstance(Q_ARG(int, 12345)));
int test_value = A_obj->TestValue; // returns 12345, not 99999
}
I'm trying to use a QDeclarativeListProperty in order to manage a list of parameters, mostly for the purposes of displaying them in a ListView. However, I would also like to be able to directly access the parameters in QML from the QDeclarativeListProperty so that I can display/modify individual parameters on different screens.
My class is called ParameterClass, for which I've created a QList:
class SystemData : public QObject
{
Q_OBJECT
Q_PROPERTY(QDeclarativeListProperty<ParameterClass> parameters READ parameters CONSTANT)
QDeclarativeListProperty<ParameterClass> parameters();
...
QList<ParameterClass *> m_parameterList;
}
I've also registered the ParameterClass class and set up an instance of my SystemData as a property, which I know is necessary.
m_context->setContextProperty("SystemData", m_pSystemData);
qmlRegisterType<ParameterClass>();
Now, what I want to do within QML is something like this:
Rectangle {
id: frame
property variant parameter: SystemData.parameters[5]
...
}
I'm just not getting it to work: I keep getting back [undefined]. Am I wasting my time, or am I missing something?
Edit:
I've changed things to use the suggestion from ... . Here are some selections from my updated code.
main.cpp:
#include <QApplication>
#include <QSplashScreen>
#include <QLocale>
#include <QLibraryInfo>
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include <QDeclarativeEngine>
#include <QObject>
#include <QDeclarativeListProperty>
#include "systemdata.h"
#include "parameterclass.h"
static const QString contentPath = "qrc:/qml/qml/pk_ui/";
static const QString filename(contentPath + "main.qml");
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QDeclarativeView mainView;
SystemData* systemData = SystemData::getInstance();
QThread thread;
UpdateWorker updateWorker;
QObject::connect((const QObject*)systemData, SIGNAL(startWork()),
(const QObject*)&updateWorker, SLOT(doWork()));
updateWorker.moveToThread(&thread);
thread.start();
systemData->startUpdates();
QFont defaultFont;
defaultFont.setFamily("Sans Serif");
QApplication::setFont(defaultFont);
// Register types to be available in QML
qmlRegisterType<ParameterClass>();
qmlRegisterUncreatableType<SystemEnum>("SystemEnums", 1, 0, "SystemEnum", QString());
mainView.engine()->rootContext()->setContextProperty("SystemData", systemData);
// Set view optimizations not already done for QDeclarativeView
mainView.setResizeMode(QDeclarativeView::SizeRootObjectToView);
mainView.setAttribute(Qt::WA_OpaquePaintEvent);
mainView.setAttribute(Qt::WA_NoSystemBackground);
mainView.setSource(QUrl(filename));
mainView.show();
return app.exec();
}
The ParameterClass looks like this:
class ParameterClass : public QObject
{
Q_OBJECT
Q_PROPERTY(int type READ get_type NOTIFY typeChanged)
Q_PROPERTY(bool enabled READ get_ParameterEnabled WRITE set_ParameterEnabled NOTIFY enabledChanged)
Q_PROPERTY(int groupID READ get_GroupID WRITE set_GroupID NOTIFY groupIDChanged)
Q_PROPERTY(int unitID READ get_UnitID WRITE set_UnitID NOTIFY unitIDChanged)
Q_PROPERTY(int securityLevel READ get_SecurityLevel WRITE set_SecurityLevel NOTIFY securityLevelChanged)
Q_PROPERTY(QString parameterName READ get_ParameterName NOTIFY parameterNameChanged)
Q_PROPERTY(QString shortDescription READ get_ShortDescription NOTIFY shortDescriptionChanged)
Q_PROPERTY(int currentValue READ get_CV WRITE set_valueptrvalue NOTIFY currentValueChanged)
Q_PROPERTY(int lowerBound READ get_LB NOTIFY lowerBoundChanged)
Q_PROPERTY(int upperBound READ get_UB NOTIFY upperBoundChanged)
public:
struct ValueTypes
{
enum
{
IntegerType,
StringType,
StringListType
};
};
ParameterClass(QObject *parent = 0);
int get_type();
bool get_ParameterEnabled();
int get_GroupID();
int get_UnitID();
int get_SecurityLevel();
QString get_ParameterName();
QString get_ShortDescription();
int get_CV() { return *CurrentValuePtr; }
int get_LB() { return *LowerBoundPtr; }
int get_UB() { return *UpperBoundPtr; }
void set_ParameterEnabled(bool InParameterEnabled);
void set_GroupID(int InGroupID);
void set_UnitID(int InUnitID);
void set_SecurityLevel(int InSecurityLevel);
signals:
void typeChanged();
void enabledChanged();
void groupIDChanged();
void unitIDChanged();
void securityLevelChanged();
void parameterNameChanged();
void shortDescriptionChanged();
private:
int type;
bool ParameterEnabled;
int GroupID;
int UnitID;
int SecruityLevel;
QString ParameterName;
QString ShortDescription;
int * CurrentValuePtr;
int * LowerBoundPtr;
int * UpperBoundPtr;
};
And my QML file:
Rectangle {
id: frame
property int val: SystemData.parameters[4].currentValue
...
}
It looks like I'm still getting an undefined value in this case. I'm trying to debug now, so that I can provide more information.
It's very much possible. The key is to make sure you register the QML type and set the context property before setting the source on your QDeclarativeView.
Here's a working example -
main.cpp:
#include <QApplication>
#include <QtDeclarative>
class MyPropertyObject : public QObject {
Q_OBJECT
Q_PROPERTY(int value READ value CONSTANT)
public:
MyPropertyObject(int value = -1) : m_value(value) { }
int value() const {
return m_value;
}
private:
int m_value;
};
class MyObject : public QObject {
Q_OBJECT
Q_PROPERTY(QDeclarativeListProperty<MyPropertyObject> props READ props CONSTANT)
public:
MyObject() {
m_props.append(new MyPropertyObject(55));
m_props.append(new MyPropertyObject(44));
m_props.append(new MyPropertyObject(33));
}
QDeclarativeListProperty<MyPropertyObject> props() {
return QDeclarativeListProperty<MyPropertyObject>(this, m_props);
}
private:
QList<MyPropertyObject *> m_props;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDeclarativeView view;
view.engine()->rootContext()->setContextProperty(QLatin1String("tester"), new MyObject);
qmlRegisterType<MyPropertyObject>();
view.setSource(QUrl("qrc:///qml/main.qml"));
view.setResizeMode(QDeclarativeView::SizeRootObjectToView);
view.resize(300, 300);
view.show();
return a.exec();
}
#include "main.moc"
main.qml:
import QtQuick 1.1
Rectangle {
property variant foo: tester.props[2].value
Text {
anchors.centerIn: parent
text: parent.foo
}
}
Note: read the docs for the QDeclarativeListProperty constructors. The one I'm using for this example is not the preferred one, but is good for quick prototypes.
I can't see what I'm doing wrong. I'm trying to create some properties in a class (as I did before) but this time I'm getting the error "class Foo has no member named MyProp"
The header is:
#ifndef P_H
#define P_H
#include <QObject>
class P : public QObject
{
Q_OBJECT
Q_PROPERTY(int Prop READ getProp WRITE setProp)
public:
explicit P(QObject *parent = 0);
int getProp() const;
void setProp(int nP);
private:
int m_p;
};
#endif // P_H
and the cpp file is:
#include "p.h"
P::P(QObject *parent) :
QObject(parent)
{
}
int P::getProp() const
{
return m_p;
}
void P::setProp(int nP)
{
m_p = nP;
}
But when I try to use foobar.P I got the error class P has no member named P. I've been reading Qt documentation and I can't see any difference. Does anyone see what I'm doing wrong?
I'm using Qt Creator 2.4.1 and Qt 4.8.
[... Edit ...]
Here is how I'm trying to use it:
#include "p.h"
int main(int argc, char *argv[])
{
P c;
c.Prop = 2;
return 0;
}
This is the simplest example I could think of and I got the same error.
Thanks in advance.
You need to use it like this:
P c;
c.setProperty("Prop", 42); // set the property
c.property("Prop"); // retrieve the property