我的Qt eventFilter()并没有停止事件

时间:2017-01-13 09:14:38

标签: c++ qt modal-dialog

我的eventFilter出现了根本性的错误,因为它让每个单个事件通过,而我想停止所有 。我已经在QEventeventFilter()等上阅读了大量文档,但显然我错过了一些重要内容。基本上,我试图基于QDialog为我的弹出窗口类创建自己的模态功能。我想实现自己的,因为内置的setModal(true)包含很多功能,例如玩QApplication::Beep(),我想要排除。 基本上,我想丢弃所有进入创建弹出窗口的QWidget(窗口)的事件。到目前为止我所拥有的是,

// popupdialog.h
#ifndef POPUPDIALOG_H
#define POPUPDIALOG_H

#include <QDialog>
#include <QString>

namespace Ui {class PopupDialog;}

class PopupDialog : public QDialog
{
   Q_OBJECT
public:
    explicit PopupDialog(QWidget *window=0, QString messageText="");
    ~PopupDialog();
private:
    Ui::PopupDialog *ui;
    QString messageText;
    QWidget window; // the window that caused/created the popup
    void mouseReleaseEvent(QMouseEvent*); // popup closes when clicked on
    bool eventFilter(QObject *, QEvent*);
};

...

// popupdialog.cpp
#include "popupdialog.h"
#include "ui_popupdialog.h"

PopupDialog::PopupDialog(QWidget *window, QString messageText) :
    QDialog(NULL), // parentless
    ui(new Ui::PopupDialog),
    messageText(messageText),
    window(window)
{
    ui->setupUi(this);
    setAttribute(Qt::WA_DeleteOnClose, true); // Prevents memory leak
    setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
    ui->message_text_display->setText(messageText);

    window->installEventFilter(this);

    //this->installEventFilter(window); // tried this also, just to be sure ..
}

PopupDialog::~PopupDialog()
{
    window->removeEventFilter(this);
    delete ui;
}

// popup closes when clicked on
void PopupDialog::mouseReleaseEvent(QMouseEvent *e)
{
    close();
}

问题在于,过滤器无法正常工作。请注意,如果我写一个std::cout  在if(...)内部,我发现每当事件发送到window时都会触发,它只是不会阻止它们。

bool PopupDialog::eventFilter(QObject *obj, QEvent *e)
{
    if( obj == window )
        return true; //should discard the signal (?)
    else
        return false; // I tried setting this to 'true' also without success
}

当用户与主程序交互时,可以像这样创建PopupDialog:

PopupDialog *popup_msg = new PopupDialog(ptr_to_source_window, "some text message");
popup_msg->show();
// I understand that naming the source 'window' might be a little confusing.
// I apologise for that. The source can in fact be any 'QWidget'.

其他一切都按预期工作。只有事件过滤器失败。 我希望过滤器删除发送到创建弹出窗口的窗口的事件;比如鼠标点击和按键,直到弹出窗口关闭。当有人在我的代码中指出一个微不足道的修复时,我期待非常尴尬。

3 个答案:

答案 0 :(得分:3)

您必须忽略window小部件树中到达的所有事件。因此,如果要过滤的对象是eventFilter的后代,则需要在应用程序范围内安装window并检查。换句话说:替换

window->installEventFilter(this);

通过

QCoreApplication::instance()->installEventFilter(this);

以这种方式实现事件过滤器功能:

bool PopupDialog::eventFilter(QObject *obj, QEvent *e)
{
    if ( !dynamic_cast<QInputEvent*>( event ) )
        return false;

    while ( obj != NULL )
    {
        if( obj == window )
            return true;
        obj = obj->parent();
    }
    return false;
}

我试了一下,测试了它,它对我有用。

注意:在我的体验中使用Qt中的事件过滤器有点混乱,因为它发生的事情并不十分透明。预计会不时出现错误。如果您和您的客户因灰色主窗口出现问题,您可以考虑禁用主窗口。

答案 1 :(得分:2)

在大量的反馈,反馈,建议和时间投入广泛的研究之后,我终于找到了我认为最优,最安全的解决方案。我希望向大家表达我真诚的感谢,感谢他们对Kuba Ober所描述的事情的帮助&#34;(...)并不像你想象的那么简单&#34;。

我们希望过滤掉小部件中的所有特定事件,包括其子级这很难,因为事件可能会在儿童的默认事件过滤器中被捕获并作出响应,之前它们被程序员实现的父自定义过滤器捕获并过滤。以下代码通过在创建所有子项时安装过滤器来解决此问题。 此示例假定使用Qt Creator UI表单,并基于以下博客文章: How to install eventfilters for all children

// The widget class (based on QMainWindow, but could be anything) for
// which you want to install the event filter on, includings its children

class WidgetClassToBeFiltered : public QMainWindow
{
    Q_OBJECT
public:
    explicit WidgetClassToBeFiltered(QWidget *parent = 0);
    ~WidgetClassToBeFiltered();
private:
    bool eventFilter(QObject*, QEvent*);
    Ui::WidgetClassToBeFiltered *ui;
};

...

WidgetClassToBeFiltered::WidgetClassToBeFiltered(QWidget *parent) :
    QMainWindow(parent), // Base Class constructor
    ui(new Ui::WidgetClassToBeFiltered)
{
    installEventFilter(this); // install filter BEFORE setupUI.
    ui->setupUi(this);
}

...

bool WidgetClassToBeFiltered::eventFilter(QObject *obj, QEvent* e)

{    
    if( e->type() == QEvent::ChildAdded ) // install eventfilter on children
    {
        QChildEvent *ce = static_cast<QChildEvent*>(e);
        ce->child()->installEventFilter(this);
    }
    else if( e->type() == QEvent::ChildRemoved ) // remove eventfilter from children
    {
        QChildEvent *ce = static_cast<QChildEvent*>(e);
        ce->child()->removeEventFilter(this);
    }
    else if( (e->type() == QEvent::MouseButtonRelease) ) // e.g. filter out Mouse Buttons Relases
    {

       // do whatever ..
       return true; // filter these events out
    }

    return QWidget::eventFilter( obj, e ); // apply default filter
}

注意这是有效的,因为eventfilter会在已添加的子项上自行安装!因此,它也应该在不使用UI表单的情况下工作。

答案 2 :(得分:0)

请参阅此代码以过滤掉特定事件: -

class MainWindow : public QMainWindow
{
public:
    MainWindow();

protected:
    bool eventFilter(QObject *obj, QEvent *ev);

private:
    QTextEdit *textEdit;
};

MainWindow::MainWindow()
{
    textEdit = new QTextEdit;
    setCentralWidget(textEdit);

    textEdit->installEventFilter(this);
}

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == textEdit) {
        if (event->type() == QEvent::KeyPress) {
            QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
            qDebug() << "Ate key press" << keyEvent->key();
            return true;
        } else {
            return false;
        }
    } else {
        // pass the event on to the parent class
        return QMainWindow::eventFilter(obj, event);
    }
}

如果要在多个小部件上设置更具体的事件过滤器,可以参考以下代码:

 class KeyPressEater : public QObject
 {
     Q_OBJECT

 protected:
     bool eventFilter(QObject *obj, QEvent *event);
 };

 bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
 {
     if (event->type() == QEvent::KeyPress) {
         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
         qDebug("Ate key press %d", keyEvent->key());
         return true;
     } else {
         // standard event processing
         return QObject::eventFilter(obj, event);
     }
 }


 KeyPressEater *keyPressEater = new KeyPressEater(this);
 QPushButton *pushButton = new QPushButton(this);
 pushButton->installEventFilter(keyPressEater);