parente/pyttsx — Issue #7: Threading problems with OS X
Author: mrmekon
Created: 2011-12-08 19:54:00+00:00
State: open

PyTTSX does not seem to work in a python thread in OS X. The exact failure depends on whether PyGTK is also in use.

I am initializing a pyttsx engine:
engine = pyttsx.Engine()

And trying to speak in an infinite loop:
while True:
text = "Say something, say something, anything."
engine.say(text)
engine.runAndWait()

I have tried this with and without PyGTK, and both in its own thread and in the main python thread. I have encountered a number of different results.

A demo is available here: https://gist.github.com/1447666

## In second thread, no GTK

*Setup:*
I do NOT import gtk or gobject. I execute the above code in a thread using Python's threading.Thread() class.

*Execution result:*
The text is spoken one time, and then the program hangs.

*Trace results:*
The queue in pyttsx's driver.py "pumps" one time, gets the text out, and speaks it. The queue never "pumps" again, so the self._engine.endLoop() function never runs. The thread hangs forever.

## In second thread, with GTK

*Setup:*
I import gtk and gobject, run gobject.threads_init(), and run gtk.main(). The above code is run in a thread.

*Execution result:*
The text is spoken once, and then the program exits.

*Trace result:*
PyTTSX's nsss.py driver calls AppHelper.runConsoleEventLoop(). When the speech is complete, self._engine.endLoop() IS removed from the queue and executes, which calls AppHelper.stopEventLoop(). In PyObjC's AppHelper, stopEventLoop() gets None for PyObjCAppHelperRunLoopStopper.currentRunLoopStopper(), and it responds by calling NSApp()._terminate(), which kills the entire application and all threads.

## In main thread, with GTK

*Setup:*
I import gtk and gobject. I do NOT run gtk.main(). The above code is run in the main thread, no secondary thread is started.

*Execution result:*
The text is spoken once, and then the program exits.

*Trace results:*
I did not trace this case, but I can tell it is different from the others (I have print statements all throughout my pyttsx and pyobjc modules -- this case takes a different path). Notably, it does not hang forever (endLoop() is called and the queue is empty), and it is not terminated by stopEventLoop().