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.
Related
I have some classes inherited from QGraphicsItem, I also have buttons in my interface. Right now, when I press a button, an item is created in the scene. I have such signal/slot system for all of my classes, so as a result I have a lot of slot functions that are very similar, and the only difference is in the type of objects they create. Is this a good programming practice or not, because it doesn't look like it. Is there a way to simplify this?
Armchair is a class inherited from QGraphicsItem
armchair is an object of this class
this is a code example of such slot function
void MainWindow::armchairButton_clicked()
{
scene_preview->clear();
armchair = new Armchair();
scene_preview->addItem(armchair);
}
There is no general fixed rule for this, just the DRY principle (Don't Repeat Yourself).
In general, every button has a distinct function, so there must be some code to distinguish those.
Depending on how much code is common and how much is individual, you need to find a good balance.
In your case, you seem to only have 1 thing to change (the object type to add) and some common code for cleaning. This could be separated, e.g.
void MainWindow::replaceSceneObject(QGraphicsItem *i)
{
scene_preview->clear();
scene_preview->addItem(i);
}
void MainWindow::armchairButton_clicked()
{
replaceSceneObject(new Armchair);
}
As you use manual connects, you actually don't need separate slots for this, but could use lambdas in the connect calls, e.g.:
connect(armchairButton, &QPushButton:clicked, [=]{ replaceSceneObject(new Armchair); });
I have a MainWindow derived from QMainWindow and I re-implemented the closeEvent() handler.
void MainWindow::closeEvent(QCloseEvent *event)
{
if (this->okToContinue()) {
this->writeSettings();
//event->accept();
QMainWindow::closeEvent(event); // will work just fine even if this line omitted
} else {
event->ignore();
}
}
I've commented out the QMainWindow::closeEvent() to test the app will exit without the event propagating to the base implementation. Weirdly enough, it does exit.
I can just put event->ignore() outside of the if-else statement to prevent exiting but that's not the point.
Other event handlers such as keyPressEvent() don't work properly without the base implementation in their overrides but the closeEvent() does work without the base implementation. (Unless, of course, you reimplement everything)
void LineEdit::keyPressEvent(QCloseEvent *event)
{
QLineEdit::keyPressEvent(event); // will not show text in the widget if omitted
}
From what I've gathered in the documentation, once an event was handled by a widget, it does not propagate any further, unless explicitly allowed. (i.e. invoking the base's keyPressEvent() in the implementation of the child's keyPressEvent())
However a child's closeEvent() will close the application without invoking the base's closeEvent() in it's implementation. Seems like it propagates to somewhere else.
What makes this happen? Does QCloseEvent propagate to other widgets even after it was already handled?
What a nice question. (I never have thought about this.)
After this raised my curiosity, I investigated a bit. That's the nice about OpenSource software – if you are in doubt just have a look into source code. So, I digged a bit on woboq.org:
First I looked for closeEvent() in qmainwindow.cpp but I couldn't find it anywhere.
The same in qmainwindow.h.
So, I assume QMainWindow doesn't override closeEvent().
QMainWindow is derived from QWidget as easily can be found in the doc. or directly in the source code which I have already at hand:
class Q_WIDGETS_EXPORT QMainWindow : public QWidget
Thus, I switched to qwidget.h which declares
virtual void closeEvent(QCloseEvent *event);
(currently in line 634).
One click again and I approached in qwidget.cpp:9936:
void QWidget::closeEvent(QCloseEvent *event)
{
event->accept();
}
Ah, yepp.
This explains nicely why the call of base class method
QMainWindow::closeEvent(event);
works as well as a simple call to
event->accept();
or even both.
Though, I would prefer the call of the base class event handler.
I consider this as the general useful rule-of-thumb (or idiom?):
If the behavior of base class shall be extended then call the base class event handler in your overridden.
If the behavior of base class shall be replaced then don't call the base class event handler.
Of course, this is a general rule of thumb and should be re-checked depending on the specific event which has to be handled.
(And in case of doubts, there still is woboq.org...)
Suppose I have class:
class Foo : public QObject{
Q_OBJECT
public:
...
private:
Bar* m_bar;
}
And for whatever reason, I'd like to replace m_bar with a pointer to an object of DerivedBar. The use case I most often find is when I want to replace it with an object of MockBar (using the google mock framework) for unit testing.
Now I know in most cases, I could just extend the constructor to be something like:
Foo(Bar* bar, QObject* parent)
and just set m_bar accordingly.
But the problem I have is when the classes are QWidgets and I'm assembling them via Qt Designer. Imagine Foo and Bar are widgets, and Bar is placed inside of Foo from designer; their connections are also set via designer (ie, stored in the qml file). I've tried something like:
Bar* bar = foo.findChild<Bar*>();
bar = new MockBar(&foo);
(neglecting any memory leaks here, just trying to get functionality) But when I go to set expectations, it does not seem to be connected like the existing one was. Slots aren't called in response to signals. I don't think this is entirely unexpected due to the way connections are made, but I'm wondering if there's a way to get the effect I'm looking for.
(nb: for now, using qt 4.8 and gcc 4.6, which does limit some options for c++
Here's a better answer I've found: The issue for me is ultimately to do with the QWidgets generated from ui files. But since the Ui::FooWidget class is functionally just a pimpl pattern, I can create a pointer to a local copy of one, and modify Foo's constructor to allow me to optionally inject the ui implementation. If Foo is making a direct function call to BarWidget, I can replace that ui's 'barWidget' object with my MockBarWidget, and set expectations and such and perform testing. The previous answer will still satisfy to some degree when the interaction between the two widgets is one of signal/slot connections.
Previous Answer:
Here's one answer I've found so far, but I don't much like it:
Bar* bar = foo.findChild<Bar*>();
bool connectTest = QObject::connect(&foo, SIGNAL(setValue(int)), bar, SLOT(setValue(int)), Qt::UniqueConnection);
ASSERT_FALSE(connectTest);
bar = new MockBar(&foo);
connectTest = QObject::connect(&foo, SIGNAL(setValue(int)), bar, SLOT(setValue(int)), Qt::UniqueConnection);
ASSERT_TRUE(connectTest);
//... rest of test
I could probably wrap the whole thing up in some kind of helper function, and that function may be better with c++11/Qt5 where signals and slots can be method pointers. But I wonder if I'm missing an easier way to do it.
Edit: It turns out this pattern was giving me a false positive, only the first 'connectTest' part of the test was giving a valid test of behaviour. This flaw is more obvious when Foo doesn't emit a signal to bar , but merely calls a method on bar. The mock is not injected through this pattern.
There are discrepancies between respected answers here on SO and the actual Qt docs.
I've read this question and I want some further clarification. Can anyone confirm:
A signal is always protected, therefore it can be emitted only by the class or any of its subclasses. I'm not sure this is true; the question above shows answers supporting this statement. But the Qt docs say: Signals are public access functions and can be emitted from anywhere, but we recommend to only emit them from the class that defines the signal and its subclasses. So which is it?
Slots are just functions, and thus may be public, private or protected. Obviously an outside class will have the ability to control if your class connects one of its own signals to one of its own slots if the slot is public. However, again the SO information differs from the docs, which say: a signal emitted from an instance of an arbitrary class can cause a private slot to be invoked in an instance of an unrelated class. This means that private is not honored by the signal/slot mechanism?
The words public, private, protected have no use with working with the signal keyword
The emitted signal is always available to all other classes, that is, any other class may always connect to that signal (regardless of its permission to emit the signal).
Despite that all signals are viewable to by all classes, you could still have two classes with signals of the same name since the connect function takes the class name as a signal prefix (i.e. SomeClass::itsSignal)
Signals are protected in Qt4 but are public in Qt5, thus the contradictory information.
Slots are functions and public/protected/private is honored when calling them as such, when connecting to a signal, the metaobject system ignores it though.
As signals is defined as public:, prepending them with e.g. private leads
to:
private:
public: //signals:
void theSignal();
Thus it's without effect.
All classes can be connected to any signal, correct. Signals are part of the public API in that regard.
Having identical signal signatures is not a problem. The context is defined by the object specified as sender.
Using old-style connect:
Apple *apple ... Orange* orange
connect(apple, SIGNAL(changed()), this, SLOT(appleChanged()));
connect(orange, SIGNAL(changed()), this, SLOT(orangeChanged()));
The signal is specified as string here (without the class name in it), but as apple and orange have only one signal changed() each and the lookup is done in the metaobject of the QObject instance, which exists one per class (not instance), they cannot collide.
Qt 5 version with compile-time checking:
connect(apple, &Apple::changed, this, &MyReceiver::appleChanged);
Here one must specify a function, so depending on the scope, one must specify a class name (and maybe namespaces). As an ambiguous function name wouldn't be valid C++ and thus not compile, so one is safe here.
Take a look at qobjectdefs.h (QT5.0+). In there are defined the moc macros
# define signals public
As you can see the macros used in header files for signals are defined as public. As for the explicit statet public,private,protected directives, these are ignored in the signals section. Prior 5.0 versions of QT have signals defined as protected. Those were still available for connections using the SIGNAL() macro.
The slots macro
# define slots
is defined as an empty macro and therefore can be used with:
public slots:
private slots:
protected slots:
The method visibility is used for direct method calls, so private/protected cannot be called from foreign classes directly.
Using the a connect statement still works independently from the visibility. This is the intended behaviour and is implemented in the moc-generated code.
If i remember correctly in earlier versions of Qt a slot was also public automatically, but i did not find a reference for that now.
Any other class can connect to a signal from a foreign class, as long the Q_OBJECT macro is given in the class and the foreign class is known (header included). Since signals are defined per-class it is perfectly legal to have the same signal in different classes. This is also pretty convenient, for example have a signal sendInfo(QString) in all classes makes it easier to remember. The Q_OBJECT macro makes the moc to create code needed to connect signals to slots independent to visibility.
The emitted signal is always available to all other classes, that is, any other class may always connect to that signal (regardless of its permission to emit the signal).
In Qt5, this isn't necessarily true. A signal can be defined with QPrivateSignal as its final argument, and in that case, only the object which declared the signal would be able to connect to it.
Consider signal manager that receives the signal, checks for some conditions, and if they are met, transmits signal to slot, discarding signal otherwise:
signal(some args) ---> [manager] ---> slot(some args)
I can implement it for each given set of arguments using QMetaObject::invokeMethod, say for void signal(int) and void slot(int):
.h
class Manager: public QObject
{
Q_OBJECT
public:
Manager(QObject* sender,const char* signal, QObject* recv, const char* slot);
private:
bool isOkToSend();
QString slotInvokeSyntax;
QObject *recv;
private slots:
On_Signal(int);
}
.cpp
Manager::Manager(QObject* sender,const char* signal, QObject* recvIn, const char* slot)
: slotInvokeSyntax(slot)
, recv(recvIn)
{
connect(sender,signal,this,SLOT(On_Signal(int));
//retrieving method name only
slotInvokeSyntax.remove(0,1).remove(QRegExp("(*",Qt::CaseSensitive,QRegExp::Wildcard));
}
Manager::On_Signal(int val)
{
//invoking slot
if(isOkToSend())
QMetaObject::invokeMethod(recv,slotInvokeSyntax.toAscii().constData(),Q_ARG(int,val));
}
I would like to somehow generalize this for signals and slots with generic number/type of arguments - so that manager works on any pairs like signal(QString)-slot(QString), signal(int,int)-slot(int,int), ...
Is there any way to implement this functionality without adding slot for each of the argument types in Manager? In case my approach is in wrong in general, any suggestions on how to implement such manager are welcome!
EDIT
A bit of clarification on what am I trying to implement - I have large system with several possible states consisting of many smaller widgets (or sub-systems) (some sub-systems can also act both as stand-alone applications or as a part of the larger system). I'm trying to implement global observer that intercepts certain ui events (such as buttons clicks, edits in QLineEdit, QDropbox changes, etc.), and either let corresponding slot of the widget to be called, or discards it if desired action interferes with the global state of the system. I would like to do it through intercepting signal since it allows to avoid dependencies between system components and compiling each subsystem as stand-alone library (with observer not being dependent on any other part of the system and thus being put in core library), but I'm open to any other alternatives that will allow me to achieve that!
FYI, whenever you use something like this:
QString foo;
something(foo.toAscii().constData());
...you are accessing already freed memory because the data pointed to by the QByteArray::constData are valid only until the QByteArray instance lives and is not modified. In your case, the QByteArray is a temporary created by calling foo.toAscii() and is destroyed before the something is called. So this will crash at some point. edit: this does not apply to function calls, see the comments.
To your question -- it would be interesting to know what you're trying to achieve here. The Qt's metatype and metaobject system is indeed a rich one, but abusing it too much might not be the most ellegant way of solving your problem. That said, it's probably fine to use it in this "creative" way with mocked objects, in unit tests etc.
I haven't done this myself and I am not sure whether it's doable without having to touch the q_static_metacall, but it looks like QObject generic signal handler contains an answer to your question.
After Edit:
You said you're looking for a something like a common event bus; the alleged reason is to avoid excess rebuilds when the individual components change. I would not introduce a central interceptor to this architecture. If the toal amount of states is reasonably small, why don't you just let something emit signals upon entering a particular state and have all of your components react to that by enabling/disabling the individual QActions?