Python 用线程同时做两件事

Python two things at the same time with threading

我正在创建一个小程序,只是为了好玩,它可以打开程序并执行类似的操作。

我正在进行维基百科搜索,程序会读出它,我希望 MessageBoxW 将文本写到屏幕上。

我希望这两件事同时发生,因为现在它首先显示消息框,在我关闭 window 之后它正在读取文本

def Mbox(title, text, style):
    return ctypes.windll.user32.MessageBoxW(0, text, title, style)

def Mbox(title, text, style):
    return ctypes.windll.user32.MessageBoxW(0, text, title, style)


def wikipediaSearch():
    global user_input

    user_input = user_input.replace("search", '')
    result = wikipedia.summary(user_input, sentences=2)

    raw_text = result
    convert_text = unicodedata.normalize('NFKD', raw_text).encode('ascii', 'ignore')
    convert_text = convert_text.decode('utf-8')
    new_text = re.sub(r'\(.*\)', '', convert_text)
    print(new_text)

    Mbox(user_input, new_text, 0)

    voice.say(new_text)
    voice.runAndWait()

创建一个在单独的线程中运行 pyttsx 的助手 class:

import threading
import pyttsx

class Say(object):
    """Function-like class to speak using pyttsx."""
    _thread = None

    def __init__(self, message):
        if not isinstance(message, str):
            raise ValueError("message is not a string")
        if Say._thread is not None:
            Say._thread.join()
            Say._thread = None
        Say._thread = threading.Thread(target=self._worker,
                                       name="Say",
                                       args=(message,))
        Say._thread.start()

    def _worker(self, message):
        engine = pyttsx.init()
        engine.say(message)
        engine.runAndWait()

def WaitAllSaid():
    if Say._thread is not None:
        Say._thread.join()
        Say._thread = None

因为pyttsx的行为像一个singleton,并且任何时候在同一个Python进程中只有一个pyttsx实例可以说话,所以我将全局变量封装到Sayclass,并让实例构造函数等待任何现有话语完成,然后启动一个新线程让 pyttsx 说话。

本质上,Say(message) 等待任何正在进行的话语完成,然后开始说出新的声音,然后 returns。它不会等到消息完全说完才开始returns;它 returns 消息开始时立即。

WaitAllSaid() 等待正在进行的任何话语,然后获取工作线程。如果要修改 pyttsx 引擎或语音属性,请先调用 WaitAllSaid() 以确保当时没有正在进行的话语。否则可怜的 pyttsx 可能会感到困惑。

OP 的 wikipediaSearch 函数的最后四行现在变成了

    print(new_text)

    Say(new_text)
    Mbox(user_input, new_text, 0)
    WaitAllSaid()

如果 pyttsx 已经在说话,那么 Say() 会阻塞,直到前面的所有消息都被说完。它 returns 当指定的消息开始播放时立即。

WaitAllSaid() 只是 blocks 直到所说的一切都说完。您可以从 wikipediaSearch() 函数中省略它,只要您确保在 Python 程序退出之前调用 WaitAllSaid()

关于不完全常规的设计:至少在 Linux 上,如果试图对不同的语句使用相同的 pyttsx 对象,pyttsx 就会出现问题。让辅助线程创建实例效果更好。在 Linux 上测试,该模式是全局变量和各种形式的单例 classes 中最健壮的模式。我根本不使用 Windows,所以我无法对其进行测试,唉。