How to catch a signal emitted from derived c++ class in QML? - qt

My model is in C++ and front end is QML. The model consist of an interface class which contain other components. In simplied form (proof of concept stage) This interface class is pure virtual class called Base which is derived from QObject. I have a Derived class derived from Base
I am emitting signal in the derived class when data changes. My problem is how to catch this signal and process in QML?
derived.obj:-1: error: LNK2019: unresolved external symbol "public: void __thiscall Derived::somethingChanged(void)" (?somethingChanged#Derived##QAEXXZ) referenced in function "public: virtual void __thiscall Derived::doSomething(void)" (?doSomething#Derived##UAEXXZ)
My Base.h class is:
#include <QObject>
class Base : public QObject
{
Q_OBJECT
public:
explicit Base(QObject *parent = 0);
~Base();
virtual void doSomething() = 0;
signals:
public slots:
};
Derived.h is
#include "base.h"
class Derived : public Base
{
Q_OBJECT
public:
Derived();
~Derived();
virtual void doSomething();
signals:
void somethingChanged();
};
Derived.cpp is
#include "derived.h"
#include <QDebug>
Derived::Derived()
{
}
Derived::~Derived()
{
}
void Derived::doSomething()
{
qDebug() << "doSomething() called in Derived";
emit somethingChanged(); // this doesn't compile!
}
Connections {
target: What? // what should I put here,
onSomethingChanged: console.log("The application data changed!")
}
Again the problem is what do I put for target property? The model will only expose the interface class to qml which is the Base class but the signal is actually emitted in derived class. Should the signal be also virtual by any chance? How should I receive this signal in QML?

The MOC is the one who implements the function (your signal) void somethingChanged();.
An example could look like this in the moc_XYZ.cpp file.
// SIGNAL 13
void Derived::doSomething()
{
QMetaObject::activate(this, &staticMetaObject, 13, Q_NULLPTR);
}
So how do you make the MOC happy?
Add Q_OBJECT in your derived.h

In order to create interfaces to be used in Qt I have found two cases:
Based on QObject
This is the most easy form, and you are in the right way. Just create a class based on QObject with abstract methods and declare signals. Derived classes must not declare the signals again, just emit them when needed.
This is your case. MOC generator need to know that Base interface has an signal and it can be emitted. Just declare signals in Base instead derived classes.
Pure virtual class (extra info)
Sometimes you need to derive from any QObject-based class (e.g. QAbstractListModel) but it need to implement some interfaces too. C++ allows multiple inheritance, but Qt doesn't like classes that derive from two or more QObject-based classes.
So you should create a plain pure virtual class, and declare signals as any other unimplemented method.
// Base
class Base
{
public:
Base();
virtual ~Base();
virtual void doSomething() = 0;
signals:
virtual void somethingChanged() = 0;
};
Now, derive from Base and declare the signal again:
// Derived
class Derived : public QObject, public Base
{
Q_OBJECT
public:
explicit Derived(QObject *parent = 0);
~Derived();
//Implmetations of Base methods
public:
void doSomething();
//Declaration of Base signals
signals:
void somethingChanged();
}

Related

How to use Q_DECLARE_METATYPE on QGraphicsitem derived class?

I want to use my objects as QVariants and for queued connections. Therefore I want to use Q_DECLARE_METATYPE(My_Item) and from the documentation I learned that I need a public copy constructor. I tried to write one, but I failed. Than I did read this copy constructor of derived QT class and the answer by BЈовић. From there I understand that what I intended to do is not going to work. What do I have to do make my objects useable for the Metatypesystem?
My_Item is based on My_Super_Item, which look like this:
class My_Item: public My_Super_Item {
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
public:
explicit My_Item(Application *a, QString astring);
...
}
and
class My_Super_Item : public QObject, public virtual QGraphicsItem {
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
public:
My_Super_Item(My_Application *a, QString astring);
...
}

Connection of pure virtual signal of interface class

I want to connect some object's signals derived from an interface class.
The connection is done in QWidget::listenToAnimal(AnimalInterface*).
This does not work because qt_metacall is not a member of 'AnimalInterface' and static assertion failed: No Q_OBJECT in the class with the signal.
Of course AnimalInterface does not have the Q_OBJECT macro and does not inherit QObject because it is an interface...
I want to connect through the interface class because I do not want to manually retype the same code for Cat and for Dog.
Is it possible to connect the signal the way I want to? Perhaps with templates? Is this perhaps a lambda-specific problem?
header:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class AnimalInterface{
public:
virtual ~AnimalInterface();
virtual void makeSound() = 0;
/*signals*/
virtual void madeSound() = 0;
};
Q_DECLARE_INTERFACE(AnimalInterface,"interface")
class Dog : public QObject, public AnimalInterface
{
Q_OBJECT
Q_INTERFACES(AnimalInterface)
public:
void makeSound();
signals:
void madeSound();
};
class Cat : public QObject, public AnimalInterface
{
Q_OBJECT
Q_INTERFACES(AnimalInterface)
public:
void makeSound();
signals:
void madeSound();
};
class Widget : public QWidget
{
Q_OBJECT
Cat *cat_;
Dog *dog_;
public:
Widget(QWidget *parent = 0);
~Widget();
void listenToAnimal(AnimalInterface *animal);
};
#endif // WIDGET_H
cpp:
#include "widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
dog_ = new Dog;
cat_ = new Cat;
listenToAnimal(dog_);
listenToAnimal(cat_);
dog_->makeSound();
cat_->makeSound();
}
void Widget::listenToAnimal(AnimalInterface *animal)
{
connect(animal, &AnimalInterface::madeSound,
this,
[](){
qDebug()<<"animal made sound";
});
}
Widget::~Widget()
{
}
void Cat::makeSound()
{
qDebug()<<"Cat says miaow";
emit madeSound();
}
void Dog::makeSound()
{
qDebug()<<"Dog says wuff";
emit madeSound();
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
Since you know the derived type at compile type, you can connect to the proper, statically-known QObject-derived type. No need for dynamic casting or anything of the sort. You just don't want the listenToAnimal method to be available for non-AnimalInterface-inheriting types, though, even if it they have a compatible madeSound method:
C++11
#include <type_traits>
template< class T,
typename =
typename std::enable_if<std::is_base_of<AnimalInterface, T>::value>::type >
void listenToAnimal(T * animal) {
connect(animal, &T::madeSound, this, []{ qDebug() << "animal made sound"; });
}
C++03
template <class T>
void listenToAnimal(T * animal) {
Q_UNUSED(static_cast<AnimalInterface*>(animal));
connect(animal, &T::madeSound, this, &Widget::onAnimalMadeSound);
}
You can then use it without having to spell out the type - it's already known to the compiler:
listenToAnimal(dog_);
listenToAnimal(cat_);
If the derived type is not known at compile time, you have to dynamically cast to QObject and connect by name, not by method pointer. It will assert at runtime if you've passed in a wrong type - after all, it's not enough for it to be an instance of AnimalInterface, it also needs to be a QObject instance.
void listenToAnimal(AnimalInterface * animal) {
auto object = dynamic_cast<QObject*>(animal);
Q_ASSERT(object);
connect(object, SIGNAL(madeSound()), this, SLOT(onAnimalMadeSound()));
}
The fact that the type AnimalInterface has a virtual madeSound method is somewhat relevant - it guarantees that the derived class implements the method with such a signature. It doesn't guarantee that the method is a signal, though. So you should probably rethink your design and ask yourself: "What do I gain by using a static type system when I can't really use it for static type checking"?
Most likely you should make any methods that would nominally accept the AnimalInterface*, be parametrized and take a pointer to the concrete class. Modern code generators and linkers will deduplicate such code if type erasure leads to identical machine code.
Found a solution with templates. Did not work the first time I tried, obviously did something wrong first. Here it goes...
Just replace the corresponding parts from the example in the question (and remove definition of listenToAnimal from the source file):
header:
template<class T>
void listenToAnimal(AnimalInterface *animal)
{
T *animal_derivate = dynamic_cast<T*>(animal);
if (animal_derivate){
connect(animal_derivate, &T::madeSound,
this,
[](){
qDebug()<<"animal made sound";
});
}
}
cpp:
listenToAnimal<Dog>(dog_);
listenToAnimal<Cat>(cat_);
Update:
After trying Kuba Ober's answer, it seems like this is working best now:
template<typename T>
typename std::enable_if<std::is_base_of<AnimalInterface, T>::value,void>::type
listenToAnimal(T *animal)
{
connect(animal, &T::madeSound, this, [](){ qDebug()<<"animal made sound"; });
}
However, the one point still not working is how to connect if I create an animal like AnimalInterface *bird = new Bird, because it throws the same error that the base class does not have the signal.

Linker error: undefined reference to class method

I have following very simple Qt project:
I have
#ifndef ABSTRACTWIDGET_H
#define ABSTRACTWIDGET_H
#include <QWidget>
#include <QVariant>
namespace Ui {
class AbstractWidget;
}
class AbstractWidget : public QWidget
{
Q_OBJECT
public:
explicit AbstractWidget(const QString &name, QWidget *parent = 0);
~AbstractWidget();
QString getName();
virtual void setValue(const QVariant & value);
protected:
Ui::AbstractWidget *ui;
};
#endif // ABSTRACTWIDGET_H
and
#ifndef SLIDERWIDGET_H
#define SLIDERWIDGET_H
#include "abstractwidget.h"
#include <QSlider>
class SliderWidget : public AbstractWidget
{
Q_OBJECT
public:
explicit SliderWidget(const QString & name, int min, int max, QWidget *parent = 0);
~SliderWidget();
void setValue(const QVariant & value);
private:
QSlider * slider;
};
#endif // SLIDERWIDGET_H
I just made setValue virtual at the super class Abstractwidget, which drops follwing linker error:
moc_abstractwidget.cpp:-1: Fehler: undefined reference to
`AbstractWidget::setValue(QVariant const&)'
collect2.exe:-1: Fehler: error: ld returned 1 exit status
Rebuilding All and Cleaning up did not help. Why is it failing to link?
Abstract is a bad nomencalture, sorry my bad. Consider it as a super class (not abstract) from which I derive other classes.
I inject in a MainWindow::Ui::QVBoxLayout dynamically those AbstractWidget classes (for instance I have multiple SliderWidgets and ColorPickerWidget : public Abstractwidget as well). I just wanted to keep it simple.
Why derive? I have multiple QWidgets (besides SliderWidget) deriving from AbstractWidget class. All of them have a QString name. I wanted to provide that name in the base class ==> AbstractWidget.
Why virtual void setValue(): Every derived class has somesting like a slider, colorpicker etc. I implement in each derived class a setValue(QVariant) so that the colorpicker sets the QColor(QVariant::toString()) and the SliderWidget sets the sliderValue with setValue(QVariant::toInt()), etc..
setValue is public everywhere (virtual base class and derived implementations).

emitted signal is not caught in derived class

class Settings : public QObject
{
Q_OBJECT
public:
Settings();
~Settings();
void setValue(QString key, QVariant value);
// [...]
signals:
void settingsChanged();
// [...]
class ApplicationSettings : public Settings
{
public:
explicit ApplicationSettings();
~ApplicationSettings();
public slots:
void save();
// [...]
Every time I change a value via setvalue(...)in the base class,
I do emit settingsChanged().
In the constructor of ApplicationSettings I say:
connect(this, SIGNAL(settingsChanged()), this, SLOT(save()));
But save() is never called.
As I've been writing this question I noticed that I did not include Q_OBJECT in my derived class. Adding this, the signal was connected correctly. I think this question may still be useful for others, because it was also new for me that the Q_OBJECT-macro of the base class is not "inherited".

Why can't I set a QObject parent in a class of which QObject is only an indirect base?

I have a class BatchItem that inherits QObject, plus several classes that inherit from BatchItem:
#ifndef BATCHITEM_H
#define BATCHITEM_H
#include <QObject>
class BatchItem : public QObject
{
Q_OBJECT
public:
virtual void start() = 0;
virtual void stop() = 0;
signals:
/* ... some signals ... */
};
#endif // BATCHITEM_H
Example of a class that inherits from BatchItem:
#ifndef VIDEOBATCHITEM_H
#define VIDEOBATCHITEM_H
#include "batchprocessing/batchitem.h"
#include <QtCore/QObject>
class VideoBatchItem : public BatchItem
{
Q_OBJECT
public:
explicit VideoBatchItem(/* ... */, QObject *parent = 0);
void start();
void stop();
private:
/* ... some private member variables ... */
};
#endif // VIDEOBATCHITEM_H
And this is the corresponding .cpp:
#include "videobatchitem.h"
VideoBatchItem::VideoBatchItem(/* ... */,
QObject *parent) :
/* ... */,
QObject(parent)
{
/* ... */
}
/* ... */
But when I try to compile, I get the following error:
error: type ‘QObject’ is not a direct base of ‘VideoBatchItem’
Of course I see that this is correct, as QObject is only an indirect base of VideoBatchItem. But why is that a problem?
Isn't that also the case for e.g. QAbstractScrollArea, which inherits from QFrame, which in turn inherits from QWidget? They all take a QWidget as their parent, although QAbstractScrollArea only indirectly inherits from QWidget.
Unfortunately I couldn't find an answer to that in neither the documentation nor the .cpp files of the named widget classes.
Since I cannot pass a QObject parent, is there still a way to use Qt's parent-child system for the destruction of my derived batch items?
You can't call QObject base constructor. It doesn't matter about type of parent parameter but call of QObject(QObject * parent). You should call in this case BatchItem() without parameter and call setParent(parent) in constructor body, or overload BatchItem(QObject *) constructor.

Resources