Connect to protected slot in derived class - c++

This is how the declarations in the base class look:
protected:
void indexAll();
void cleanAll();
In the derived class the following does not compile:
indexAll(); // OK
connect(&_timer, &QTimer::timeout, this, &FileIndex::indexAll); // ERROR
connect(&_timer, SIGNAL(timeout()), this, SLOT(indexAll())); // OK
I would like to use the first variant of connect, since it does some compile time checks. Why is this returning the error:
error: 'void Files::FileIndex::indexAll()' is protected
void FileIndex::indexAll()
^
[...].cpp:246:58: error: within this context
connect(&_timer, &QTimer::timeout, this, &FileIndex::indexAll);
^

The 'old' style syntax works because signal emission runs through qt_static_metacall(..) which is a member of FileIndex and so has protected access.
The 'new' style syntax does work, but for this reason won't let you take the address directly of the parent class method. It will however take the 'inherited' address of indexAll(), so just change the code to:
connect(&_timer, &QTimer::timeout, this, &Derived::indexAll);

The first one is ruled by normal C++ accessibility rules. The QTimer::timeout signal call the FileIndex::indexAll over the provided function pointer directly. Of course, this is only possible when this function pointer is public (disregarding possible friend solutions). If you use function pointers you don't even need to mark the function as SLOT in the header file.
The second one is moc magic. Calls through the meta-object system. I never delved into this topic any deeper... it just worked. :-)
Ok, not the best explanation. If you want more info:
http://woboq.com/blog/new-signals-slots-syntax-in-qt5.html
http://woboq.com/blog/how-qt-signals-slots-work.html
http://woboq.com/blog/how-qt-signals-slots-work-part2-qt5.html
Good reads, but... only of interest if your are interested in the deeper workings of Qt. IMHO only necessary if you want to develop Qt itself.

Related

Cannot connect signal from an outer shared library to overloaded slot of QWidget derived class

I try to connect the signal from an outer shared library to an overloaded slot of base QWidget class's slot.
Suppose this is an instance of a class derived from QWidget
class MyClass : public QWidget;
...
MyClass* this_instance = new MyClass;
I tried the following approach
connect(obj_from_sl, SIGNAL(signalFromSL()), this, SLOT(update());
// update() is QWidget's slot
// the "sl" suffixs stands for "shared library"
but it didn't work. I have got linker errors. I have searched and found out, that
it is impossible to connect signal from an outer shared library using SIGNAL/ SLOT macros inside the connect function. It might be possible if I use function pointers.
OK. Let's change the signature:
connect(obj_from_sl, &ClassFromSL::signalFromSL, this, &QWidget::update);
^ error comes here
and voilĂ 
cannot determine which instance of an overloaded function "QWidget::update" is intended.
Could anyone help me to solve this problem?
The QWidget::update() function has different arguments, so it requires explicit casting for the new signal/slot syntax use
connect(obj_from_sl, &ClassFromSL::signalFromSL, this, static_cast<void (QWidget::*) )>(&QWidget::update));

Connecting to a base class' signal when overriding it in a subclass

Qt provides the QSpinBox, which is some far QObject derivative that provides a signal:
class QSpinBox:
public /* ... */ QObject {
signals:
void valueChanged(int i);
};
When deriving from this spin box, I might provide an override for this signal to emit some beautified version of the value:
class MySpinBox:
public QSpinBox {
private slots:
void originalValueChanged(int i) {
/* Beautify */
emit valueChanged( 42 * i );
}
signals:
void valueChanged(int myPersonalI);
};
This seems perfectly fine: The signal MySpinBox::valueChanged(int) hides the inherited signal. The only bit missing is to connect the inherited QSpinBox::valueChanged(int) to the private slot MySpinBox::originalValueChanged(int).
While this is possible using the new signals and slot syntax:
connect(
static_cast<QSpinBox *>(this), &QSpinBox::valueChanged,
this, &MySpinBox::originalValueChanged);
I am neither sure if this is allowed or sensible, nor how to connect the signal using the conventional string based signal/slot syntax. Using the latter, the slot is obviously connected to the MySpinBox::originalValueChanged(int) signal, which is clearly not intended.
Purpose
The above example is reduced to the problem I think I face. To understand why I ran into the problem and maybe to guide me out of this (pretended) broken design, think of a QDoubleSpinBox derivative to enter some physical quantitiy, e.g. speed. The user may want to enter this in some preferred unit, which may be km/h or mph. To get the unit conversion logic out of the application, I composed a new widget, derived from QWidget and containing only a QDoubleSpinBox. Then I implemented the usual spin box methods (setMinimum, setSingleStep etc.) that take the respective properties in SI dimension (i.e., m/s in this example), convert them to some chosen unit and then configure the inferior double spin box.
A setValue(double) converts its argument to the user's unit and passes it on to the inferior. In turn, there is a valueChanged(double) signal that is emitted whenever the inferior spin box emits valueChanged, but with the quantity converted back to the SI dimension.
So in the end I was tinkering about not composing a QWidget derivative with an inferior spin box but to derive from the QDoubleSpinBox and reimplement.
Pros and cons appear partly clear, including the issue explained by #Pavel Strakhov below already. Composing the widget in fact gets me rid of wrongly inherited methods being called (due to not being virtual), at the cost of a wrapping QWidget. So as an intermediate step, I'm tinkering of deriving private from QDoubleSpinBox. This is surely not a matter of resources in a GUI application, but to me it's also a matter of learning and tinkering.
Then I stumbled about the signal and wondered what Qt might think about it.
Note that QSpinBox::valueChanged is not virtual, meaning that if one have an instance of MySpinBox stored in QSpinBox* pointer and calls its valueChanged method, QSpinBox::valueChanged will be executed instead of MySpinBox::valueChanged. And Qt internal functions will definitely have QSpinBox* pointer to the instance. This inconsistency can be a source of errors. Non-virtual functions are not meant to be redefined in subclasses, and you should not do that. Neither of Qt built-in classes uses such redefinition. Subclasses usually add new signals leaving base class signals untouched.

Passing arguments with signals and slots in qt

I am new to qt and i have got a question.
I wanted to connect signals and slots.
QObject::connect(_Requests, SIGNAL(newJobsAvailable(const MyClass &)), _Object, SLOT(doSend(const MyClass &)));
The qt complains about not being able to queue MyClass and etc.
How do i declare it correctly with
qRegisterMetaType<const MyClass &>("const MyClass&");
If Qt complains about not being able to queue you class this means that, Qt is unable to copy and put inside QVariant object of your class.
This only means that only direct connection will work. What does it mean? If you are using default value of last argument in connect then connection will not work between threads!
Setting last argument of connect to Qt::DirectConnection should silence the warning, and value Qt::QueuedConnection will not work at all.
Another way to fix it is to register your type. But you should do this without any qualifiers!
qRegisterMetaType<MyClass>("MyClass");
If you are using Qt5 then consider use of Q_GADGET macro in MyClass (just put it in beginning of class definition and add header to HEADERS in pro file).
You need to make sure that MyClass has public default and copy constructors. Because the object might be copied, even if you declare signals and slots with const ref.
If for some reason copy is out of the question then pass pointer as suggested by ratchet

Signals and Slots - Passing Data

Im trying to connect a signal to a slot and pass through a vector but im not having much luck.
res = QObject::connect(storePayments, SIGNAL(existingPurchasesResponseSuccess(std::vector<QString>)), this, SLOT(RefreshPurchasesSuccess(std::vector<QString>)));
Slot:
void RefreshPurchasesSuccess(std::vector<QString>);
void Store::RefreshPurchasesSuccess(std::vector<QString> previousPurchasesArray)
{
//do something
}
Signal:
void existingPurchasesResponseSuccess(std::vector<QString>);
vector<QString> previousPurchasesArray;
emit existingPurchasesResponseSuccess(previousPurchasesArray);
It says the signal/slot is not defined, but when I take out the vector it works, so it must be something wrong with that. Am I defining it wrong?
Thanks
If you use custom structure like std::vector<QString> you must declare and register metatype
Q_DECLARE_METATYPE(std::vector<QString>)
"Ideally, this macro should be placed below the declaration of the class or struct. If that is not possible, it can be put in a private header file which has to be included every time that type is used in a QVariant." -- Qt documentation on Q_DECLARE_METATYPE
For queued connections you may need qRegisterMetatype
qRegisterMetaType<std::vector<QString> >();
qRegisterMetaType can be called for example in main() even before QApplication::exec().
Also remember that you must use Q_OBJECT macro if your class declares any signals or slots.
"The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt's meta-object system."
If you have no reason to use std::vector<QString> then it would be much simpler to use QStringList, which is already known to Qt's meta-object system, provides many convenient methods to manipulate its content and as a standard Qt type will fit to non-yours slot definitions.

connecting a function to a boost::signal runs, but doesn't call the function

I have a class Yarl in my code with a member function refresh that I want to bind to two boost::signals. One of these signals is a member of a class EventHandler defined like this:
class EventHandler {
public:
boost::signal<void()> sigRefresh;
};
The other is a free floating signal in another file declared like this:
namespace utility {
static boost::signal<void()> signal_refresh;
}
in a member function of Yarl, I connect refresh to the signals like this:
events::EventHandler eventHandler;
eventHandler.sigRefresh.connect(boost::bind(&Yarl::refresh, this));
utility::signal_refresh.connect(boost::bind(&Yarl::refresh, this));
and later on I call both signals like this:
sigRefresh();
signal_refresh();
This code compiles and runs, and sigRefresh works exactly as expected. However, nothing happens when I call signal_refresh. As far as I can tell, refresh never actually got connected to signal_refresh. Anyone see what I'm doing wrong?
I'm taking a guess that you are multiply defining signal_refresh. The static keyword before it's declaration suggests to me the code snippet is in a header file and you had to put the static there to get it to compile without redefined symbol errors. If you have done this then every source file including the header will get a unique copy of signal_refresh and thus the instance you are calling may not be the instance you connected it to.
I may be totally off mark here but it is possible.