• 【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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109



    测试代码与运行结果:

    与鼠标相关的部分枚举量:
    • 单击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(""))
            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())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    运行结果




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

  • 相关阅读:
    Docker第四天作业
    mybatis.spring.MyBatisSystemException【bug】
    基于深度学习的加密恶意流量检测
    PCL点云处理之基于FPFH特征的全局配准流程具体实现(二百二十一)
    智能家居的四种无线技术
    自动化测试平台设计与实现详解
    穿山甲广告位生效时间,广告位不合法,广告位错误等
    Java架构师内功嵌入式技术
    【MapGIS精品教程】002:GDB本地数据库的使用
    卷积神经网络
  • 原文地址:https://blog.csdn.net/weixin_44733774/article/details/134349820