pyqt5 qthread冻结,然后在线程启动时退出



我是Gui-gui编程的新手,需要QThread应用程序的帮助。

我设计了一个GUI-Programmmm,该编程记录了来自麦克风的信号,并同时绘制了它。现在,我想在另一个线程中评估信号,因此它仍然在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,朱莉娅

稍作尝试不同的东西后,我发现了一种硫酸。因此,问题确实是qapplication.processevents-part。这是为了在PYQT中完成循环,但是我的是一个无尽的循环,仅在按钮klick之后就停止了。这就是为什么我每次使用它冻结的原因。

现在的解决方案是将绘图零件也放在新线程中,该线程可以访问Gui-Window。

这是新的代码,可以快速运行良好:

# 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()

最新更新