Qt使用事件过滤器eventFilter进行(包括弹窗)窗口移动(原理+实现)

前言

由于在实际开发中,窗口自带标题栏不能满足我们的美观,就希望自行画窗口标题栏,但是去掉标题栏,窗体却无法移动,就需要自己实现窗体移动,这个时候就要重写鼠标事件。

常用的方法是重写控件的事件处理函数:如重写keyPressEvent(),mousePressEvent()和paintEvent(),这是最常用的事件处理方法,我们已经看到过很多这样的例子了。

还有一种方法是重写QObject::event(),在事件到达事件处理函数前处理它。

但是重写事件处理函数例如mousePressEvent,只能针对当前窗口改变事件动作,加入此时有个弹窗就无法满足进行移动。

这里介绍第三种方法:给QApplication对象安装事件过滤器,qApp(唯一的QApplication对象)安装了事件过滤器,程序中所有对象的每个事件在被送到任何其它事件过滤器之前都要送到eventFilter()函数中。

一、Qt中事件机制

事件主要分为两种:

  1. 在与用户交互时发生。比如按下鼠标(mousePressEvent),敲击键盘(keyPressEvent)等
  2. 系统自动发生,比如计时器事件(timerEvent)

它有如下特点:
3. 每种事件对应一个事件处理器,如鼠标移动对应mouseMoveEvent()
4. 在发生事件时(比如说上面说的按下鼠标),就会产生一个QEvent对象,这个QEvent对象会传给当前组件的event函数
5. Qt程序的main函数中需要创建一个QApplication对象,然后调用exec函数。这将令程序进入一个死循环,并不断监听应用程序的事件,发生事件时就生成一个QEvent对象。这称为事件循环

Qt系统在处理事件时,有一种机制叫事件传播机制。我们主要做的就是利用事件传播机制来进行过滤全局事件

二、实现

1.窗体设置自定义属性可移动
void CMainFrm::SetFrmAttribute()
{
    
    
    this->setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint);	//屏蔽标题栏
    this->setProperty("canMove", true);	//设置自定义属性可移动
}
2.自定义类获取App

头文件:

#ifndef CAPPINIT_H
#define CAPPINIT_H

#include <QObject>
#include <QApplication>
#include <QWidget>

class CAppInit : public QObject
{
    
    
    Q_OBJECT
public:
    static CAppInit* GetInstance();	//单例模式,需要用到锁
    ~CAppInit();

public:
    void startFilter();		//将该事件过滤器加入到QApplication中

protected:
    bool eventFilter(QObject *watched, QEvent *event) override;	//重写事件过滤器

private:
    explicit CAppInit(QObject *parent = nullptr);

private:
    static CAppInit*    m_pInstance;
    static QPoint       m_qMousePoint;		//记录当前位置
    static bool         m_bIsMousePressed;	//记录鼠标松放
};

#endif // CAPPINIT_H

cpp文件

#include "CAppInit.h"
#include <QMutex>
#include <QMutexLocker>
#include <QMouseEvent>

CAppInit* CAppInit::m_pInstance = NULL;
QPoint CAppInit::m_qMousePoint;
bool CAppInit::m_bIsMousePressed = false;

CAppInit* CAppInit::GetInstance()
{
    
    
    if(m_pInstance == NULL)
    {
    
    
        QMutex mutex;
        QMutexLocker locker(&mutex);
        m_pInstance = new CAppInit;
    }
}

CAppInit::CAppInit(QObject *parent) : QObject(parent)
{
    
    

}

CAppInit::~CAppInit()
{
    
    
    m_pInstance = NULL;
}

void CAppInit::startFilter()
{
    
    
    qApp->installEventFilter(this);	//qApp为QApplication宏,qt中只有一个qApp
}

bool CAppInit::eventFilter(QObject *watched, QEvent *event)
{
    
    
    QWidget* pWidget = qobject_cast<QWidget*>(watched); //转换事件对象
    if(!pWidget->property("canMove").toBool())          //自定义属性,判断是否需要移动,如不需要则将事件送出
        return QObject::eventFilter(watched, event);

    QMouseEvent* pEvent = static_cast<QMouseEvent *>(event);    //将事件转换成鼠标事件,进行鼠标事件过滤
    switch (pEvent->type())
    {
    
    
    case QEvent::MouseButtonPress:
        if(pEvent->button() == Qt::LeftButton)
        {
    
    
            m_bIsMousePressed = true;
            m_qMousePoint = pEvent->globalPos() - pWidget->pos();   //如果左键按下则记录当前鼠标在widget中位置
        }
        return true;
    case QEvent::MouseButtonRelease:
        m_bIsMousePressed = false;
        return true;
    case QEvent::MouseMove:
        if(m_bIsMousePressed && (pEvent->buttons() == Qt::LeftButton))
        {
    
    
            pWidget->move(pEvent->globalPos() - m_qMousePoint);
            return true;
        }
    default:
        break;
    }

    return QObject::eventFilter(watched, event);
}

使用时直接在main中调用CAppInit->GetInstance()->startFilter()即可。

扫描二维码关注公众号,回复: 13106100 查看本文章

猜你喜欢

转载自blog.csdn.net/qq_42956179/article/details/109614406