This documentation states
If you want to register an enum that is declared in another class, the enum must be fully qualified with the name of the class defining it. In addition, the class defining the enum has to inherit QObject as well as declare the enum using Q_ENUMS().
However I can't make this work, in the following example.
Class A:
#ifndef CLASSA_H
#define CLASSA_H
#include <classb.h>
class ClassA : public QObject
{
Q_OBJECT
Q_ENUMS(ClassB::TestEnum)
public:
explicit ClassA(QObject *parent = 0) : QObject(parent)
{
const QMetaObject *metaObj = this->metaObject();
qDebug() << metaObj->enumeratorCount();
}
};
#endif // CLASSA_H
ClassB:
#ifndef CLASSB_H
#define CLASSB_H
#include <QDebug>
#include <QMetaEnum>
#include <QObject>
class ClassB : public QObject
{
Q_OBJECT
Q_ENUMS(TestEnum)
public:
enum TestEnum { A, B, C };
explicit ClassB(QObject *parent = 0) : QObject(parent)
{
const QMetaObject *metaObj = this->metaObject();
qDebug() << metaObj->enumeratorCount();
}
};
#endif // CLASSB_H
main:
#include <classa.h>
#include <classb.h>
int main()
{
ClassA objectA;
ClassB objectB;
}
Expected output:
1
1
Actual Output:
0
1
Here is a summary of a little research:
Information stated in the documentation about registration of a enum declared in another class looks outdated.
Q_ENUMS(Class::EnumName doesn't create a new enumerator, and useless.
When you declare a new Q_PROPERTY in ClassA you should use full form of the enum ClassB::EnumName.
As soon as EnumName is registered in ClassB it doesn't need to be registered any more.
A property created using a enumerator from another class works correctly.
class ClassA : public QObject
{
public:
Q_OBJECT
Q_PROPERTY(ClassB::TestEnum test READ test)
public:
explicit ClassA(QObject *parent = 0)
{
const QMetaObject *metaObj = this->metaObject();
qDebug() << metaObj->enumeratorCount();
QMetaProperty property = metaObj->property(metaObj->indexOfProperty("test"));
if (property.isEnumType())
{
const QMetaEnum& enumerator = property.enumerator();
qDebug() << enumerator.name();
for (int i = 0 ; i < enumerator.keyCount(); i++)
{
qDebug() << QLatin1String(enumerator.key(i)) << enumerator.value(i);
}
}
}
ClassB::TestEnum test() const
{
return ClassB::A;
}
};
Output:
0
TestEnum
"A" 0
"B" 1
"C" 2
Related
I try to get a QObject-subclassed object from a Qt shared library to a Qt application dynamically.
I have tried to apply a previous answer about this subject : QLibrary - import a class
Here is my common interface tabappinterface.h:
#ifndef TABAPP_H
#define TABAPP_H
#include <QObject>
#include <QWidget>
class TabAppInterface : public QObject
{
Q_OBJECT
public:
virtual ~TabAppInterface()
{
}
QWidget *app;
QString title;
};
#endif // TABAPP_H
My class dll-side mytabapp.h:
#ifndef MYTAB_H
#define MYTAB_H
#include "tabapp_global.h"
#include "tabappinterface.h"
class MYTABSHARED_EXPORT MyTabApp: public TabAppInterface
{
public:
MyTabApp();
virtual ~MyTabApp();
QWidget *app;
QString title;
};
extern "C" MYTABSHARED_EXPORT TabAppInterface *getTabApp();
and its implementation mytabapp.cpp:
#include "mytabapp.h"
MyTabApp::MyTabApp()
{
app = new AppWidget();
title = QStringLiteral("My Tab App");
}
MyTabApp::~MyTabApp()
{
}
TabAppInterface *getTabApp()
{
return new MyTabApp();
}
My app-side implementation:
void ContainerMainWindow::loadLibraries()
{
QLibrary myLib("mytabapp.dll");
if(myLib.isLoaded())
{
qDebug() << "Loaded!";
}
typedef TabAppInterface *(*tabAppGetProt)();
auto tabAppGetter = (tabAppGetProt) myLib.resolve("getTabApp");
if(tabAppGetter)
{
auto *tabApp = tabAppGetter(); // not null
qDebug() << tabApp->title; // print empty string
qDebug() << (QWidget *)(tabApp->app); // SEGFAULT
}
}
As stated in comment in the last lines, the object members are not retrieved although tabApp is not null.
Any idea?
Thanks!
You're accessing the base class variables (fields) title and app, which nobody ever initialized. These variables aren't virtual at all, so those in the derived class just hide those in the base class. As a solution, you can declare them as protected in the base class and encapsulate them in getters and setters.
This way, your base class is like:
class TabAppInterface : public QObject
{
Q_OBJECT
public:
virtual ~TabAppInterface(){}
QWidget *getApp() const { return app; }
void setApp(QWidget *value) { app = value; }
QString getTitle() const { return title; }
void setTitle(const QString &value) { title = value; }
protected:
QWidget *app;
QString title;
};
and the derived class is just like:
class MYTABSHARED_EXPORT MyTabApp: public TabAppInterface
{
public:
MyTabApp();
virtual ~MyTabApp();
};
You can still directly access the variables inside the derived class (i.e. initialize them in constructor) and through the getters/setters methods from outside:
auto *tabApp = tabAppGetter();
qDebug() << tabApp->getTitle();
qDebug() << tabApp->getApp();
I have a question again:
I have a class PBV t(inherits from Tab) hat has a class Geometry. Geometry is the parent of Geo_1. From Geo_1 I want to have access to methods of PBV ( e.g. printContent . How do I do that? I am able to create Signal-Slots but since I have to use methods of PBV often that would make lots of Signal-Slots.
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() { }
One approach, as referred to by comments, is to keep track of the parent class in the constructor of the child:
In Geometry.h, add a private member variable:
private:
PBV* m_pParentPBV;
Then, in the constructor in Geometry.cpp, set this to the parent you are already passing:
Geometry::Geometry(QObject *_parent)
: QObject(_parent)
{
m_pParentPBV = dynamic_cast<PBV*>(_parent);
}
Now you can call methods on the parent using m_pParentPBV->printContent() etc.
I've created an SSCE to better explain this.
In a class, I have a variable, a struct, and a declaration of the struct. Within that struct is a constructor, a variable, a struct and a declaration of that struct. And inside THAT struct is a constructor.
So is goes Mother > Daughter > GDaughter aka class > struct > struct
mother.h
#ifndef MOTHER_H
#define MOTHER_H
#include <QSplitter>
class Mother : public QSplitter
{
Q_OBJECT
public:
Mother(int);
int age;
struct Daughter
{
Daughter();
int height1;
struct GDaughter
{
GDaughter();
};
GDaughter *kate;
};
Daughter *tina;
};
#endif // MOTHER_H
Now let's take a look at the constructors/source. Here lies my issue.
mother.cpp
#include "mother.h"
#include <QDebug>
Mother::Mother(int a)
{
age = a;
tina = new Daughter();
}
Mother::Daughter::Daughter()
{
qDebug() << age; //Not going to work... I get it. Daughter isnt a derived class
height1 = 10;
kate = new GDaughter();
}
Mother::Daughter::GDaughter::GDaughter()
{
qDebug() << height1; //Why not? The GDaughter instance is a member of Daughter!
}
Both qDebug() lines throw is not a type name, static, or enumerator.
The goal is to create "child" structs dynamically. So a parent struct might have 0 child structs, 1 child struct, or even 100 child structs. That is why I am using structs instead of derived classes. This setup looks like it would work except for the problem where "parent" variables can't be accessed.
I will include the other files anyway:
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mother.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mom = new Mother(50);
}
MainWindow::~MainWindow()
{
delete ui;
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "mother.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
Mother *mom;
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
If I am misunderstanding how to go about making something like this then let me know.
Thanks for your time.
AFTER ANSWER
In the mother.h I added parent pointers:
struct Daughter
{
Daughter(Mother *p); //Here
int height1;
struct GDaughter
{
GDaughter(Daughter *p); //And here
};
GDaughter *kate;
};
and in mother.cpp I filled in the needed code:
Mother::Mother(int a)
{
age = a;
tina = new Daughter(this); //Here
}
Mother::Daughter::Daughter(Mother *m) //Here
{
qDebug() << m->age; //Here
height1 = 10;
kate = new GDaughter(this); //Here
}
Mother::Daughter::GDaughter::GDaughter(Daughter *d) //Here
{
qDebug() << d->height1; //Here
}
keep pointer(std::weak_ptr if you use smart pointers, for example) to parent class object in nested object.
explicit MainWindow(QWidget *parent = 0);
look at Qt code here, you pass pointer to the parent object in constructor,
if parent is nullptr that means MainWindow has no parent object, Also you can keep
pointer to MainWindow parent object and then static_cast it to exact class
#include <iostream>
class A {
class B{
public:
B(A *p):parent(p) { parent->hello(); }
private:
A *parent;
};
public:
A() { b = new B(this); }
void hello() { std::cout << "hello world" << std::endl; }
private:
B *b;
};
With plain enums I was able to access Q_ENUMS properties and specific, the character represenation of enums, with following code:
// in .h
class EnumClass : public QObject
{
Q_OBJECT
public:
enum MyEnumType { TypeA, TypeB };
Q_ENUMS(MyEnumType)
private:
MyEnumType m_type;
};
// in .cpp
m_type = TypeA;
...
const QMetaObject &mo = EnumClass::staticMetaObject;
int index = mo.indexOfEnumerator("MyEnumType");
QMetaEnum metaEnum = mo.enumerator(index);
QString enumString = metaEnum.valueToKey(m_type); // contains "TypeA"
If I want to use the c++11 feature for strong typed enums like
enum class MyEnumType { TypeA, TypeB };
accessing the meta information does not work anymore. I guess, that Qt does not recognize it as an enum anymore.
Is there any solution to access the character represenation of an enum while using strong typed enums?
Q_ENUMS is obsolete, and Q_ENUM should be used instead, but the following code works for me with either of them (Qt 5.5, your issue might be caused by an old Qt version; also this question is relevant):
.h:
#include <QObject>
class EnumClass : public QObject
{
Q_OBJECT
public:
enum class MyEnumType { TypeA, TypeB };
EnumClass();
Q_ENUM(MyEnumType)
private:
MyEnumType m_type;
};
.cpp:
#include <QDebug>
#include <QMetaEnum>
#include <QMetaObject>
EnumClass::EnumClass()
{
m_type = MyEnumType::TypeA;
const QMetaObject &mo = EnumClass::staticMetaObject;
int index = mo.indexOfEnumerator("MyEnumType");
QMetaEnum metaEnum = mo.enumerator(index);
// note the explicit cast:
QString enumString = metaEnum.valueToKey(static_cast<int>(m_type));
qDebug() << enumString;
}
main:
int main()
{
EnumClass asd;
return 0;
}
output:
"TypeA"
You can use Q_ENUM macro and template with QMetaEnum:
in.h:
#pragma once
#include <QObject>
#include <QString>
#include <QMetaEnum>
template<typename T>
QString enumToString(T value)
{
int castValue = static_cast<int>(value);
return QMetaEnum::fromType<T>().valueToKey(castValue);
}
class Enum : public QObject
{
Q_OBJECT
public:
enum class Color {
NO_COLOR = 0,
RED,
GREEN,
BLUE,
};
Q_ENUM(Color)
Enum();
Enum(Color color);
QString toString();
private:
Color m_value {Color::NO_COLOR};
};
in.cpp:
#include "in.h"
Enum::Enum()
{
}
Enum::Enum(Color color = Color::NO_COLOR) : m_value(color)
{
}
QString Enum::toString()
{
return enumToString(m_value);
}
main.cpp
#include "in.h"
#include <QDebug>
int main()
{
Enum none;
Enum red(Enum::Color::RED);
Enum green(Enum::Color::GREEN);
Enum blue(Enum::Color::BLUE);
qDebug() << none.toString();
qDebug() << red.toString() << green.toString() << blue.toString();
return 0;
}
output:
"NO_COLOR"
"RED" "GREEN" "BLUE"
When using enum class , cast the type to int in valueToKey():
For a QObject class , we ca directly use QMetaEnum , this direct approach should and actually works, both getting key and value ; tested Qt_5_10
declaration:
class EnumClass : public QObject
{
Q_OBJECT
public:
enum class MyEnumType { TypeA, TypeB };
Q_ENUM(MyEnumType)
...
};
Usage:
QMetaEnum metaEnum = QMetaEnum::fromType<EnumClass::MyEnumType>();
qDebug() << metaEnum.valueToKey(static_cast<int>(EnumClass::MyEnumType::TypeA));
qDebug() << metaEnum.keyToValue("TypeB");
...
result :
"TypeA"
1
I want to write an application which consists of several plugins. My idea is that the plugin itself is always just a factory which then can create the required object.
So I created an Interface for the pluginfactory called AbstractFactoryPlugin with a pure virtual method called create. Because of the fact that I want to have several different plugins (factories) with the same interface I can’t put the Q_DECLARE_INTERFACE macro in the AbstractFactoryPlugin header file.
So for a sample plugin I created an Interface which inherits from AbstractFactoryPlugin called IMyObjectFactoryPlugin and declared this interface with the DECLARE macro. Further the MyObjectFactoryPlugin then inherits from QObject and IMyObjectFactoryPlugin.
But when I load the plugin and call the create function, the create function of MyObjectFactoryPlugin seems to be never called. What am I doing wrong?
Many thanx in advance.
Sourcecode:
#ifndef ABSTRACTPLUGINFACTORY_H
#define ABSTRACTPLUGINFACTORY_H
#include "IAnObject.h"
class AbstractFactoryPlugin
{
public:
virtual ~AbstractFactoryPlugin() {}
virtual IAnObject *create(QObject *parent) = 0;
};
#endif // ABSTRACTPLUGINFACTORY_H
#ifndef IMYOBJECTFACTORYPLUGIN_H
#define IMYOBJECTFACTORYPLUGIN_H
#include "AbstractFactoryPlugin.h"
#include <QtPlugin>
class IMyObjectFactoryPlugin : public AbstractFactoryPlugin
{
public:
virtual ~IMyObjectFactoryPlugin() {}
};
QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(IMyObjectFactoryPlugin,
"org.MyObjectFactoryPlugin");
QT_END_NAMESPACE
#endif // IMYOBJECTFACTORYPLUGIN_H
#ifndef MYOBJECTPLUGIN_H
#define MYOBJECTPLUGIN_H
#include <QtPlugin>
#include "IMyObjectFactoryPlugin.h"
#include "AbstractFactoryPlugin.h"
class MyObjectFactoryPlugin : public QObject, IMyObjectFactoryPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.MyObjectFactoryPlugin" )
Q_INTERFACES(IMyObjectFactoryPlugin)
public:
MyObjectFactoryPlugin(QObject *parent = 0);
IAnObject *create(QObject *parent);
};
#endif // MYOBJECTPLUGIN_H
#include "MyObject.h"
#include "MyObjectFactoryPlugin.h"
#include <QDebug>
MyObjectFactoryPlugin::MyObjectFactoryPlugin(QObject *parent) :
QObject(parent)
{
}
IAnObject *MyObjectFactoryPlugin::create(QObject *parent)
{
qDebug() << Q_FUNC_INFO << "was called";
return new MyObject();
}
#ifndef IANOBJECT_H
#define IANOBJECT_H
class IAnObject
{
public:
IAnObject() {isInitialized = false;}
virtual ~IAnObject() {}
virtual bool initialize() = 0;
protected:
bool isInitialized;
};
#endif // IANOBJECT_H
#include "IAnObject.h"
#ifndef MYOBJECT_H
#define MYOBJECT_H
class MyObject : public IAnObject
{
public:
MyObject();
bool initialize();
};
#endif // MYOBJECT_H
#include "MyObject.h"
MyObject::MyObject()
{
}
bool MyObject::initialize()
{
return true;
}
#include <QCoreApplication>
#include <QDir>
#include <QPluginLoader>
#include <QDebug>
#include <E:/QtProjects/_test_PlugIn/MyPlugin/AbstractFactoryPlugin.h>
#include <E:/QtProjects/_test_PlugIn/MyPlugin/IAnObject.h>
#include <E:/QtProjects/_test_PlugIn/MyPlugin/IMyObjectFactoryPlugin.h>
IAnObject *loadPlugin()
{
QDir dir(QCoreApplication::applicationDirPath());
qDebug() << dir;
foreach(QString fileName, dir.entryList(QDir::Files))
{
QPluginLoader pluginLoader(dir.absoluteFilePath(fileName));
QObject *plugin = pluginLoader.instance();
qDebug() << fileName;
if(plugin)
{
qDebug() << "##### Plugin load ok #####";
AbstractFactoryPlugin *abstractFactoryPlugin = reinterpret_cast<AbstractFactoryPlugin *>(plugin);
return abstractFactoryPlugin->create(NULL);
}
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "loadPlugin:" << loadPlugin();
return a.exec();
}
Any Qt5 plugin must inherit from QObject, if you want your base class to be independent from QObject you'll have to create an intermediary class that inherits both AbstractFactoryPlugin and QObject (let's call it AbstractMyObjectFactoryPlugin).
#ifndef ABSTRACTMYOBJECTFACTORYPLUGIN_H
#define ABSTRACTMYOBJECTFACTORYPLUGIN_H
#include "AbstractFactoryPlugin.h"
class AbstractMyObjectFactoryPlugin : public QObject, public AbstractFactoryPlugin
{
Q_OBJECT
public:
AbstractMyObjectFactoryPlugin(QObject *parent = 0) : QObject(parent) { }
virtual ~AbstractMyObjectFactoryPlugin() { }
};
Q_DECLARE_INTERFACE(AbstractMyObjectFactoryPlugin,
"org.AbstractFactoryPlugin");
#endif // ABSTRACTMYOBJECTFACTORYPLUGIN_H
To create a plugin, you'll have to inherit from this class and override the create() method:
class MyObjectFactoryPlugin : public AbstractMyObjectFactoryPlugin
{
Q_OBJECT
Q_INTERFACES(AbstractMyObjectFactoryPlugin)
Q_PLUGIN_METADATA(IID "org.MyObjectFactoryPlugin" FILE "MyObjectFactoryPlugin.json")
public:
MyObjectFactoryPlugin(QObject* parent = 0) : AbstractMyObjectFactoryPlugin(parent) { }
virtual IAnObject *create(QObject *parent);
virtual ~MyObjectFactoryPlugin() { }
};
To cast the loaded plugin (a QObject) to AbstractFactoryPlugin safetly, you'll have to cast it to the intermediary class first.
AbstractFactoryPlugin * objectFactoryPlugin = qobject_cast< AbstractMyObjectFactoryPlugin * >(plugin);
The implicit assignment will cast it to the right base class.
Note: You'll have to check if the cast is successful first if you have other inheritance trees.