I have two classes A
and B
, here is a snippet in B.h
:
#include "A.h"
class B : public QDialog
{
Q_OBJECT
public:
void do_something();
private:
A *a;
}
and in B.cpp
:
B::B(QWidget *parent) :
QDialog(parent),
ui(new Ui::B)
{
a = new A();
a.show();
}
So what should I do if I want to call do_something()
when the ui
of A
is closed (by pressing Alt-F4
, for example) ? Seems the way of signal-slot
cannot apply here.
Thanks a lot !
If modifying the A widget is option, add a signal to it and override closeEvent
or hideEvent
and emit the new signal there. This is robust and you have complete control on what happens. However, rest of the answer is for case where, for whaever reason, you are unable or unwilling to mess with A and want a solution in class B.
If you can set Qt::WA_DeleteOnClose
attribute on A
, then simple way is to make B::doSomething()
(note the common Qt method naming convention) a slot and connect destroyed
signal of A instance to that.
B::B(QWidget *parent) : QDialog(parent), ui(new Ui::B)
{
a = new A();
a->setAttribute(Qt::WA_DeleteOnClose);
connect(a, SIGNAL(destroyed(QObject*)), SLOT(doSomething()));
a.show();
}
Of course this works even if you delete a;
explicitly, and not automatically with the attribute.
Note: In this case, when the object may get deleted whenever, you should really use QPointer
for the a
pointer, to avoid accidentally referencing a dangling pointer.
If you don't want to have the A instance deleted when it is closed (and re-created if it is needed again), then other way is to install event filter on the A instance, and detect the closeEvent
. This has the potential problem, that the widget can reject the close event, and in any case doSomething
is called before the closing. If this is a problem, it can be solved by using QMetaObject::invokeMethod
static method to delay the doSomething
(it must be slot or invokable method for this!) call to happen after close event is over and program returns to event loop. Then in doSomething
check if a
is really hidden. Instead of QEvent::Close
, detecting QEvent::Hide
might work well, too.
To implement this, override the virtual eventFilter
method in B, something like this:
bool B::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Close) { //or QEvent::Hide maybe
qDebug("QCloseEvent!");
// check that object is indeed this->a before calling this->doSomething()
if (qobject_cast<QObject *>(a) == obj) {
doSomething();
//alternative, make doSomething to be called later from event loop:
//QMetaObject::invokeMethod(this, "doSomething", Qt::QueuedConnection);
} else {
qDebug("...but the object is not what is expected, a bug?");
}
// do not block the event, just detect it
}
// proceed with standard event processing
return QObject::eventFilter(obj, event);
}
Then start listening events of an A
instance with installEventFilter, something like:
B::B(QWidget *parent) : QDialog(parent), ui(new Ui::B)
{
a = new A();
installEventFilter(a);
a.show();
}
If you can't use a modal window (or dialog) than you have a little more work, you'll need to subclass QWidget (or QDialog or other QWidget derived class, whatever you need) and override QWidget::closeEvent (for class A) and from here call a method of the parent (i suppose a will be a child of this in your code: a = new A(this);) or code your own signal/slot.
And if you decide to use a modal dialog, the code becomes simpler, see example code in the QDialog's documentation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With