How can I do a computing program in Python 3 such that it does computing while program is running, shows the computing status when one presses s and quits the program when one presses ctrl+c? I know I can use try-catch but how can I read when user presses s? I know input but it stops the computing when waits the key press. I'm using Ubuntu 17.04 on bash command line.
I tried
n = 0
try:
while True:
n += 1
except KeyboardInterrupt:
print("Stopping")
Since you are on Linux you can use the curses module to do that.
The way curses works is simple. We set curses to "monitor" the compute() function (the keypress variable works as an event handler and you can call it as you wish - I chose to call it keypress).
As you can also read in the documentation here, the most common way to get input to a window is to use its getch() method, but getch() pauses and waits for the user to hit a key. To change this behavior we use the method nodelay(1). By using nodelay(1), getch() for the window becomes non-blocking and returns curses.ERR (a value of -1) when no input is ready.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import curses
def compute(keypress):
keypress.nodelay(1)
n = 0
try:
while True:
n += 1
key = keypress.getch()
if key != -1 and key==115: # ord('s')=115
keypress.addstr(str(n) +'\n')
except KeyboardInterrupt:
sys.exit(0)
if __name__ == '__main__':
curses.wrapper(compute)
Related
I am trying to create a script which will read a text file and then speak one line of the file every time I press a button on my keyboard. I am using the system accessibility voice on my Mac as the voice sounds more human than some of the python modules I tried.
So far my script runs and speaks a line but the spoken lines are not in order but rather are just lines spoken at random, whereas I would like each line to be spoken once and in order.
Also, I would rather use another key (not alphanumeric) such as right arrow to invoke the function but not sure how to specify that in my script?
I am just learning to code so any help would be much appreciated.
I am using Python 3.9.1 on Mac OSX 10.15.5
filename = ('file.txt')
with open(filename) as f:
lines = f.readlines()
# for x in lines:
# pass
def say():
while True:
try: # used try so that if any key other than the specified key is pressed an error will not be shown
for x in lines:
if keyboard.is_pressed('l'): # if key 'l' is pressed
subprocess.call(['say', x])
print('You Pressed A Key!')
#break # finishing the loop
except:
break # this should break loop if user presses a key other than the given key but doesn't work
say()
Ok I have now managed to fix the issue of reading from a file and reading each line in sequence (see clode below).
And aparantly you can just specify the key with 'up' or 'right' which works. It does output random characters to the screen so if anyone has a better way of doing this, I'd love to hear it
import keyboard
import subprocess
filename = ('text.txt')
with open(filename) as f:
lines = f.readlines()
def say():
i=0
while i <len(lines):
try: # used try so that if any key other than the specified key is pressed an error will not be shown
if keyboard.is_pressed('up'): # if key 'up' is pressed
subprocess.call(['say', lines[i]])
i += 1
except:
break # this should break loop if user presses a key other than the given key but doesn't work
say()
import keyboard
import subprocess
CURRENT_LINE = 0 #records line to be read out, first line is 0.
def ttsLine(line): #TTS function, uses macs say as per you requested.
try:
k = subprocess.check_output(["say",line.strip()]) #using subprocess, line.strip() to clean each line.
return True #returns true so we know if say command was successful
except:
return False #returns false if exceptions occured.
filename = ('text.txt') #text file with lines to be read.
with open(filename) as f:
lines = f.readlines() #saving lines to array.
while True:
keyboard.wait("up") #Waits until up key is pressed.
if not ttsLine(lines[CURRENT_LINE]): #if ttsLine returned false, something went wrong and script halts.
print("Something went wrong while reading out", lines[CURRENT_LINE])
break
CURRENT_LINE += 1 #else we update line to next one
if CURRENT_LINE == len(lines): #if all lines have been read, script ends.
break
I am running a process in a command window (Windows 7, Python 3.1) where I would like the user to abort the process by pressing Esc key. However, pressing Esc doesn't appear to do anything, the loop never breaks. I have also tried running the script from within my IDE (Wing), but again, the loop cannot be interrupted.
The following is a stripped-down version of my proof-of-concept test...
import msvcrt
import time
aborted = False
for time_remaining in range(10,0,-1):
# First of all, check if ESCape was pressed
if msvcrt.kbhit() and msvcrt.getch()==chr(27):
aborted = True
break
print(str(time_remaining)) # so I can see loop is working
time.sleep(1) # delay for 1 second
#endfor timing loop
if aborted:
print("Program was aborted")
else:
print("Program was not aborted")
time.sleep(5) # to see result in command window before it disappears!
If anyone could tell me where I might be going wrong I would be most grateful.
Python 3 strings are unicode and, therefore, must be encoded to bytes for comparison. Try this test:
if msvcrt.kbhit() and msvcrt.getch() == chr(27).encode():
aborted = True
break
Or this test:
if msvcrt.kbhit() and msvcrt.getch().decode() == chr(27):
aborted = True
break
Or this test:
if msvcrt.kbhit() and ord(msvcrt.getch()) == 27:
aborted = True
break
You should really strip down more, like this one below:
>>> import msvcrt
>>> ch = msvcrt.getch()
# Press esc
>>> ch
b'\x1b'
>>> chr(27)
'\x1b'
>>> ch == chr(27)
False
So here is the problem: msvcrt.getch() returns bytes, chr(27) returns string. In Python 3 they are two distinct types, so the "==" part will never work, and the if statement will always be evaluated as False.
The solution should be obvious to you.
More about strings vs bytes, from the book Dive into Python 3.
The interactive console is very useful for debugging, try use it more :)
You don't need encode, decode, chr, ord, ....
if msvcrt.kbhit() and msvcrt.getch() == b'\x1b':
or if you'd like to see "27" in the code somewhere:
if msvcrt.kbhit() and msvcrt.getch()[0] == 27:
Python 2/3 compatible code:
import time, sys
ESC = '\x1b'
PY3K = sys.version_info >= (3,)
if PY3K:
from msvcrt import kbhit, getwch as _getch
else:
from msvcrt import kbhit, getch as _getch
while not kbhit() or _getch() != ESC:
print(time.asctime())
time.sleep(1)
Code parts are taken from pager module with more stuff inside.
Have you tried using a different key to test if it's not just that key?
Did you also try the examples at (http://effbot.org/librarybook/msvcrt.htm) to see if they worked?
I have this script:
import subprocess
p = subprocess.Popen(["myProgram.exe"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
while True:
out, _ = p.communicate(input().encode())
print(out.decode())
which works fine until the second input where I get:
ValueError: Cannot send input after starting communication
Is there a way to have multiple messages sent between the parent and child process in Windows ?
[EDIT]
I don't have access to the source code of myProgram.exe
It is an interactive command line application returning results from queries
Running >> myProgram.exe < in.txt > out.txt works fine with in.txt:
query1;
query2;
query3;
Interacting with another running process via stdin/stdout
To simulate the use case where a Python script starts a command line interactive process and sends/receives text over stdin/stdout, we have a primary script that starts another Python process running a simple interactive loop.
This can also be applied to cases where a Python script needs to start another process and just read its output as it comes in without any interactivity beyond that.
primary script
import subprocess
import threading
import queue
import time
if __name__ == '__main__':
def enqueue_output(outp, q):
for line in iter(outp.readline, ''):
q.put(line)
outp.close()
q = queue.Queue()
p = subprocess.Popen(["/usr/bin/python", "/test/interact.py"],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
# stderr = subprocess.STDOUT,
bufsize = 1,
encoding ='utf-8')
th = threading.Thread(target=enqueue_output, args=(p.stdout, q))
th.daemon = True
th.start()
for i in range(4):
print("dir()", file=p.stdin)
print(f"Iteration ({i}) Parent received: {q.get()}", end='')
# p.stdin.write("dir()\n")
# while q.empty():
# time.sleep(0)
# print(f"Parent: {q.get_nowait()}")
interact.py script
if __name__ == '__main__':
for i in range(2):
cmd = raw_input()
print("Iteration (%i) cmd=%s" % (i, cmd))
result = eval(cmd)
print("Iteration (%i) result=%s" % (i, str(result)))
output
Iteration (0) Parent received: Iteration (0) cmd=dir()
Iteration (1) Parent received: Iteration (0) result=['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'cmd', 'i']
Iteration (2) Parent received: Iteration (1) cmd=dir()
Iteration (3) Parent received: Iteration (1) result=['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'cmd', 'i', 'result']
This Q&A was leveraged to simulate non-blocking reads from the target process: https://stackoverflow.com/a/4896288/7915759
This method provides a way to check for output without blocking in the main thread; q.empty() will tell you if there's no data. You can play around with blocking calls too using q.get() or with a timeout q.get(2) - the parameter is number of seconds. It can be a float value less than zero.
Text based interaction between processes can be done without the thread and queue, but this implementation gives more options on how to retrieve the data coming back.
The Popen() parameters, bufsize=1 and encoding='utf-8' make it possible to use <stdout>.readline() from the primary script and sets the encoding to an ascii compatible codec understood by both processes (1 is not the size of the buffer, it's a symbolic value indicating line buffering).
With this configuration, both processes can simply use print() to send text to each other. This configuration should be compatible for a lot of interactive text based command line tools.
I've created a function using the carriage return from this and threading trick to exit loop from this. The function will keep counting until a keystroke is detected.
Everything works fine except an additional line is printed whenever I kill the loop.
For example in console:
Press anything to stop it: 6 #<------------- pressed enter
Press anything to stop it: 7 #<------------- additional line produced
import _thread
import time
import sys
def input_thread(list):
input()
list.append(None)
def do_stuff():
counter = 0
list = []
_thread.start_new_thread(input_thread,(list,))
while not list:
time.sleep(0.1)
sys.stdout.write("Press anything to stop it: %d \r" % (counter))
sys.stdout.flush()
counter += 1
How can I prevent extra line being printed? I suspect it has something to do with the while loop has to finish additional loop in order to exit. If the answer to this question is too obvious please let me know the keyword to search for.
Thanks a million!
I get that it's a very difficult thing to pick up on in the background, but what I'm looking for is a program that records a keypress, and how much time it takes between keypresses. None of what I have looked into was able to record in the background, or actually work.
EDIT:
import win32con, ctypes, ctypes.wintypes
def esc_pressed():
print("Hotkey hit!")
ctypes.windll.user32.RegisterHotKey(None, 1, 0, 0xDD) # this binds the ']' key
try:
msg = ctypes.wintypes.MSG()
ctypes.windll.user32.GetMessageA
while ctypes.windll.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
esc_pressed()
ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
ctypes.windll.user32.DispatchMessageA(ctypes.byref(msg))
finally:
ctypes.windll.user32.UnregisterHotKey(None, 1)
This allows for the program to work in the background, but it takes the inputted character you bound, instead of picking up on it. I still need to make sure the inputted character gets to the window with focus.
You probably have to catch the key and then simulate the same key press again. Try checking out Python Keyboard module for that.
EDIT: Added code sample.
import keyboard, time
def KeyPressSample(startkey='tab', endkey='esc'):
while True: # making a inifinte loop
try:
if keyboard.is_pressed(startkey):
time.sleep(0.25)
print("%s Key pressed." % startkey)
elif keyboard.is_pressed(endkey):
print("%s Key pressed." % endkey)
break
except KeyboardInterrupt:
print('\nDone Reading input. Keyboard Interupt.')
break
except Exception as e:
print(e)
break