坤坤音效键盘(Python实现)

news2025/7/3 14:25:30

文章目录

  • 坤坤音效键盘说明
  • 坤坤音效键盘效果展示
  • 代码实现
    • 安装第三方库
    • 准备音频
    • 监听键盘
    • 播放音频
    • 编写逻辑
    • 引入线程
    • 打包成exe程序

坤坤音效键盘说明

坤坤音效键盘说明:

  1. 单独按下 j 、 n 、 t 、 m j、n、t、m jntm 按键,会对应触发 “鸡”、“你”、“太”、“美” 音效。
  2. 连续按下 j n t m jntm jntm 按键,会触发 “鸡你太美” 的较长音效。
  3. 连续按下 n g m ngm ngm 按键,会触发 “你干嘛” 的较长音效。
  4. 按下Esc按键,会触发 “鸡你太美版《澎湖湾》” 的长音效。
  5. 按下左Ctrl键,会触发 “鸡你太美版《想某人》” 的超长音效。
  6. 按下小键盘上的数字键或小数点键,停止播放音频并终止程序。

说明一下: 对于连续按键触发的音效,不要求快速连续按下,只要连续即可。

坤坤音效键盘效果展示

说明一下: 为了让大家知道我按下了哪些按键,视频中将我按下的按键进行了打印。

代码实现

安装第三方库

该程序需要用到以下两个第三方库:

  • playsound模块: 使用该模块中的playsound函数来播放音频。
  • pynput模块: 使用该模块中的Listener对象来监听键盘按键。

在命令行或终端中输入以下命令进行安装:

pip install pynput==1.6.8
pip install playsound==1.2.2

说明一下: 这里下载第三方库时最好不要下载最新版本的。

准备音频

准备几个想要播放的音频,在Python程序所在目录下创建一个子目录,将这些音频文件放到这个子目录当中。比如:

在这里插入图片描述

说明一下: 博主的这些音频文件是在B站上找到的,大家可以去各个资源网站上下载音频文件,也可以自行录音。

监听键盘

监听键盘

监听键盘的步骤如下:

  1. 通过from关键字导入pynput模块中的keyboard模块。
  2. 创建一个listener对象,创建对象时为其设置一个回调函数。
  3. 调用listener对象的start方法,让listener开始监听键盘按键。
  4. 调用listener对象的join方法,防止程序直接退出(listener本质是一个线程)。

代码如下:

from pynput import keyboard


def onRelease(key):
    print(f'用户输入: {key}')


listener = keyboard.Listener(on_release=onRelease)
listener.start()
listener.join()

此时每当按键被敲击时,listener就会自动调用我们设置的回调函数,进而打印出被敲击的按键。

在这里插入图片描述

说明一下:

  • Listener的构造函数主要有两个参数,一个是on_press,另一个是on_release,设置给on_press的回调函数会在按键被按下时调用,而设置给on_release的回调函数会在按键被释放时调用。
  • 构造Listener对象时,设置给on_press和on_release的回调函数必须有一个参数,该参数在按键被按下或释放时由Listener自动传入,表示被按下或释放的按键。

普通键和特殊键

虽然在回调函数中通过print能够直接打印出被按下的按键,但实际这个参数并不是字符串类型的,我们不能将该参数直接与字符串进行比较,这样得不到正确的比较结果。

正确的做法如下:

  • 如果用户按下的是普通键(键盘上所有的字母、数字、符号),可以通过参数对象的char成员变量得知用户按下的是哪个按键,这个char成员变量的类型是字符串str类型的。
  • 如果用户按下的是特殊键(普通键以外的按键),可以通过参数对象的name成员变量得知用户按下的是哪个按键,这个name成员变量的类型是字符串str类型的。

但实际我们并不知道用户本次按键按下的是普通键还是特殊键,并且如果用户按下的是普通键,那么参数对象是没有name成员变量的,反之,如果用户按下的是特殊键,那么参数对象是没有char成员变量的。如果访问了不存在的成员变量,那么程序就会抛出异常AttributeError

这时可以借助异常来进行处理:

  • 将处理普通键的代码逻辑放到try块中,将处理特殊键的代码逻辑放到except块中。
  • 当用户按下按键后,会先执行try块中的代码逻辑,如果用户按下的是普通键,那么程序不会抛出异常,正常执行。
  • 如果用户按下的是特殊键,那么当访问参数对象的char成员变量时就会抛出异常AttributeError,但由于我们对异常AttributeError进行了捕捉,因此程序不会终止,此时执行流会跳转到except块中,执行except块中处理特殊键的代码逻辑。

代码如下:

def onRelease(key):
    try:
        print(f'用户输入: {key.char}')
        print(type(key.char))  # <class 'str'>
    except AttributeError:
        print(f'用户输入: {key.name}')
        print(type(key.name))  # <class 'str'>

判断特殊键的另一种方式

当用户按下的是特殊键时,除了通过参数对象的name成员变量得知用户按下的是哪个按键之外,还可以通过如下方式进行比较:

# 下面两种比较方式都可以
if key.name == 'ctrl_l':
    print('用户按下的是左Ctrl键')
if key == keyboard.Key.ctrl_l:
    print('用户按下的是左Ctrl键')

说明一下: Key是keyboard模块中的一个枚举类,Key中枚举出了各个特殊键。

播放音频

播放音频

播放音频的步骤如下:

  1. 通过from关键字导入playsound模块中的playsound函数。
  2. 调用playsound函数时,传入需要播放的音频的路径。

代码如下:

from playsound import playsound

playsound('sound/j.mp3')

编写逻辑

建立映射关系

为了能够快速获得一个字符串对应的音频路径,可以使用字典建立字符串与对应音频的映射关系。

代码如下:

# 建立字符串与对应音频的映射
letterToAudio = {
    'j': 'sound/j.mp3',
    'n': 'sound/n.mp3',
    't': 'sound/t.mp3',
    'm': 'sound/m.mp3',
    'jntm': 'sound/jntm.mp3',
    'ngm': 'sound/ngm.mp3',
    'esc': 'sound/phw.mp3',
    'ctrl_l': 'sound/xmr.mp3'
}

编写逻辑

代码逻辑的编写如下:

  • 将处理普通键的代码逻辑放到try块中,将处理特殊键的代码逻辑放到except块中。
  • 为了实现特定连续按键触发特定音频的功能,需要用history变量记录历史敲击过的字母,每当按键被敲击时就可以通过history变量来判断是否触发连续字母音效了。
  • 在处理普通键时,需要优先判断是否触发连续字母音效,如果没有触发连续字母音效再判断是否触发单字母音效,因为触发连续字母音效的最后一个字母可能也会触发单字母音效。
  • 在处理特殊键时,直接判断用户按下的按键是否会触发音效即可。

代码如下:

history = ''  # 记录历史敲击过的字母


def onRelease(key):
    global history
    audio = ''
    try:
        print(f'用户输入: {key.char}')
        # 记录敲击过的字母
        if len(history) < 4:
            history += key.char
        else:
            history = history[1:] + key.char

        # 优先判断是否触发连续字母音效,再判断是否触发单字母音效
        if history == 'jntm':
            audio = letterToAudio[history]
        elif history[-3:] == 'ngm':
            audio = letterToAudio[history[-3:]]
        elif key.char in 'jntm':
            audio = letterToAudio[key.char]
    except AttributeError:
        print(f'用户输入: {key.name}')
        # 按下的不是普通键,可以把history清空
        history = ''
        # 判断是否触发音效
        if key == keyboard.Key.esc:
            audio = letterToAudio['esc']
        elif key == keyboard.Key.ctrl_l:
            audio = letterToAudio['ctrl_l']
    # 判断是否本次敲击按键是否触发音效
    if audio != '':
        playsound(audio)

说明一下:

  • 变量history没必要将历史敲击过的字母全部记录下来,因为这里触发连续字母音效的最长连续字母就是'jntm',长度为4,因此history只需要记录最近4次敲击过的字母即可。
  • 在判断是否触发'ngm'的连续音效时,history的长度可能为3,也可能为4,这时需要通过负索引的方式对history进行切片操作,保证是在用history中的后三个字母在进行判断。

引入线程

当前程序存在的问题

现在我们编写的代码已经可以运行了,但当前的效果体验并不好:

  • 在播放音频的时候我们打字会卡顿,并且在当前音频未播放完之前的按键无法触发其他音频。
  • 根本原因就是因为此时监听键盘按键和播放音频都是由同一个线程处理的,因此线程在播放音频的时候无法监听键盘按键。

为了解决这个问题,我们可以在调用playsound播放音频的时候创建一个线程,让该线程去执行播放音频的动作,而让当前线程继续进行按键监听操作。

引入线程

引入线程的步骤如下:

  1. 通过from关键字导入threading模块中的Thread类(threading是标准库中的模块,不需要额外安装)。
  2. 创建线程时需要创建一个Thread对象,然后调用Thread对象的start方法启动线程。
  3. 在创建Thread对象时,需要通过target参数指定该线程启动后要执行的程序例程,通过args参数指定调用该程序例程时需要传入的参数。

代码如下:

# 创建线程对象,并指定其要执行的程序例程
t = Thread(target=playsound, args=(audio, ))
# 启动线程
t.start()

说明一下:

  • 创建Thread对象时,传入的args参数的类型是元组类型,因此如果只需要传入一个参数,就需要以(arg, )的方式传入,这后面这个逗号是不可省略的,否则就不是元组类型了。
  • Python默认创建线程后,不管主线程是否执行完毕,都会等待子线程执行完毕才一起退出,因此主线程是否join子线程结果都一样。
  • 如果这里调用了join,那么监听键盘按键的线程就会被阻塞,直到子线程将音频播放完毕,此时音频的播放过程变成了串行的。

完整代码

引入线程后的完整代码如下,此时在播放音频的时候敲键盘就不会存在卡顿现象,并且在音频播放期间能够再次触发其他音频。

from pynput import keyboard
from playsound import playsound
from threading import Thread

# 建立字符串与对应音频的映射
letterToAudio = {
    'j': 'sound/j.mp3',
    'n': 'sound/n.mp3',
    't': 'sound/t.mp3',
    'm': 'sound/m.mp3',
    'jntm': 'sound/jntm.mp3',
    'ngm': 'sound/ngm.mp3',
    'esc': 'sound/phw.mp3',
    'ctrl_l': 'sound/xmr.mp3'
}
history = ''  # 记录历史敲击过的字母


def onRelease(key):
    global history
    audio = ''
    try:
        print(f'用户输入: {key.char}')
        # 记录敲击过的字母
        if len(history) < 4:
            history += key.char
        else:
            history = history[1:] + key.char

        # 优先判断是否触发连续字母音效,再判断是否触发单字母音效
        if history == 'jntm':
            audio = letterToAudio[history]
        elif history[-3:] == 'ngm':
            audio = letterToAudio[history[-3:]]
        elif key.char in 'jntm':
            audio = letterToAudio[key.char]
    except AttributeError:
        print(f'用户输入: {key.name}')
        # 按下的不是普通键,可以把history清空
        history = ''
        # 判断是否触发音效
        if key == keyboard.Key.esc:
            audio = letterToAudio['esc']
        elif key == keyboard.Key.ctrl_l:
            audio = letterToAudio['ctrl_l']
    # 判断是否本次敲击按键是否触发音效
    if audio != '':
        # 创建线程对象,并指定其要执行的程序例程
        t = Thread(target=playsound, args=audio)
        # 启动线程
        t.start()


listener = keyboard.Listener(on_release=onRelease)
listener.start()
listener.join()

打包成exe程序

一、打包资源文件夹

当前项目播放音频时需要用到的音频文件就叫做资源文件,博主将这些资源文件放在了一个名为sound的文件夹中。如下:

在这里插入图片描述

二、修改KunKunKeyboard.py文件

我们需要在KunKunKeyboard.py文件中加入如下函数,该函数是用于生成资源文件的访问路径的。

# 生成资源文件访问路径
def resource_path(relative_path):
    if getattr(sys, 'frozen', False):  # 是否Bundle Resource
        base_path = sys._MEIPASS
    else:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

代码中所有使用资源文件路径的地方,都需要通过调用该函数来生成资源文件的访问路径,然后再通过这个生成的路径来访问资源文件,我们只需要将字典中的内容更改一下即可。如下:

# 建立字符串与对应音频的映射
letterToAudio = {
    'j': resource_path(os.path.join('sound', 'j.mp3')),
    'n': resource_path(os.path.join('sound', 'n.mp3')),
    't': resource_path(os.path.join('sound', 't.mp3')),
    'm': resource_path(os.path.join('sound', 'm.mp3')),
    'jntm': resource_path(os.path.join('sound', 'jntm.mp3')),
    'ngm': resource_path(os.path.join('sound', 'ngm.mp3')),
    'esc': resource_path(os.path.join('sound', 'phw.mp3')),
    'ctrl_l': resource_path(os.path.join('sound', 'xmr.mp3'))
}

说明一下: join是os.path模块中的一个函数,它的作用是将多个路径进行拼接。

三、准备图标文件

如果你想要修改生成的exe程序的图标的话,那么你需要准备一个 32 × 32 32\times32 32×32像素的图片文件,图片文件需要为ico格式,可以使用百度的JPG在线转ICO:https://www.aconvert.com/cn/icon/jpg-to-ico/
在这里插入图片描述

将生成的图标文件KunKunKeyboard.py的同级目录下。如下:

在这里插入图片描述

三、生成KunKunKeyboard.spec文件并修改

在命令行或终端中输入以下命令,生成KunKunKeyboard.spec文件:

pyi-makespec -F -i logo.ico KunKunKeyboard.py

此时在KunKunKeyboard.py的同级目录下会生成一个KunKunKeyboard.spec文件。如下:
在这里插入图片描述

此时,打开KunKunKeyboard.spec文件,并将做如下修改:

在这里插入图片描述
说明一下:

  • 在修改之前,datas的值为一个空列表,即datas=[]
  • 这里修改的意思是,将KunKunKeyboard.py目录下的sound目录及其该目录中的文件加入生成的exe程序中,在运行时放在临时文件的根目录下,名称为sound。

四、生成exe程序

最后在命令行或终端中输入以下命令:

pyinstaller KunKunKeyboard.spec

此时在KunKunKeyboard.py的同级目录下会生成一个dict目录,生成的exe程序就在该目录中。如下:
在这里插入图片描述

拓展内容(非必须)

如果在修改KunKunKeyboard.spec时,同时将console的值设置为False。如下:

在这里插入图片描述

那么此时生成的exe程序在运行时将不会弹出窗口,程序运行后也不会在任务栏显示。需要注意的是,如果要这样做,需要将程序中两处调用print函数的地方注释掉,因为此时没有窗口供print输出,如果print被调用那么程序将会抛异常。

此时要终止这个程序有以下几种方法:

  • 打开任务管理器,找到该进程将其终止。
  • 关机重启。
  • 按下小键盘上的数字键,让程序因抛异常而终止。

说明一下: 代码中没有考虑小键盘中的数字按键和小数点按键,当这些按键被按下时会抛出没有被捕获到的异常,可以将这些按键当作暂停键。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科技视界杂志科技视界杂志社科技视界编辑部2022年第21期目录

科技视界杂志科技视界杂志社科技视界编辑部2022年第21期目录 科普论坛《科技视界》投稿&#xff1a;cnqikantg126.com 天敌昆虫——让农业生产更安全 季香云; 1-3 储粮昆虫三维模型Web可视化技术研究与应用 阎磊;马宏琳;李亮;李鹏翔;王义超; 4-6 科学实验 非均匀催…

wy的leetcode刷题记录_Day33

wy的leetcode刷题记录_Day33 时间&#xff1a;2022-11-4 目录wy的leetcode刷题记录_Day33754. 到达终点数字题目介绍思路代码收获199. 二叉树的右视图题目介绍思路代码收获754. 到达终点数字 今天的每日一题是&#xff1a;754. 到达终点数字 题目介绍 在一根无限长的数轴上…

CSS:变量函数var和自定义属性

文章目录CSS变量var()函数CSS变量 CSS变量分为两部分&#xff1a;变量声明和变量使用。 变量的声明是由CSS自定义属性和对应的属性朱组成的&#xff0c;比如&#xff1a; :root {--custom-color: deepskyblue;}在这段代码中&#xff0c;–custom-color是属于css的自定义属性名…

方法的使用

目录 1. 举例说明什么叫方法 2. 方法概念及使用 2.1 什么是方法(method) 1.2 方法定义 1.3 方法调用的执行过程 1.4 实参和形参的关系(重要) 2. 方法重载 2.1 为什么需要方法重载 2.3 方法签名 3. 递归 递归执行过程分析 1. 举例说明什么叫方法 我们利用面向对象的方…

2022年特色小镇行业研究报告

第一章 行业概况 特色小镇是在几平方公里土地上集聚特色产业、生产生活生态科技相融合、不同于行政建制镇和产业园区的创新创业平台。根据类型的不同&#xff0c;特色小镇可以分为三类&#xff0c;即产业类、社区类和旅游类。 产业类&#xff1a;通过招商引资吸引企业进入&…

门控循环单元(GRU)【动手学深度学习v2】

理论 候选隐藏状态。 圆圈 表示 按元素乘法。 这里面的 这个符号值得是 按元素相乘。 Rt理解为 和Ht 长度一样的一维向量。&#xff08;这么理解&#xff09; 这里如果Rt长的像0的话&#xff0c;那么乘出来的结果&#xff0c;就也像0。 要是像0 的话&#xff0c;相当于是说…

文件操作之文件系统

目录 一 磁盘 1 磁盘的物理结构 2 磁盘在物理结构上如何存储数据 CHS寻址 3 从物理结构到抽象结构 LBA寻址 4管理 二 块组 boot block super block inode table data blocks block bitmap GDT&#xff1a;Global Descriptor Table 块组描述符 三 文件名和目录之…

网课查题接口搭建

网课查题接口搭建 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击跳…

字符串匹配算法(C/Java实现)

目录BF算法C语言实现Java实现KMP算法Java实现C语言实现next[]数组的优化BF算法 BF算法&#xff0c;即暴力(Brute Force)算法&#xff0c;是普通的模式匹配算法&#xff0c;BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配&#xff0c;若相等&#xff0c;…

R语言代做实现:混合正态分布EM最大期望估计法

全文链接&#xff1a;http://tecdat.cn/?p4815 原文出处&#xff1a;拓端数据部落公众号 因为近期在分析数据时用到了EM最大期望估计法这个算法&#xff0c;在参数估计中也用到的比较多。然而&#xff0c;发现国内在R软件上实现高斯混合分布的EM的实例并不多&#xff0c;大多…

Linux系统下实现开机自动加载驱动模块

在使用模块化加载驱动时&#xff0c;若系统内部存在同类别设备驱动&#xff0c;可能会出现无法加载我们添加的动态模块&#xff0c;比如Linux系统内置了CDC驱动&#xff0c;当我们使用兼容CDC和VCP驱动USB转串口芯片时&#xff0c;就会出现上电出现的是CDC串口&#xff0c;从而…

vue3 组件响应式v-model 失效,实践踩坑,一文搞懂组件响应式原理,对初学者友好

文章目录前情提要实战解析最后前情提要 vue3的v-model已经有了变化&#xff0c;假如你还不知道其中细节&#xff0c;看完这篇文章你就完全明白了&#xff0c;我以踩坑的场景来进行解析。起因是在我的项目中需要一个输入框组件&#xff0c;这个组件用来根据输入异步查询系统内已…

Python编程 基础数据类型

作者简介&#xff1a;一名在校计算机学生、每天分享Python的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言&#xff1a; 一.Python基础数据类型 1.为什么会有数据类型&#xff1f;&am…

公共云和私有云之间的区别

目前&#xff0c;越来越多的公司正在调整云服务来运行他们的应用程序。其实&#xff0c;有不同类型的云部署模型来满足客户的不同需求。云部署模型分为三种类型&#xff1a;公有云、私有云和混合云(公有云和私有云的混合)。在本文中&#xff0c;我们将对公共云和私有云之间的区…

【数据结构】单链表——单链表的定义及基本操作的实现(头插、尾插、头删、尾删、任意位置的插入与删除)

&#x1f9d1;‍&#x1f4bb;作者&#xff1a; 情话0.0 &#x1f4dd;专栏&#xff1a;《数据结构》 &#x1f466;个人简介&#xff1a;一名双非编程菜鸟&#xff0c;在这里分享自己的编程学习笔记&#xff0c;欢迎大家的指正与点赞&#xff0c;谢谢&#xff01; 单链表前言…

分享30个PHP源码,总有一款适合你

链接&#xff1a;https://pan.baidu.com/s/1dVbUn5YFMOze4J-K8sCAXQ?pwdeinu 提取码&#xff1a;einu 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c;大家下载后可以看到。 Emlog for SAE 适合新浪sae使用的个人博客…

网关Gateway-快速上手

gateway网关官方文档: https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/# 网关的概念 网关作为流量的入口&#xff0c;常用的功能包括路由转发&#xff0c;权限校验&#xff0c;限流等。 Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关…

Java:修改Jar的源码,并上传Nexus私有仓库,替换jar版本

第一步&#xff1a;修改jar包源代码 建一个全类名一模一样的类&#xff0c;然后把要修改的类的代码复制过去&#xff0c;然后编译生成class。然后拿编译后的class覆盖到jar中对应的位置 第二步&#xff1a;上传nexus jar文件&#xff0c;pom文件&#xff1a;在本地仓库中可以…

Linux操作系统~进程有哪些状态?

目录 R状态 S/D状态 什么是D状态 T状态 X状态 Z状态 什么是等待队列&#xff0c;什么是运行队列&#xff0c;什么是挂起/阻塞&#xff0c;什么叫唤醒进程 对比宏观上操作系统的三种状态 从操作系统宏观的概念上讲&#xff0c;进程有三种状态&#xff0c;就绪态&#xff0…

自动化测试和测试自动化你分的清楚吗?

目录 前言 两种自动化测试 为什么测试自动化对连续测试至关重要 使测试自动化成为现实 拥抱连续测试 总结 重点&#xff1a;配套学习资料和视频教学 前言 当我们谈论持续测试&#xff0c;以及持续交付和DevOps时&#xff0c;“自动化”一词就泛滥了。从根本上讲&#xf…