我正在try 创建一个应用程序,它将帮助训练人的耳朵听到不同的音调.我目前正在研究一个按钮数组,它将具有从八度3到5的每个色调音符.当这些按钮中的一个被按下时,它将执行上面用Electron 游戏包编写的功能.

我遇到的问题是,一旦按钮被按下,整个应用程序似乎都在等待,直到播放完该音符(只有1秒).这些按钮将注册输入,然后播放它们,只是不是同时播放.我希望能够在任何时候点击任何音符并让它被播放.

import pygame
import time
import pygame.midi

# mixer config
# freq = 44100  # audio CD quality
# bitsize = -16   # unsigned 16 bit
# channels = 2  # 1 is mono, 2 is stereo
# buffer = 1024   # number of samples
# pygame.mixer.init(freq, bitsize, channels, buffer)
# pygame.mixer.music.set_volume(0.8)

pygame.midi.init()
fps = 60
timer = pygame.time.Clock()
player= pygame.midi.Output(0)
player.set_instrument(1,1) #127 is max

major=[0,4,7,12]

def go(note):
    player.note_on(note, 127,1)
    time.sleep(1)
    player.note_off(note,127,1)

我正在使用pyqt5编写应用程序,相关的按钮代码如下.我不确定问题是与按钮如何执行功能有关,还是与pyGame MIDI功能有关.

import sys
import pygame
import time
from PyQt5.QtCore import *
from PyQt5.QtGui  import *
from PyQt5.QtWidgets import *
import chromatic_player

#small change for new branch

lowO= 3
highO = 5
Octaves = list(range(lowO,highO+1))
#Notes = ["A","A#/Bb","B","C","C#/Db","D","D#/Eb","E","F","F#/Gb","G"]
Notes = ["C","C#","D","D#","E","F","F#","G","A","A#","B"]
global glo
glo=0

class CustomButton(QPushButton):
    def __init__(self, text='',octave='', parent=None):
        self.octave = octave
        self.text = text
        super(QPushButton, self).__init__(text, parent=parent)
        self.setGeometry(QRect(30, 40, 41, 41))
        self.button_show()
        self.setId = self.text
        
    
    def button_show(self):
        self.clicked.connect(self.on_click)

    def on_click(self):
        chromatic_player.go(int(Notes.index(self.text[:-1]))+12*self.octave)
        print("User Clicked Me")
        print(self.text)
        print(glo + 1)
        


class Color(QWidget):
    def __init__(self, color):
        super(Color, self).__init__()
        self.setAutoFillBackground(True)
        palette = self.palette()
        palette.setColor(QPalette.Window, QColor(color))
        self.setPalette(palette)


class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()

        self.setWindowTitle("My App")

        layout1 = QHBoxLayout()
        layout3 = QGridLayout()
        layout1.setContentsMargins(0,0,0,0)
        layout1.setSpacing(5)
        layout1.addWidget(Color('green'))
        layout3.addWidget(Color('red'))
        layout3.addWidget(Color('purple'))

        for j in Octaves:
            layoutTemp = QVBoxLayout()
            for i in Notes:
                buttontemp = CustomButton("{}{}".format(i,j),j)
                #buttontemp.clicked.connect(buttontemp.on_click)
                layoutTemp.addWidget(buttontemp)
            layout1.addLayout(layoutTemp)
        
        layout1.addLayout( layout3 )
        widget = QWidget()
        widget.setLayout(layout1)
        self.setCentralWidget(widget)

app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

推荐答案

事实是,you正在通过拨打time.sleep(1)来让它等待.

这将完全阻塞事件队列,这意味着在该函数返回之前,Qt完全可以做到nothing,包括对用户事件作出react 或绘制/更新小部件.

一种正确的方法是拨打note_on,然后使用单杆QTimer拨打note_off.

简单的解决方案可能如下所示:

def go(note):
    player.note_on(note, 127, 1)
    QTimer.singleShot(1000, lambda:
        player.note_off(note, 127, 1))

请注意,更合适的程序实现应该考虑以下几个方面:

  • 它不应该直接负责按键来播放音符,而是"请求"它;自定义信号当然更适合这一点;
  • 玩家本身可能应该是一个类,这样你最终可以更容易地控制它的方面;
  • 通过从实际从现有范围和列表创建的字符串重建笔记来获得笔记是没有什么意义的;

所以:

  • 你错过了G夏普;
  • 您的布局用法令人困惑,对总是水平对齐的小部件使用多个垂直布局没有什么意义:使用网格布局;

考虑到上述情况,以下是该玩家的一个可能的类:

class Player(object):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # note that the pygame initialization has been moved here
        pygame.midi.init()
        self.pygame_player = pygame.midi.Output(0)
        self.pygame_player.set_instrument(1, 1)

    def play(self, note):
        self.pygame_player.note_on(note, 127, 1)
        QTimer.singleShot(1000, lambda:
            self.pygame_player.note_off(note, 127, 1))

使用类的好处是,可以通过添加功能来轻松改进它:例如,使用播放音符的"队列",这样您最终可以在计时器结束之前停止所有音符;或者更改乐器、默认音量或音符持续时间.

以下是该按钮和图形用户界面的改进版本:

class CustomButton(QPushButton):
    playNote = pyqtSignal(int)
    def __init__(self, note, octave, parent=None):
        text = Notes[note] + str(octave)
        super().__init__(text, parent=parent)
        self.note = note + 12 * octave
        self.clicked.connect(self.emitSignal)

    def emitSignal(self):
        self.playNote.emit(self.note)


class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()

        self.setWindowTitle("My App")

        self.player = Player()

        mainLayout = QHBoxLayout()
        mainLayout.setContentsMargins(0, 0, 0, 0)

        mainLayout.addWidget(Color('green'))
        buttonLayout = QGridLayout()
        mainLayout.addLayout(buttonLayout)

        rightLayout = QVBoxLayout()
        mainLayout.addLayout(rightLayout)
        rightLayout.addWidget(Color('red'))
        rightLayout.addWidget(Color('purple'))

        column = 0
        for j in range(lowO, highO + 1):
            for i in range(12):
                buttontemp = CustomButton(i, j)
                buttonLayout.addWidget(buttontemp, i, column)
                buttontemp.playNote.connect(self.player.play)
            column += 1
        
        widget = QWidget()
        widget.setLayout(mainLayout)
        self.setCentralWidget(widget)

请注意,Color小部件将不会正确显示,因为自定义小部件没有最小大小.你应该在它的__init__中添加一个基本的setMinimumSize(),或者正确地实现sizeHint().

最后,不要出于错误的原因使用global(在OOP中,实际上是always).使其成为某个对象的实例属性:对玩家使用类的另一个好处是,您可以为该实例使用glo(它应该有一个更好的名称),并在其中递增它.

Python相关问答推荐

如何将新的SQL服务器功能映射到SQL Alchemy的ORM

有没有方法可以修复删除了换码字符的无效的SON记录?

将numpy矩阵映射到字符串矩阵

如何终止带有队列的Python进程?+ 队列大小的错误?

Python -Polars库中的滚动索引?

将轨迹优化问题描述为NLP.如何用Gekko解决这个问题?当前面临异常:@错误:最大方程长度错误

比较2 PD.数组的令人惊讶的结果

为什么我的Python代码在if-else声明中的行之前执行if-else声明中的行?

在Python中管理打开对话框

修复mypy错误-赋值中的类型不兼容(表达式具有类型xxx,变量具有类型yyy)

为什么抓取的HTML与浏览器判断的元素不同?

把一个pandas文件夹从juyter笔记本放到堆栈溢出问题中的最快方法?

我想一列Panadas的Rashrame,这是一个URL,我保存为CSV,可以直接点击

如何使用SentenceTransformers创建矢量嵌入?

将scipy. sparse矩阵直接保存为常规txt文件

try 检索blob名称列表时出现错误填充错误""

为什么在FastAPI中创建与数据库的连接时需要使用生成器?

PYTHON、VLC、RTSP.屏幕截图不起作用

从源代码显示不同的输出(机器学习)(Python)

如何训练每一个pandaprame行的线性回归并生成斜率