【PyQt】(自制类)处理鼠标点击逻辑

写了个自认为还算不错的类,用于简化mousePressEventmouseMoveEventmouseReleaseEvent中的鼠标信息。

功能有以下几点:
  • 鼠标当前状态,包括鼠标左/中/右键和单击/双击/抬起
  • 鼠标防抖(仅超出一定程度时才判断鼠标发生了移动),灵敏度可设置;
  • 鼠标长按(在鼠标长按并且未发生移动时触发),时长可设置;
  • 鼠标双击(两次点击的时间间隔足够小时判断为双击),时长可设置;
  • 鼠标偏移量,仅鼠标按下时有效,可返回自点击时的总偏移量,也可返回与上次鼠标事件之间的相对偏移量
补充:

这个自制类在多键按下时会产生歧义,也就是没法处理有如刁难一般的操作,像是右键拖拽然后左键来添乱之类的。本来是想再重新写份代码以填补这个缺陷的,但想想就有点怪,什么场合下才需要满足这种怪异的操作。




自制类XJ_MouseStatus

#XJ_MouseStatus.py
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import QPoint,Qt,QObject
from PyQt5.QtGui import QMouseEvent

__all__=['XJ_MouseStatus']
class XJ_MouseStatus(QObject):#mousePressEvent、mouseMoveEvent和mouseReleaseEvent特供。只处理单键(多键行为请在外部代码控制)
    longClick=pyqtSignal()#鼠标原地不动长按时触发

    __antiJitter=5#防抖,当鼠标点击位置与鼠标当前位置的曼哈顿距离不超过该值时仍将鼠标视为不动状态
    __doubleClickInterval=500#双击间隔(ms)
    __longPressInterval=500#长按间隔(ms)
    __record={
        'lastPress':None,#上一次按下时的信息
        'lastMouse':None,#上一次的鼠标信息
        'currMouse':None,#当前鼠标信息
        }
    __press=[QMouseEvent.MouseButtonRelease,QMouseEvent.MouseButtonPress,QMouseEvent.MouseButtonDblClick]#偷懒用的
    __move=False#用于判断是否长按
    __timerID=0#鼠标按下时对应的定时器
    class __Data:
        pos=None#鼠标位置
        btn=None#鼠标按键(左中右)
        pressStatus=None#鼠标当前按下状态(单双击/抬起)
        timeStamp=None#鼠标事件时间刻
        def __init__(self,event):
            self.pos=event.globalPos()#这里不用pos是为了防暴毙
            self.btn=event.button()
            self.pressStatus=event.MouseButtonRelease
            self.timeStamp=event.timestamp()

    def __init__(self,*arg):
        super().__init__(*arg)
        record=self.__record.copy()
        fakeEvent=QMouseEvent(QMouseEvent.MouseButtonRelease,QPoint(0,0),Qt.NoButton,Qt.NoButton,Qt.NoModifier)
        data=self.__Data(fakeEvent)
        data.timeStamp-=self.__doubleClickInterval#小防,避免开局单击时触发双击行为
        record['lastMouse']=data
        record['currMouse']=data
        record['lastPress']=data
        self.__record=record
    def timerEvent(self,event):
        record=self.__record
        press=self.__press
        tId=event.timerId()
        cId=self.__timerID
        self.killTimer(event.timerId())
        if(cId==tId):#当前定时器
            if(not self.__move and record['currMouse'].pressStatus!=press[0]):#未发生移动,未抬起鼠标,触发长按信号
                self.longClick.emit()

    def Set_DoubleClickInterval(self,interval):#设置双击时间间隔(ms)
        self.__doubleClickInterval=interval
    def Set_LongPressInterval(self,interval):#设置长按时间间隔(ms)
        self.__longPressInterval=interval
    def Set_AntiJitter(self,val):#设置防抖值
        self.__antiJitter=val if val>0 else 0

    def Get_Position(self):#返回鼠标坐标。是屏幕坐标(global),需要使用QWidget.mapFromGlobal(QPoint)自行转换为控件相对坐标
        return self.__record['currMouse'].pos
    def Get_PressButtonStatus(self):#返回当前鼠标的键(左中右)以及按下状态(单击/双击/抬起)
        return self.__record['currMouse'].btn,self.__record['currMouse'].pressStatus
    def Get_MoveDelta(self,total=True,strict=True):#返回鼠标移动量(仅鼠标按下时有效),为QPoint对象
        press=self.__press
        record=self.__record
        data_curr=record['currMouse']
        if(data_curr.pressStatus!=press[0]):#说明鼠标按下
            if(not strict or self.__move):#严格模式下,仅判定发生移动时计算移动量
                p1=record['currMouse'].pos
                if(total):
                    p2=record['lastPress'].pos
                else:
                    p2=record['lastMouse'].pos
                return QPoint(p1.x()-p2.x(),p1.y()-p2.y())
        return QPoint(0,0)
    def Get_HasMoved(self):#判断是否发生移动(毕竟用Get_MoveDelta来判断移动的发生是有点麻烦,还不如多一个函数
        return self.__move

    def Opt_Update(self,event):#更新状态
        press=self.__press
        record=self.__record
        data_curr=self.__Data(event)
        if(event.type()==press[1] or event.type()==press[2]):#单/双击
            self.__move=False
            data_old=record['lastPress']
            data_curr.pressStatus=press[1]
            if(data_old.btn==data_curr.btn):#同键位按下
                if(data_curr.timeStamp-data_old.timeStamp<self.__doubleClickInterval):#在时间间隔内
                    if(data_old.pressStatus!=press[2]):#没有双击过
                        data_curr.pressStatus=press[2]#双击
            record['lastPress']=data_curr
            record['lastMouse']=data_curr
            record['currMouse']=data_curr
            self.__timerID=self.startTimer(self.__longPressInterval)
        else:#移动/抬起
            data_curr.btn=event.buttons()
            data_curr.pressStatus=record['lastMouse'].pressStatus
            if(event.type()==press[0]):#抬起
                if(data_curr.btn==Qt.NoButton):#确保无按键按下时设置为Release
                    data_curr.pressStatus=press[0]
                    data_curr.btn=event.button()
            else:#移动(QMouseEvent.MouseMove)
                if(data_curr.pressStatus!=press[0] and not self.__move):#判断有无发生拖拽
                    delta=self.Get_MoveDelta(strict=False)
                    if(abs(delta.x())+abs(delta.y())>self.__antiJitter):
                        self.__move=True
                        record['currMouse'].pos=record['lastPress'].pos
            record['lastMouse']=record['currMouse']
            record['currMouse']=data_curr



测试代码与运行结果:

与鼠标相关的部分枚举量:
  • 单击QMouseEvent.MouseButtonPress
  • 双击QMouseEvent.MouseButtonDblClick
  • 抬起QMouseEvent.MouseButtonRelease
  • 左键Qt.LeftButton
  • 中键Qt.MidButton
  • 右键Qt.RightButton
#Main.py
import sys
from PyQt5.QtWidgets import QApplication,QWidget
from XJ_MouseStatus import *

class Test(QWidget):
    __mouseStatus=None
    def __init__(self,*arg):
        super().__init__(*arg)
        ms=XJ_MouseStatus()
        ms.longClick.connect(lambda:print("<LongClick!>"))
        self.__mouseStatus=ms
    def __EasyPrint(self):
        press={
            QMouseEvent.MouseButtonRelease:"Release",
            QMouseEvent.MouseButtonPress:"Press",
            QMouseEvent.MouseButtonDblClick:"DblClick",}
        button={
            Qt.LeftButton:'Left',
            Qt.MidButton:'Middle',
            Qt.RightButton:'Right',}
        tPoint=lambda point:(point.x(),point.y())
        tBtn=lambda btn:[button[key] for key in button if key&btn]
        tBtnStatus=lambda status:(tBtn(status[0]),press[status[1]])

        ms=self.__mouseStatus
        pos=tPoint(self.mapFromGlobal(ms.Get_Position()))
        moveDelta=tPoint(ms.Get_MoveDelta())
        btnStatus=tBtnStatus(ms.Get_PressButtonStatus())
        print(f'pos{pos},\tdelta{moveDelta},\t{btnStatus[0]}-{btnStatus[1]}')
        if(btnStatus[1]=='Release'):
            print()
    def mousePressEvent(self,event):
        self.__mouseStatus.Opt_Update(event)
        self.__EasyPrint()
    def mouseMoveEvent(self,event):
        self.__mouseStatus.Opt_Update(event)
        self.__EasyPrint()
    def mouseReleaseEvent(self,event):
        self.__mouseStatus.Opt_Update(event)
        self.__EasyPrint()


if __name__=='__main__':
    app = QApplication(sys.argv)

    t=Test()
    t.show()

    sys.exit(app.exec())

运行结果




本文发布于CSDN,未经个人允许不得私自转载:https://blog.csdn.net/weixin_44733774/article/details/134349820

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值