我是GUI编程的新手,需要QThread应用程序的帮助。
我设计了一个GUI-Programm,它记录来自麦克风的信号并同时将其绘制在图中。 现在我想评估另一个线程中的信号,因此它仍然在GUI中记录和绘制。 流和绘图工作正常但每次我启动线程时GUI都会冻结然后退出。 有人知道我的代码中我做错了什么,我没有那么多编程经验?
# Imports ----------------------------
import sys
import time
import numpy as np
import pyaudio
from PyQt5 import QtGui, QtWidgets, QtCore
import matplotlib
from matplotlib.mlab import find
import matplotlib.gridspec as gridspec
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
matplotlib.use('Qt5Agg')
class Window(QtWidgets.QMainWindow):
def __init__(self): # template for rest of GUI,
super(Window, self).__init__()
self.setGeometry(50, 50, 1500, 900)
self.centralwidget = QtWidgets.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.channels = 2 # StereoSignal
self.fs = 44100 # Samplingrate
self.Chunks = 4096 # Buffersize
self.streamstart = False
self.audiodata = [] # to buffer streaming-values in
self.tapeLength = 4 # seconds
self.tape = np.empty(self.fs * self.tapeLength) * np.nan # tape to store signal-chunks
self.home()
def home(self):
btn = QtWidgets.QPushButton("Stream and Plot", self) # Button to start streaming
btn.clicked.connect(self.plot)
btn.move(100, 100)
btn = QtWidgets.QPushButton("Stop", self) # Button to stop streaming
btn.clicked.connect(self.stop_signal)
btn.move(200, 100)
btn = QtWidgets.QPushButton("Evaluate", self) # Button for the Evaluation
btn.clicked.connect(self.evaluation)
btn.move(100, 140)
self.textEdit = QtWidgets.QTextEdit(self) # Show text of evaluation
self.textEdit.move(250, 170)
self.textEdit.resize(200, 200)
self.scrollArea = QtWidgets.QScrollArea(self) # Scroll-Area to plot signal (Figure) in
self.scrollArea.move(75, 400)
self.scrollArea.resize(600, 300)
self.scrollArea.setWidgetResizable(False)
self.figure = Figure((15, 2.8), dpi=100) # figure instance (to plot on) F(width, height, ...)
self.canvas = FigureCanvas(self.figure)
self.scrollArea.setWidget(self.canvas)
self.gs = gridspec.GridSpec(1, 1)
self.ax = self.figure.add_subplot(self.gs[0])
self.figure.subplots_adjust(left=0.05)
def start_stream(self, start=True):
"""start a Signal-Stream with pyAudio, with callback (to also play immediately)"""
if start is True:
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format=pyaudio.paFloat32, channels=self.channels, rate=self.fs, input=True,
output=True, frames_per_buffer=self.Chunks, stream_callback=self.callback)
self.streamstart = True
self.stream.start_stream()
print("Recording...")
def callback(self, in_data, frame_count, time_info, flag):
"""Callback-Function which stores the streaming data in a list"""
data = np.fromstring(np.array(in_data).flatten(), dtype=np.float32)
self.audiodata = data
print("appending...")
return data, pyaudio.paContinue
def tape_add(self):
"""add chunks from (callback)-list to tapes for left and right Signalparts"""
if self.streamstart:
self.tape[:-self.Chunks] = self.tape[self.Chunks:]
self.taper = self.tape # tape for right signal
self.tapel = self.tape # tape for left signal
self.tapel[-self.Chunks:] = self.audiodata[::2]
self.taper[-self.Chunks:] = self.audiodata[1::2]
print("taping...")
else:
print("No streaming values found")
def plot(self):
"""Start the streaming an plot the signal"""
print("(Stereo-)Signal streaming & plotting...")
if self.streamstart:
pass
else:
self.start_stream(start=True)
self.t1 = time.time()
time.sleep(0.5)
while self.streamstart:
QtWidgets.QApplication.processEvents() # does this still work with threads?
print("Plotting...")
self.tape_add()
self.timeArray = np.arange(self.taper.size)
self.timeArray = (self.timeArray / self.fs) * 1000 # scale to milliseconds
self.ax.clear()
self.ax.plot(self.timeArray, (self.taper / np.max(np.abs(self.taper))), '-b')
self.ax.grid()
self.ax.set_ylabel("Amplitude")
self.ax.set_xlabel("Samples")
self.canvas.draw()
def stop_signal(self):
print("Stopping Signal.")
if self.streamstart:
print("Stop Recording")
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
self.streamstart = False
else:
pass
def evaluation(self):
""" Start the evaluation in another Thread"""
threader = WorkerThread(self.taper, self.tapel)
thread = QtCore.QThread()
# threader.threadDone.connect(self.thread_done) # doesn't work yet
thread.started.connect(threader.run)
thread.start() # start thread
class WorkerThread(QtCore.QObject):
def __init__(self, taper, tapel): # take the tape-parts from the original thread
# super().__init__() # do I need this or next?
QtCore.QThread.__init__(self)
self.__taper = taper
self.__tapel = tapel
def run(self):
"""Do evaluation, later mor, for now just some calculations"""
print("Evaluating Signal")
self.tpr = self.__taper.astype(np.float32, order='C') / 32768 # here the GUI freezes and then exits
self.tpl = self.__tapel.astype(np.float32, order='C') / 32768
# cut nan-values if there are some
self.r = self.tpr[~np.isnan(self.tpr)]
self.l = self.tpl[~np.isnan(self.tpl)]
# normalize signals
self.left2 = (self.l / np.max(np.abs(self.l)))
self.right2 = (self.r / np.max(np.abs(self.r)))
self.norm_audio2 = np.array((self.left2, self.right2)) # like channels (in de_interlace)
# do some calculations
self.databew = """ Mute, Loudness and PSNR/MOS...
Dominant fundamental frequencies etc.
"""
print(self.databew)
# self.textEdit.append(self.databew) # would this work?
# self.threadDone.emit('Thread-Bewertung Done.') # later implemented
def main():
app = QtWidgets.QApplication(sys.argv)
GUI = Window()
GUI.show()
sys.exit(app.exec_())
main()
所以流媒体部分工作,也许有人可以告诉我线程部分有什么问题,我想用录制的信号做一些简单的计算? 当我停止记录和绘图并将信号放在缓冲区中时,线程不能用于信号静止记录,但也不能。 对不起,我无法得到一个更简单的程序,使用类似的值,同样的问题出现了。
希望有人能帮助我吗?
谢谢,朱莉娅答案 0 :(得分:0)
经过一些尝试不同的事情后,我发现了一个闷闷不乐。所以问题确实是QApplication.ProcessEvents-part。这是为了在PyQt中完成循环,但是我的循环是一个无限循环,只有在按下按钮后停止。这就是每次我使用它时GUI都冻结的原因。
现在的解决方案是将绘图部分放在一个新的Thread中,它可以访问GUI窗口。
这是新代码,它运行得很好而且速度合理:
# Imports ----------------------------
import sys
import time
import numpy as np
import pyaudio
from PyQt5 import QtGui, QtWidgets, QtCore
import matplotlib
import matplotlib.gridspec as gridspec
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
matplotlib.use('Qt5Agg')
class Window(QtWidgets.QMainWindow):
def __init__(self): # template for rest of GUI,
super(Window, self).__init__()
self.setGeometry(50, 50, 1500, 900)
self.centralwidget = QtWidgets.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.channels = 2 # StereoSignal
self.fs = 44100 # Samplingrate
self.Chunks = 4096 # Buffersize
self.streamstart = False
self.audiodata = [] # to buffer streaming-values in
self.tapeLength = 4 # seconds
self.tape = np.empty(self.fs * self.tapeLength) * np.nan # tape to store signal-chunks
self.home()
def home(self):
btn = QtWidgets.QPushButton("Stream and Plot", self) # Button to start streaming
btn.clicked.connect(self.plot)
btn.move(100, 100)
btn = QtWidgets.QPushButton("Stop", self) # Button to stop streaming
btn.clicked.connect(self.stop_signal)
btn.move(200, 100)
btn = QtWidgets.QPushButton("Evaluate", self) # Button for the Evaluation
btn.clicked.connect(self.evaluation)
btn.move(100, 140)
self.textEdit = QtWidgets.QTextEdit(self) # Show text of evaluation
self.textEdit.move(250, 170)
self.textEdit.resize(200, 200)
self.scrollArea = QtWidgets.QScrollArea(self) # Scroll-Area to plot signal (Figure) in
self.scrollArea.move(75, 400)
self.scrollArea.resize(600, 300)
self.scrollArea.setWidgetResizable(False)
self.figure = Figure((15, 2.8), dpi=100) # figure instance (to plot on) F(width, height, ...)
self.canvas = FigureCanvas(self.figure)
self.scrollArea.setWidget(self.canvas)
self.gs = gridspec.GridSpec(1, 1)
self.ax = self.figure.add_subplot(self.gs[0])
self.figure.subplots_adjust(left=0.05)
def start_stream(self, start=True):
"""start a Signal-Stream with pyAudio, with callback (to also play immediately)"""
if start is True:
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format=pyaudio.paFloat32, channels=self.channels, rate=self.fs, input=True,
output=True, frames_per_buffer=self.Chunks, stream_callback=self.callback)
self.streamstart = True
self.stream.start_stream()
print("Recording...")
def callback(self, in_data, frame_count, time_info, flag):
"""Callback-Function which stores the streaming data in a list"""
data = np.fromstring(np.array(in_data).flatten(), dtype=np.float32)
self.audiodata = data
print("appending...")
return data, pyaudio.paContinue
def tape_add(self):
"""add chunks from (callback)-list to tapes for left and right Signalparts"""
if self.streamstart:
self.tape[:-self.Chunks] = self.tape[self.Chunks:]
self.taper = self.tape # tape for right signal
self.tapel = self.tape # tape for left signal
self.tapel[-self.Chunks:] = self.audiodata[::2]
self.taper[-self.Chunks:] = self.audiodata[1::2]
print("taping...")
else:
print("No streaming values found")
def plot(self):
"""Start the streaming an plot the signal"""
print("(Stereo-)Signal streaming & plotting...")
self.plot_thread = PlotThead(self)
self.plot_thread.start()
def stop_signal(self):
print("Stopping Signal.")
if self.streamstart:
print("Stop Recording")
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
self.streamstart = False
self.plot_thread.stop()
else:
pass
def evaluation(self):
""" Start the evaluation in another Thread"""
self.thread = WorkerThread(self, self.taper, self.tapel)
self.thread.start() # start thread
class PlotThead(QtCore.QThread):
def __init__(self, window):
QtCore.QThread.__init__(self)
self.deamon = True
self.__is_running = True
self.window = window
def stop(self):
self.__is_running = False
def run(self):
if self.window.streamstart:
pass
else:
self.window.start_stream(start=True)
self.window.t1 = time.time()
time.sleep(0.5)
while self.window.streamstart and self.__is_running:
print("Plotting...")
self.window.tape_add()
self.window.timeArray = np.arange(self.window.taper.size)
self.window.timeArray = (self.window.timeArray / self.window.fs) * 1000 # scale to milliseconds
self.window.ax.clear()
self.window.ax.plot(self.window.timeArray, (self.window.taper / np.max(np.abs(self.window.taper))), '-b')
self.window.ax.grid()
self.window.ax.set_ylabel("Amplitude")
self.window.ax.set_xlabel("Samples")
self.window.canvas.draw()
class WorkerThread(QtCore.QThread):
def __init__(self, window, taper, tapel): # take the tape-parts from the original thread
QtCore.QThread.__init__(self)
self.__taper = taper
self.__tapel = tapel
self.deamon = True
self.window = window
def run(self):
"""Do evaluation, later mor, for now just some calculations"""
print("Evaluating Signal")
self.tpr = self.__taper.astype(np.float32, order='C') / 32768 # here the GUI freezes and then exits
self.tpl = self.__tapel.astype(np.float32, order='C') / 32768
# cut nan-values if there are some
self.r = self.tpr[~np.isnan(self.tpr)]
self.l = self.tpl[~np.isnan(self.tpl)]
# normalize signals
self.left2 = (self.l / np.max(np.abs(self.l)))
self.right2 = (self.r / np.max(np.abs(self.r)))
self.norm_audio2 = np.array((self.left2, self.right2)) # like channels (in de_interlace)
# do some calculations
self.databew = """ Mute, Loudness and PSNR/MOS...
Dominant fundamental frequencies etc.
"""
print(self.databew)
self.window.textEdit.append(self.databew) # would this work?
def main():
app = QtWidgets.QApplication(sys.argv)
GUI = Window()
GUI.show()
sys.exit(app.exec_())
main()