【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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/133878.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TMUX命令的基本操作和使用

tmux&#xff1a;是两个单词的缩写&#xff0c;即“Terminal MultipleXer”&#xff0c;意思是“终端复用器”。 TMUX使用场景&#xff1a;假如你需要跑大模型或者数据集特别大的AI任务时&#xff0c;它往往需要花较长时间才能跑完&#xff0c;在跑的过程中&#xff0c;不能断…

用朴素贝叶斯实现垃圾邮箱分类实验报告

一、实验目的 1.会用Python创建朴素贝叶斯模型 2.使用朴素贝叶斯模型对垃圾邮件分类 3.会把文本内容变成向量 4.会用评价朴素贝叶斯模型的分类效果 二、设备与环境 Jupyter notebook Python3.9 三、实验原理 四、实验内容 1.把给定的数据集message.csv拆分成训练集和测试集&…

LeetCode【207】课程表

题目&#xff1a; 思路&#xff1a; https://www.jianshu.com/p/25868371ddfc/ 代码&#xff1a; public boolean canFinish(int numCourses, int[][] prerequisites) {// 入度int[] indegress new int[numCourses];// 每个点对应的边,出边Map<Integer, List<Intege…

upload 文件自动上传写法,前后端 下载流文件流

<el-uploadv-model:file-list"fileList":action"app.api/student/student/import":headers"{// Content-Type: multipart/form-data;boundary----split-boundary, 此处切记不要加&#xff0c;否则会造成后端报错 Required request part file is…

Python编程:从入门到实践 (项目3—Web应用程序—学习问题汇总)(新手避坑必看)

本人系统环境&#xff1a; WIN10系统 Python 3.9 Django 2.1.5 书本环境&#xff1a; Python 3.x Django 1.8.5 基于Django 开发一个名为“学习笔记”的项目&#xff0c;这是一个在线的日志系统&#xff0c;能够记录所学习的有关特定主题的知识。 建立项目 要编写一个名为“…

第十周学习记录

阅读MARS MARS创新点&#xff1a; (1)实例感知。模拟器使用独立的网络分别对前景实例和背景环境进行建模&#xff0c;以便可以单独控制实例的静态&#xff08;例如大小和外观&#xff09;和动态&#xff08;例如轨迹&#xff09;属性。 (2)模块化。模拟器允许在不同的 NeRF 主干…

补坑:Java的字符串String类(3):再谈String

不太熟悉字符串的可以看看这两篇文章 补坑&#xff1a;Java的字符串String类&#xff08;1&#xff09;-CSDN博客 补坑&#xff1a;Java的字符串String类&#xff08;2&#xff09;&#xff1a;一些OJ题目-CSDN博客 字符串创建对象 public static void main(String[] args) …

compile: version “go1.19“ does not match go tool version “go1.18.1“

** 1 安装了新版本的go后 为什么go version 还是旧版本&#xff1f; ** 如果你已经按照上述步骤安装了新版本的 Go&#xff0c;但 go version 命令仍然显示旧版本&#xff0c;可能是因为你的环境变量设置不正确或未正确生效。你可以尝试以下方法来解决问题&#xff1a; 重新…

YOLOV5改进:RefConv | 即插即用重参数化重聚焦卷积替代常规卷积,无额外推理成本下涨点明显

1.该文章属于YOLOV5/YOLOV7/YOLOV8改进专栏,包含大量的改进方式,主要以2023年的最新文章和2022年的文章提出改进方式。 2.提供更加详细的改进方法,如将注意力机制添加到网络的不同位置,便于做实验,也可以当做论文的创新点 3.涨点效果:RefConv,实现有效涨点! 论文地址 …

优雅关闭TCP的函数shutdown效果展示

《TCP关闭的两种方法概述》里边理论基础&#xff0c;下边是列出代码&#xff0c;并且进行实验。 服务端代码graceserver.c的内容如下&#xff1a; #include "lib/common.h"static int count;static void sig_int(int signo) {printf("\nreceived %d datagrams\…

nature日报:为什么印度德里现在的空气污染如此严重?

为什么印度德里现在的空气污染如此严重&#xff1f; 后季风季节为印度大城市的空气污染积累创造了理想的条件。 本文整理扩展自2023年11月10日nature杂志的NEWS EXPLAINER——Why is Delhi’s air pollution so bad right now? (nature.com) Highlights 季风期间&#xff0…

经典与现代:燃木壁炉的家居装饰灵感

燃木壁炉已经成为许多家庭的温馨选择&#xff0c;但在选择时需要考虑一些要点&#xff0c;以确保它适合你的家。让我们用通俗易懂的你们看看如何选择最适合你的燃木壁炉。 首先&#xff0c;考虑你喜欢的风格。燃木壁炉有各种设计&#xff0c;从古老传统到现代时尚都有。如果你…

Centos7安装PostgreSQL 14

环境&#xff1a; Centos7安装PostgreSQL_14版本数据库&#xff1b; 打开官方网站&#xff1a;PostgreSQL: Linux downloads (Red Hat family) 一、 版本选择 复制、粘贴并运行如下脚本&#xff1a; 二、安装步骤 这些命令是在 CentOS 7.x 系统上安装和配置 PostgreSQL 14 的步…

Install Nginx in Linux

Nginx是一款轻量级的Web服务器、反向代理服务器&#xff0c;由于它的内存占用少&#xff0c;启动极快&#xff0c;高并发能力强&#xff0c;在互联网项目中广泛应用。 1.yum 安装 nginx [rootVM-8-7-centos nginx]# yum install -y nginx Loaded plugins: fastestmirror, lang…

经典猜数游戏(python类封装)

五次机会猜测100以内随机正整数&#xff0c;我用初通的python类封装了代码并清屏上一次猜测提示&#xff0c;难有所增加咯。 (笔记模板由python脚本于2023年11月09日 12:31:30创建&#xff0c;本篇笔记适合掌握python循环和条件分支语句用法&#xff0c;初通python类的coder翻阅…

为什么审计平台不适合进行数据库变更管理?

关于视源电子 广州视源电子科技股份有限公司 (CVTE) 成立于 2005 年 12 月&#xff0c;旗下拥有多家业务子公司。 截至 2022 年底&#xff0c;公司总人数超 6000 人&#xff0c;约 60% 为技术人员。公司的主营业务为液晶显示主控板卡和交互智能平板等显控产品的设计、研发与销…

Shopee收款账户怎么设置?shopee收款方式选哪种

Shopee作为一家领先的电子商务平台&#xff0c;为卖家提供了多种收款方式。无论是在线支付、虚拟账户余额还是线下支付&#xff0c;卖家可以根据自己的需求和交易情况来进行选择。然而&#xff0c;在选择收款方式时&#xff0c;安全性、便捷性和市场适应性是需要考虑虾皮Shopee…

ChatGLM3 langchain_demo 代码解析

ChatGLM3 langchain_demo 代码解析 0. 背景1. 项目代码结构2. 代码解析2-1. utils.py2-2. ChatGLM3.py2-3. Tool/Calculator.py2-4. Tool/Weather.py2-5. main.py 0. 背景 学习 ChatGLM3 的项目内容&#xff0c;过程中使用 AI 代码工具&#xff0c;对代码进行解释&#xff0c;…

SpringMvc 常见面试题

1、SpringMvc概述 1.1、什么是Spring MVC &#xff1f;简单介绍下你对springMVC的理解? Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架&#xff0c;通过把Model&#xff0c;View&#xff0c;Controller分离&#xff0c;将web层进行职责解耦&am…

C++算法:矩阵中的最长递增路径

涉及知识点 拓扑排序 题目 给定一个 m x n 整数矩阵 matrix &#xff0c;找出其中 最长递增路径 的长度。 对于每个单元格&#xff0c;你可以往上&#xff0c;下&#xff0c;左&#xff0c;右四个方向移动。 你 不能 在 对角线 方向上移动或移动到 边界外&#xff08;即不允…