Qt - 当另一个对话框关闭时如何实现我想要的功能?

6

我有两个类,AB,这是B.h文件中的一个片段:

#include "A.h"
class B : public QDialog
{
    Q_OBJECT
public:
    void do_something();
private:
    A *a;
}

并且在 B.cpp 文件中:

B::B(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::B)
{
    a = new A();
    a.show();
}

如果我想在 Aui 关闭时调用 do_something() 怎么办(例如通过按 Alt-F4)?好像这里不能使用 signal-slot 的方式。
非常感谢!


A的类型是什么?QWidget还是QDialog? - UmNyobe
@UmNyobe 两个对话框。顺便问一下,这有什么关系吗? - yakiang
2个回答

7

修改A类以在关闭时发出信号

如果修改A窗口部件是可选的,则向其添加一个信号并覆盖closeEventhideEvent,然后在那里发出新信号。这是稳健的,并且您完全控制发生的情况。但是,如果由于某种原因您无法或不愿意干扰A并且想在B类中获得解决方案,则其余答案适用。

使用Qt信号进行QObject删除

如果您可以在A上设置Qt::WA_DeleteOnClose属性,则简单的方法是将B::doSomething()(注意常见的Qt方法命名约定)作为槽连接到A实例的destroyed信号

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();
}

当然,即使您明确地 delete a;,而不是通过属性自动删除,这也可以工作。
注意:在这种情况下,当对象可能随时被删除时,您应该真正使用 QPointer 用于 a 指针,以避免意外引用悬空指针。
使用 Qt 事件过滤器
如果您不想在关闭A实例时将其删除(并在需要时重新创建),则另一种方法是在A实例上安装事件过滤器,并检测closeEvent。这可能存在问题,因为小部件可以拒绝关闭事件,在任何情况下都会在关闭之前调用doSomething。如果这是一个问题,可以使用QMetaObject :: invokeMethod静态方法来延迟doSomething的调用(必须是槽或可调用方法!),以便在关闭事件结束并程序返回事件循环后发生。然后在doSomething中检查a是否真的隐藏了。检测QEvent :: Hide而不是QEvent :: Close也可能很好用。
为了实现这个功能,在B类中覆盖虚拟的eventFilter方法,例如:
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);
}

然后使用installEventFilter开始监听一个A实例的事件,类似这样:

B::B(QWidget *parent) : QDialog(parent), ui(new Ui::B)
{
    a = new A();
    installEventFilter(a);
    a.show();
}

事件过滤器解决方案存在一个关键缺陷:在关闭 A 之前会调用 doSomething,并且如果 A 忽略事件并保持打开状态,则也会调用它。正确的方法是在 A 接受 closeEvent 时从 A 发出信号。(此外,隐藏和关闭之间存在差异;接受关闭将隐藏小部件)。 - peppe
@peppe,我不认为这是一个关键性的缺陷,因为它取决于doSomething做什么,以及实际小部件是否有拒绝关闭事件的代码。无论如何,解决这个问题的方法是通过事件循环调用doSomething,并测试a是否确实被隐藏(或删除...)。但还是很好的建议!我稍后会改进答案。 - hyde
@yakiang 在 peppe 的评论后进行了编辑,你可能想要检查一下。 - hyde

3
如果您无法使用模态窗口(或对话框),那么您需要更多的工作,您需要子类化QWidget(或QDialog或其他QWidget派生类,无论您需要什么)并重写QWidget :: closeEvent(对于类A),然后从这里调用父类的方法(我想a将是this的子类在您的代码中:a = new A(this); )或编写自己的信号/插槽。

如果您决定使用模态对话框,则代码变得更简单,请参见QDialog的文档中的示例代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接