我正在尝试创建一个 QThread 对象,其任务是处理来自不同小部件的所有请求以在声卡上播放信号。我将此对象命名为“sound_engine”,并将其保留为应用程序中使用 sounddevice 模块的唯一对象。
当我启动时,sound_engine 的运行是非阻塞的。我要求它在“运行”方法中播放 2 秒的蜂鸣声,并且它不会阻塞 GUI。然而,当我向其“嘟嘟”插槽发出信号以播放另一种声音时,它会阻止发出信号的对象,在本例中是我的主窗口“mw”。我认为这与“运行”返回后线程退出有关,但是正确的方法是什么?
import sys
import numpy as np
import sounddevice as sd
from PySide6 import QtWidgets as qtw
from PySide6 import QtCore as qtc
from __feature__ import snake_case
from __feature__ import true_property
class SoundEngine(qtc.QThread):
def __init__(self):
super().__init__()
self.FS = 48000
def run(self):
self.start_stream()
# do a start beep for testing
self.beep(2, 200)
def start_stream(self):
self.stream = sd.Stream(samplerate=self.FS, channels=2)
self.dtype = self.stream.dtype
self.channel_count = self.stream.channels[0]
self.stream.start()
@qtc.Slot(float, float)
def beep(self, T, freq):
t = np.arange(T * self.FS) / self.FS
y = np.tile(0.1 * np.sin(t * 2 * np.pi * freq), self.channel_count)
y = y.reshape((len(y) // self.channel_count, self.channel_count)).astype(self.dtype)
self.stream.write(y)
class MainWindow(qtw.QMainWindow):
signal_beep = qtc.Signal(float, float)
def __init__(self, sound_engine):
super().__init__()
self.create_widgets()
self.place_widgets()
self.make_connections()
def create_widgets(self):
self._beep_freq_dial = qtw.QDial(minimum=200,
maximum=2000,
wrapping=False,
)
self._beep_freq_display = qtw.QLCDNumber()
self._beep_pusbutton = qtw.QPushButton("Beep")
def place_widgets(self):
self._center_layout = qtw.QVBoxLayout()
self._center_widget = qtw.QWidget()
self._center_widget.set_layout(self._center_layout)
self.set_central_widget(self._center_widget)
self._center_layout.add_widget(self._beep_freq_dial)
self._center_layout.add_widget(self._beep_freq_display)
self._center_layout.add_widget(self._beep_pusbutton)
def make_connections(self):
self._beep_pusbutton.clicked.connect(
lambda: self.signal_beep.emit(1, self._beep_freq_dial.value)
)
self.signal_beep.connect(sound_engine.beep)
self._beep_freq_display.display(self._beep_freq_dial.value)
self._beep_freq_dial.valueChanged.connect(self._beep_freq_display.display)
if __name__ == "__main__":
app = qtw.QApplication(sys.argv)
sound_engine = SoundEngine()
sound_engine.start(qtc.QThread.HighPriority)
mw = MainWindow(sound_engine)
mw.show()
app.exec()
我尝试放入其他功能而不是“声音设备”,认为硬件访问可能会导致此问题。事实并非如此,即使是简单的“睡眠”也会被阻塞。
在你的情况下,通过子类化 QThread 你不会获得任何东西。更默认的方法是将工作对象移交给线程,然后简单地运行该线程。
SoundEngine 将成为一个简单的 QObject:
...
class SoundEngine(qtc.QObject):
def __init__(self):
...
主要功能如下所示:
if __name__ == "__main__":
app = qtw.QApplication(sys.argv)
sound_engine = SoundEngine()
thread = qtc.QThread()
sound_engine.move_to_thread(thread)
thread.started.connect(sound_engine.run)
thread.start()
mw = MainWindow(sound_engine)
mw.show()
app.exec()
通过导入带有别名的模块并使用 Snake_case 作为用 C++ 构建的框架,你对自己和其他人都没有好处。 任何人发现您的问题的机会都会大大降低。