Python:使用tkinter和sounddevice的线程GUI冻结问题

问题描述

我有一个用于处理来自声音设备的音频数据的代码

我的代码通过 tkinter 构建 GUI,并在按下按钮时通过 sounddevice 处理音频数据。

我成功地使用线程类实时处理音频数据。

当我按下开始按钮时,来自麦克风的输入声音完美地输出到扬声器。

但是,停止按钮有问题。

当我按下停止按钮时,我的代码尝试终止线程,但会出现 GUI 冻结。而且线程不会死。

根据堆栈溢出的大量信息,我做了几次尝试,但都失败了。

请检查我的代码并给我一些建议。

这是我的代码

import sounddevice as sd
import numpy as np
import tkinter as tk
from tkinter import ttk
from threading import Thread


class StreamThread(Thread):
    def __init__(self):
        super().__init__()
        self.input_device_index = 0
        self.output_device_index = 4
        self.BLOCK_SHIFT = 128
        self.SAMPLING_RATE = 16000
        self.BLOCK_LEN = 512
        self.soUND_DEVICE_LATENCY = 0.2

    def run(self):
        with sd.Stream(device=(self.input_device_index,self.output_device_index),samplerate=self.SAMPLING_RATE,blocksize=self.BLOCK_SHIFT,dtype=np.float32,latency=self.soUND_DEVICE_LATENCY,channels=1,callback=self.callback):
            input()  # Input start

    def callback(indata,outdata,frames,time,status):
        outdata[:] = indata

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        self.title("Please Help Me")
        self.geometry("400x300")
        self.resizable(0,0)

        start_button = tk.Button(self,overrelief="solid",width=15,command=lambda: start_button_clicked(),text="Start",repeatdelay=1000,repeatinterval=100)
        start_button.grid(column=0,row=5)

        stop_button = tk.Button(self,command=lambda: stop_button_clicked(),text="Stop",repeatinterval=100)
        stop_button.grid(column=0,row=6)


def start_button_clicked():
    stream_thead.start()


def stop_button_clicked():
    # this is problem point
    if stream_thead.isAlive():
        sd.CallbackStop()
        sd.CallbackAbort()
        stream_thead.join()


if __name__ == "__main__":
    stream_thead = StreamThread()
    stream_thead.daemon = True  # set Daemon thread

    app = App()
    app.mainloop()

解决方法

您的代码存在问题:

  • 在 GUI 应用程序中使用控制台 input()。据我了解,您使用 input() 将线程任务置于等待状态。建议改用 threading.Event.wait()
  • sd.CallbackStop()sd.CallbackAbort() 不能破坏 input()。使用 threading.Event.set() 打破 threading.Event.wait()
  • self 中缺少 def callback(indata,...) 参数。应该是 def callback(self,indata,...)

下面是修改代码以解决上述问题:

...
from threading import Thread,Event
...
class StreamThread(Thread):
    ...

    def run(self):
        self.event = Event()
        with sd.Stream(device=(self.input_device_index,self.output_device_index),samplerate=self.SAMPLING_RATE,blocksize=self.BLOCK_SHIFT,dtype=np.float32,latency=self.SOUND_DEVICE_LATENCY,channels=1,callback=self.callback) as self.stream:
            #input()  # Input start
            self.event.wait()

    def terminate(self):
        self.stream.abort() # abort the stream processing
        self.event.set() # break self.event.wait()

    def callback(self,outdata,frames,time,status):
        outdata[:] = indata

...

def stop_button_clicked():
    if stream_thread.is_alive():
        stream_thread.terminate()
        stream_thread.join()

...

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...