pyQt5 执行在使用 pyttsx3 时停止,即使使用 Threading

Posted

技术标签:

【中文标题】pyQt5 执行在使用 pyttsx3 时停止,即使使用 Threading【英文标题】:pyQt5 execution stops when using pyttsx3 even with Threading 【发布时间】:2021-12-16 17:18:24 【问题描述】:

嗨,基本上我正在编写一个应用程序,它应该提供一个 GUI 以及语音识别命令,并且程序应该在 TTS 中回答。我写了一个小测试程序,因为我想学习 pyQt5 的线程,因为它需要保持 GUI 响应 - 这是我目前的理解,除非它尝试 TTS,否则它似乎可以工作。

现在我有一个问题,只要我不 TTS 输入,一切正常。但是对于 pyttsx3, .runAndWait() 函数会退出我的代码的执行。 这是有问题的代码:(GUI 有滑块来检查线程是否工作)

import sys
import speech_recognition as sr
import pyttsx3
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

recognizer = sr.Recognizer()
speaker = pyttsx3.init()
voices = speaker.getProperty('voices')
# speaker.setProperty('voice', voices[33].id)
# speaker.setProperty('rate', 190)

class DlgMain(QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Test")

        self.btnStart = QPushButton("Start")
        self.btnStart.clicked.connect(self.evt_btnStart_clicked)

        self.dial = QSlider()
        self.lcd = QLCDNumber()
        self.dial.valueChanged.connect(self.lcd.display)

        self.lytLCD = QHBoxLayout()
        self.lytLCD.addWidget(self.dial)
        self.lytLCD.addWidget(self.lcd)

        self.lytMain = QVBoxLayout()
        self.lytMain.addWidget(self.btnStart)
        self.lytMain.addLayout(self.lytLCD)
        self.setLayout(self.lytMain)

    def evt_btnStart_clicked(self):
        # In this function create an instance of the worker class
        self.worker = WorkerThread()
        self.worker.start()
        # Catching our own "update" signal
        #self.worker.update_progress.connect(self.evt_update_progress)



# Whatever should be run in the thread must now run in the worker class!
class WorkerThread(QThread):
    # Create our own signal to send current info to GUI
    #update_progress = pyqtSignal(int)

    def run(self):

        global recognizer

        while True:
            try:

                with sr.Microphone(device_index=0) as source:

                    recognizer.adjust_for_ambient_noise(source, duration=.2)
                    print("Listening...")
                    recognizer.energy_threshold = 4000
                    audio = recognizer.listen(source)

                    print("Recognizing...")
                    message = recognizer.recognize_google(audio).lower()
                    speaker.say(message)
                    speaker.runAndWait()
                    # print(message)

            except sr.UnknownValueError:
                recognizer = sr.Recognizer()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    dlgMain = DlgMain()
    dlgMain.show()
    sys.exit(app.exec_())

我必须给 TTS 自己的线程吗?或者我可以用 pyqtSignal 以某种方式解决这个问题吗?我真的不确定要搜索什么,到目前为止还没有找到类似的问题。

我希望有人可以帮助我。提前致谢!

【问题讨论】:

尝试在终端或提示符下运行程序,看看是否能得到更有用的回溯。也尝试在run函数中创建所有TTS相关对象(recognizerspeakervoices)。 好的,我把tts的东西放到run函数里了。并从终端运行程序。可悲的是,这显然也只是用退出代码 0 关闭了应用程序。没有错误没有什么。我很确定它是 runAndWait() 函数。我发现其他人也有类似的问题,经常使用 tkinter(显然没有解决方案)。我想我会尝试找到一个不同的 tts 库,并希望这能解决问题。 【参考方案1】:

好的,我自己解决了这个问题。实际上,破坏程序的是 pyttsx3 的 runAndWait() 函数。相反,我现在使用 gTTS、pydub、soundfile、playsound 和 pyrubberband 的组合。

speak 函数现在如下所示:

from gtts import gTTS
from pydub import Audiosegment
import playsound
import soundfile as sf
import pyrubberband as pyrb

def speakMsg(message):

    # Write the message to mp3
    tts = gTTS(text=message, lang="en")
    tts.save("clear_msg.mp3")

    # Change the file to wav format so it can be edited
    sound = AudioSegment.from_mp3("clear_msg.mp3")
    sound.export("clear_msg.wav", format="wav")

    # Make playback faster (and pitch it down)
    data, samplerate = sf.read("clear_msg.wav")
    data_stretch = pyrb.time_stretch(data, samplerate, 1.4)
    data_shift = pyrb.pitch_shift(data_stretch, samplerate, 0)

    # Safe it as new file and play it.
    sf.write("edit_msg.wav", data_shift, samplerate, format='wav')
    playsound.playsound("edit_msg.wav")

看起来超级不必要的复杂实际上是惊人的性能!而且该程序现在也可以工作了:D 你可以只用 gTTS 做到这一点,但我认为 gTTS 的声音太慢了。所以我加快了速度并降低了音调,这样它就不会变得太“花栗鼠”了。这就是为什么我做了这么多额外的事情!

【讨论】:

以上是关于pyQt5 执行在使用 pyttsx3 时停止,即使使用 Threading的主要内容,如果未能解决你的问题,请参考以下文章

从不同类添加的小部件内的 PyQt5 停止计时器

如何在不关闭 GUI 窗口的情况下停止运行 PyQt5 程序?

在 PyQt5 中取消 QThread

PyQt5 QThread 不会因终止或标志变量而停止

pyttsx3 的使用教程

绘制实时传感器数据时,PyQtGraph 停止更新并冻结