So I'm trying to utilize msvcrt.getch() to make an option to quit(without using KeyBoardInterrupt) anywhere in the program.
My code currently looks like this:
import msvcrt
import sys
print("Press q at any time to quit")
while True:
pressedKey = msvcrt.getch()
if pressedKey == 'q':
sys.exit()
else:
# do some setup
if myvar == "string":
try:
# do stuff
except:
# do stuff
else:
#do stuff
How do I run the while loop to detect the keypress of q at the same time as I'm running the other (the # do stuff blocks)?
That way, if the user goes ahead with the program, they it'll only run it once. But if they hit q, then the program will quit.
You could read keys in a separate thread or (better) use msvcrt.kbhit() as #martineau suggested:
#!/usr/bin/env python
import msvcrt
from Queue import Empty, Queue
from threading import Thread
def read_keys(queue):
for key in iter(msvcrt.getch, 'q'): # until `q`
queue.put(key)
queue.put(None) # signal the end
q = Queue()
t = Thread(target=read_keys, args=[q])
t.daemon = True # die if the program exits
t.start()
while True:
try:
key = q.get_nowait() # doesn't block
except Empty:
key = Empty
else:
if key is None: # end
break
# do stuff
If I wanted to do something in the main code when the second thread detected a certain keypress, how would I act on that?
You do not react to the key press in the main thread until code reaches q.get_nowait() again i.e., you won't notice the key press until "do stuff" finishes the current iteration of the loop. If you need to do something that may take a long time then you might need to run it in yet another thread (start new thread or use a thread pool if blocking at some point is acceptable).
Related
I recently got to know about the python module signal. With that, we can capture a SIGINT and do what we want after capturing it. I used it as below. In that case I am just using SIGINT to print that program is going to be stopped and stop the program.
import signal
import os
import time
def signalHandler(signalnumb, frame):
print("Signal Number:", signalnumb, " Frame: ", frame)
print('Exiting the program...')
os._exit(0)
signal.signal(signal.SIGINT, signalHandler)
c=0
# Loop infinite times using the while(1) which is always true
while 1:
print(c)
#sleep for 1 seconds using the sleep() function in time
time.sleep(1)
c=c+1
Now I want to give any signal from keyboard(for example pressing 'q') and as soon as signal was recieved, the python program should be stopped. Has anyone got some experience on how to do that? Any other method rather than using signal module (for example using multithreading) is accepted.
Edit1-
Later I tried to use pynput module as suggested in one of a similar kind of question. For sure I have done a mistake. It doesn't work as I expected. It means with a key press, I couldn't stop the for loop from running.
from pynput import keyboard
import time
def on_press(key):
for i in range(100):
print(i)
time.sleep(1)
if key == keyboard.Key.esc:
return False # stop listener
try:
k = key.char # single-char keys
except:
k = key.name # other keys
if k in ['1', '2', 'left', 'right']: # keys of interest
# self.keys.append(k) # store it in global-like variable
print('Key pressed: ' + k)
return False # stop listener; remove this if want more keys
listener = keyboard.Listener(on_press=on_press)
listener.start() # start to listen on a separate thread
listener.join() # remove if main thread is polling self.keyspython
Can someone point out how to do it using pynput in correct way?
This was my original implementation:
a = input('Press a key to exit')
if a:
exit(0)
However, it seems that you need a piece of code that will allow for any key to be clicked and immediately exit out of the program, without hitting enter afterwards. This may be a better way to do that:
import readchar
print("Press Any Key To Exit")
k = readchar.readchar()
Hope this helps!
After carefully understanding about the threads and pynput module, I managed to stop a for loop (or any program which runs as a separate thread) using a key press callback.
from pynput import keyboard
import os
import threading
import time
loop_running = False
def on_press(key):
print(dir(key))
global loop_running
#if key == keyboard.Key.delete and not loop_running:
if ('char' in dir(key)) and (key.char == 's') and (not loop_running):
t=threading.Thread(target=start_loop)
t.start()
#elif key==keyboard.Key.tab: #if this is used, the attributes related to 'key' will be changed. you can see them since I have used a print(dir(key))
elif key.char == 'q':
loop_running=False
def start_loop():
global loop_running
loop_running = True
for i in range(100):
if not loop_running:
os._exit(0)
print(i)
time.sleep(1)
with keyboard.Listener(on_press=on_press) as listner:
listner.join()
In a Python script I'd like to continiously call a function and, at the same time, listen for the user having pressed the ESC key which would then exit the program.
This is my current code:
import threading
import msvcrt
def wait_for_esc():
while True:
key = ord(msvcrt.getch())
if key == 27:
print("ESC")
exit(0)
def do_something():
while True:
call_function()
thread_1 = threading.Thread(name="wait_for_esc", target=wait_for_esc())
thread_2 = threading.Thread(name="do_something", target=do_something())
thread_1.start()
thread_2.start()
However it seems as if thread_1 blocks thread_2 until any key has been pressed.
What's a possible solution to run both thread independent from each other?
When you pass in the target task to the thread, you need to pass the function object - not call the function. You need to remove the paranthesis at the end of your function name.
thread_1 = threading.Thread(name="wait_for_esc", target=wait_for_esc)
thread_2 = threading.Thread(name="do_something", target=do_something)
And it should work.
I'm new to Python and I figured I'd make a simple autoclicker as a cool starter project.
I want a user to be able to specify a click interval and then turn the automatic clicking on and off with a hotkey.
I am aware of Ctrl-C, and you'll see that in my current code, but I want the program to work so that the hotkey doesn't have to be activated in the python window.
import pyautogui, sys
print("Press Ctrl + C to quit.")
interval = float(input("Please give me an interval for between clicks in seconds: "))
try:
while True:
pyautogui.click()
except KeyboardInterrupt:
print("\n")
Do I need to make a tkinter message box to make the switch or can I use a hotkey?
Thanks for the help.
Update
import multiprocessing
import time
import pyHook, pyautogui, pythoncom
import queue
click_interval = float(input("Please give an interval between clicks in seconds: "))
class AutoClicker(multiprocessing.Process):
def __init__(self, queue, interval):
multiprocessing.Process.__init__(self)
self.queue = queue
self.click_interval = click_interval
def run(self):
while True:
try:
task = self.queue.get(block=False)
if task == "Start":
print("Clicking...")
pyautogui.click(interval == click_interval)
except task == "Exit":
print("Exiting")
self.queue.task_done()
break
return
def OnKeyboardEvent(event):
key = event.Key
if key == "F3":
print("Starting auto clicker")
# Start consumers
queue.put("Start")
queue.join
elif key == "F4":
print("Stopping auto clicker")
# Add exit message to queue
queue.put("Exit")
# Wait for all of the tasks to finish
queue.join()
# return True to pass the event to other handlers
return True
if __name__ == '__main__':
# Establish communication queues
queue = multiprocessing.JoinableQueue()
# create a hook manager
hm = pyHook.HookManager()
# watch for all mouse events
hm.KeyDown = OnKeyboardEvent
# set the hook
hm.HookKeyboard()
# wait forever
pythoncom.PumpMessages()
AutoClicker.run(self)
First of all if you want to monitor global input outside the Python window you will need pyHook or something similar. It will allow you to monitor keyboard events. I chose to act upon the F3 and F4 key-presses, which are used for starting and stopping the autoclicker.
To accomplish what you've asked, the best way I'm aware of is to create a process which will do the clicking, and to communicate with it through the use of a Queue. When the F4 key is pressed, it will add an "Exit" string to the queue. The autoclicker will recognize this and then return.
Before the F4 key has been pressed, the queue will remain empty and the queue.Empty exception will continually occur. This will execute a single mouse click.
import multiprocessing
import time
import pyHook, pyautogui, pythoncom
import queue
class AutoClicker(multiprocessing.Process):
def __init__(self, queue, interval):
multiprocessing.Process.__init__(self)
self.queue = queue
self.click_interval = interval
def run(self):
while True:
try:
task = self.queue.get(block=False)
if task == "Exit":
print("Exiting")
self.queue.task_done()
break
except queue.Empty:
time.sleep(self.click_interval)
print("Clicking...")
pyautogui.click()
return
def OnKeyboardEvent(event):
key = event.Key
if key == "F3":
print("Starting auto clicker")
# Start consumers
clicker = AutoClicker(queue, 0.1)
clicker.start()
elif key == "F4":
print("Stopping auto clicker")
# Add exit message to queue
queue.put("Exit")
# Wait for all of the tasks to finish
queue.join()
# return True to pass the event to other handlers
return True
if __name__ == '__main__':
# Establish communication queues
queue = multiprocessing.JoinableQueue()
# create a hook manager
hm = pyHook.HookManager()
# watch for all mouse events
hm.KeyDown = OnKeyboardEvent
# set the hook
hm.HookKeyboard()
# wait forever
pythoncom.PumpMessages()
Keep in mind this implementation is far from perfect, but hopefully it's helpful as a starting point.
Why can't I use a simple while loop or if/else statement?
A while loop is blocking, which means when the loop is running it blocks all other code from executing.
So once the clicker loop starts it will be stuck in that loop indefinitely. You can't check for F4 key presses while this is happening because the loop will block any other code from executing (ie. the code that checks for key-presses). Since we want the auto-clicker clicks and the key-press checks to occur simultaneously, they need to be separate processes (so they don't block each other).
The main process which checks for key presses needs to be able to communicate somehow with the auto-clicker process. So when the F4 key is pressed we want the clicking process to exit. Using a queue is one way to communicate (there are others). The auto-clicker can continuously check the queue from inside the while loop. When we want to stop the clicking we can add an "Exit" string to the queue from the main process. The next time the clicker process reads the queue it will see this and break.
Note: I want to do this without using any external packages, like PyGame, etc.
I am attempting to capture individual keypresses as they arrive and perform an action for specific characters, whether I simply want to "re-echo" the character, or not display it at all and do something else.
I have found a cross-platform (though not sure about OS X) getch() implementation because I do not want to read a whole line like input() does:
# http://code.activestate.com/recipes/134892/
def getch():
try:
import termios
except ImportError:
# Non-POSIX. Return msvcrt's (Windows') getch.
import msvcrt
return msvcrt.getch
# POSIX system. Create and return a getch that manipulates the tty.
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(fd)
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
[Attempt 1]
I first tried a simple while-true loop to poll getch, but if I type too fast, characters go missing. Reducing the sleep time makes it worse. The debug statements only print on press of the enter key and not consistently in time nor position. (It appears there might be some line buffering going on?) Taking out the loop and sleep lets it work once but perfectly.
#!/usr/bin/env python3
import sys
import tty
import time
def main():
while True:
time.sleep(1)
sys.stdout.write(" DEBUG:Before ")
sys.stdout.write(getch())
sys.stdout.write(" DEBUG:After ")
if __name__ == "__main__":
main()
[Attempt 2]
I got an example for using a threaded approach (https://stackoverflow.com/a/14043979/2752206) but it "locks up" and won't accept any input (including Ctrl-C, and etc)..
#!/usr/bin/env python3
import sys
import tty
import time
import threading
key = 'Z'
def main():
threading.Thread(target=getchThread).start()
while True:
time.sleep(1)
sys.stdout.write(" DEBUG:Before ")
sys.stdout.write(key)
sys.stdout.write(" DEBUG:After ")
def getchThread():
global key
lock = threading.Lock()
while True:
with lock:
key = getch()
if __name__ == "__main__":
main()
Does anyone have any advice or guidance? Or more importantly, can someone explain why the two attempts do not work? Thanks.
First off, I don't really thing you need multithreading. You'd need that if you, for example, wanted to do some tasks like drawing on screen or whatever and capturing keys while you do this.
Let's consider a case where you only want to capture keys and after each keypress execute some action: Exit, if x was pressed, otherwise just print the character. All you need for this case is simple while loop
def process(key):
if key == 'x':
exit('exitting')
else:
print(key, end="", flush=True)
if __name__ == "__main__":
while True:
key = getch()
process(key)
Notice absence of sleep(). I am assuming you thought getch() won't wait for user input so you set 1s sleep time. However, your getch() waits for one entry and then returns it. In this case, global variable is not really useful, so you might as well just call process(getch()) inside the loop.
print(key, end="", flush=True) => the extra arguments will ensure pressed keys stay on one line, not appending newline character every time you print something.
The other case, where you'd want to execute different stuff simultaneously, should use threading.
Consider this code:
n = 0
quit = False
def process(key):
if key == 'x':
global quit
quit = True
exit('exitting')
elif key == 'n':
global n
print(n)
else:
print(key, end="", flush=True)
def key_capturing():
while True:
process(getch())
if __name__ == "__main__":
threading.Thread(target=key_capturing).start()
while not quit:
n += 1
time.sleep(0.1)
This will create global variable n and increment it 10 times a second in main thread. Simultaneously, key_capturing method listens to keys pressed and does the same thing as in previous example + when you press n on your keyboard, current value of the global variable n will be printed.
Closing note: as #zondo noted, you really missed braces in the getch() definition. return msvcrt.getch should most likely be return msvcrt.getch()
I am trying to run a program that takes in input as a job is getting done. I have looked through several forms, and looked into the documentation. I'm running this in Debian, and I understand that I can use this getch function to receive characters without hitting the return key. To break it down, this is what I am trying to implement in my infinite while loop
Take in input (threading didn't work here for me
Put input into Queue
If there are no running jobs, start the job with the item in front of the queue as a variable
I am also running the threading module to execute another instruction. Is there any way I can do this?
Update: This is what I have tried so far:
First, I tried using a timer from the threading module to stop it from waiting, which went something like this.
def getchnow():
def time_up():
answer= None
print 'time up...'
wait = Timer(5,time_up) # x is amount of time in seconds
wait.start()
try:
print "enter answer below"
answer = getch()
except Exception:
print 'pass\n'
answer = None
if answer != True: # it means if variable have somthing
wait.cancel() # time_up will not execute(so, no skip)
return answer
line = getchnow()
#Add line variable to queue
#Do stuff with queue
The problem here is that it still waited for user input.
I then tried to put the getch function into another thread.
q = Queue.Queue
q.put(getch())
if q.get() != True: # it means if variable have somthing
line = q.get()
#Add line variable to queue
#Do stuff with queue
This attempt doesn't let me do anything.
I read more of this link, and there was an implementation of what I wanted at the bottom.
I used the select module for a Non-Blocking implementation on Linux.
This times out in (5 seconds here) if no input is received.
Particularly useful when used in a thread, so that the getch call is
non-blocking and will allow the thread to exit cleanly
# This class gets a single character input from the keyboard
class _GetchUnix:
def __init__(self):
import tty, sys
from select import select
def __call__(self):
import sys, tty, termios
from select import select
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
[i, o, e] = select([sys.stdin.fileno()], [], [], 2)
if i:
ch=sys.stdin.read(1)
else:
ch=''
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
getch = _GetchUnix()
# End Class
I've also used [i, o, e] = select([sys.stdin.fileno()], [], [], 2), but I've heard it might not work on windows. If anyone still needs a multi-threaded, non-blocking input example:
import threading
import sys
import time
bufferLock=threading.Lock()
inputBuffer=[]
class InputThread(threading.Thread):
def run(self):
global inputBuffer
print("starting input")
while True:
line=sys.stdin.readline()
bufferLock.acquire()
inputBuffer.insert(0,line)
bufferLock.release()
input_thread=InputThread()
input_thread.start()
while True:
time.sleep(4)
bufferLock.acquire()
if len(inputBuffer)>0:
print("Popping: "+inputBuffer.pop())
bufferLock.release()