PyQt5 应用程序 - 使用计时器运行带有循环的函数使其“冻结”
Posted
技术标签:
【中文标题】PyQt5 应用程序 - 使用计时器运行带有循环的函数使其“冻结”【英文标题】:PyQt5 app - using timer to run a function with a loop makes it "freeze up" 【发布时间】:2020-08-20 10:09:52 【问题描述】:我正在尝试为我创建的终端应用程序创建一个 GUI,该应用程序监听您的麦克风以确定您在吉他上演奏的音符。我想使用 PyQt5,这是我第一次使用它(尤其是任何 GUI 框架)。
我可以很好地创建 UI,但是我用于笔记检测的主要功能会被 QTimer 调用,这使得应用程序非常缓慢且无响应。我认为我在如何设置方面做得非常错误,但我自己找不到最好的方法。
这是我的代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QGridLayout, QLabel
from PyQt5.QtGui import QFont
from PyQt5.QtCore import QTimer, QTime, Qt
import pyaudio
import numpy as np
from aubio import pitch
from helpers import pitch2note
# set up globals
CHUNK = 1024
FORMAT = pyaudio.paFloat32
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5000
tolerance = 0.8
downsample = 1
win_s = 4096 // downsample # fft size
hop_s = 1024 // downsample # hop size
pitch_o = pitch("yinfft", win_s, hop_s, RATE)
pitch_o.set_unit("Hz")
pitch_o.set_silence(-40)
pitch_o.set_tolerance(tolerance)
class Window(QWidget):
def __init__(self):
super().__init__()
self.setupUI()
def setupUI(self):
# UI design is very much in progress
# not final but it shouldn't matter for this case
self.setGeometry(100, 100, 800, 400)
self.noteLabel = QLabel('Note played:')
layout = QGridLayout()
font = QFont('Arial', 60, QFont.Bold)
self.note = QLabel() # the variable I will modify later
self.note.setAlignment(Qt.AlignCenter)
# setting font to the label
self.note.setFont(font)
# adding widgets to the layout
layout.addWidget(self.noteLabel, 1, 0)
layout.addWidget(self.note, 1, 1)
# setting the layout to main window
self.setLayout(layout)
# creating a timer object
timer = QTimer(self)
# adding action to timer
# this is what might be wrong
timer.timeout.connect(self.getNote)
# update the timer every 33ms (30fps?)
# or is it too much?
timer.start(33)
def getNote(self):
# main function handling note detection
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
frames = []
notes_list = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
buffer = stream.read(CHUNK)
frames.append(buffer)
signal = np.frombuffer(buffer, dtype=np.float32)
pitch_of_note = pitch_o(signal)[0]
if pitch_of_note != 0:
note_played = pitch2note(pitch_of_note)
notes_list.append(note_played[:-1])
# we append only note and not number (eg. E and not E2)
if len(notes_list) == 10:
# We listen for 10 signals and then select the most frequent note in the list
# This is because when you pluck a note the frequency can deviate as you pluck it strongly
most_likely_note = max(notes_list, key=notes_list.count)
stream.stop_stream()
stream.close()
p.terminate()
self.label.setText(most_likely_note)
return most_likely_note
# create pyqt5 app
App = QApplication(sys.argv)
# create the instance of our Window
window = Window()
# showing all the widgets
window.show()
# start the app
App.exit(App.exec_())
这是帮助文件:
from math import log2
A4 = 440
C0 = A4*pow(2, -4.75)
NOTE_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
def pitch2note(freq):
h = round(12*log2(freq/C0))
octave = h // 12
n = h % 12
return NOTE_NAMES[n] + str(octave)
【问题讨论】:
【参考方案1】:我建议在这里使用 QThread,因为您不希望 UI 在录制时阻塞。 https://doc.qt.io/qtforpython/PySide2/QtCore/QThread.html
或者你也可以看看 PyAudio 的回调模式,它也可以非阻塞工作 https://people.csail.mit.edu/hubert/pyaudio/docs/
【讨论】:
以上是关于PyQt5 应用程序 - 使用计时器运行带有循环的函数使其“冻结”的主要内容,如果未能解决你的问题,请参考以下文章
如何在循环中打开(和关闭)PyQt5 应用程序,并让该循环多次运行