2017-09-11 65 views
0

我是GUI编程新手,需要QThread应用程序的帮助。Pyqt5 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() 

所以流部件的工作,也许有人可以告诉我什么是错的穿线部分,在这里我想要做一些简单的计算与记录的信号? 当我停止录制并绘制信号并将信号放入缓冲区时,线程不能用于仍在录制的信号,但也不能。 对不起,我无法得到一个简单的程序,使用类似的值,在相同的问题发生。

希望有人能帮助我吗?

THX,朱莉娅

+0

的万阿英,蒋达清可能是在主部分的QtWidgets.QApplication.processEvents(),但如果我没有它, GUI已经停止记录和绘图。还有另一种方法可以做到吗? – Audiosnow

回答

0

一点尝试不同的事情后,我发现了一个sulition。所以这个问题确实是QApplication.ProcessEvents部分。这是为了完成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()