Why is setting the accept flag of certain QEvents sufficient to cause behaviour?
-
Hi forums,
I understand that in the Qt events system, events are dispatched to appropriate objects by calling their event() function with the relevant QEvent object as a parameter.
I also understand that these QEvent objects (and all its subclass objects) have an accept flag, which can either be:
- set to indicate that the current recipient object will process/handle the event (the event handling will be contained within the recipient object)
- cleared to indicate that the current recipient object does not wish to handle the event (therefore the event will be passed on to the current recipient's parent object, and this can go on recursively until the accept flag is set by some object or there are no more parent objects and the event is discarded)
Therefore, it is my understanding that the purpose of the accept flag of a QEvent is to inform Qt when the propagation chain of a particular event can be terminated.
Then, it is also my understanding that it is the job of the recipient QObject's event() function (or more specific functions such as mouseMoveEvent() if appropriate) implementations to produce the behaviour that is expected as a response to the event.
With this in mind, I was confused when I found out that with certain events, like QCloseEvent, the simple act of setting their accept flag is sufficient to cause behaviour (in this case, to close the widget).
The reason it confuses me is because I thought that the behaviour associated with the closeEvent would be coded within the recipient object's closeEvent() function, and therefore that merely setting the accept flag of the event object would not be sufficient to actually cause the window to close.
What have I missed here? Are the accept flags of QEvent objects inherently associated with code required to produce behaviour? If so, then what is the purpose of writing "handling" behaviour code within the event functions of the recipient?
Thanks in advance!
-
@Yuta
Maybe this will help. It is not thecloseEvent()
which does the window closing. It isclose()
which does that.closeEvent()
is a notification that handles close being requested.Closes this widget. Returns true if the widget was closed; otherwise returns false.
First it sends the widget a
QCloseEvent
. The widget is hidden if it accepts the close event. If it ignores the event, nothing happens. The default implementation ofQWidget::closeEvent()
accepts the close event.Also, this shows why either
accept()
or calling baseQWidget::closeEvent()
both accept the event, and allowclose()
to proceed. -
Hi,
The CloseEvent handling is "reversed". By default the closing shall happen so you have to stop it from happening if that's what you want.
-
@SGaist Hi, thank you for your reply. Yes, I understand that the handling is "reversed" in closeEvents. And I also understand that in most cases, the accept flag of event objects are set by default, and so the default behaviour of a close event is to close the window. But what I am wondering is why, when/if you reimplement closeEvent() functions, all you need to do is event->accept() to close the window. I thought that you would need to write specific handling code within the closeEvent() function to actually close the window, but I am confused because actually no code is required, and all you need is event->accept(). In other words, I thought that the purpose of the accept flags on events was solely to guide the event propagation process, and nothing else. I thought it was the job of event handling functions such as event(), mouseMoveEvent() or closeEvent() to contain code that produces behaviours in response to events. But, in the case of closeEvent(), it seems that the accept flag acutally serves a purpose in producing the actual behaviour of the event (window closing), because all that an implementation of closeEvent() has to do to allow the window to close is to make sure that event->isAccepted() == true. This is where I got confused. Are the accept flags of event objects commonly associated with their actual behaviours within the application that you are building? And in the case of closeEvents, where is the code located which is responsible for actually carrying out the behaviour (i.e. closing the window)?– because it is certainly not directly inside closeEvent() and I am confused. I hope that I am explaining my question clearly...
-
Accept
in this case does not mean that theaccept
itself closes the window. It "swallows" the event and signals that the event was handled and can be removed from Event Queue. Then the window willhide
and get destroyed (destructed).
So there is no code to literallyclose
the widget/window inaccept
, if this is what you thought :)QWidget::closeEvent
is the event handler for anyQWidget
. If you don't want the default behavior, you can reimplement it and set your custom handling to your widget. -
@Pl45m4 Thanks for your reply, and for all your collective patience with my question!
So let's say that you want to reimplement the
QWidget::closeEvent()
for your custom window, because you want to handle cases where theQCloseEvent
is ignored (e.g. the window contains work that should be saved first). I have seen many examples and tutorials where they reimplement the function in a way similar to below:void YourCustomWindow::closeEvent(QCloseEvent * event) { if (windowCanBeClosed()) { // If the window can be closed, close it by calling event->accept() event->accept(); } else { // Otherwise, the window should remain open, so call event->ignore() event->ignore(); } }
Now, in the above example I understand why calling
event->ignore()
causes theQCloseEvent
to be ignored, and therefore the window to remain open. However, I am confused why simply callingevent->accept()
leads to the window actually closing. I thought that you would at least need to call the default event handler function (i.e.QWidget::closeEvent()
in this case) for the event to be properly handled and produce any behaviour, for example like this:void YourCustomWindow::closeEvent(QCloseEvent * event) { if (windowCanBeClosed()) { // If the window can be closed, first accept the event event->accept(); // Then close the window by calling the default event handler QWidget::closeEvent(event); } else { // Otherwise, the window should remain open so ignore the event event->ignore(); } }
But apparently, you don't even need to call the base class's default
closeEvent()
function! All you need to do is justevent->accept()
. This is why I am confused. Who is actually handling theQCloseEvent
by hiding and destroying the window? It seems like the first example ofYourCustomWindow::closeEvent()
should not do anything, because all it is doing is just callingevent->accept()
orevent->ignore()
.I hope this makes my question clearer– I apologise that I am probably very bad at explaining my question and I thank your patience!
-
@Yuta
Maybe this will help. It is not thecloseEvent()
which does the window closing. It isclose()
which does that.closeEvent()
is a notification that handles close being requested.Closes this widget. Returns true if the widget was closed; otherwise returns false.
First it sends the widget a
QCloseEvent
. The widget is hidden if it accepts the close event. If it ignores the event, nothing happens. The default implementation ofQWidget::closeEvent()
accepts the close event.Also, this shows why either
accept()
or calling baseQWidget::closeEvent()
both accept the event, and allowclose()
to proceed. -
-
-
BTW:
You should call the base implementation because theoretically you can do anything in your reimplementedcloseEvent
, even stuff that does not handle the event, but calls some of your functions. After that, you would call the base implementation to keep the default handling (otherwise the window would not close properly)For example:
MyWigdet::closeEvent(QCloseEvent *e){ // Saves some settings before closing the widget writeToFile(); QWidget::closeEvent(e); }
-
@JonB and @Pl45m4 thank you for your answers.
@Pl45m4 said in Why is setting the accept flag of certain QEvents sufficient to cause behaviour?:
The window/widget is "closed" using
hide
+ its destructor.Ah– so then who calls the window's
hide()
and destructor? In acloseEvent()
reimplementation like the one below (which is seen in many example projects, and it works) those functions are not overtly called anywhere.void YourCustomWindow::closeEvent(QCloseEvent * event) { if (windowCanBeClosed()) { event->accept(); } else { event->ignore(); } }
And I suspect it must have something to do with the
QCloseEvent
object itself, because from the above code we can infer that whether the window will be closed or not depends solely on whether the accept flag of theQCloseEvent
is set or cleared. -
@JonB Apologies! I did read your reply when you first posted it, but I was too thick to understand what you actually meant. I have just re-read it and thought about it– and what you are saying has answered my question! Thank you. I will mark this post as solved.
-
@Yuta
BTW: for the kind of detail you are looking for, are you aware that even if you do not have the Qt sources/do not compile Qt yourself (e.g. I do not, and do not have the source files). there is a site which has all the Qt source code and allows you to click to navigate on all the code calls?You will find the code for
QWidget::close()
at https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qwidget.cpp.html#_ZN7QWidget5closeEv It actually just reads:bool QWidget::close() { return d_func()->close_helper(QWidgetPrivate::CloseWithEvent); }
so you will have some clicking to do (here just click on
close_helper
, it's immediately above) if you want to follow what it actually performs :) But if I wanted your level of detail I would at least give it a go!While you are there you will also see the base implementation of
QWidget::closeEvent()
:void QWidget::closeEvent(QCloseEvent *event) { event->accept(); }
This is why it doesn't make any difference whether your override calls the base implementation (at least so long as you inherit directly from
QWidget
, so there is no other possible overriding going on --- if you have a deeper class, I would prefer to call the base just in case it does something else) or just goesevent->accept()
directly, or both!