This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 11 years ago.
I have a small python application, which uses pyttsx for some text to speech.
How it works:
simply say whatever is there in the clipboard.
The program works as expected inside eclipse. But if run on cmd.exe it only works partly if the text on the clipboard is too large(a few paras). Why ?
when run from cmd, it prints statements , but the actual 'talking' doesn't work(if the clipboard text is too large
Here is a of the program part which actually does the talking: As can be seen the 'talking' part is handled inside a thread.
def saythread(queue , text , pauselocation, startingPoint):
saythread.pauselocation = pauselocation
saythread.pause = 0
saythread.engine = pyttsx.init()
saythread.pausequeue1 = False
def onWord(name, location, length):
saythread.pausequeue1 = queue.get(False)
saythread.pause = location
saythread.pauselocation.append(location)
if saythread.pausequeue1 == True :
saythread.engine.stop()
def onFinishUtterance(name, completed):
if completed == True:
os._exit(0)
def engineRun():
if len(saythread.pauselocation) == 1:
rate = saythread.engine.getProperty('rate')
print rate
saythread.engine.setProperty('rate', rate-30)
textMod = text[startingPoint:]
saythread.engine.say(text[startingPoint:])
token = saythread.engine.connect("started-word" , onWord )
saythread.engine.connect("finished-utterance" , onFinishUtterance )
saythread.engine.startLoop(True)
engineRun()
if saythread.pausequeue1 == False:
os._exit(1)
def runNewThread(wordsToSay, startingPoint):
global queue, pauselocation
e1 = (queue, wordsToSay, pauselocation, startingPoint)
t1 = threading.Thread(target=saythread,args=e1)
t1.start()
#wordsToSay = CLIPBOARD CONTENTS
runNewThread(wordsToSay,0)
Thanks
Edit: I have checked than the python version used is the same 2.7 . The command used to run the program in cmd : python d:\python\play\speech\speechplay.py
Checked that the problem is not in the code that reads the text from the clipboard.
You should check if your eclipse setup specifies custom environment variables for the project which do not exist outside Eclipse. Especially:
PYTHONPATH (and also additional projects on which your program could depend in your setup)
PATH
Use
import os
print os.environ['PATH']
print os.environ['PYTHONPATH']
at the beginning of your program to compare both settings.
Misc stylistic advices:
don't use os._exit, prefer sys.exit (you should only use os._exit in a child process after a call to os.fork, which is not available on Windows)
I think a threading.Event would be more appropriate than a queue.Queue
I'd use a subclass approach for the thread with methods rather than a function with inner functions
For example:
import threading
import sys
import pyttsx
class SayThread(threading.Thread):
def __init__(self, queue, text, pauselocation, startingPoint, debug=False):
threading.Thread.__init__(self)
self.queue = queue
self.text = text
self.pauselocation = pauselocation
self.startingPoint = startingPoint
self.pause = 0
self.engine = pyttsx.init(debug=debug)
self.pausequeue1 = False
def run(self):
if len(self.pauselocation) == 1:
rate = self.engine.getProperty('rate')
print rate
self.engine.setProperty('rate', rate-30)
textMod = self.text[self.startingPoint:]
self.engine.say(self.text[self.startingPoint:])
self.engine.connect("started-word", self.onWord )
self.engine.connect("finished-utterance", self.onFinishUtterance )
self.engine.startLoop(True)
if self.pausequeue1 == False:
sys.exit(1)
def onWord(self, name, location, length):
self.pausequeue1 = self.queue.get(False)
self.pause = location
self.pauselocation.append(location)
if self.pausequeue1 == True :
self.engine.stop()
def onFinishUtterance(self, name, completed):
if completed == True:
sys.exit(0)
def runNewThread(wordsToSay, startingPoint):
global queue, pauselocation
t1 = SayThread(queue, wordsToSay,
pauselocation, startingPoint)
t1.start()
#wordsToSay = CLIPBOARD CONTENTS
runNewThread(wordsToSay,0)
In fact, eclipse itself uses a commandline command to start it's apps.
You should check what command eclipse is giving to start the program. It might be a bit verbose, but you can start from there and test what is necessary and what isn't.
You can find out the commandline eclipse uses by running the program and then selecting the output in the debug window. Right-click it, select properties and you're done.
If you don't have a debug window you can open it window/show view/(other possibly)/debug.
turns out pythonpath wasn't set properly on my system.
Edit: turns out pythonpath isn't the problem. I have no idea whats the problem. arghhhhhhhhhhhhhhhhhhhhhhhh
Related
I'm developing a Kodi add-on that uses a custom overlay. It's almost completely working now, except for one runtime error I get every time I start a video, after reaching 5 seconds in the video ("Error Contents: Control does not exist in window"). Here's the code for addon.py below:
import xbmc, xbmcaddon, xbmcgui, os
ADDON = xbmcaddon.Addon()
addonpath = ADDON.getAddonInfo('path')
class OverlayBackground(object):
def __init__(self):
self.showing = False
self.window = xbmcgui.Window()
origin_x = 0
origin_y = 0
window_w = self.window.getWidth()
window_h = self.window.getHeight()
self._background = xbmcgui.ControlImage(origin_x, origin_y, window_w, window_h, os.path.join(addonpath,"resources","skins","default","media","background.png"))
def show(self):
self.showing=True
self.window.addControl(self._background)
def hide(self):
self.showing=False
self.window.removeControl(self._background)
def _close(self):
if self.showing:
self.hide()
else:
pass
try:
self.window.clearProperties()
except: pass
if (__name__ == '__main__'):
monitor = xbmc.Monitor()
while not monitor.abortRequested():
if xbmc.Player().isPlaying():
theOverlay = OverlayBackground()
if(xbmc.Player().getTime() < 5):
theOverlay.show()
else:
theOverlay.hide()
xbmc.sleep(1000)
The error came from "self.window.removeControl(self._background)", as mentioned in this paste bin. It seems odd, because I'm sure that the control was added prior to running this command, so I'm not sure what's causing this command to crash the add-on.
I tried asking over in the Kodi forums for help, and was told that I need to get the ID of the player that I'm controlling (and linked to the documentation for Control), but I'm not sure how to implement that, even after researching the documentation extensively.
I would highly appreciate any clarification on this. Thank you so much in advance!
you need to check if the overlay already removed
def show(self):
if not self.showing:
self.showing=True
self.window.addControl(self._background)
def hide(self):
if self.showing:
self.showing=False
self.window.removeControl(self._background)
Source code
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
if is_admin():
app = Application(backend='uia').start("C:\\Program Files (x86)\\Advantech\\AdamApax.NET Utility\\Program\\AdamNET.exe")
win = app['Advantech Adam/Apax .NET Utility (Win32) Version 2.05.11 (B19)']
win.wait('ready')
win.menu_select("Setup->Refresh Serial and Ethernet")
win.top_window().print_control_identifiers(filename="file.txt")
# win.top_window().OKButton.click_input() ---------This is what I hope to do
else
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)
Problem Statement
I had to run this application with elevation rights. The above is my code. The problem is I can't identify the window (view in output image) that pops up after selection from menu. I need to close the window. Please excuse the line
win.top_window().print_control_identifiers(filename="file.txt")
It was meant write the identifiers into a text file because the structure of this code does not display the outputs for me to view. However, since nothing is appended, I guess pywinauto couldn't identify the dialog.
For a clearer understanding, please view the image (input) of when it selects the menu.
Input
Now, it pops up with this dialog (output)
Output
I've also used spy to identify the caption and it gives:
(Handle: 004E07D4,
Caption: Information,
Class: #32770(Dialog),
Style: 94C801C5)
Other things I've tried:
Besides using win.topwindow() to identify the dialog, I've used
win[Information].OKButton.click_input()
win[Information].OK.click_input()
win[Information].OK.close()
win[Information].OK.kill(soft=false)
win.Information.OKButton.click_input()
win.Information.OK.click_input()
win.Information.OK.close()
win.Information.OK.kill(soft=false)
app[Information] ...... curious if I could discover the new window from original application
I've also send keys like enter, space, esc & alt-f4 to close the dialog with libraries like keyboard, pynput & ctypes. It still doesn't work.
Link to download the same application: http://downloadt.advantech.com/download/downloadsr.aspx?File_Id=1-1NHAMZX
Any help would be greatly appreciated !
I finally found a thread that demonstrated the way multi thread works to solve this issue. I tried it myself and it works. It's a little different as a few parts of the code have depreciated. Here is the link to the solution:
How to stop a warning dialog from halting execution of a Python program that's controlling it?
Here are the edits I made to solve the problem:
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
if is_admin():
def __init__(self, window_name, quit_event):
threading.Thread.__init__(self)
self.quit_event = quit_event
self.window_name = window_name
def run(self):
while True:
try:
handles = windows.find_windows(title=self.window_name)
except windows.WindowNotFoundError:
pass
else:
for hwnd in handles:
app = Application()
app.connect(handle=hwnd)
popup = app[self.window_name]
popup.close()
if self.quit_event.is_set():
break
time.sleep(1)
quit_event = threading.Event()
mythread = ClearPopupThread('Information', quit_event)
mythread.start()
application = Application(backend="uia").start("C:\\Program Files (x86)\\Advantech\\AdamApax.NET Utility\\Program\\AdamNET.exe")
time.sleep(2)
win = application['Advantech Adam/Apax .NET Utility (Win32) Version 2.05.11 (B19)']
win.menu_select("Setup->Refresh Serial and Ethernet")
quit_event.set()
else:
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)
The best thing is this solution works for every other dialog that halts the main script from working & I could use them to do different actions like clicking buttons, inserting values, by adding more multi threads.
Basically, I am asking how to put a continuously updating program on display into tkinter's text widget.
from tkinter import Tk, Frame, Text, BOTH
class FrameApp(Frame):
def __init__(self, parent):
Frame.__init__(self, parent, background="white")
self.parent = parent
self.parent.title("Ethis")
self.pack(fill=BOTH, expand=1)
self.centerWindow()
def centerWindow(self):
w = 900
h = 450
sw = self.parent.winfo_screenwidth()
sh = self.parent.winfo_screenheight()
x = (sw - w)/2
y = (sh - h)/2
self.parent.geometry("%dx%d+%d+%d" % (w, h, x, y))
def theText(self):
w = Text ()
def main():
root=Tk()
app = FrameApp(root)
root.mainloop()
if __name__ == '__main__':
main()
This is my tkinter program. As you can see, I've centered it and set it up with a text function defined as theText(self). I have done anything with theText(self) because I don't know where to begin. This works fine alone, it starts up, as expected, in the center with it's title.
# Money Generator Mark 1
import time
t = 'true'
while t == 'true':
s = 0
x = 1
print ("You have $%s." % (s))
time.sleep(.75)
t = 'false'
while t == 'false':
s = s + (1 * x)
print ("You have $%s." % (s))
time.sleep(.75)
if s >= 100 and s < 200:
x = 2
if s >= 200:
x = 4
Here I have another program that works fine on it's own. I've dubbed it Money Generator as is akin to Cookie Clicker and Candy Box, those types of things. This also works fine in the command box, function and printing to there. I was wondering how to integrate these two separate programs so that the second program that is listed here will be displayed in tkinter's window.
Here is my new code that has a new issue. I'm receiving an error stating that 'generate_money' is not defined in the theText function. These new functions are within my frameApp class.
def theText(self):
self.w = Text()
self.t = threading.Thread(target=generate_money, args=(self.w))
self.t.daemon = True
def generate_money(textwidget):
p = subprocess.Popen([sys.executable, os.path.join('window.py', 'moneygenerator.py')],
stdout = subprocess.PIPE)
for line in p.stdout:
do_stuff_with(textwidget, line)
p.close()
This is unfortunately going to be a little trickier than you'd like.
The first part is easy: you can just use the subprocess module to run the background script and capture its output:
p = subprocess.Popen([sys.executable, os.path.join(scriptpath, 'moneygenerator.py')],
stdout = subprocess.PIPE)
for line in p.stdout:
do_stuff_with(line)
p.close()
The problem is that doing this in the middle of a Tkinter callback will block the entire program until the background program is done. So, instead of getting updated on each line, you'll just freeze up the app until the OS kills you/displays a beachball/etc.
There are two usual solutions to doing stuff without blocking: Do it on a thread—which is easy, except that you can't access Tkinter widgets from a background thread. Or check the subprocess's pipe in a non-blocking way—which would be great if there were a cross-platform way to do that without potentially blocking forever. (There are, however, third-party "async subprocess" wrappers on PyPI, which may be an alternate way to solve this problem.)
So, you need to combine both of these. Have a thread that blocks on the subprocess and posts messages on something that you can check in a non-blocking way in the main thread, like a queue.Queue.
But, instead of writing it yourself, there's a nice wrapper called mtTkinter that does most of the hard part for you. You can just write your background thread as if it were legal to access the widgets, and it will intercept that access and turn it into queue posts. Sadly, mtTkinter doesn't work in Python 3—but it looks pretty easy to fix; I slapped together a port in a few minutes. Although I haven't done much testing on it, I think this is probably the simplest way forward.
So:
from tkinter import Tk, Frame, Text, BOTH
import subprocess
import sys
import threading
# ...
def generate_money(textwidget):
p = subprocess.Popen([sys.executable, os.path.join(scriptpath, 'moneygenerator.py')],
stdout = subprocess.PIPE)
for line in p.stdout:
do_stuff_with(textwidget, line)
p.close()
# ...
class FrameApp(Frame):
# ...
def theText(self):
self.w = Text()
self.t = threading.Thread(target=generate_money, args=(self.w,))
You'll probably want to add some way to either tell self.t to shut down early, or just wait for it to finish, at quit time. (Or, alternatively, because you know that it can't leave any "dangerous garbage" behind it killed suddenly, you may be able to just set self.t.daemon = True.) But this is enough to show the basics.
I'm currently writing a piece of code to test windows app based on pyautowin.
When of the test is to check if we can minimized the window.
Below is the code:
MyApp.Start_(bittorrentApp)
time.sleep(2)
w_handle = pywinauto.findwindows.find_windows(title=u'Bittorrent Automation Task', class_name='WindowsForms10.Window.8.app.0.2bf8098_r15_ad1')[0]
window = MyApp.window_(handle=w_handle)
window.Click()
window.ClickInput(coords = (300,10))
time.sleep(1)
lStyles = win32api.GetWindowLong(GWL_STYLE);
if( lStyles & WS_MINIMIZE ):
print "minimized"
else:
print "not minimized"
I have imported win32api and I can minimized the window.
By the way
lStyles = win32api.GetWindowLong(GWL_STYLE);
return an error, saying GWL_STYLE is not defined
Any idea ?
pywinauto already has all such functionality.
if window.HasStyle(pywinauto.win32defines.WS_MINIMIZE):
window.Minimize()
That's all in HwndWrapper class. You can see all its attributes when typing window.WrapperObject(). in popup hint. WrapperObject() call is usually hidden for readability, but it's called implicitly anyway.
BTW, GetWindowLong(handle, style) has 2 parameters.
Here is the code sample:
class RunGui (QtGui.QMainWindow)
def __init__(self, parent=None):
...
QtCore.Qobject.connect(self.ui.actionNew, QtCore.SIGNAL("triggered()"), self.new_select)
...
def normal_output_written(self, qprocess):
self.ui.text_edit.append("caught outputReady signal") #works
self.ui.text_edit.append(str(qprocess.readAllStandardOutput())) # doesn't work
def new_select(self):
...
dialog_np = NewProjectDialog()
dialog_np.exec_()
if dialog_np.is_OK:
section = dialog_np.get_section()
project = dialog_np.get_project()
...
np = NewProject()
np.outputReady.connect(lambda: self.normal_output_written(np.qprocess))
np.errorReady.connect(lambda: self.error_output_written(np.qprocess))
np.inputNeeded.connect(lambda: self.input_from_line_edit(np.qprocess))
np.params = partial(np.create_new_project, section, project, otherargs)
np.start()
class NewProject(QtCore.QThread):
outputReady = QtCore.pyqtSignal(object)
errorReady = QtCore.pyqtSignal(object)
inputNeeded = QtCore.pyqtSignal(object)
params = None
message = ""
def __init__(self):
super(NewProject, self).__init__()
self.qprocess = QtCore.QProcess()
self.qprocess.moveToThread(self)
self._inputQueue = Queue()
def run(self):
self.params()
def create_new_project(self, section, project, otherargs):
...
# PyDev for some reason skips the breakpoints inside the thread
self.qprocess.start(command)
self.qprocess.waitForReadyRead()
self.outputReady.emit(self.qprocess) # works - I'm getting signal in RunGui.normal_output_written()
print(str(self.qprocess.readAllStandardOutput())) # prints empty line
.... # other actions inside the method requiring "command" to finish properly.
The idea is beaten to death - get the GUI to run scripts and communicate with the processes. The challenge in this particular example is that the script started in QProcess as command runs an app, that requires user input (confirmation) along the way. Therefore I have to be able to start the script, get all output and parse it, wait for the question to appear in the output and then communicate back the answer, allow it to finish and only then to proceed further with other actions inside create_new_project()
I don't know if this will fix your overall issue, but there are a few design issues I see here.
You are passing around the qprocess between threads instead of just emitting your custom signals with the results of the qprocess
You are using class-level attributes that should probably be instance attributes
Technically you don't even need the QProcess, since you are running it in your thread and actively using blocking calls. It could easily be a subprocess.Popen...but anyways, I might suggest changes like this:
class RunGui (QtGui.QMainWindow)
...
def normal_output_written(self, msg):
self.ui.text_edit.append(msg)
def new_select(self):
...
np = NewProject()
np.outputReady.connect(self.normal_output_written)
np.params = partial(np.create_new_project, section, project, otherargs)
np.start()
class NewProject(QtCore.QThread):
outputReady = QtCore.pyqtSignal(object)
errorReady = QtCore.pyqtSignal(object)
inputNeeded = QtCore.pyqtSignal(object)
def __init__(self):
super(NewProject, self).__init__()
self._inputQueue = Queue()
self.params = None
def run(self):
self.params()
def create_new_project(self, section, project, otherargs):
...
qprocess = QtCore.QProcess()
qprocess.start(command)
if not qprocess.waitForStarted():
# handle a failed command here
return
if not qprocess.waitForReadyRead():
# handle a timeout or error here
return
msg = str(self.qprocess.readAllStandardOutput())
self.outputReady.emit(msg)
Don't pass around the QProcess. Just emit the data. And create it from within the threads method so that it is automatically owned by that thread. Your outside classes should really not have any knowledge of that QProcess object. It doesn't even need to be a member attribute since its only needed during the operation.
Also make sure you are properly checking that your command both successfully started, and is running and outputting data.
Update
To clarify some problems you might be having (per the comments), I wanted to suggest that QProcess might not be the best option if you need to have interactive control with processes that expect periodic user input. It should work find for running scripts that just produce output from start to finish, though really using subprocess would be much easier. For scripts that need user input over time, your best bet may be to use pexpect. It allows you to spawn a process, and then watch for various patterns that you know will indicate the need for input:
foo.py
import time
i = raw_input("Please enter something: ")
print "Output:", i
time.sleep(.1)
print "Another line"
time.sleep(.1)
print "Done"
test.py
import pexpect
import time
child = pexpect.spawn("python foo.py")
child.setecho(False)
ret = -1
while ret < 0:
time.sleep(.05)
ret = child.expect("Please enter something: ")
child.sendline('FOO')
while True:
line = child.readline()
if not line:
break
print line.strip()
# Output: FOO
# Another line
# Done