自学内容网 自学内容网

Qt3学习使用事件过滤器(二)

上一篇我们根据实际使用提出了事件过滤器的需求,那么现在就部署吧,首先

#include "mycapture.h"
#include <qapplication.h>
#include <qpainter.h>
#include <qstring.h>
#include <qtimer.h>
#include <chrono>


ScreenCaptureWidget::ScreenCaptureWidget(QWidget* parent) : QWidget(parent)
{
    // 确保接收鼠标事件
    setMouseTracking(true);
    m_lastClickTime = 0;
    m_clickCount = 0;
    m_lastRightClickTime = 0;
    m_rightClickCount = 0;
      m_clicknum = 0;
}


bool ScreenCaptureWidget::eventFilter(QObject *o, QEvent *e)
{
    if (!o ||!e)
        return true;
    

    if (o == this && e->type() == QEvent::MouseButtonPress)
    {
        
        int interval = QApplication::doubleClickInterval();
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(e);
        if (mouseEvent->button() == Qt::LeftButton)
        {
            
            auto currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
            long long atime = currentTime - static_cast<long long>(m_lastClickTime);
            if (currentTime - static_cast<long long>(m_lastClickTime) < static_cast<long long>(QApplication::doubleClickInterval()))
            {
                qDebug("the mouse default interval (%d).", QApplication::doubleClickInterval());
                m_clickCount++;
            }
            else
            {
                m_clickCount = 1;
            }
            m_lastClickTime = currentTime;
            m_clicknum++;
            qDebug("The mouse two interval (%lld) It is the (%d) times.", atime, m_clicknum); 
            if (m_clickCount == 1)
            {
                QTimer::singleShot(QApplication::doubleClickInterval(), this, SLOT(handleSingleClick()));
            }
            else if (m_clickCount == 2)
            {
                handleDoubleClick(mouseEvent);
                m_clickCount = 0;
                return true;
            }
        }
        else if (mouseEvent->button() == Qt::RightButton)
        {
            auto currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
            if (currentTime - m_lastRightClickTime < QApplication::doubleClickInterval())
            {
                m_rightClickCount++;
            }
            else
            {
                m_rightClickCount = 1;
            }
            m_lastRightClickTime = currentTime;


            if (m_rightClickCount == 1)
            {
                QTimer::singleShot(QApplication::doubleClickInterval(), this, SLOT(handleSingleRightClick()));
            }
            else if (m_rightClickCount == 2)
            {
                handleDoubleRightClick(mouseEvent);
                m_rightClickCount = 0;
                return true;
            }
        }
    }
    return QWidget::eventFilter(o, e);
}


void ScreenCaptureWidget::handleSingleClick()
{
    qDebug("Single click detected.");
    // 取消定时器,防止误触发双击事件
    QTimer::singleShot(0, this, SLOT(cancelClickCount()));
}



void ScreenCaptureWidget::handleDoubleClick(QMouseEvent *event)
{
    qDebug("Double click detected at (%d, %d).", event->x(), event->y());
}


void ScreenCaptureWidget::handleSingleRightClick()
{
    qDebug("Single right click detected.");
    QTimer::singleShot(0, this, SLOT(cancelRightClickCount()));
}


void ScreenCaptureWidget::handleDoubleRightClick(QMouseEvent *event)
{
    qDebug("Double right click detected at (%d, %d).", event->x(), event->y());
}


void ScreenCaptureWidget::cancelClickCount()
{
    m_clickCount = 0;
}


void ScreenCaptureWidget::cancelRightClickCount()
{
    m_rightClickCount = 0;
}


void ScreenCaptureWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawText(10, 10, "Hello, World!");
    painter.end();
}

以上代码实现了在当前窗口根据不同鼠标点击触发事件,并设置了相应的槽函数,注意其中的时间判断需要使用chrono库,由于qt3没有 qint64,那么尝试改成int行不行了,不行,因为计算时间间隔是按毫秒来计算的(鼠标双击事件都是毫秒级200-900ms)这就会导致计算出来的时间最小分辨率是1s不满足要求,使用int也会导致utc时间越界的问题,所以必须使用long long,还需要注意的是 qDebug("The mouse two interval (%lld) It is the (%d) times.", atime, m_clicknum); ,这里面lld是对这个long long类型的调用,如果写成d程序也能运行,但是后面那个d就显示不出来了,属于不容易发现的问题

qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
                if (currentTime - m_lastClickTime < QApplication::doubleClickInterval())

,以上代码实现了对指定窗口应用了事件过滤器,要生效需要使用如下:

#include <qapplication.h>
#include "mycapture.h"


int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    ScreenCaptureWidget widget;
widget.resize(350,260);
    widget.show();
    // 为 widget 安装事件过滤器
    widget.installEventFilter(&widget);
 QObject::connect(&widget, SIGNAL(clickedOnExitArea()), &app, SLOT(quit()));
    QObject::connect( &app, SIGNAL(lastWindowClosed()),
                       &app, SLOT(quit()) );
    return app.exec();
}

可以看到有一条对自己注册了事件过滤器,代码耦合度很高,那么,可不可以将事件过滤器提出出来,降低耦合度呢,widget.installEventFilter(&widget);这样的调用明显在类自己内部完成,以下是修改代码,包装成独立的类

#include "GlobalKeyFilter.h"


GlobalKeyFilter::GlobalKeyFilter(QObject *parent) : QObject(parent)
{
}


bool GlobalKeyFilter::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress)
    {
        QKeyEvent *keyEvent = (QKeyEvent *)event;
        auto it = keyCallbacks.find(keyEvent->key());
        if (it!= keyCallbacks.end())
        {
            it->second();
            return true;
        }
    }
    return QObject::eventFilter(obj, event);
}


void GlobalKeyFilter::setKeyCallback(Qt::Key key, std::function<void()> callback)
{
    keyCallbacks[key] = callback;
}


//GlobalKeyFilter.h"文件内容如下
#ifndef GLOBALKEYFILTER_H
#define GLOBALKEYFILTER_H

#include <qobject.h>
#include <qevent.h>
#include <iostream>
#include <map>
#include <functional>

class GlobalKeyFilter : public QObject
{
    Q_OBJECT
public:
    explicit GlobalKeyFilter(QObject *parent = nullptr);
    bool eventFilter(QObject *obj, QEvent *event);

    // 设置特定按键组合的回调函数
    void setKeyCallback(Qt::Key key, std::function<void()> callback);

private:
    // 存储按键和对应回调函数的映射
    std::map<Qt::Key, std::function<void()>> keyCallbacks;
};

#endif // GLOBALKEYFILTER_H










好的,GlobalKeyFilter globalFilter;
    app.installEventFilter(&globalFilter);
    globalFilter.setKeyCallback(Qt::Key_Escape, handleEsc);运行一下,报错:原因如下: 无法从“int”转换为“const Qt::Key”
.\MouseEventFilter.cpp(12): note: 转换为枚举类型需要显式强制转换(static_cast、C 样式强制转换或带圆括号函数样式强制转换)
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.42.34433\include\xtree(1397): note: 或    “std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<const Qt::Key,std::function<void (void)>>>>> std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::find(const _Other &) const”

修改方案

  1. 将 keyEvent->key() 的结果显式转换为 Qt::Key 类型,使用 static_cast<Qt::Key>(keyEvent->key())
     

代码如下:

#include "MouseEventFilter.h"

GlobalKeyFilter::GlobalKeyFilter(QObject *parent) : QObject(parent)
{
}

bool GlobalKeyFilter::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress)
    {
       QKeyEvent *keyEvent = (QKeyEvent *)event;
        Qt::Key key = static_cast<Qt::Key>(keyEvent->key());
        auto it = keyCallbacks.find(key);
        if (it!= keyCallbacks.end())
        {
            it->second();
            return true;
        }
    }
    return QObject::eventFilter(obj, event);
}

void GlobalKeyFilter::setKeyCallback(Qt::Key key, std::function<void()> callback)
{
    keyCallbacks[key] = callback;
}

 main.cpp调用和回调函数:

#include <qapplication.h>
#include "MouseEventFilter.h"
#include "capture.h"
void handleEsc()
{
    qDebug( "Esc key pressed globally.");
}

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    ScreenCaptureWidget widget;
    widget.show();
GlobalKeyFilter globalFilter;
    app.installEventFilter(&globalFilter);
    globalFilter.setKeyCallback(Qt::Key_Escape, handleEsc);

QObject::connect(&app, SIGNAL(lastWindowClosed()),
        &app, SLOT(quit()));
    return app.exec();
}

这样就行了


原文地址:https://blog.csdn.net/fleetstar/article/details/145227481

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!