简体   繁体   中英

Python two things at the same time with threading

I'm creating a small program, just for fun, that opens programs and do things like that.

I'm doing a Wikipedia search and the program will read it out and I want the text to be written out to the screen by MessageBoxW.

I want this two thing to happen at the same, because now it frist showing the message box and after I closed the window then it's reading up the text

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()

Create a helper class that runs pyttsx in a separate thread:

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

Because pyttsx behaves like a singleton , and only one instance of pyttsx can speak in the same Python process at any time, I encapsulated the global variables into the Say class, and have the instance constructor wait for any existing utterances to complete, then start a new thread for pyttsx to speak.

Essentially, Say(message) waits for any utterances in progress to complete, then starts speaking the new voice, and returns. It does not wait for the message to be completely spoken before it returns; it returns immediately when the message begins.

WaitAllSaid() waits for any utterances in progress, then reaps the worker thread. If you want to modify the pyttsx engine or voice properties, call WaitAllSaid() first to make sure no utterances are in progress at that time. Otherwise poor pyttsx might get confused.

The last four lines of OP's wikipediaSearch function now becomes something like

    print(new_text)

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

If pyttsx is already speaking, then Say() blocks until all previous messages have been said. It returns immediately when the specified message starts to play.

The WaitAllSaid() just blocks until everything that has been said has been uttered. You can omit it from the wikipediaSearch() function, as long as you make sure that WaitAllSaid() is called before the Python program exits.

On the not-exactly conventional design: On Linux at least, pyttsx has issues if one tries to use the same pyttsx object for separate statements. Having the helper thread create the instance works much better. Testing on Linux, this pattern was the most robust one among global variables and various forms of singleton classes. I do not use Windows at all, so I cannot test on it, alas.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM