“NoneType”对象没有属性“earlierDate”

2024-10-02 08:18:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我刚刚开始学习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\”属性也就不足为奇了


Tags: text文本typingdefas错误语音future
1条回答
网友
1楼 · 发布于 2024-10-02 08:18:33

所以,我已经解决了我的两个问题,但是,我以一种不令人满意的方式解决了它们。有点黑。但这是我能做的最好的了,除了解剖

1)首先,对于我的run loop already started错误问题: 我已经将我的引擎初始化移回了textToSpeech函数之外的全局级别(就像在我的初始代码片段中一样)。 然后,每次调用textToSpeech函数之前,我都会输入以下代码:

try:
    engine.endLoop()
except Exception as e:
    pass

这样,当有一个循环已经在运行时,它会在新调用之前停止,从而防止错误的发生。如果没有循环运行,则什么也不会发生

2)我关于'NoneType' object has no attribute 'earlierDate_'错误的主要问题有点深。我看了各种来源,但我不完全明白那里发生了什么。 正如我在第二次更新中所写,错误源于PyObjCTools/AppHelper.pynextfire使用来自NSRunLooplimitDateForMode方法初始化,根据Apple文档,如果此模式没有输入源,则返回nil

下一步是查看pyttsx3/nsss.py这个来自PyObjCTools/AppHelper.py的方法在哪里被实例化。但是我没有真正理解实例化是如何工作的,以及如何修复NSRunLoop显然是在没有输入源的情况下被实例化的,或者是否应该修复这个事实。 因此,我进行了一次卑鄙的攻击,通过在earlierDate_调用中切换nextfiresoon来改变PyObjCTools/AppHelper.py

    try:

        while stopper.shouldRun():
            nextfire = runLoop.limitDateForMode_(mode)
            if not stopper.shouldRun():
                break

            soon = NSDate.dateWithTimeIntervalSinceNow_(maxTimeout)
            nextfire = soon.earlierDate_(nextfire)
            if not runLoop.runMode_beforeDate_(mode, nextfire):
                stopper.stop()

    finally:
        PyObjCAppHelperRunLoopStopper.removeRunLoopStopperFromRunLoop_(runLoop)

soon变量始终正确地实例化为NSDate对象,因此方法earlierDate可用。显然,它也可以用nil作为参数

当然,如果在两个相关库中的一个库中有一个合适的修复程序,那就更好了。我的猜测是pyttsx3/nsss.py需要一些工作,但我对NSRunLoops了解得不够,无法得出明智的意见

相关问题 更多 >

    热门问题