IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> 【Python】MouseBoard-以键盘进行鼠标微操 -> 正文阅读

[Python知识库]【Python】MouseBoard-以键盘进行鼠标微操

动机

很多人都会有一种经历,就是自己在截图的时候,就差一点点,但是因为鼠标移动太快,迟迟把握不住距离。又比如ps,ppt的时候,要把一个东西放到合适的位置,又是尴尬的因为速度太快而放不到地方。

从人机交互的角度理解,就是计算机不能够充分理解人类的意图。那么有两种解决方式,一是用自动化的方式,通过一种规则计算让计算机做出我们期望的应对。二是创造一种方式,给计算机传递合理的信息。

设计思路

手动微调

这个思路相比于纯自动是可以轻易实现的。

使用键盘输入上下左右,进行简单移动。实际上在ps和ppt位置放置里是有这个功能的,点一下可以通过上下左右移动。但是这个只是对图形进行作用,而不是传递到鼠标上的,在截图的时候就会很难受,我们需要的是在激活一种模式的情况下,将上下左右直接投射到鼠标上。

创建一个键盘监听,监控上下左右,对应微微移动鼠标即可。

调整逻辑

我们的调正并不是乱调,这会干扰其他鼠标操作,要搞清楚什么时候应该微调。仔细思考,我们最需要微调的时候其实只有按住鼠标左键的时候,所以逻辑也就很直观:如果鼠标处在按下状态,鼠标对我们的四个方向键才会有响应。

补充,键盘按下一个键是会连续输入的,所以可以直接实现按住快速滑动的效果。

你这时可能会反驳,其他情况下也会有需要啊,这就需要另一个激活机制了,这就是双击caps_lock键,同样可以激活,这样就可以同时使用方向键和鼠标移动了,在没有按下左键的情况下。

我还做了辅助按键,因为有时候上下左右是会有副作用的,这时就可以用wsad这类键去实现,这两个辅助键灵活使用有奇效。不激活也不会影响到正常输入。

代码

from pynput import mouse
from pynput import keyboard
from pynput.keyboard import Key, KeyCode
from pynput.mouse import Button
import time


class MouseBoard:
    def __init__(self):
        """
        本程序实现从键盘到鼠标移动的映射,可以实现当前电脑最小粒度微调
        本程序专注于实现:移动
        设有两个激活条件:双击caps_lock或者按下鼠标左键
        一个退出激活条件:松开鼠标左键
        当处于激活条件:可以使用辅助键或者上下左右实现移动,分别适用于非输入与输入情况
        辅助键:默认edsf为上下左右,可以通过修改config中的四个键来实现自定义
        不设界面,一个退出条件:esc键
        """
        # 设置默认参数
        self.mouse_ctrl = mouse.Controller()
        self.move_activate = False  # 移动状态
        self.activate_key = Key.caps_lock  # 激活移动模式的键
        self.pre_time = time.time()  # 用于测试时间间隔
        self.time_threshold = 0.3  # 双击时间阈值
        self.dir_keys = []  # 控制上下左右的辅助键

        # 读取文件实现自定义辅助键位,如果读取失败或者配置异常就用默认辅助
        try:
            with open('config.txt', encoding='utf-8') as file_obj:
                line = file_obj.readline().strip()[0:4]
                if line != '':  # 正常情况
                    for i in range(4):
                        self.dir_keys.append(KeyCode.from_char(line[i]))
                else:  # 配置文件为空
                    self.dir_keys = [KeyCode.from_char('e'), KeyCode.from_char('d'),
                                     KeyCode.from_char('s'), KeyCode.from_char('f')]
        except FileNotFoundError as e:  # 没有配置文件
            self.dir_keys = [KeyCode.from_char('e'), KeyCode.from_char('d'),
                             KeyCode.from_char('s'), KeyCode.from_char('f')]

        except IndexError as e:  # 配置残缺
            self.dir_keys = [KeyCode.from_char('e'), KeyCode.from_char('d'),
                             KeyCode.from_char('s'), KeyCode.from_char('f')]

        # 两个监控器
        self.key_listener = keyboard.Listener(
            on_press=self.on_press,
            on_release=self.on_release
        )
        self.mouse_listener = mouse.Listener(
            on_move=None,
            on_click=self.mouse_click,
            on_scroll=None
        )
        self.key_listener.start()
        self.mouse_listener.start()
        self.key_listener.join()
        self.mouse_listener.join()

    def on_press(self, key):
        print(key, 'press')
        if key == self.activate_key:  # 键盘激活方式
            temp_time = time.time()
            if temp_time - self.pre_time < self.time_threshold:
                print('activate')
                self.move_activate = True
            self.pre_time = temp_time

        if self.move_activate:  # 已经激活,检测上下左右
            if key == Key.up or key == self.dir_keys[0]:
                self.mouse_ctrl.move(0, -1)
            elif key == Key.down or key == self.dir_keys[1]:
                self.mouse_ctrl.move(0, 1)
            elif key == Key.left or key == self.dir_keys[2]:
                self.mouse_ctrl.move(-1, 0)
            elif key == Key.right or key == self.dir_keys[3]:
                self.mouse_ctrl.move(1, 0)

    def on_release(self, key):
        if key == Key.esc:
            print('stop')
            self.mouse_listener.stop()
            self.key_listener.stop()

    def mouse_click(self, x, y, button, pressed):  # 通过变量监控鼠标状态
        if button == Button.left:
            if pressed:
                self.move_activate = True
                print('activate')
            else:
                self.move_activate = False
                print('not activate')


if __name__ == "__main__":
    MouseBoard()


操作冲突问题

你可能会很好奇,为什么我选择用caps_lock键作为滚动的激活键,事实是因为操作会大量冲突。

最开始我是打算按下鼠标中键,然后上下左右的,没响应,冲突。
后来尝试鼠标右键,不行。
那就用键盘,试了一下别的都有组合效果,只有shift,tab,caps了,但是shift也冲突,tab有单独作用,只有caps的作用和我们的目标不会在同时使用,所以就勉为其难地用了caps

纯自动——失败

失败的尝试

强化版:增加UI设置与托盘管理

代码如下,注释自看

# 置顶用
from PyQt5.QtCore import Qt
# 样式相关
from PyQt5.QtGui import QIcon, QFont, QCloseEvent
# 组件相关与托盘相关
from PyQt5.QtWidgets import QWidget, QFormLayout, \
    QLabel, QVBoxLayout, QHBoxLayout, QPushButton, \
    QLineEdit, QSystemTrayIcon, QMenu, QAction, qApp, QApplication

# 导入应用操作相关的模块
import sys

# 导入键鼠操作模块
from pynput import mouse
from pynput import keyboard
from pynput.keyboard import Key, KeyCode
from pynput.mouse import Button
import time


# 辅助类
class Util:
    @staticmethod  # 获取辅助按键列表
    def get_auxiliary_keys():
        auxiliary_keys = []
        try:
            with open('config.txt', 'r', encoding='utf-8') as file_obj:
                line = file_obj.readline().strip()[0:4]
                if line != '':  # 正常情况
                    for i in range(4):
                        auxiliary_keys.append(KeyCode.from_char(line[i]))
                else:  # 配置文件为空
                    auxiliary_keys = [KeyCode.from_char('e'), KeyCode.from_char('d'),
                                      KeyCode.from_char('s'), KeyCode.from_char('f')]
        except FileNotFoundError as e:  # 没有配置文件
            auxiliary_keys = [KeyCode.from_char('e'), KeyCode.from_char('d'),
                              KeyCode.from_char('s'), KeyCode.from_char('f')]

        except IndexError as e:  # 配置残缺
            auxiliary_keys = [KeyCode.from_char('e'), KeyCode.from_char('d'),
                              KeyCode.from_char('s'), KeyCode.from_char('f')]
        finally:
            return auxiliary_keys

    @staticmethod  # 设置辅助按键列表
    def set_auxiliary_keys(line):
        # print(line)
        with open('config.txt', 'w', encoding='utf-8') as file_obj:
            file_obj.write(line)


# 界面类
class MouseBoardUI(QWidget):
    """
    显示主界面
    """
    def __init__(self):
        super(MouseBoardUI, self).__init__()
        self.init_UI()
        self.tray.show()  # 显示托盘
        self.setWindowFlags(Qt.WindowStaysOnTopHint)  # 窗口置顶

    def init_UI(self):
        # 窗口设置
        self.setWindowTitle('MouseBoard辅助工具')
        self.setWindowIcon(QIcon('MouseBoard.ico'))
        # 字体设置
        self.fonts = [QFont(), QFont()]
        self.fonts[0].setPointSize(30)
        self.fonts[1].setPointSize(20)

        # 显示参数模块
        self.state_show = QFormLayout()
        self.activate_state = QLabel()
        self.activate_state.setText(f'当前状态:休眠')
        self.activate_state.setFont(self.fonts[0])
        self.activate_key = QLabel()
        self.activate_key.setText(f'激活方式:双击caps_lock或者按下鼠标左键')
        self.activate_key.setFont(self.fonts[1])
        self.main_key = QLabel()
        self.main_key.setText(f'移动方式一:↑上 ↓下 ←左 →右')
        self.main_key.setFont(self.fonts[1])
        keys = Util.get_auxiliary_keys()
        keys = [str(e).strip('\'') for e in keys]
        self.auxiliary_key = QLabel()
        self.auxiliary_key.setText(f'移动方式二:{keys[0]}{keys[1]} 下 '
                                   f'{keys[2]}{keys[3]} 右')
        self.auxiliary_key.setFont(self.fonts[1])
        self.state_show.addRow(self.activate_state)
        self.state_show.addRow(self.activate_key)
        self.state_show.addRow(self.main_key)
        self.state_show.addRow(self.auxiliary_key)

        # 参数设置栏,嵌套
        self.state_set = QVBoxLayout()
        self.set_label = QLabel()
        self.set_label.setText('按上下左右顺序设置辅助键(如 wsad)\n')
        self.set_label.setFont(self.fonts[1])
        self.state_set.addWidget(self.set_label)
        self.state_update = QHBoxLayout()
        self.state_set.addLayout(self.state_update)  # 嵌套布局
        self.directions_input = QLineEdit()  # 文本框
        self.directions_input.setFont(self.fonts[0])
        self.update_btn = QPushButton()
        self.update_btn.setText('提交修改')  # 按钮
        self.update_btn.setFont(self.fonts[0])
        self.update_btn.clicked.connect(self.update_config)
        self.state_update.addWidget(self.directions_input)
        self.state_update.addWidget(self.update_btn)

        # 设置主布局
        vbox = QVBoxLayout()  # 初始化垂直布局
        vbox.addLayout(self.state_show)
        vbox.addLayout(self.state_set)
        self.setLayout(vbox)
        # 添加托盘,把自己和托盘模板绑定
        self.tray = TrayModel(self)

    def bound_BackListener(self, back_listener):
        """
        因为MouseBoardUI要包含MouseBoard引用,MouseBoard要包含MouseBoardUI引用
        所以不能再初始化实现,只能用一个函数实现
        """
        self.MouseBoard = back_listener
        # print(self.MouseBoard.auxiliary_keys)

    def update_config(self):
        line = self.directions_input.text()
        Util.set_auxiliary_keys(line)
        self.auxiliary_key.setText(f'移动方式二:{line[0]}{line[1]} 下 '
                                   f'{line[2]}{line[3]} 右')
        self.MouseBoard.auxiliary_keys = Util.get_auxiliary_keys()

    def closeEvent(self, a0: QCloseEvent) -> None:  # 阻止事件
        a0.ignore()
        self.hide()
        self.tray.show()


# 托盘类
class TrayModel(QSystemTrayIcon):
    """
    托盘显示
    """

    def __init__(self, bound_window):
        super(TrayModel, self).__init__()
        self.window = bound_window
        self.init_UI()

    def init_UI(self):
        # 悬浮显示
        self.setToolTip('MouseBoard')
        # 初始化菜单
        self.menu = QMenu()
        # 设置两个QAction,绑定函数动作,QAction相当于中间动作,可以理解为调出菜单后
        self.manage_action = QAction('参数设置', self, triggered=self.manage)
        self.quit_action = QAction('退出MouseBoard', self, triggered=self.quit)
        # 将QAction加入菜单
        self.menu.addAction(self.manage_action)
        self.menu.addAction(self.quit_action)
        # 将菜单绑定到托盘
        self.setContextMenu(self.menu)
        # 设置图标
        self.setIcon(QIcon('MouseBoard.ico'))  # 先将Ico转化为QIcon
        # 左键激活
        self.activated.connect(self.app_click)

    def manage(self):
        self.window.showNormal()  # 恢复正常,激活窗口
        self.window.activateWindow()

    def quit(self):
        qApp.quit()  # 退出绑定的程序

    def app_click(self):
        self.window.showNormal()  # 恢复正常,激活窗口
        self.window.activateWindow()


# 后台监控类
class MouseBoardBack:
    def __init__(self, main_UI):
        """
        本程序实现从键盘到鼠标移动的映射,可以实现当前电脑最小粒度微调
        本程序专注于实现:移动
        设有两个激活条件:双击caps_lock或者按下鼠标左键
        一个退出激活条件:松开鼠标左键
        当处于激活条件:可以使用辅助键或者上下左右实现移动,分别适用于非输入与输入情况
        辅助键:默认edsf为上下左右,可以通过修改config中的四个键来实现自定义
        不设界面,一个退出条件:esc键
        """
        # 绑定监控UI
        self.UI = main_UI
        # 设置默认参数
        self.mouse_ctrl = mouse.Controller()
        self.move_activate = False  # 移动状态
        self.activate_key = Key.caps_lock  # 激活移动模式的键
        self.pre_time = time.time()  # 用于测试时间间隔
        self.time_threshold = 0.3  # 双击时间阈值
        self.auxiliary_keys = Util.get_auxiliary_keys()  # 控制上下左右的辅助键

        # 两个监控器
        self.key_listener = keyboard.Listener(
            on_press=self.on_press,
            on_release=self.on_release
        )
        self.mouse_listener = mouse.Listener(
            on_move=None,
            on_click=self.mouse_click,
            on_scroll=None
        )
        self.key_listener.start()
        self.mouse_listener.start()

    def on_press(self, key):
        # print(key, 'press')
        if key == self.activate_key:  # 键盘激活方式
            temp_time = time.time()
            if temp_time - self.pre_time < self.time_threshold:
                self.UI.activate_state.setText('当前状态:激活')
                self.move_activate = True
            self.pre_time = temp_time

        if self.move_activate:  # 已经激活,检测上下左右
            if key == Key.up or key == self.auxiliary_keys[0]:
                self.mouse_ctrl.move(0, -1)
            elif key == Key.down or key == self.auxiliary_keys[1]:
                self.mouse_ctrl.move(0, 1)
            elif key == Key.left or key == self.auxiliary_keys[2]:
                self.mouse_ctrl.move(-1, 0)
            elif key == Key.right or key == self.auxiliary_keys[3]:
                self.mouse_ctrl.move(1, 0)

    def on_release(self, key):
        pass

    def mouse_click(self, x, y, button, pressed):  # 通过变量监控鼠标状态
        if button == Button.left:
            if pressed:
                self.move_activate = True
                self.UI.activate_state.setText('当前状态:激活')
            else:
                self.move_activate = False
                self.UI.activate_state.setText('当前状态:休眠')


if __name__ == "__main__":
    app = QApplication(sys.argv)
    main = MouseBoardUI()  # 参数主界面
    back_listener = MouseBoardBack(main)  # 键鼠监控
    main.bound_BackListener(back_listener)  # 将MouseBoard绑定到main
    main.show()  # 显示主界面
    sys.exit(app.exec_())  # (主程序退出后)关闭App线程,监听线程自然关闭

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:44:49  更:2022-07-03 10:46:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 1:12:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计