用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这片沃土上,继续播种灵感,收获更多温馨与惊喜。每一次的回归,都是新旅程的开始,愿与每一位读者共享知识的盛宴。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django后台数据获取展示

​ 续接Django REST Framework,使用Vite构建Vue3的前端项目 1.跨域获取后台接口并展示 安装Axios npm install axios --save 前端查看后端所有定义的接口 // 访问后端定义的可视化Api接口文档 http://ip:8000/docs/ // 定义的学生类信息 http://ip:8000/api/v1…

Ubuntu下交叉编译器工具链的安装方法

本篇文章记录Ubuntu下交叉编译器工具链的安装方法。 目录 一、交叉编译器 1、交叉编译器简介 2、获取交叉编译器 3、安装交叉编译器 4、安装相关库 二、结语 一、交叉编译器 1、交叉编译器简介 交叉编译器是一种编译器,它在一种平台上运行,但生成…

如何获取VS Code扩展的版本更新信息

获取VS Code 扩展的版本更新的需求 因为企业内部有架设私有扩展管理器的要求,但是对于一些官方市场的插件,希望可以自动获取这些扩展的更新并上传至私有扩展管理器。于是就有了本篇介绍的需求: 通过API的方式获取VS Code 扩展的更新。 关于…

HarmonyOS Next 系列之列表下拉刷新和触底加载更多数据实现(十一)

系列文章目录 HarmonyOS Next 系列之省市区弹窗选择器实现(一) HarmonyOS Next 系列之验证码输入组件实现(二) HarmonyOS Next 系列之底部标签栏TabBar实现(三) HarmonyOS Next 系列之HTTP请求封装和Token…

STM32入门开发操作记录(九)——外部时钟定时器

目录 一、项目准备1. 工程模板2. 器件接线 二、外部时钟1. 端口复用2. 流程示意 三、定时器模块Timer.cTimer.h 四、遮光计数 一、项目准备 1. 工程模板 本篇项目所用模板包含以下模块,声明函数见头文件,模块添加和函数功能详见往期记录。   2. 器件…

Python之格式化输出

格式化输出 方法一:用%方法二:用format()函数设置输出的内容的宽度和小数位数 方法一:用% 直接用print()函数对字符串进行输出,是没有进行格式化控制的。 格式化,是对输出内容的显示方式进行设置。 首先,要…

小程序滑动单元格

项目场景&#xff1a;小程序用户管理列表&#xff0c;通过单元格滑动实现“密码重置”、“删除”功能。 技术框架&#xff1a;uniapp、uview3、ts 效果如下&#xff1a; 前端页面&#xff1a; <template><view class"fui-wrap"><view class"f…

Spring Boot 的Web开发

Spring Boot 的Web开发 一、 静态资源映射规则 总结&#xff1a; 只要静态资源放在类路径下&#xff1a; called /static (or /public or /resources or /METAINF/resources 访问 &#xff1a; 当前项目根路径/ 静态资源名 二、 enjoy模板引擎 Enjoy模板引擎是一个轻量级的…

数据结构-常见的七大排序

上节中我们学习了七大排序中的五种(插入排序、希尔排序、堆排序、选择排序、交换排序) 数据结构-常见的七大排序-CSDN博客 这节我们将要学习快速排序(hoare、指针法、挖洞法(快排的延伸)、快速排序非递归(栈)) 1.快速排序 1.1 hoare法 1.1思路 1.选出一个key&#xff0c;一…

浅看MySQL数据库

有这么一句话&#xff1a;“一个不会数据库的程序员不是合格的程序员”。有点夸张&#xff0c;但是确是如此。透彻学习数据库是要学习好多知识&#xff0c;需要学的东西也是偏难的。我们今天来看数据库MySQL的一些简单基础东西&#xff0c;跟着小编一起来看一下吧。 什么是数据…

Windows 11环境下安装uwsgi的步骤和方法

正在用Django做个小网站&#xff0c;经常要用runserver启动服务观察效果&#xff0c;很不方便&#xff0c;就想装个uwsgi&#xff0c;让服务总是在后台运行&#xff0c;免得切换。上网一查发现&#xff0c;在windows下安装uwsgi不是一件简单的事情&#xff0c;很多人在尝试之后…

Python | Leetcode Python题解之第338题比特位计数

题目&#xff1a; 题解&#xff1a; class Solution:def countBits(self, n: int) -> List[int]:bits [0]for i in range(1, n 1):bits.append(bits[i & (i - 1)] 1)return bits

Spring Web MVC入门(下)

1. 响应 1.1 返回静态页面 创建前端页面&#xff0c;如下图所示&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Index页面</title> </head> <body>Hello,Spring MVC…

设计模式21-组合模式

设计模式21-组合模式&#xff08;Composite Pattern&#xff09; 写在前面 动机定义与结构定义结构主要类及其关系 C代码推导优缺点应用场景总结补充叶子节点不重载这三个方法叶子节点重载这三个方法结论 写在前面 数据结构模式 常常有一些组件在内部具有特定的数据结构。如何…

CVPR2023《DNF: Decouple and Feedback Network for Seeing in the Dark》暗光图像增强论文阅读笔记

相关链接 论文链接 https://openaccess.thecvf.com/content/CVPR2023/papers/Jin_DNF_Decouple_and_Feedback_Network_for_Seeing_in_the_Dark_CVPR_2023_paper.pdf 代码链接 https://github.com/Srameo/DNF 摘要 RAW数据的独特属性在低光照图像增强方面展现出巨大潜力。…

使用RKNN在Orange Pi 5 (RK3588s) 上部署推理PPO深度学习模型

文章目录 一、前言1️⃣、Orange Pi 是什么&#xff1f;2️⃣、PPO 是什么&#xff1f;3️⃣、RKNN 是什么&#xff1f;3️⃣、ONNX 是什么&#xff1f; 二、项目简介三、部署流程1️⃣、PPO 网络结构2️⃣、PPO 输出模型&#xff0c;模型转换&#xff0c;以及对比检查3️⃣、.…

ECMAScript6语法:默认参数和rest参数

1、默认参数 默认参数即在定义函数的参数列表中指定了默认值的参数。在 ES5 中&#xff0c;并没有提供在参数列表中指定参数默认值的语法&#xff0c;要想为函数的参数指定默认值&#xff0c;只能在函数体中实现&#xff0c;示例代码如下&#xff1a; function table(width, …

【性能优化】使用Perfetto定位应用启动性能的瓶颈

Android应用启动优化相关的文章已经有很多人都写过了&#xff0c;但是主要都是聚焦在&#xff0c;为了启动性能都做了哪些改动上&#xff0c;少见有文章会说应该如何分析、识别应用的启动性能。 本篇文章将会结合我个人对Perfetto的实际使用经历&#xff0c;讲解车载应用的启动…

前端post传入拿到数据,后端报null,并且能够添加或者编辑成功

检查conterller层注解接到实体类的注解是不是没加&#xff08; RequestBody &#xff09; 后端&#xff1a; 前端&#xff1a; 那么就看注解&#xff0c;因为contrller层有个接值注解&#xff08; RequestBody &#xff09;

MySQL基础练习题44-只出现一次的最大数字

目录 题目 情况一 准备数据 分析数据 情况二 准备数据 实现一 题目 单一数字 是在 MyNumbers 表中只出现一次的数字。 找出最大的 单一数字 。如果不存在 单一数字 &#xff0c;则返回 null 。 情况一 准备数据 ## 创建库 create database db; use db;## 创建表 Cre…