添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《 阿里云开发者社区用户服务协议 》和 《 阿里云开发者社区知识产权保护指引 》。如果您发现本社区中有涉嫌抄袭的内容,填写 侵权投诉表单 进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

事件分发函数

传递事件的通常方式是调用虚函数。例如,QMouseEvent通过调用QWidget::mousePressEvent()来传递。这个虚函数负责进行适当的响应,通常是处理鼠标点击,并发出对应的信号。如果在虚函数的实现中不执行所有必要的工作,则可能需要调用基类的实现。

如果希望替换基类的事件处理函数,则必须自己实现所有内容。但是,如果您只想扩展基类的功能,那么您可以实现您想要的功能,并调用基类来获得您不想处理的任何情况的默认行为。

有时,没有这样一个特定于事件的函数,或者特定于事件的函数是不够的。最常见的例子是按Tab键。通常情况下,QWidget会拦截这些来移动键盘焦点,但也有一些小部件本身需要Tab键。

这些对象可以重新实现QObject::event()(通用事件处理程序),并在通常处理之前或之后执行它们的事件处理,或者它们可以完全替换函数。一个非常不寻常的小部件既解释Tab又具有特定于应用程序的自定义事件,它可能包含以下event()函数:

bool Widget::event(QEvent* ev)override
    //如果是按键按下事件
    if (ev->type() == QEvent::Type::KeyPress)
        QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
        if (ke->key() == Qt::Key::Key_Tab)
            //对tab键按下进行处理
            qInfo() << "处理 tab 按键...";
            return true;    //返回true表示此事件已经处理了,不会继续传播
    // 调用父类实现处理其它的事件
    return QWidget::event(ev);

注意:对于所有未处理的情况,仍然调用QWidget::event(),并且返回值指示是否处理了事件;true值防止事件被发送到其他对象。返回true表示此事件已经处理了,不需要再处理了。

无边框窗口拖动

使用事件分发函数实现无边框窗口的拖动

bool event(QEvent* ev)override
    if (ev->type() == QEvent::MouseButtonPress)
        auto me = static_cast<QMouseEvent*>(ev);
        pressPos = me->pos();
        return true;
    else if (ev->type() == QEvent::MouseMove)
        auto me = static_cast<QMouseEvent*>(ev);
        //如果鼠标左键是按下的
        if(me->buttons() & Qt::MouseButton::LeftButton)
            this->move(me->globalPosition().toPoint() - pressPos);
        return true;
    return QWidget::event(ev);

自定义事件

发送事件的函数

许多应用程序都希望创建和发送它们自己的事件。通过构造合适的事件对象并使用QCoreApplication::sendEvent()和QCoreApplication::postEvent()发送事件,您可以以与Qt自己的事件循环完全相同的方式发送事件。

sendEvent()立即处理事件。当它返回时,事件过滤器和/或对象本身已经处理了该事件。对于许多事件类,都有一个名为isAccepted()的函数,它告诉您事件是被最后一个调用的处理程序接受还是拒绝的。

postEvent()将事件发送到队列中,以便稍后进行分派。下次Qt的主事件循环运行时,它会分发所有发布的事件,并进行一些优化。例如,如果有几个调整大小事件,它们将被压缩为一个。同样适用于绘制事件:QWidget::update()调用postEvent(),它通过避免多次重绘来消除闪烁并提高速度。

自定义事件

要创建自定义类型的事件,需要定义一个事件号,该事件号必须大于等于QEvent::User,并且可能需要继承QEvent的子类,以便传递关于自定义事件的特定信息。有关更多详细信息,请参阅QEvent文档。

系统定义的事件号

QEvent::Type 这个枚举类型定义了Qt中有效的事件类型。事件类型和每个类型的专门类如下:

值(value) QEvent::None 不是一个事件 QEvent::ActionAdded 一个新 action 被添加(QActionEvent) QEvent::ActionChanged 一个 action 被改变(QActionEvent) QEvent::ActionRemoved 一个 action 被移除(QActionEvent) QEvent::ActivationChange Widget 的顶层窗口激活状态发生了变化 QEvent::ApplicationActivate 这个枚举已被弃用,使用 ApplicationStateChange 代替 QEvent::ApplicationActivated ApplicationActivate 这个枚举已被弃用,使用 ApplicationStateChange 代替 QEvent::ApplicationDeactivate 这个枚举已被弃用,使用 ApplicationStateChange 代替 QEvent::ApplicationFontChange 应用程序的默认字体发生了变化 QEvent::ApplicationLayoutDirectionChange 应用程序的默认布局方向发生了变化 QEvent::ApplicationPaletteChange 应用程序的默认调色板发生了变化 QEvent::ApplicationStateChange 应用程序的状态发生了变化 QEvent::ApplicationWindowIconChange 应用程序的图标发生了变化 QEvent::ChildAdded 一个对象获得孩子(QChildEvent) QEvent::ChildPolished 一个部件的孩子被抛光(QChildEvent) QEvent::ChildRemoved 一个对象时区孩子(QChildEvent) QEvent::Clipboard 剪贴板的内容发生改变 QEvent::Close Widget 被关闭(QCloseEvent) QEvent::CloseSoftwareInputPanel 一个部件要关闭软件输入面板(SIP) QEvent::ContentsRectChange 部件内容区域的外边距发生改变 QEvent::ContextMenu 上下文弹出菜单(QContextMenuEvent) QEvent::CursorChange 部件的鼠标发生改变 QEvent::DeferredDelete 对象被清除后将被删除(QDeferredDeleteEvent) QEvent::DragEnter 在拖放操作期间鼠标进入窗口部件(QDragEnterEvent) QEvent::DragLeave 在拖放操作期间鼠标离开窗口部件(QDragLeaveEvent) QEvent::DragMove 拖放操作正在进行(QDragMoveEvent) QEvent::Drop 拖放操作完成(QDropEvent) QEvent::DynamicPropertyChange 动态属性已添加、更改或从对象中删除 QEvent::EnabledChange 部件的 enabled 状态已更改 QEvent::Enter 鼠标进入部件的边界(QEnterEvent) QEvent::EnterEditFocus 编辑部件获得焦点进行编辑,必须定义 QT_KEYPAD_NAVIGATION QEvent::EnterWhatsThisMode 当应用程序进入“What’s This?”模式,发送到 toplevel 顶层部件 QEvent::Expose 当其屏幕上的内容无效,发送到窗口,并需要从后台存储刷新 QEvent::FileOpen 文件打开请求(QFileOpenEvent) QEvent::FocusIn 部件或窗口获得键盘焦点(QFocusEvent) QEvent::FocusOut 部件或窗口失去键盘焦点(QFocusEvent) QEvent::FocusAboutToChange 部件或窗口焦点即将改变(QFocusEvent) QEvent::FontChange 部件的字体发生改变 QEvent::Gesture 触发了一个手势(QGestureEvent) QEvent::GestureOverride 触发了手势覆盖(QGestureEvent) QEvent::GrabKeyboard Item 获得键盘抓取(仅限 QGraphicsItem) QEvent::GrabMouse 项目获得鼠标抓取(仅限 QGraphicsItem) QEvent::GraphicsSceneContextMenu 在图形场景上的上下文弹出菜单(QGraphicsScene ContextMenuEvent) QEvent::GraphicsSceneDragEnter 在拖放操作期间,鼠标进入图形场景(QGraphicsSceneDragDropEvent) QEvent::GraphicsSceneDragLeave 在拖放操作期间鼠标离开图形场景(QGraphicsSceneDragDropEvent) QEvent::GraphicsSceneDragMove 在场景上正在进行拖放操作(QGraphicsSceneDragDropEvent) QEvent::GraphicsSceneDrop 在场景上完成拖放操作(QGraphicsSceneDragDropEvent) QEvent::GraphicsSceneHelp 用户请求图形场景的帮助(QHelpEvent) QEvent::GraphicsSceneHoverEnter 鼠标进入图形场景中的悬停项(QGraphicsSceneHoverEvent) QEvent::GraphicsSceneHoverLeave 鼠标离开图形场景中一个悬停项(QGraphicsSceneHoverEvent) QEvent::GraphicsSceneHoverMove 鼠标在图形场景中的悬停项内移动(QGraphicsSceneHoverEvent) QEvent::GraphicsSceneMouseDoubleClick 鼠标在图形场景中再次按下(双击)(QGraphicsSceneMouseEvent) QEvent::GraphicsSceneMouseMove 鼠标在图形场景中移动(QGraphicsSceneMouseEvent) QEvent::GraphicsSceneMousePress 鼠标在图形场景中按下(QGraphicsSceneMouseEvent) QEvent::GraphicsSceneMouseRelease 鼠标在图形场景中释放(QGraphicsSceneMouseEvent) QEvent::GraphicsSceneMove 部件被移动(QGraphicsSceneMoveEvent) QEvent::GraphicsSceneResize 部件已调整大小(QGraphicsSceneResizeEvent) QEvent::GraphicsSceneWheel 鼠标滚轮在图形场景中滚动(QGraphicsSceneWheelEvent) QEvent::Hide 部件被隐藏(QHideEvent) QEvent::HideToParent 子部件被隐藏(QHideEvent) QEvent::HoverEnter 鼠标进入悬停部件(QHoverEvent) QEvent::HoverLeave 鼠标留离开悬停部件(QHoverEvent) QEvent::HoverMove 鼠标在悬停部件内移动(QHoverEvent) QEvent::IconDrag 窗口的主图标被拖走(QIconDragEvent) QEvent::IconTextChange 部件的图标文本发生改变(已弃用) QEvent::InputMethod 正在使用输入法(QInputMethodEvent) QEvent::InputMethodQuery 输入法查询事件(QInputMethodQueryEvent) QEvent::KeyboardLayoutChange 键盘布局已更改 QEvent::KeyPress 键盘按下(QKeyEvent) QEvent::KeyRelease 键盘释放(QKeyEvent) QEvent::LanguageChange 应用程序翻译发生改变 QEvent::LayoutDirectionChange 布局的方向发生改变 QEvent::LayoutRequest 部件的布局需要重做 QEvent::Leave 鼠标离开部件的边界 QEvent::LeaveEditFocus 编辑部件失去编辑的焦点,必须定义 QT_KEYPAD_NAVIGATION QEvent::LeaveWhatsThisMode 当应用程序离开“What’s This?”模式,发送到顶层部件 QEvent::LocaleChange 系统区域设置发生改变 QEvent::NonClientAreaMouseButtonDblClick 鼠标双击发生在客户端区域外 QEvent::NonClientAreaMouseButtonPress 鼠标按钮按下发生在客户端区域外 QEvent::NonClientAreaMouseButtonRelease 鼠标按钮释放发生在客户端区域外 QEvent::NonClientAreaMouseMove 鼠标移动发生在客户区域外 QEvent::MacSizeChange 用户更改了部件的大小(仅限 OS X) QEvent::MetaCall 通过 QMetaObject::invokeMethod() 调用异步方法 QEvent::ModifiedChange 部件修改状态发生改变 QEvent::MouseButtonDblClick 鼠标再次按下(QMouseEvent) QEvent::MouseButtonPress 鼠标按下(QMouseEvent) QEvent::MouseButtonRelease 鼠标释放(QMouseEvent) QEvent::MouseMove 鼠标移动(QMouseEvent) QEvent::MouseTrackingChange 鼠标跟踪状态发生改变 QEvent::Move 部件的位置发生改变(QMoveEvent) QEvent::NativeGesture 系统检测到手势(QNativeGestureEvent) QEvent::OrientationChange 屏幕方向发生改变(QScreenOrientationChangeEvent) QEvent::Paint 需要屏幕更新(QPaintEvent) QEvent::PaletteChange 部件的调色板发生改变 QEvent::ParentAboutToChange 部件的 parent 将要更改 QEvent::ParentChange 部件的 parent 发生改变 QEvent::PlatformPanel 请求一个特定于平台的面板 QEvent::PlatformSurface 原生平台表面已创建或即将被销毁(QPlatformSurfaceEvent) QEvent::Polish 部件被抛光 QEvent::PolishRequest 部件应该被抛光 QEvent::QueryWhatsThis 如果部件有“What’s This?”帮助,应该接受事件 QEvent::ReadOnlyChange 部件的 read-only 状态发生改变 QEvent::RequestSoftwareInputPanel 部件想要打开软件输入面板(SIP) QEvent::Resize 部件的大小发生改变(QResizeEvent) QEvent::ScrollPrepare 对象需要填充它的几何信息(QScrollPrepareEvent) QEvent::Scroll 对象需要滚动到提供的位置(QScrollEvent) QEvent::Shortcut 快捷键处理(QShortcutEvent) QEvent::ShortcutOverride 按下按键,用于覆盖快捷键(QKeyEvent) QEvent::Show 部件显示在屏幕上(QShowEvent) QEvent::ShowToParent 子部件被显示 QEvent::SockAct Socket 激活,用于实现 QSocketNotifier QEvent::StateMachineSignal 信号被传递到状态机(QStateMachine::SignalEvent) QEvent::StateMachineWrapped 事件是一个包装器,用于包含另一个事件(QStateMachine::WrappedEvent) QEvent::StatusTip 状态提示请求(QStatusTipEvent) QEvent::StyleChange 部件的样式发生改变 QEvent::TabletMove Wacom 写字板移动(QTabletEvent) QEvent::TabletPress Wacom 写字板按下(QTabletEvent) QEvent::TabletRelease Wacom 写字板释放(QTabletEvent) QEvent::OkRequest Ok 按钮在装饰前被按下,仅支持 Windows CE QEvent::TabletEnterProximity Wacom 写字板进入接近事件(QTabletEvent),发送到 QApplication QEvent::TabletLeaveProximity Wacom 写字板离开接近事件(QTabletEvent),发送到 QApplication QEvent::ThreadChange 对象被移动到另一个线程。这是发送到此对象的最后一个事件在上一个线程中,参见:QObject::moveToThread() QEvent::Timer 定时器事件(QTimerEvent) QEvent::ToolBarChange 工具栏按钮在 OS X 上进行切换 QEvent::ToolTip 一个 tooltip 请求(QHelpEvent) QEvent::ToolTipChange 部件的 tooltip 发生改变 QEvent::TouchBegin 触摸屏或轨迹板事件序列的开始(QTouchEvent) QEvent::TouchCancel 取消触摸事件序列(QTouchEvent) QEvent::TouchEnd 触摸事件序列结束(QTouchEvent) QEvent::TouchUpdate 触摸屏事件(QTouchEvent) QEvent::UngrabKeyboard Item 失去键盘抓取(QGraphicsItem) QEvent::UngrabMouse Item 失去鼠标抓取(QGraphicsItem、QQuickItem) QEvent::UpdateLater 部件应该排队在以后重新绘制 QEvent::UpdateRequest 部件应该被重绘 QEvent::WhatsThis 部件应该显示“What’s This”帮助(QHelpEvent) QEvent::WhatsThisClicked 部件的“What’s This”帮助链接被点击 QEvent::Wheel 鼠标滚轮滚动(QWheelEvent) QEvent::WinEventAct 发生了 Windows 特定的激活事件 QEvent::WindowActivate 窗口已激活 QEvent::WindowBlocked 窗口被模态对话框阻塞 QEvent::WindowDeactivate 窗户被停用 QEvent::WindowIconChange 窗口的图标发生改变 QEvent::WindowStateChange 窗口的状态(最小化、最大化或全屏)发生改变(QWindowStateChangeEvent) QEvent::WindowTitleChange 窗口的标题发生改变 QEvent::WindowUnblocked 一个模态对话框退出后,窗口将不被阻塞 QEvent::WinIdChange 本地窗口的系统标识符发生改变 QEvent::ZOrderChange 部件的 z 值发生了改变,该事件不会发送给顶层窗口

用户事件的值应该介于 QEvent::User 和 QEvent::MaxUser之间。

为方便起见,可以使用 [static] int QEvent::registerEventType(int *hint* = -1) 函数来注册和存储一个自定义事件类型,这样做会避免意外地重用一个自定义事件类型。

自定义事件号

enum EventType 
    NumberEvent = QEvent::Type::User

自定义事件类

// 自定义事件类
class NumberChangeEvent : public QEvent
public:
    NumberChangeEvent(int number)
        : QEvent(QEvent::Type(NumberEvent))
        , m_number(number)
    ~NumberChangeEvent()
        qInfo() << __FUNCTION__;
    qint32 getNumber(){ return m_number; }
private:
    int m_number;

发送和处理事件

#include <QApplication>
#include <QWidget>
#include <QEvent>
#include <QPushButton>
// 自定义事件类型
enum EventType
    NumberEvent = QEvent::Type::User
// 自定义事件类
class NumberChangeEvent : public QEvent
public:
    NumberChangeEvent(int number)
        : QEvent(QEvent::Type(NumberEvent))
        , m_number(number)
    ~NumberChangeEvent()
        qInfo() << __FUNCTION__;
    qint32 getNumber(){ return m_number; }
private:
    int m_number;
// 自定义事件处理
class Widget : public QWidget
    Q_OBJECT
public:
    Widget(QWidget* parent = nullptr)
        :QWidget(parent)
        resize(640,480);
        m_btn = new QPushButton("发送自定义事件", this);
        // 点击按钮发送事件
        connect(m_btn, &QPushButton::clicked, this, [=](){
            static int i = 0;
            NumberChangeEvent ne(i++);
            //qApp->sendEvent(this, &ne);
            QApplication::sendEvent(this, &ne);
    ~Widget()
protected:
    bool event(QEvent* ev) override
        if(ev->type() == QEvent::Type(NumberEvent))
            auto nev = static_cast<NumberChangeEvent*>(ev);
            qInfo() << "Number: " << nev->getNumber();
        return QWidget::event(ev);
private:
    QPushButton* m_btn;
int main(int argc, char *argv[])
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
#include "main.moc"

除了使用事件分发函数event处理自定义事件外,也可以使用customEvent处理自定义事件,它是专门用来接收自定义事件。

void customEvent(QEvent* ev) override
    if(ev->type() == EventType::NumberEvent)
        auto nev = static_cast<NumberChangeEvent*>(ev);
        qInfo() << "Number: " << nev->getNumber();

sendEvent与postEvent的区别

使用栈区对象与堆区对象分别对两种发送方式进行测试

connect(m_btn, &QPushButton::clicked, this, [=](){
    NumberChangeEvent ne1(1);
    QApplication::sendEvent(this, &ne1);
    NumberChangeEvent ne2(1);
    QApplication::postEvent(this, &ne2);

当使用postEvent发送栈区事件对象时会直接引发中断,因为postEvent是将事件放入事件队列中稍后处理,栈区事件对象出作用域后内存就会被释放,我认为这是引发中断的原因。而使用sendEvent发送栈区事件对象不会中断,内存也会自己释放(出作用域后释放)。

当使用sendEvent发送堆区对象后,内存不会被自动释放,需要手动释放。而使用postEvent发送堆区事件对象内存会自动释放,不必手动释放。

因此,当使用栈区对象时只能使用sendEvent发送事件,使用堆区对象时,使用sendEvent发送事件之后需要手动释放事件对象内存,使用postEvent发送事件会自动释放内存。另外,使用postEvent发送事件时可以设置事件的优先级,让发送的事件被优先处理。

事件传播机制

事件传播的过程

Qt的事件产生之后,不是直接传递给了对象的,需要经过一系列的过程。

  • 事件首先由Qt的ServerApplication去接收来自于外部或内部的一些行为,鼠标点击,键盘输入,时钟事件等,分析并决定送往对应的对象去处理(内部管理机制),最后会调用[virtual] bool QCoreApplication::notify ( QObject * receiver, QEvent * event ) 去处理,当然这个是虚函数,你可以在子类去重新实现它 。

  • 在notify(…)中,在发给对应的接收者前,会先把消息送给QApplication。所以如果想在你界面的Widget前先处理那些事 件,那么你可以给QApplication对象installEventFilter,然后在对应的eventFilter()里先把这些事件都给过一 遍,然后你可以过滤一些不必要事件。

  • 如果QApplication没有处理那些事件,然后就是交给事件接收对象了。在这个对象接收前,也可以为这对象加一个事件过滤器,同样是 installEventFilter。

  • 如果eventFilter没有过滤某些事件,那么就会将事件交给接收者的event()函数(你可以根据不同类型的事件尽情处理),如果event事件在接受者处理后,也不会上传给父类的event,否则会上传进入父类的event。

  • 默认event()函数根据事件类型会调用不同的事件处理函数,类似mousePressEvent(),keyPressEvent()去分别处理他们。

  • 事件传播到父组件

    当控件忽略事件时,事件会继续往上传播,这里的传播是指传播给父组件

  • QEvent有 accept()函数 和ignore()函数:

  • accept():本组件处理该事件,这个事件就不会被继续传播给其父组件;

  • ignore():本组件不想要处理这个事件,这个事件会被继续传播给其父组件;

  • 值得注意的是所有的事件默认都是接受的(即不会传递到父组件)

  • 忽略和接受案例

  • 当默认接受事件时,在本组件处理之后,事件不会传递到父组件mousePressEvent处理。

    #include <QApplication>
    #include <QWidget>
    #include <QPushButton>
    #include <QMouseEvent>
    class Button : public QPushButton
    public:
        Button(const QString& text,QWidget* parent = nullptr)
            :QPushButton(text,parent)
        void mousePressEvent(QMouseEvent* ev)override
            qInfo() << __FUNCTION__;
            //默认是接受事件的,父组件不会收到鼠标点击事件
            ev->accept();
            //如果忽略事件,父组件就会收到鼠标点击事件
            //ev->ignore();
    class Widget : public QWidget
        Q_OBJECT
    public:
        Widget(QWidget* parent = nullptr)
            :QWidget(parent)
            resize(640,480);
            Button* btn = new Button("text", this);
        ~Widget()
    protected:
        void mousePressEvent(QMouseEvent* ev)override
            qInfo() << __FUNCTION__;
    int main(int argc, char *argv[])
        QApplication a(argc, argv);
        Widget w;
        w.show();
        return a.exec();
    #include "main.moc"
    

    当忽略事件时,在本组件处理之后,事件会被传递到父组件mousePressEvent处理。

    class Button : public QPushButton
    public:
        Button(const QString& text,QWidget* parent = nullptr)
            :QPushButton(text,parent)
        void mousePressEvent(QMouseEvent* ev)override
            qInfo() << __FUNCTION__;
            //默认是接受事件的,父组件不会收到鼠标点击事件
            //ev->accept();
            //如果忽略事件,父组件就会收到鼠标点击事件
            ev->ignore();
     
  • event事件分发函数返回值的作用
  • #include <QApplication>
    #include <QWidget>
    #include <QPushButton>
    #include <QMouseEvent>
    class Button : public QPushButton
    public:
        Button(const QString& text,QWidget* parent = nullptr)
            :QPushButton(text,parent)
        bool event(QEvent* ev)override
            if (ev->type() == QEvent::Type::MouseButtonPress)
                qInfo() << __FUNCTION__;
                return false;    //返回false,表示我没有处理这个事件,可以传递给父组件
                //return true;    //返回true,表示我处理了这个事件,不再继续传递了
            return QPushButton::event(ev);
    class Widget : public QWidget
        Q_OBJECT
    public:
        Widget(QWidget* parent = nullptr)
            :QWidget(parent)
            resize(640,480);
            Button* btn = new Button("text", this);
            Q_UNUSED(btn)
        ~Widget()
    protected:
        bool event(QEvent* ev)override
            if (ev->type() == QEvent::Type::MouseButtonPress)
                qInfo() << __FUNCTION__;
            return QWidget::event(ev);
    int main(int argc, char *argv[])
        QApplication a(argc, argv);
        Widget w;
        w.show();
        return a.exec();
    #include "main.moc"
    

    事件分发函数event返回false,表示本组件没有处理这个事件,事件会被传递到父组件处理,返回true则表示本组件处理了这个事件,不需要再让父组件处理了。

    鼠标单击事件与按钮单击信号的关联

    class Button : public QPushButton
    public:
        Button(const QString& text,QWidget* parent = nullptr)
            :QPushButton(text,parent)
        void mousePressEvent(QMouseEvent* ev) override
            //return QPushButton::mousePressEvent(ev);
    Button* btn = new Button("text", this);
    connect(btn, &Button::clicked, this, [](){
        qInfo() << "鼠标单击";
    

    要想触发鼠标点击的信号与槽就必须处理鼠标按下事件

    void mousePressEvent(QMouseEvent* ev) override
        return QPushButton::mousePressEvent(ev);
    

    有的时候,对象需要查看(可能还需要拦截)传递给另一个对象的事件。例如,对话框通常想要过滤一些小部件的按键;例如,修改返回键处理。

    QObject::installEventFilter()函数通过设置一个事件过滤器来实现这一点,使指定的过滤器对象在其QObject::eventFilter()函数中接收目标对象的事件。事件过滤器在目标对象处理事件之前处理事件,允许它根据需要检查和丢弃事件。可以使用QObject::removeEventFilter()函数删除现有的事件过滤器。

    当调用过滤器对象的eventFilter()实现时,它可以接受或拒绝事件,并允许或拒绝对事件的进一步处理。如果所有事件过滤器都允许对事件进行进一步处理(每个返回false),则将事件发送到目标对象本身。如果其中一个停止处理(通过返回true),目标和任何后面的事件过滤器都不会看到事件。

    无边框窗口拖动(事件过滤器实现)

    class Widget : public QWidget
        Q_OBJECT
    public:
        Widget(QWidget* parent = nullptr)
            :QWidget(parent)
            resize(640, 480);
            setWindowFlag(Qt::WindowType::FramelessWindowHint);
            setMouseTracking(true);
            // 安装事件过滤器
            this->installEventFilter(this);
        bool eventFilter(QObject* object, QEvent* ev)override
            auto w = qobject_cast<QWidget*>(object);
            if (!w)
                return false;
            if (ev->type() == QEvent::MouseButtonPress)
                auto me = static_cast<QMouseEvent*>(ev);
                pressPos = me->pos();
                return true;
            else if (ev->type() == QEvent::MouseMove)
                auto me = static_cast<QMouseEvent*>(ev);
                if (me->buttons() & Qt::MouseButton::LeftButton)
                    w->move(me->globalPosition().toPoint() - pressPos);
                // 已经处理了,不需要再处理MouseMoveEvent事件了
                 return true;
            return QObject::eventFilter(object, ev);
    private:
        QPoint pressPos;
    

    在自己类里面重写eventFilter来过滤事件,但是如果我有很多个无边框窗口,都要实现移动,这个方法就不太方便了。可以先写一个拖拽控件的过滤器对象,然后哪儿需要使用,就直接加载事件过滤器对象即可。

    // 无边框窗口拖拽的事件过滤器对象
    class DragWidgetFilter :public QObject
    public:
        DragWidgetFilter(QObject* parent = nullptr)
            :QObject(parent)
        bool eventFilter(QObject* object,QEvent*ev)override
            auto w = qobject_cast<QWidget*>(object);
            if (!w)
                return false;
            if (ev->type() == QEvent::MouseButtonPress)
                auto me = static_cast<QMouseEvent*>(ev);
                pressPos = me->pos();
            else if (ev->type() == QEvent::MouseMove)
                auto me = static_cast<QMouseEvent*>(ev);
                if (me->buttons() & Qt::MouseButton::LeftButton)
                    w->move(me->globalPosition().toPoint() - pressPos);
            return QObject::eventFilter(object,ev);
    private:
        QPoint pressPos;
    class Widget : public QWidget
        Q_OBJECT
    public:
        Widget(QWidget* parent = nullptr)
            :QWidget(parent)
            resize(640, 480);
            setWindowFlag(Qt::WindowType::FramelessWindowHint);
            setMouseTracking(true);
            // 使用事件过滤器对象加载事件过滤器
            this->installEventFilter(new DragWidgetFilter(this));
    

    事件跟信号的区别