Qt plugin: Unable to get enum's metadata from a Qt plugin - c++

I can access a lot of metadata from my Qt plugin, but I cannot access the enumerations as QMetaEnums. I am however able to get the method in my class which returns that enum, and am able to convert it to QMetaType and get its id (1026). I need the info contained in QMetaEnum too. I think I am missing something. Please take a look at my code:
//Plugin interface
class PluginInterface
{
public:
virtual void initialize() = 0;
};
#define PluginInterface_iid "pluginInterface"
Q_DECLARE_INTERFACE(PluginInterface, PluginInterface_iid)
//Actual plugin implementing PluginInterface
enum Fruit{ Apple, Pear, Mango };
Q_DECLARE_METATYPE(Fruit)
class MYQTCALCPLUGINSHARED_EXPORT MyQtCalcPlugin : public QObject, PluginInterface
{
Q_CLASSINFO("version", "0.1")
Q_OBJECT
Q_PLUGIN_METADATA(IID PluginInterface_iid FILE "myqtcalcplug.json")
Q_INTERFACES(PluginInterface)
Q_ENUMS(Fruit)
public:
explicit MyQtCalcPlugin(QObject *parent = 0);
void MyQtCalcPlugin::initialize()
{
qRegisterMetaType<MyQtCalcPlugin*>("MyQtCalcPluginPtr");
qRegisterMetaType<Fruit>("Fruit");
qRegisterMetaType<Fruit*>("FruitPtr");
}
public slots:
Fruit TasteFruit()
{
return Fruit::Apple;
}
};
#endif // MYQTCALCPLUGIN_H
//application that is reading the metadata
QPluginLoader pluginLoader(pluginPath);
if (pluginLoader.load());
QObject *pluginInstance = pluginLoader.instance();
auto pluginInterface = qobject_cast<PluginInterface*>(pluginInstance);
pluginInterface->initialize();
const QMetaObject *pMetaObject = pluginInstance->metaObject();
int count = pMetaObject->enumeratorCount(); //count becomes 0

Enum has been moved from outside of the class to inside of it. This fixed the issue. Q_DECLARE_META_TYPE(Fruit) and qRegisterMetaType were redundant:
#define PluginInterface_iid "pluginInterface"
Q_DECLARE_INTERFACE(PluginInterface, PluginInterface_iid)
//Actual plugin implementing PluginInterface
class MYQTCALCPLUGINSHARED_EXPORT MyQtCalcPlugin : public QObject, PluginInterface
{
Q_CLASSINFO("version", "0.1")
Q_OBJECT
Q_PLUGIN_METADATA(IID PluginInterface_iid FILE "myqtcalcplug.json")
Q_INTERFACES(PluginInterface)
Q_ENUMS(Fruit)
public:
enum Fruit
{
Apple,
Pear,
Mango
};
}

Related

create and set custom read only attached properties from cpp

say i have some custom view class in c++ which defines a set of attached properties. A component delegate to instantiate a viewItem for when a model object is added. How would i make the Attached properties work properly, to keep external data the attache cannot set itself?
Demo code example
class someModel: public QObject
{
QOBJECT
Q_PROPERTY(data_1 ...)
Q_PROPERTY(data_2 ...)
public:
SomeView(QObject* parent);
};
class SomviewViewAttached: public QObject
{
QOBJECT
// read only properties with signals
Q_PROPERTY(SomeView view...)
Q_PROPERTY(SomeModel model...)
Q_PROPERTY(int index ...)
public:
SomviewViewAttached(QObject* parent);
};
class SomeView : public QQuickItem
{
QOBJECT
// ill skip giving the full get setter implementation
Q_PROPERTY(QQmlCommponent delegate .........)
public:
SomeView(QObject* parent);
Q_INVOKABLE void addToView(SomeModel* model);
private:
List<SomeModel*> items;
static SomviewViewAttached *qmlAttachedProperties(QObject *item)
{
return new SomeViewAttached(item);
}
};
QML_DECLARE_TYPEINFO(SomeView,QML_HAS_ATTACHED_PROPERTIES)
in qml the usage would be
SomeView{
id: view
Component{
Item{
foo1: model.data_1
foo2: model.data_2
number : SomeView.index
condition : SomeView.view.something...
}
}
Component.onCompleted: view.addToView(new someModel)
}

Object members not retrieved from a Qt dll

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

Qt property outside of base class

Im using a QML frontend for my C++ App which worked fine so far. However, I planned to tidy up my code and split functions into smaller classes
At first, my Property decleration looked like this:
class mainBoard : public QObject
{
Q_OBJECT
Q_PROPERTY(double baroAltitude MEMBER baroAltitude NOTIFY pressureChanged)
public:
explicit mainBoard(QObject *parent = 0);
void start();
private:
double baroAltitude = 0;
signals:
void pressureChanged();
};
Now, I do have this external class, with my getter method.
#include "pressuresensor.h"
class mainBoard : public QObject
{
Q_OBJECT
Q_PROPERTY(double baroAltitude READ pressureSensors.getBaroAltitude NOTIFY pressureSensors.pressureChanged)
public:
explicit mainBoard(QObject *parent = 0);
void start();
private:
pressureSensor pressureSensors;
};
But now, all I get is:
mainboard.h:25: Parse error at "pressureSensors"
error: [moc_mainboard.cpp] Error 1
Is there a better, or correct (because its working :D ) way for it?
thanks!
Q_PROPERTY does not support getters/setters methods which are not part of the class in question.
If you really want to keep the pressureSensor class you have to provide getters/setters in the mainBoard class and forward the calls.
class mainBoard : public QObject
{
Q_OBJECT
Q_PROPERTY(double baroAltitude READ getBaroAltitude)
public:
double getBaroAltitude() const {
return pressureSensors.getBaroAlitude();
}
private:
pressureSensor pressureSensors;
};

Design Issue with Qt

using Qt 5.0.0
The following is roughly an Observer pattern (the code is stripped to bare minimum to explain only the problem):
class A : public QObject
{
Q_OBJECT
public:
void registerListner(Observer *pObs);
static A* getInstance();
signals:
void sig();
};
void A::registerListner(Observer *pObs)
{
connect(this, SIGNAL(sig()), pObs, SLOT(slo));
}
////////////////////////////////////////////////////////////////
class Observer : public QObject
{
Q_OBJECT
public slots:
virtual void slo() = 0;
};
class ConcreteObserver : public Observer , public QWidget
{
Q_OBJECT
public: //re-mentioning "slots" is not necessary
virtual void slo();
};
ConcreteObserver *pCObs = new ConcreteObserver;
A::getInstance()->registerListner(pCObs);
/////////////////////////////////////////////////////////////
problem (apart from dreaded-diamond):
Can't inherit multiple times from QObject - moc does not allow it.
One possible solution is derive Observer from QWidget and then ConcreteObserver from Observer alone. However this is putting a constraint on ConcreteObserver. Maybe ConcreteObserver_2 needs to derive from QDialog instead etc.
How do i solve this design problem? Is there anything specific to Qt 5.0.0 Signal-Slot (in addition to earlier versions) that can solve this, or what would you suggest?
If runtime warnings are not enough for you, you can add a bit of compile-time type checking by making registerListener a function template and avoid multiple inheritance of QObject by not defining an Observer class per-se.
Here's what this could look like: (Note: my SFINAE skills are non-existent, this could probably be made nicer.)
#include <QObject>
#include <QDebug>
#include <type_traits>
class A : public QObject
{
Q_OBJECT
public:
template <typename T>
void registerListener(T *pObs)
{
static_assert(std::is_base_of<QObject, T>::value,
"Listener must be a QObject");
static_assert(std::is_same<void,
decltype(std::declval<T>().slo())
>::value,
"Slot slo must have signature void slo();");
connect(this, SIGNAL(sig()), pObs, SLOT(slo()));
}
static A* getInstance() { return instance; }
static void init() { instance = new A; }
void doStuff() { emit sig(); }
signals:
void sig();
private:
static A *instance;
};
A few test cases:
class BadObject1 : public QObject
{
Q_OBJECT
public:
BadObject1() {}
public slots:
void slo(int){}
};
class BadObject2 : public QObject
{
Q_OBJECT
public:
BadObject2() {}
public slots:
int slo(){return 0;}
};
struct BadObject3 {
void slo();
};
class ObservedObject : public QObject
{
Q_OBJECT
public:
ObservedObject(QString const& name): QObject() {
setObjectName(name);
}
public slots:
virtual void slo(){
qDebug() << objectName();
}
};
class ObservedObject2 : public ObservedObject
{
Q_OBJECT
public:
ObservedObject2(QString const& name)
: ObservedObject(name + " (derived)") {}
};
And a main file:
#include "A.h"
A* A::instance = 0;
int main(int , char **)
{
A::init();
A::getInstance()->registerListener(new BadObject1);
A::getInstance()->registerListener(new BadObject2);
A::getInstance()->registerListener(new BadObject3);
A::getInstance()->registerListener(new ObservedObject("foo"));
A::getInstance()->registerListener(new ObservedObject2("bar"));
A::getInstance()->doStuff();
}
You'll get compiler errors for all the BadObjectN cases. If you comment them out, the output will look like:
"foo"
"bar (derived)"
A warning though: this will not check if the void slo(); member is indeed a slot. You can check that at runtime with something like:
if (pObs->metaObject()->indexOfSlot("slo()") == -1) {
qDebug() << "Class" << pObs->metaObject()->className()
<< "doesn't have a slo slot.";
::exit(1);
}
This will work and do what is expected (unless you've got a class hierarchy where the slot wasn't declared virtual - then strange things will happen in derived classes that omit the slots "specifier". So I advocate that your docs not have the comment you have above about that specifier: it is always a good idea to have it when overloading a slot).
I don't believe this last check is achievable at compile-time, "slot resolution" is done with a runtime walk of the QObject meta-data and involves parsing moc-generated strings. Even if it was with some recursive template magic, I don't think it's work the effort. You'll get a runtime error message at registration type in which you can include the actual class name of the faulty object. That's a very accurate error message, and should be caught by the simplest testcases.

how to implement OOP using QT

this is a simple OOP QT question.
my app consists of main window (QMainWindow) and a table (QTableWidget).
in the main window i have arguments and variables which i would like to pass to the table class, and to access methods in main widnow class from the table class, how should i do it ?
mainwindow.h
class MainWindow : public QMainWindow {
Q_OBJECT
private:
int a;
int b;
Spreadsheet *spreadsheet;
public:
void set_a(int);
void set_b(int);
spreadsheet.h
class Spreadsheet : public QTableWidget {
Q_OBJECT
public:
Spreadsheet(QWidget *parent = 0);
atm i define Spreadsheet like this:
spreadsheet = new Spreadsheet(this);
and i'd like to access set_a() from spreadsheet.cpp...
You should consider a different design, you are tightly coupling your code.
Maybe something like the following...
class Spreadsheet : public QTableWidget
{
Q_OBJECT
signals:
void aValueChanged(int value);
void bValueChanged(int value);
public:
void doSomething()
{
emit aValueChanged(100);
}
};
class MainWindow : public QMainWindow
{
public:
MainWindow() :
a(0),
b(0)
{
connect(&spreadsheet, SIGNAL(aValueChanged(int)), this, SLOT(setA(int)));
connect(&spreadsheet, SIGNAL(bValueChanged(int)), this, SLOT(setB(int)));
spreadsheet.doSomething();
}
slots:
void setA(int value) { a = value; }
void setB(int value) { b = value; }
private:
Spreadsheet spreadsheet;
int a;
int b;
};
This is completely untested but gives you an idea.
You can use the parent() method in the Spreadsheet object to get a pointer to your MainWindow.
For example,
// spreadsheet.cpp
MainWindow* mainWindow = (MainWindow*) this->parent();
mainWindow->set_a(123);
Of course, the parent object passed to Spreadsheet's constructor should be your MainWindow instance for this to work.
However, you should seriously consider oscode's suggestion, since it also points you towards creating a more Qt-like API.