我刚刚开始学习Python,为此,我正在致力于实现一个简单的聊天机器人。这很好,直到我想实现一些文本到语音的功能,当它们出现在屏幕上时,它会说话。 为了实现这一点,我必须深入研究多线程,而这正是我所面临的困境:
import concurrent.futures
import pyttsx3
from time import sleep
import sys
# Settings
engine = pyttsx3.init()
voices = engine.getProperty('voices')
engine.setProperty('voice', voices[0].id)
typing_delay=0.035
def textToSpeech(text):
engine.say(text)
engine.runAndWait()
def typing(sentence):
for char in sentence:
sleep(typing_delay)
sys.stdout.write(char)
sys.stdout.flush()
# def parallel(string):
# tasks = [lambda: textToSpeech(string), lambda: typing("\n> "+string+"\n\n")]
# with ThreadPoolExecutor(max_workers=2) as executor:
# futures = [executor.submit(task) for task in tasks]
# for future in futures:
# try:
# future.result()
# except Exception as e:
# print(e)
def parallel(text):
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
future_tasks = {executor.submit(textToSpeech, text), executor.submit(typing, "\n> "+text+"\n\n")}
for future in concurrent.futures.as_completed(future_tasks):
try:
data = future.result()
except Exception as e:
print(e)
# Test Sentence
parallel("Greetings Professor Falken")
上面的两个函数应该并行运行。我为parallel()函数尝试了两种不同的实现(其中一种被注释掉),但是这两种实现都会产生相同的结果。对于聊天机器人发出的第一行文本,我确实收到了文本和;演讲,但我得到了一个错误:
'NoneType' object has no attribute 'earlierDate_'
在那之后,我只得到文本,没有更多的语音和错误:run loop already started
我假设concurrent.futures
中的某个地方是属性'earlierDate_'
,并且我没有正确地处理它,因此文本到语音线程永远不会停止。但我不知道如何修复它
我希望这里有人有一个可能有用的想法。我已经将代码缩减到尽可能小的部分,但仍然可以运行和测试
附录:我在Python3.8上导入pyttsx3
时遇到问题,所以我降级到Python3.7,在那里它似乎可以工作
更新: 所以我突然想到,当我专注于多线程时,问题可能一直是我的文本到语音实现
最明显的是我在全球范围内初始化了我的语音引擎。因此,我将设置移动到textToSpeech函数中:
def textToSpeech(text):
engine = pyttsx3.init()
voices = engine.getProperty('voices')
engine.setProperty('voice', voices[0].id)
engine.say(text)
engine.runAndWait()
run loop already started
错误现在没有立即出现,我得到文本&;在前几次聊天机器人互动中讲话。
每次聊天机器人输出后,我仍然会收到'NoneType' object has no attribute 'earlierDate_'
错误,最终run loop already started
错误再次出现,我失去了声音。尽管如此,我想还是更近了一步
更新2:
又挖了一天,我想我又近了一步。 这似乎是与多线程相关的Mac特定问题。我在人们遇到这个问题的不同领域发现了多个问题
我已经在PyObjCTools/AppHelper.py中找到了问题
在这里,我们具有以下功能:
def runConsoleEventLoop(
argv=None, installInterrupt=False, mode=NSDefaultRunLoopMode, maxTimeout=3.0
):
if argv is None:
argv = sys.argv
if installInterrupt:
installMachInterrupt()
runLoop = NSRunLoop.currentRunLoop()
stopper = PyObjCAppHelperRunLoopStopper.alloc().init()
PyObjCAppHelperRunLoopStopper.addRunLoopStopper_toRunLoop_(stopper, runLoop)
try:
while stopper.shouldRun():
nextfire = runLoop.limitDateForMode_(mode)
if not stopper.shouldRun():
break
soon = NSDate.dateWithTimeIntervalSinceNow_(maxTimeout)
nextfire = nextfire.earlierDate_(soon)
if not runLoop.runMode_beforeDate_(mode, nextfire):
stopper.stop()
finally:
PyObjCAppHelperRunLoopStopper.removeRunLoopStopperFromRunLoop_(runLoop)
靠近按钮的这一行就是罪魁祸首:nextfire = nextfire.earlierDate_(soon)
对象nextfire
似乎是一个日期。在Objective-C中,NSDate对象确实有一个earlierDate()
方法,所以它应该可以工作。但是初始化有问题。当我print(nextfire)
时,我得到None
。因此,非类型对象没有“earlierDate\”属性也就不足为奇了
所以,我已经解决了我的两个问题,但是,我以一种不令人满意的方式解决了它们。有点黑。但这是我能做的最好的了,除了解剖
1)首先,对于我的
run loop already started
错误问题: 我已经将我的引擎初始化移回了textToSpeech
函数之外的全局级别(就像在我的初始代码片段中一样)。 然后,每次调用textToSpeech
函数之前,我都会输入以下代码:这样,当有一个循环已经在运行时,它会在新调用之前停止,从而防止错误的发生。如果没有循环运行,则什么也不会发生
2)我关于
'NoneType' object has no attribute 'earlierDate_'
错误的主要问题有点深。我看了各种来源,但我不完全明白那里发生了什么。 正如我在第二次更新中所写,错误源于PyObjCTools/AppHelper.py
nextfire
使用来自NSRunLoop
的limitDateForMode
方法初始化,根据Apple文档,如果此模式没有输入源,则返回nil
下一步是查看
pyttsx3/nsss.py
这个来自PyObjCTools/AppHelper.py
的方法在哪里被实例化。但是我没有真正理解实例化是如何工作的,以及如何修复NSRunLoop
显然是在没有输入源的情况下被实例化的,或者是否应该修复这个事实。 因此,我进行了一次卑鄙的攻击,通过在earlierDate_
调用中切换nextfire
和soon
来改变PyObjCTools/AppHelper.py
:soon
变量始终正确地实例化为NSDate
对象,因此方法earlierDate
可用。显然,它也可以用nil
作为参数当然,如果在两个相关库中的一个库中有一个合适的修复程序,那就更好了。我的猜测是
pyttsx3/nsss.py
需要一些工作,但我对NSRunLoops
了解得不够,无法得出明智的意见相关问题 更多 >
编程相关推荐