How to use pyttsx in a python thread - multithreading

I am working with pyttsx3 for text-to-speech. I realized that I can use it within a thread (or I am doing something wrong). Do you know why?
Code Example:
from threading import Thread
import pyttsx3
def myfunc():
engine = pyttsx3.init()
engine.say("ok")
engine.runAndWait()
t = Thread(target=myfunc)
t.start()
Error:
File "/usr/local/Cellar/python3/3.6.4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/usr/local/Cellar/python3/3.6.4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "test.py", line 9, in myfunc
engine.runAndWait() #blocks
File "/usr/local/lib/python3.6/site-packages/pyttsx3/engine.py", line 188, in runAndWait
self.proxy.runAndWait()
File "/usr/local/lib/python3.6/site-packages/pyttsx3/driver.py", line 204, in runAndWait
self._driver.startLoop()
File "/usr/local/lib/python3.6/site-packages/pyttsx3/drivers/nsss.py", line 33, in startLoop
AppHelper.runConsoleEventLoop()
File "/usr/local/lib/python3.6/site-packages/PyObjCTools/AppHelper.py", line 241, in runConsoleEventLoop
nextfire = nextfire.earlierDate_(soon)
AttributeError: 'NoneType' object has no attribute 'earlierDate_'

The error seems to be that it doesn't run in threads on osx. Here are some examples that might work well:
If you just need to convert text into speech, you can use os.system('say %s'):
import os
def myfunc():
os.system('say ok')
gTTS or Google's TextToSpeech engine, supports 64 languages, including italian. Usage:
from gtts import gTTS
import os
tts = gTTS(text='Good morning', lang='it')
tts.save("good.mp3")
os.system("mpg321 good.mp3")

I think pyttsx3 can't make a sound in multiple threads (it's OK to make a sound only once). If you want to make a sound in multiple threads, the Macos system should use the say command.
Other operating systems should also have corresponding commands
import sys
import os
import threading
print('threading1:',threading.activeCount())
text = 'Please start'
def myfunc():
while True:
# os.system('say another')
# os.system('say -v Daniel "another"')
# os.system('say -v Daniel "[[rate 160]] another"') # 速度默认200
os.system('say -v Daniel -r 140 "{}"'.format(text))
os.system('say -v Samantha -r 140 "{}"'.format(text))
# print(111)
loopThread = threading.Thread(target=myfunc, name='backgroundMusicThread')
loopThread.daemon = True
loopThread.start()
print('threading2:',threading.activeCount())
while True:
text = input('input:\n')
if text == 'end':
sys.exit()

Related

No current context error after Python 3 migration, should I include more logic?

I'm converting a Google App Engine Python 2 project to Python 3
My understanding from reading the documentation I understand the preferred path is to run the main python program directly, skipping dev_appserver.py as was done in the past.
https://cloud.google.com/appengine/docs/standard/python3/tools/local-devserver-command
python3 main.py
--- main.py --
# coding: utf-8
import flask
from flask import request, redirect, url_for
from flask_basicauth import BasicAuth
#from urllib# import urlparse, urlunparse
import config
--- config.py
import model
...
33: CONFIG_DB = model.Config.get_master_db()
--- model/config.py
from __future__ import absolute_import
from google.cloud import ndb
#from oauth2client.contrib.appengine import CredentialsNDBProperty
from api import fields
import config
import model
import util
...
class Config(model.Base, model.ConfigAuth):
...
#classmethod
def get_master_db(cls):
57: return cls.get_or_insert('master')
When running the following trace is prod
Traceback (most recent call last):
File "main.py", line 9, in <module>
import config
File "/home/ffej/cloudpayback/main/config.py", line 33, in <module>
CONFIG_DB = model.Config.get_master_db()
File "/home/ffej/cloudpayback/main/model/config.py", line 57, in get_master_db
return cls.get_or_insert('master')
File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/_options.py", line 89, in wrapper
return wrapped(*pass_args, **kwargs)
File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/utils.py", line 146, in positional_wrapper
return wrapped(*args, **kwds)
File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/model.py", line 5698, in _get_or_insert
return cls._get_or_insert_async(
File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/_options.py", line 89, in wrapper
return wrapped(*pass_args, **kwargs)
File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/utils.py", line 146, in positional_wrapper
return wrapped(*args, **kwds)
File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/model.py", line 5811, in _get_or_insert_async
key = key_module.Key(cls._get_kind(), name, parent=parent, **key_args)
File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/key.py", line 290, in __new__
context = context_module.get_context()
File "/home/ffej/cloudpayback/env/lib/python3.8/site-packages/google/cloud/ndb/context.py", line 96, in get_context
raise exceptions.ContextError()
google.cloud.ndb.exceptions.ContextError: No current context. NDB calls must be made in context established by google.cloud.ndb.Client.context.
Is there additional logic that should be included after migration to start/initialize the datastore?
Thanks,
Jeff
The API for NDB library in Python3 has changed significantly. For developing on localhost you have to:
run DataStore Emulator, since you're not running dev_appserver anymore:
$ gcloud beta emulators datastore start
if you use the new NDB Library, then each NDB operation needs to be wrapped in a context manager:
with ndb_client.context(): # <- you need this line
cls.get_or_insert('master')
edit: instead of wrapping each NDB call with a context manager, you can use a middleware which will wrap the whole request cycle into the NDB context:
class NDBMiddleware:
def __init__(self, app):
self.app = app
self.client = ndb_client
def __call__(self, environ, start_response):
with self.client.context():
return self.app(environ, start_response)
app = Flask(__name__)
app.wsgi_app = NDBMiddleware(app.wsgi_app)

Pyinstaller "ValueError: Can't mix absolute and relative paths"

I am using windows 10 and anaconda3 to manage my python packages. This is my first time to use python, and I'm trying to make my own gui program with pyqt5. Also I'm trying to make .exe file using Pyinstaller. The issue I am running into is that the .exe is throwing the error block:
(pyqt5_env) C:\Python Projects>pyinstaller -w -F App_ver05.py
268 INFO: PyInstaller: 4.0.dev0+b3dd91c8a8
268 INFO: Python: 3.7.7 (conda)
268 INFO: Platform: Windows-10-10.0.18362-SP0
Traceback (most recent call last):
File "c:\anaconda3\envs\pyqt5_env\lib\runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "c:\anaconda3\envs\pyqt5_env\lib\runpy.py", line 85, in _run_code
exec(code, run_globals)
File "C:\Anaconda3\envs\pyqt5_env\Scripts\pyinstaller.exe\__main__.py", line 7, in <module>
File "c:\anaconda3\envs\pyqt5_env\lib\site-packages\PyInstaller\__main__.py", line 112, in run
spec_file = run_makespec(**vars(args))
File "c:\anaconda3\envs\pyqt5_env\lib\site-packages\PyInstaller\__main__.py", line 58, in run_makespec
spec_file = PyInstaller.building.makespec.main(filenames, **opts)
File "c:\anaconda3\envs\pyqt5_env\lib\site-packages\PyInstaller\building\makespec.py", line 458, in main
specfile.write(onefiletmplt % d)
File "c:\anaconda3\envs\pyqt5_env\lib\site-packages\PyInstaller\building\makespec.py", line 101, in __repr__
self.variable_prefix, self.filename_suffix = make_variable_path(self.path)
File "c:\anaconda3\envs\pyqt5_env\lib\site-packages\PyInstaller\building\makespec.py", line 84, in make_variable_path
if os.path.commonpath([filename, from_path]) == from_path:
File "c:\anaconda3\envs\pyqt5_env\lib\ntpath.py", line 615, in commonpath
raise ValueError("Can't mix absolute and relative paths") from None
ValueError: Can't mix absolute and relative paths
The same error occurs regardless of which .py file is used. For information, I wrote the used code below.
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
btn1 = QPushButton('&Button1', self)
btn1.setCheckable(True)
btn1.toggle()
vbox = QVBoxLayout()
vbox.addWidget(btn1)
self.setLayout(vbox)
self.setWindowTitle('QPushButton')
self.setGeometry(300, 300, 300, 200)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyApp()
sys.exit(app.exec_())
I uninstalled and reinstalled pyinstaller but it didn't work. I don't think its a code issue because the code is really simple. Can anyone give a solution or hint for this problem?
What happened for me is that the absolute path name had a space in it. Once the project was moved to a directory without spaces it was able to build
The solution of this problem is to use the full file name and append the flag -F to the file name. In your case for example if for the file App_ver05.py the absolute path is :
/home/user123/Desktop/foldername/App_ver05.py
Then use the command :
pyinstaller -F /home/user123/Desktop/foldername/App_ver05.py

Error when loading audio file from zip in python

I am making a game, and I need to load some password protected audio files from a .zip file, but I get this error:
io.UnsupportedOperation: seek
io.UnsupportedOperation: seek
io.UnsupportedOperation: seek
b'hey you did it!' #THIS IS FROM THE PROGRAM
Traceback (most recent call last):
File "C:\Python36\lib\zipfile.py", line 849, in read
data = self._read1(n)
File "C:\Python36\lib\zipfile.py", line 917, in _read1
data += self._read2(n - len(data))
File "C:\Python36\lib\zipfile.py", line 949, in _read2
data = self._fileobj.read(n)
File "C:\Python36\lib\zipfile.py", line 705, in read
self._file.seek(self._pos)
AttributeError: 'NoneType' object has no attribute 'seek'
And this is my code below:
from zipfile import ZipFile
from PIL import Image
from io import BytesIO
import pygame
from pygame.locals import *
import pyganim
import sys
pygame.init()
root = pygame.display.set_mode((320, 240), 0, 32)
pygame.display.set_caption('image load test')
#THIS IS HOW TO LOAD IMAGES (WORKS)
with ZipFile("spam.zip", 'r') as archive:
mcimg = archive.read('a.png', pwd=b'onlyforthedev')
mc = pygame.image.load(BytesIO(mcimg))
anime = pyganim.PygAnimation([(mc, 100),
(mc, 100)])
anime.play()
#THIS IS HOW TO LOAD MUSIC (DOES NOT WORK)
with ZipFile('spam.zip') as zippie:
with zippie.open('zora.mp3', pwd=b'onlyforthedev') as zora:
pygame.mixer.music.load(zora)
pygame.mixer.music.play(-1)
#THIS IS HOW TO LOAD TEXT (WORKS)
with ZipFile('spam.zip') as myzip:
with myzip.open('eggs.txt', pwd=b'onlyforthedev') as myfile:
print(myfile.read())
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
root.fill((100, 50, 50))
anime.blit(root, (100, 50))
pygame.display.update()
What can I do to load sound files without raising such an error? And what is 'seek'?
I also get this error on python 3.6.
I am going to guess that pygame.mixer.music.load calls the seek method on zippie, which is a ZipExtFile.
From python 3.7 ZipExtFile objects now have a seek method. I think that if you upgrade to python 3.7.2 or newer, then your error should go away.
Try to replace
pygame.mixer.music.load(zora)
with
with BytesIO(zora.read()) as zora_bio:
pygame.mixer.music.load(zora_bio)
This worked for me on python 3.6 with h5py.File().
I'm guessing it's the same problem as with pygame..load().
EDIT:
I now realize the above solution already exists in your code when you LOAD IMAGES:
with ZipFile("spam.zip", 'r') as archive:
mcimg = archive.read('a.png', pwd=b'onlyforthedev')
mc = pygame.image.load(BytesIO(mcimg))
So for uniformity, you could LOAD MUSIC in a similar way:
with ZipFile('spam.zip') as zippie:
zora = zippie.read('zora.mp3', pwd=b'onlyforthedev')
pygame.mixer.music.load(BytesIO(zora))

GUI under embedded Linux / python 3.3.2

I'm writing a GTK+ app using embedded/extended Python3 as a scripting language. What I'd like do is make it possible for those scripts to include menus, but, so far, things aren't cooperating.
I do the usual python initialisation with:
PyImport_AppendInittab("gfig", &PyInit_gfig);
Py_SetProgramName (L"gfig");
Py_Initialize();
and then some setup with:
PyRun_SimpleString (
"import gfig\n"
"import sys\n" ........ )
(gfig is the app I'm writing, as well as the name of extension module.)
Then I run the app using
PyRun_File (fp, pyfile, Py_file_input, global_dict, local_dict);
to run the script:
from gi.repository import Gtk
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Hello World")
self.button = Gtk.Button(label="Click Here")
self.button.connect("clicked", self.on_button_clicked)
self.add(self.button)
def on_button_clicked(self, widget):
print("Hello World")
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
This results in:
[0] /mnt/disk5/personal/tinkering/gf3/src >./gf py4
Traceback (most recent call last): File "py4", line 3, in
from gi.repository import Gtk File "",
line 1567, in _find_and_load File "",
line 1534, in _find_and_load_unlocked File "/usr/lib64/python3.3/site-packages/gi/importer.py",
line 68, inload_module dynamic_module._load() File "/usr/lib64/python3.3/site-packages/gi/module.py",
line 289, in _load self._overrides_module = importlib.import_module('gi.overrides.' +
self._namespace) File "/usr/lib64/python3.3/importlib/init.py",
line 90, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "/usr/lib64/python3.3/site-packages/gi/overrides/Gtk.py",
line 1523,in initialized, argv = Gtk.init_check(sys.argv)
AttributeError 'module' object has no attribute 'argv'
Here's the script:
from gi.repository import Gtk
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Hello World")
self.button = Gtk.Button(label="Click Here")
self.button.connect("clicked", self.on_button_clicked)
self.add(self.button)
def on_button_clicked(self, widget):
print("Hello World")
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
which works fine when from the command line:
[0] /mnt/disk5/personal/tinkering/gf3/src >python3 py4
I see that the script ends with "Gtk.main()" which, since I'm running python embedded under GTK+, looks like it may give me trouble in the future.
(I've tried Tk, wx, and, I think, Qt. None of them worked, but I didn't explore why not in any detail. I'd like to use Gtk.)
Any help greatly appreciated.

How to replace execfile() with exec() in Pydev on Ctrl+Alt+Enter key binding?

There's a problem in Pydev 2.5 and Python 3.2 with trying to load the module contents "into" the interactive console: when you hit Ctrl+Alt+Enter, Pydev fires execfile(filename) instead of exec(compile(open(filename).read(), filename, 'exec'), globals, locals) - the latter being execfile()'s replacement in Python 3+...
So, how to change this behaviour?
ETA: to be a bit more concrete, things go like this: I create a new PyDev Module, say 'test.py', write some simple function def f(n): print(n), hit Ctrl+Alt+Enter, then I select "Console for currently active editor" and Python 3.2 interpreter, interactive console wakes up, and then I get this:
>>> import sys; print('%s %s' % (sys.executable or sys.platform, sys.version))
PyDev console: using default backend (IPython not available).
C:\Program Files (x86)\Python\3.2\python.exe 3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)]
>>> execfile('C:\\testy.py')
>>> f(1)
Traceback (most recent call last):
File "<console>", line 1, in <module>
NameError: name 'f' is not defined
As you can see, it still uses execfile() instead of exec(), that replaced it in Python 3+...
EDIT:
The actual problem is that the execfile is not getting the proper globals in the PyDev redefinition. So, if the execfile did execfile('my_file', globals()), it would work... I'll change the implementation of PyDev so that if the globals is not passed, it'll do:
if glob is None:
import sys
glob = sys._getframe().f_back.f_globals
(you can fix that in your local install at plugins/org.python.pydev_XXX/PySrc/_pydev_execfile.py and it should work -- please let me know if it doesn't).
INITIAL ANSWER:
That's not possible, but PyDev does redefine execfile when you're in its console on Python 3, so, it should still work... is that not working for you for some reason?
Also, the replacement: exec(compile(open(filename).read(), filename, 'exec'), globals, locals) is broken for some situations in Python 3.
The actual redefinition (which should work in all situations and is available in PyDev) is:
#We must redefine it in Py3k if it's not already there
def execfile(file, glob=None, loc=None):
if glob is None:
glob = globals()
if loc is None:
loc = glob
stream = open(file, 'rb')
try:
encoding = None
#Get encoding!
for _i in range(2):
line = stream.readline() #Should not raise an exception even if there are no more contents
#Must be a comment line
if line.strip().startswith(b'#'):
#Don't import re if there's no chance that there's an encoding in the line
if b'coding' in line:
import re
p = re.search(br"coding[:=]\s*([-\w.]+)", line)
if p:
try:
encoding = p.group(1).decode('ascii')
break
except:
encoding = None
finally:
stream.close()
if encoding:
stream = open(file, encoding=encoding)
else:
stream = open(file)
try:
contents = stream.read()
finally:
stream.close()
exec(compile(contents+"\n", file, 'exec'), glob, loc) #execute the script

Resources