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...)
Related
For a school project we have to make a 'game', in this game we visualize a maze and find our way through it. This visualization has to be possible in two different ways, in our case a text-based visualization and a more fancy way.
We have to use Qt for this, we are using a QMainWindow with a QWidget in it, this widget will be one or the other visualization. Seeing that during the game we should be able to switch between visualizations, we use the Strategy Pattern, so made an interface (ViewInterface) and both visualizations implement this. Beside implementing ViewInterface, both visualizations inherit another class, in the text-based this is QPlainTextEdit (to have a QWidget with text) and in the Fancy this is QDialog. Probem here is that in my controller, I have a pointer to a ViewInterface which is used to fill the QWidet but to do this ViewInterface also has to inherit from QWidget, which causes this error: QObject is an ambiguous base of 'TerminalView'.
Since the switching between views can be done while playing the game, and update should be called only on the currently-active view, we can't pass the specific view to 'setWidget'.
Am I doing something wrong or how can I solve this? (I already thought about it but can't come with solutions).
The problem here is that you inherit QObject twice: first in ViewInterface hierarchy, second in QDialog hierarchy (diamond problem). Try using virtual inheritance for your FancyView and TextView classes (DOES NOT WORK, SINCE VIRTUAL INHERITANCE SHOULD BE USED IN ENTIRE HIERARCHY)
But there is another problem with your design: both QDialog and QPlainTextEdit inherit QWidget. To resolve this issue you may do the following:
Make ViewInterface abstract without inheriting from QObject. This abstract class will define the interface of your FancyView and TextView and may or may not implement some common logic.
Implement FancyView and TextView with multiple inheritance from QDialog and ViewInterface and from QPlainTextEdit and ViewInterface respectively.
This way you may not encounter any problems with ambiguous base class.
UPDATE:
Did not see your edit yet: indeed this would solve the issue, but
another problem rises: if I do this, I can't use a ViewInterface
pointer to set my QWidget. It is possible indeed, but in my opinion
this is not really clean
Well, that is a real problem. An obvious solution is not to use ViewInterface* instead of QWidget*. But this means that you need to change quite a lot of code and it may actually be not that great in your case.
With respect to the given comment, I propose another solution:
Inherit ViewInterface from QWidget (with all desired interface functions):
class ViewInterface: public QWidget {
Q_OBJECT
...
}
In ViewInterface constructor set layout to be used by widget and set it up:
ViewInterface::ViewInterface (QWidget* i_parent)
: QWidget (i_parent) {
auto layout {new QGridLayout (this)};
// Zeroed margins to make full fit.
layout->setContentsMargins (0, 0, 0, 0);
setLayout (layout);
}
In constructors of derived classes add specific widget to layout:
class FancyView : public ViewInterface {
Q_OBJECT
FancyView (QWidget* i_parent)
: ViewInterface (i_parent)
, dialog_widget_ {new QDialog (this)} {
layout->addWidget (dialog_widget_);
}
...
private:
QDialog* dialog_widget_;
}
Implement desired interface using target widget. If you want to process events, you may use QObject::eventFilter (). In this case you should set your FancyView object from the code above as event filter for dialog_widget_.
NOTE: In this case you can not use FancyView as QDialog. A workabound for this issue is proxy QDialog signals, slots and public functions and create yet another wrapper class FancyViewDialog, that works as proxy for the methods, signals and slots in FancyView. That is not a fantastic solution, but I do not see any other way around diamond problem that allows "is-a" relation between ViewInterface and QWidget.
An interface should be abstract with virtual methods and have no concrete base classes. ViewInterface should not inherit from QWidget. That fixes your problem.
Now there are at least two solutions to converting the ViewInterface instance to QWidget:
Template the users of ViewInterface and have them ensure that the concrete type used does in fact derive from QWidget. That will work if the type is there's no runtime polymorphism.
If there's runtime polymorphism, add a QWidget * widget() = 0 method to the interface, and implement it in the derived methods. It's trivial: QWidget * widget() override { return this; }.
The interface can have both signals and slots - they must be virtual methods, but they certainly will work. See e.g. this answer to get started with virtual signals.
If you want to share some code between the two concrete implementations of ViewInterface, you can have an additional class that derives from ViewInterface to provide the shared code. Both TerminalView and FancyView would derive from that class. The helper class could be parametrized on the base class type, to have it jump through less hoops to access the widget, e.g.: class TerminalView : ViewHelper<QPlainTextEdit> { ... };
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.
I have a standard c++ class, and I want one of its functions to communicate with a Qt widget's slot.
Lets say I have a function foo() in my C++ class, and I want that when ever this function gets called it signal to QWidget for corresponding slot. Since QWidget is used for display purposes
and the standard C++ class is doing something else and I want to changes visual based upon some conditions( i.e. when foo() gets called).
Is it feasible that I can make a C++ class function to trigger a slot of QWidget class?
I'm programming in Qt Creator and encountered a problem. I made a singleton class, and I'm trying to connect it to a slot in the widget, but it doesn't take the pointer that returns
from Singleton::getInstance() as the same instance that emits the signal.
My code is as follows:
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void setString(int var);
}
Implementation:
connect(Singleton::getInstance(),SIGNAL(changeString(int)),this,SLOT(setString(int)));
Signal in the singleton class:
signals:
void changeString(int var);
the call to the signal in the singleton class:
emit(Singleton::getInstance()->changeString(5));
Nothing happens when signal emits. The debugger doesn't enter the slot.
Most probably it's the differing method signature of the slot - setString(IMSS_Status); vs. setString(int);
Also, in case of a custom type, you should call
qRegisterMetaType<IMSS_Status>("IMSS_Status");
to register it correctly with the meta type system. Otherwise, queued slot executions won't work, for instance.
Your problem may be caused by signature of the methods. Before you make connect signal with slot u should define your signal to system by using qRegisterMetaType<Your Class >("Class Definition name");
And is the signal of changeString signature ? "void changeString(int)"
i had encountered your problem and i solved my problem by making qRegisterMetaType and controlling signatures.
Does the Singleton class have the Q_OBJECT macro also? I don't know if it needs it also or not.
I would lean towards emit(Singleton::getInstance()->changeString(5)); not being correct.
emit should be called from inside a Singleton function as follows:
emit(changeString(5));
I'm surprised the code you posted compiles and runs without warning, I would have expected a runtime error about the signal not existing.
Can Qt signals be public or private? Can I create internal signals, which are seen only inside the class?
Update: I have a class with some internal signals. How can I make those signals invisible for other classes (encapsulation & information hiding)?
No. Signals cannot be public or private. Qt signals are protected class methods.
"signals" keyword is defined in qobjectdefs.h (line 69 as for Qt 4.6.1):
# define signals protected
UPDATE: signals are only protected upto and including all minor versions of Qt 4. From Qt 5.0 onwards they are public. See https://stackoverflow.com/a/19130831.
A common way, e.g. seen in kdelibs, is this:
Q_SIGNALS:
#ifndef Q_MOC_RUN
private: // don't tell moc, doxygen or kdevelop, but those signals are in fact private
#endif
void somePrivateSignal();
This makes the signal private, i.e. it can only be emitted by the class itself but not by its subclasses. To not make the "private:" overrule Q_SIGNALS (moc wouldn't see somePrivateSignal as signal then), it's inside Q_MOC_RUN, which is only defined when moc runs.
Edit: This approach doesn't work for the new-style connects introduced with Qt 5 (connect(a, &A::someSignal, b, &B::someSlot)), as they require the signal to be accessible.
Signals was protected in Qt4, in Qt5 they are public. Int Qt5 you can make them private by adding QPrivateSignal as the last argument. More on this: http://woboq.com/blog/how-qt-signals-slots-work-part2-qt5.html
Slots are simple methods which can be public, protected, or private.
As Andrei pointed it out, signal are only a redefinition of protected, meaning they can only be emitted by the class in which they are defined.
If you want to make a class emit a signal from anoter one, you have to add it a public method (or slot) like this one:
void emitTheSignal(...) {
emit theSignal(...);
}
Qt signals are public in the sense that any object can connect to any signal.
All the existing answers are incorrect.
A signal can be made private by adding a QPrivateSignal type to its definition as the last argument:
signals:
void mySignal(QPrivateSignal);
QPrivateSignal is a private struct created in each QObject subclass by the Q_OBJECT macro, so you can only create QPrivateSignal objects in the current class.
Technically, the signal still has public visibility, but it can only be emitted by the class that created it.
Nowadays you can use Q_SIGNAL macro and normal C++ access specifier:
protected:
Q_SIGNAL void myProtectedSignal();
private:
Q_SIGNAL void myPrivateSignal();
You can use the PIMPL pattern for that. Your private signals exists in the private implementation only.