GUI 在向 QThread 对象的槽发出信号时被阻塞

问题描述 投票:0回答:1

我正在尝试创建一个 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()

我尝试放入其他功能而不是“声音设备”,认为硬件访问可能会导致此问题。事实并非如此,即使是简单的“睡眠”也会被阻塞。

python pyside qthread pyside6
1个回答
0
投票

在你的情况下,通过子类化 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++ 构建的框架,你对自己和其他人都没有好处。 任何人发现您的问题的机会都会大大降低。

© www.soinside.com 2019 - 2024. All rights reserved.