用python制作88键赛博钢琴(能用鼠标键盘进行弹奏)
用python制作88键赛博钢琴
前言
恭喜这位博主终于想起了自己的账号密码!
时光荏苒,转眼间已逾一年未曾在此留下墨香。尽管这一年间,博主投身于无尽的忙碌与挑战之中,但令人欣慰的是,那份初心与热情似乎并未因岁月的流转而有丝毫减退,依旧保持着与往昔相同的热情与活力。
提及趣事,前不久博主精心筹备,欲在女友生辰之际,以一份特别的礼物——一台37键的童趣钢琴,为她编织一段温馨的记忆。怎料,这份心意与紧随其后的七夕佳节完美邂逅,却因工作的突然召唤,让博主不得不带着遗憾踏上异乡的征途,错过了亲自弹奏《两只老虎》的温馨时刻。
望着视频中女友指尖跳跃,旋律悠扬,那份未能亲临现场的遗憾化作了创新的火花。博主灵机一动,决定跨越千山万水,用指尖下的键盘,在数字世界中续写音乐的浪漫。说干就干,经过一番不懈的努力与探索,几个小时后,一个别出心裁的“键盘钢琴”奇迹般地诞生了!
请允许我们一同见证这创意的结晶。
效果图

功能实现
使用一个JSON文件作为核心,来控制整体界面布局、每个键对应的mp3文件、简谱标识、键盘映射等。
使用PyQt5实现界面绘制。
使用pygame库播放音乐,会更加流畅、连贯。
使用keyboard实现键盘监控。
使用Thread多线程,防止pygame和PyQt5线程冲突
最终实现的功能很简单,鼠标点击或键盘敲击对应的键即可进行弹奏。
源代码
import sys
import keyboard
import pygame
import jsonfrom threading import Thread
from PyQt5.QtWidgets import QMainWindow, QWidget, QPushButton, QApplication, QLabel# 读取数据文件
piano_key = json.load(open('JSON/piano_key.json', 'r', encoding='utf8'))# 主窗口
class MainWindow(QMainWindow):def __init__(self, parent=None):super(MainWindow, self).__init__(parent)# 获取桌面尺寸desktop = QApplication.desktop()screen_rect = desktop.screenGeometry()# 设置主窗口比例main_width = int(screen_rect.width() * 0.9)main_height = int(screen_rect.height() * 0.4)self.resize(main_width, main_height)# 固定窗口大小self.setFixedSize(self.width(), self.height())# 窗口居中self.move((screen_rect.width() - main_width) // 2, (screen_rect.height() - main_height) // 2)# 状态栏和标题self.status = self.statusBar()self.status.showMessage('不是88键买不起,而是赛博钢琴更有性价比!')self.setWindowTitle('赛博钢琴')# 创建容器存放琴键container = QWidget(self)self.setCentralWidget(container)# 遍历查询黑白键的数量,用于计算每个键宽度black_key_num = sum(1 for key in piano_key if 's' in key['sound'])white_key_num = len(piano_key) - black_key_numself.buttons = []button_width = main_width / white_key_numwhite_key_index = 0for index, key in enumerate(piano_key):button = QPushButton(container)button.setObjectName(key['sound'])button.clicked.connect(self.on_button_clicked)self.set_button_style(button, 's' in key['sound'])if 's' in key['sound']:button.resize(button_width * 0.8, main_height * 0.6)button.move((white_key_index - 1) * button_width + button_width * 0.6, 0)button.raise_()else:button.resize(button_width, main_height)button.move(white_key_index * button_width, 0)button.lower()white_key_index += 1self.add_label(container, key, white_key_index, button_width, main_height)self.buttons.append(button)# 匹配并添加labeldef add_label(self, container, key, white_key_index, button_width, main_height):label_map = {'a': '6', 'A': '6','b': '7', 'B': '7','c': '1', 'C': '1','d': '2', 'D': '2','e': '3', 'E': '3','f': '4', 'F': '4','g': '5', 'G': '5'}label_text = label_map.get(key['sound'][0], '') + f"\n{key['key']}"label = QLabel(label_text, container)label.move(white_key_index * button_width - button_width * 0.5, main_height - 60)# 初始化黑白键样式def set_button_style(self, button, is_black_key):if is_black_key:button.setStyleSheet("""QPushButton {background-color: black;color: white;border: 1px solid black;padding: 0;margin: 0;text-align: center;}QPushButton::hover {background-color: lightgray;}QPushButton:pressed {background-color: gray;}""")else:button.setStyleSheet("""QPushButton {background-color: white;color: black;border: 1px solid black;padding: 0;margin: 0;text-align: center;}QPushButton::hover {background-color: lightgray;}QPushButton:pressed {background-color: gray;}""")# 键盘按下改变样式def change_button_color(self, index):self.buttons[index].setStyleSheet("background-color: gray;")# 抬起后恢复样式def release_button_color(self, index, is_black_key):self.set_button_style(self.buttons[index], is_black_key)# 鼠标点击播放def on_button_clicked(self):button = self.sender()pygame.mixer.Sound('MP3/' + button.objectName()).play()# 初始化 PyQt 应用
app = QApplication(sys.argv)
# 实例化窗口
form = MainWindow()# 键盘按下触发
def on_action(event):try:sound = next(item['sound'] for item in piano_key if item['key'] == event.name)index = next(index for index, item in enumerate(piano_key) if item['key'] == event.name)if event.event_type == keyboard.KEY_DOWN:pygame.mixer.Sound('MP3/' + sound).play()form.change_button_color(index)elif event.event_type == keyboard.KEY_UP:form.release_button_color(index, 's' in sound)except StopIteration:print(f"No sound file found for key: {event.name}")# 键盘监听
def start_keyboard_listener():keyboard.hook(on_action)keyboard.wait()def main():# 显示窗口form.show()# 初始化 Pygame 混音器pygame.mixer.init()# 启动键盘监听线程listener_thread = Thread(target=start_keyboard_listener)listener_thread.daemon = Truelistener_thread.start()# 进入事件循环sys.exit(app.exec_())if __name__ == "__main__":main()
未来功能扩展
1.自定义功能:用户可以自定义每个琴键对应的键盘值,并保存,这也是我使用JSON文件控制整体的原因。
2.丰富标识:目前琴键上只有简谱的标识,后续会添加Do、Ra、C4、D4等标识。
3.自动弹奏:用户可以以某种方式将谱子录入或导入,程序根据谱子自动弹奏。
4.边弹边记:开启后,接下来的一段弹奏会以乐谱的形式保存下来。
5.趣味玩法:可能会像节奏大师那样?
6.有奇思妙想的兄弟,可以评论区或私信告诉我,我可能会将它实现并放到下一篇博客中。
结束语
在创意与爱的交织下,这不仅仅是一段代码的展现,更是一次心灵的触碰与跨越。期待未来能在CSDN这片沃土上,继续播种灵感,收获更多温馨与惊喜。每一次的回归,都是新旅程的开始,愿与每一位读者共享知识的盛宴。
相关文章:
用python制作88键赛博钢琴(能用鼠标键盘进行弹奏)
用python制作88键赛博钢琴 前言 恭喜这位博主终于想起了自己的账号密码! 时光荏苒,转眼间已逾一年未曾在此留下墨香。尽管这一年间,博主投身于无尽的忙碌与挑战之中,但令人欣慰的是,那份初心与热情似乎并未因岁月的流…...
zdpgo_gin_login 框架20240815更新,增加注册路由的功能,一个方法自动拥有注册和登录两个API接口
zdpgo_gin_login 适配gin框架的登录注册功能组件,通过本框架轻松拥有登录注册相关的API接口 特性: 自动生成注册接口,具备密码加密的功能自动生成登录接口,具备JWT Token生成的功能 安装 go get github.com/zhangdapeng520/z…...
搭配Intel第13代酷睿处理器
高性能内存硬盘这么买 intel第13代酷睿已经于2022年10月底正式上市。相比于第12代酷睿性能大涨,内置20条PCle通道(16条PCle 5.0和4条PCle 4.0)、可最多支持128GB DDR5 5600/DDR4 3200双通道内存,搭配Z790系列主板组建高端性能平台,满足未来设计、游戏、专业应用等需求。如…...
uniapp快速回顾,新学websocket连接和BLE连接
Uni APP的学习 官方文档 uni-app官网 (dcloud.net.cn) 任何的博客都不如官方文档 一、快速复习 文件结构 main.js 功能:项目的入口文件,初始化 Vue 实例。 App.vue功能:根组件,包含应用的基本结构和全局样式。 manifest.js…...
激光测距传感器
系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 前言一、产品原理:二、产品介绍:三、应用特点四、应用案例:1.冶金钢铁板卷材开卷工…...
从数据分析到智能生产:AI在工业中的应用与未来
导语 | 人工智能技术的迅猛发展,正在引领第四次工业革命悄然而至。尽管 AI 技术在工业领域的部署仍有诸多难题亟待解决,但这并不能阻挡历史趋势的车轮滚滚向前,AI 正在为工业领域带来新的变革。今天,我们特邀了上海腾展长融董事 &…...
讲讲android art虚拟机的内存
Android 的 ART(Android Runtime)虚拟机的内存管理是一个复杂但重要的部分。 ART 虚拟机的内存主要包括以下几个关键区域: Java 堆(Java Heap): 这是存储 Java 对象实例的主要区域。堆内存被进一步划分为不…...
构建高效社群生态:探索社群系统的力量与未来
在数字化时代的大潮中,社群系统作为连接人与人、促进信息交流与资源共享的重要平台,正日益成为企业、组织乃至个人不可或缺的一部分。它不仅为成员提供了展示自我、学习成长的舞台,更为社群管理者创造了高效管理、精准运营的工具。今天&#…...
数据结构——排序(3):交换排序(续)
目录 一、快速排序 (1)hoare版本 ①思路 ②过程图示 ③思考 ④代码实现 ⑤代码解释 (2)挖坑法 ①思路 ②过程图示 ③思考 ④代码实现 ⑤代码解释 (3)lomuto前后指针 ①思路 ②过程图示 ③思考 ④代码实现 ⑤代…...
2024最新版本Python安装及开发环境配置(vscodepython)
python安装 去Python官网下载最新版本: 接下来请一步步按照图片操作: 这样子就安装完成了 测试Python安装是否成功 先打开终端 右键Windows徽标,点击终端 然后输入python,如果如下图所示,就说明安装成功࿰…...
机器学习的定义
机器学习 机器学习的定义 机器学习是人工智能的一个分支,它使计算机系统能够从经验中学习并改进,而无需进行明确的编程。机器学习算法分析和解释数据,然后使用该数据来做出预测或决策,随着时间的推移,它们会变得更加准…...
2024-08-05升级问题:Android中ScrollView嵌套listview并解决listview显示问题
问题: 当ScrollView嵌套ListView时,ListView的高度设置为wrap_content时出现ListView的高度不能完全展开,而只显示的第一个Item。 解决方法: 按item的个数乘以高度计算出listview的总高度,并在数据变化时直接设置lis…...
【热度文章】Java设计模式之中介者模 式
ava 中的中介者模式 中介者模式(Mediator Pattern)是一种行为型设计模式,它通过一个中介对象来封装一系列对象之间的交互,使这些对象之间不需要显式地相互引用,从而降低了对象之间的耦合度。 中介者模式的主要角色&…...
【3.0】vue3语法
【3】vue3语法 【一】vue前提 【1】定义变量 # 1 const是常量--》不允许变的 # 2 咱们用 ref包裹后,是个对象,虽然对象是常量,对象不能变,对象.value可以变化 # 3 之所以定义成const原因是,后期不能修改对象 【对象.…...
Navicat Monitor 荣获 2024 年 DBTA “最佳数据库性能解决方案”读者选择奖
近期,Navicat 以其卓越的服务器监控与深度分析能力在众多杰出竞争者中脱颖而出,其监控产品 Navicat Monitor 荣获了 2024 年度 DBTA 读者选择奖中的“最佳数据库性能解决方案”殊荣。该奖项不仅是对 Navicat Monitor 在数据库监控与分析领域非凡实力的权…...
[论文笔记]ZeRO: Memory Optimizations Toward Training Trillion Parameter Models
引言 今天带来ZeRO: Memory Optimizations Toward Training Trillion Parameter Models的论文笔记。 大型深度模型提供了显著的准确性提升,但训练数十亿到数万亿个参数是具有挑战性的。现有的解决方案,如数据并行和模型并行,存在基本的局限…...
shuashuashua
CVE-2023-2130 靶标介绍: 在SourceCodester采购订单管理系统1.0中发现了一项被分类为关键的漏洞。受影响的是组件GET参数处理器的文件/admin/suppliers/view_details.php中的一个未知函数。对参数id的操纵导致了SQL注入。可以远程发起攻击。 通过标靶介绍可以知道…...
k8s之HPA
目录 1.HPA 2.部署 metrics-server 3.部署 HPA 4.总结 1.HPA HPA(Horizontal Pod Autoscaling)Pod 水平自动伸缩,Kubernetes 有一个 HPA 的资源,HPA 可以根据 CPU 利用率自动伸缩一个 Replication Controller、 Deployment 或…...
fun状态上传,并可手动控制
文章目录 引言上传原因:矛盾点:基础工程源码: 代码实操fun状态上传fun状态下发控制 引言 上传原因: 续上一节, 我们把fun像小灯一样, 加入了预警工程, 但是我们fun其实还有其他用处, 比如我们人工手动开风扇, 排风, 所以我们需要把fun的状态上传, 然后也可以通过服务器手动控制…...
【Canvas与艺术】四扇叶结
注意:此是一个看起来简单,实际上需要细细计算调整的拓扑图。 【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head&…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》
近日,嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》,海云安高敏捷信创白盒(SCAP)成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天,网络安全已成为企业生存与发展的核心基石,为了解…...
2025.6.9总结(利与弊)
凡事都有两面性。在大厂上班也不例外。今天找开发定位问题,从一个接口人不断溯源到另一个 接口人。有时候,不知道是谁的责任填。将工作内容分的很细,每个人负责其中的一小块。我清楚的意识到,自己就是个可以随时替换的螺丝钉&…...
【技巧】dify前端源代码修改第一弹-增加tab页
回到目录 【技巧】dify前端源代码修改第一弹-增加tab页 尝试修改dify的前端源代码,在知识库增加一个tab页"HELLO WORLD",完成后的效果如下 [gif01] 1. 前端代码进入调试模式 参考 【部署】win10的wsl环境下启动dify的web前端服务 启动调试…...
STM32 低功耗设计全攻略:PWR 模块原理 + 睡眠 / 停止 / 待机模式实战(串口 + 红外 + RTC 应用全解析)
文章目录 PWRPWR(电源控制模块)核心功能 电源框图上电复位和掉电复位可编程电压监测器低功耗模式模式选择睡眠模式停止模式待机模式 修改主频一、准备工作二、修改主频的核心步骤:宏定义配置三、程序流程:时钟配置函数解析四、注意…...
HarmonyOS-ArkUI 自定义弹窗
自定义弹窗 自定义弹窗是界面开发中最为常用的一种弹窗写法。在自定义弹窗中, 布局样式完全由您决定,非常灵活。通常会被封装成工具类,以使得APP中所有弹窗具备相同的设计风格。 自定义弹窗具备的能力有 打开弹窗自定义布局,以…...
