How to use Python keyboard module to persistently detect input? - python

I'm trying to write a script so that when I press "0" I will get an output, but I want to work without restarting the script, and I haven't been successful using the keyboard module which I've seen as being the most commonly used for detecting key inputs in Python.
What I have so far is
import keyboard
def goto(linenum):
global line
line = linenum
line = 1
if line == 1:
while True:
if keyboard.is_pressed('0'):
print('pressed 0')
break
goto(1)
What I tried doing was after the loop breaks to refer back to the beginning of the loop and try again from there, however the script ends after I press 0. If I remove the break then it constantly outputs "pressed 0", which is not what I want to happen.

You can use pynput to detect key input.
I would do something like this:
from pynput.keyboard import Key, Listener
def on_press(key):
if str(key).replace("'","") == '0':
print('pressed 0')
listener = Listener(on_press=on_press)
listener.start()
Once you start the listener, it will be always waiting for the key press and you can still add code after. In fact, with this example I recommend to add something after, maybe an input() so the script won't close immediately.

I think there are a few problems going on here.
goto is a pretty bad pattern to use and I don't think there's anything here to make your implementation work. It kinda sounds like you're going for some kind of recursion like approach here where when 0 is pressed, then it does some 'reset' and starts looping again. If that is the case you could do something like this:
def wait_for_zero():
# wait for press here
# Should add some sort of exit check too
# 'reset' logic, if any here
wait_for_zero()
looping with a weak condition is pretty fast cycling and generally a bad idea without some time delay. This is mentioned in the documentation for keybaord. There also is an example in the documentation for keyboard to wait for a keypress: https://github.com/boppreh/keyboard#waiting-for-a-key-press-one-time

Related

Python: detect specific key press and prompt the user

I need to print out sentence "Hello World" every 10 seconds. However, if the user either 'c' or 'Enter', the program should stop printing the sentence and prompt the user to provide with the another sentence. The user-provided sentence is checked and if the sentence contains any digits, a message shows up: "cannot contain digits". Otherwise a message shows up: "correct sentence". After displaying either of the messages, the program continues printing "Hello World".
Here is the code I have strated with. Any hints on how to continue further would be greatly appreciated.
Thanks!
import threading
def looping():
threading.Timer(10.0, looping).start()
print("Hello World!")
looping()
From my understanding of your assignment's instructions, it looks like you're on the right track with using a timer to print "Hello World"! I'd like to upvote Irmen de Jong's comment on your question with regard to the statement "threads and console input/output don't work nicely together", since I've experienced this myself in C programming.
Once you have the timer going, the text it prints to the screen shouldn't have an effect on responding to keyboard input. If it's really required to respond directly to a keypress of 'c' (not followed by 'Enter', as one would normally have to do when reading input from the keyboard with input()), I recommend following one of the solutions in Python method for reading keypress? to figure out how you would like to implement that.
EDIT: Implementing a solution using a thread-based timer is a bit more tricky than I thought.
As you may have found in your research on this problem, the threading.Timer object has both start() and stop() methods that you can use to control the execution of individual thread timers if you've saved a reference to the timer in a variable (e.g. doing my_timer = threading.Timer(10.0, looping) then calling my_timer.start() to start the timer). If you do this, you may be able to call my_timer.stop() to pause the looping, provided you've kept a proper reference to the current timer instance that you need to stop at that point in time.
To make things a bit easier, I chose to create a global variable PAUSE_LOOPING that, when set to False, will stop a new timer instance from being started when looping is called, thereby halting all further repetitions of the function until PAUSE_LOOPING is set back to True and looping() is called again:
import threading
from msvcrt import getch
PAUSE_LOOPING = False
def looping():
global PAUSE_LOOPING
if not PAUSE_LOOPING:
threading.Timer(10.0, looping).start()
print("Hello World!")
looping()
while True:
# key = ord(getch())
# if key == 13: # Enter
# PAUSE_LOOPING = True
input_string = input()
if input_string == "":
PAUSE_LOOPING = True
else:
PAUSE_LOOPING = False
looping()
Commented out in the last code block is one way to grab a key press directly (without needing to press the 'Enter' key as is required by input()) taken from the stackoverflow question I linked to earlier in my answer. This should work as long as you're using Python for Windows (so you have the MS VC++ runtime library msvcrt installed), but to make the script stop when pressing 'Enter' you can use the standard input() function. In my example, typing any other string of characters before pressing 'Enter' will resume looping after it's been paused.
NOTE: Beware of using Python's IDLE to run this code. It won't work. Instead, you must run it from the command line.

Stop keyboard.record() function at any keypress in Python

I found this keyboard module in Python which is used to log keyboard events (from what I got, mainly) using keyboard.record(), which takes in a string as a parameter, indicating at which keypress the function should stop.
So my question is.. is there any way to make the function stop at any keypress? As far as I know the module has no special keyword which would indicate such a thing.
Like I tried doing this
keys_pressed = keyboard.record(until='any')
But that's causing an error.
I don't see the point to using keyboard.record() if all you need it for is to stop at (and record) only the first keypress.
Instead, you could use keyboard.read_key() like this:
import keyboard
k = keyboard.read_key() # in my python interpreter, this captures "enter up"
k = keyboard.read_key() # so repeat the line if in the python interpreter
After digging around in the source code, it looks like this is not possible.
def record(until='escape'):
"""
Records all keyboard events from all keyboards until the user presses the
given key combination. Then returns the list of events recorded, of type
`keyboard.KeyboardEvent`. Pairs well with
`play(events)`.
Note: this is a blocking function.
Note: for more details on the keyboard hook and events see `hook`.
"""
recorded = []
hook(recorded.append)
wait(until)
unhook(recorded.append)
return recorded
The parameter until is passed into wait(). Thus, wait() must have code to handle an arbitrary key press, which it does not.
def wait(combination=None):
"""
Blocks the program execution until the given key combination is pressed or,
if given no parameters, blocks forever.
"""
wait, unlock = _make_wait_and_unlock()
if combination is not None:
hotkey_handler = add_hotkey(combination, unlock)
wait()
remove_hotkey(hotkey_handler)
Ultimately, there is no source code built to handle something like keyboard.record(until='any'), so you'll have to find a workaround. Consider checking How do I make python to wait for a pressed key. However, if you need to record the arbitrary key you would have used to stop the recording, then use J-L's workaround:
import keyboard
k = keyboard.read_key() # in my python interpreter, this captures "enter up"
k = keyboard.read_key() # so repeat the line if in the python interpreter
You can make a function that sets a hook for all keys.
import keyboard
def record_until(any_key=False):
recorded = []
keyboard.hook(recorded.append) # need this to capture terminator
wait, unlock = keyboard._make_wait_and_unlock()
if any_key:
hook = keyboard.hook(unlock)
wait()
try:
keyboard.remove_hotkey(hook)
except ValueError: pass
return recorded
record_until(any_key=True)

Python - detect keypress in shell

Trying to detect and respond to key presses in Python. Am using IDLE and Python 3.3. I have the following code so far
import msvcrt
while True:
inp = ord(msvcrt.getch())
if (inp != 255):
print(inp)
I have the IF statement because if I just allow the script to throw out the value of 'inp' it just spoons out 255 repeatedly. So I threw in the if statement to respond to anything but 255 and now when run the code does nothing but output the actual keypress character in the shell.
This is because getch reads the input immediately, it doesn't wait until you type something. When it doesn't receive any input, it will simply return "\xff" (ordinal 255).
The getch function is designed to work in a console, not in a graphical program.
When you use it in a graphical program it is going to return immediately with \xff.
If you run your program in the reguler python interpreter it will not cause you that problem.
In addition, when you run the loop with the if statement it is running continuously and using a lot more processor time then it needs. In windows the only proper way of doing so is using window messages which sounds like it is overkill for your needs.
Python has a keyboard module with many features. You Can Use It In Both Shell and Console. It Also Detect Key For The Whole Windows.
Install it, perhaps with this command:
pip3 install keyboard
Then use it in code like:
import keyboard #Using module keyboard
while True: #making a loop
try: #used try so that if user pressed other than the given key error will not be shown
if keyboard.is_pressed('a'): #if key 'a' is pressed
print('You Pressed A Key!')
break #finishing the loop
else:
pass
except:
break #if user pressed other than the given key the loop will break
You can set it to multiple Key Detection:
if keyboard.is_pressed('a') or keyboard.is_pressed('b') or keyboard.is_pressed('c'): # and so on
#then do this
When You Install The Module, got to folder:
python36-32/Lib/site-packages/keyboard
Open File_keyboard_event.py in notepad++.
There will be keyboard events.
Not Sure About All Of Them.
Thanks.

In Python 3.3.2 how do I have the code wait for the user to press the "w" key?

The question pretty much says everything. I've seen answers to similar questions, but not this question precisely, and it seems I need a more precise answer. Keep in mind that I am relatively new to programming and won't really understand any high walls of code you put up for me. Please keep it as utterly simple as possible. :(
All I know is that this was very simple (basic?) to do in BASIC, the one language wherein I have some experience.
Remember, I need a specific key pressed, not just any key.
I'm on a Mac, BTW.
the easiest way is:
x = input("Press w")
if x == "w":
#Code
or you can use enter and get rid of the if statment:
input("Press Enter to Continue...")
#Code
there are better ways but you ask for something simple
What you want is a key listener. input and raw_input will only return when they see an EOL or EOF character (e.g. you hitting the enter key). I assume you're making some sort of game (because you want to accept particular keys)?
In that case, you want a keylistener, which would make this relevant: Key Listeners in python?
Note: Since you request Python 3.x I will use input(). For Python 2.x use raw_input().
In case you want to check a single time you can do
# Request a key to be pressed by the user
res = input("Waiting for 'w' to be pressed...")
if res == "w":
# Do something if w was pressed
else:
# Do something if other then w was pressed
# Code after check
The downside of this is that your program execution will continue after the check is completed regardless of whether you are satisfied with retrieved key value or not.
In case you want to wait - as in do not continue until condition fulfilled - for the specific key you can do
while True:
res = input("Waiting for 'w' to be pressed...")
if res == "w":
# Do something if w was pressed and exit loop
break
else:
# Do something if other then w was pressed
# followed by another iteration of the loop
# Code after check
By doing that the execution of the program will be stuck inside the endless loop and will keep asking for your desired key to be pressed until the user kills the application or fulfills the requirement.
I like to use Pynput, I think you can have simpler and more elegant solutions.
Many options in the official documentation: https://pypi.org/project/pynput/
Command:
pip install pynput
Code:
from pynput import keyboard
def on_activate_a():
print('A pressed')
def on_activate_b():
print('B pressed')
def on_activate_c():
print('C pressed')
def quit():
print('QUIT')
h.stop()
with keyboard.GlobalHotKeys({
'a': on_activate_a,
'b': on_activate_b,
'c': on_activate_c,
'<ctrl>+c': quit}) as h:
h.join()

When while loop placed in wxPython events

I'm trying to write a GUI program grabbing specific contents from a webpage. The idea is when I hit the start button, the program should start extracting information from that page. And I want to add some code to check if connected to the Internet. If not, continue trying until connected.
So I just added the following code in the event, but found it didn't work. Also the whole program has to be closed in a forced way. Here's my code:
import urllib2
import time
InternetNotOn = True
while InternetNotOn:
try:
urllib2.urlopen("http://google.com")
InternetNotOn = False
print "Everyting is fine!"
except urllib2.URLError, e:
print "Error!"
time.sleep(10)
What could the problem be?
When you have an event based program, the overall flow of the program is this:
while the-program-is-running:
wait-for-an-event
service-the-event
exit
Now, lets see what happens when service-the-event calls something with a (potentially) infinite loop:
while the-program-is-running:
wait-for-an-event
while the-internet-is-on:
do-something
exit
Do you see the problem? In the worse case your program may never call wait-for-an-event again because your loop is running.
Remember: the event loop is already an infinite loop, you don't need to add another infinite loop inside of it. Instead, take advantage of the existing loop. You can use wx.CallAfter or wx.CallLater to call a method which will cause your function to be called at the next iteration of the event loop.
Then, within your function you call wx.CallAfter or wx.CallLater again to cause it to again be called on the next iteration of the event loop.
Instead of time.sleep(10) you can call wxApp::Yield and time.sleep(1) ten times.
Beware of reentrancy problems (e.g. pressing the start button again.). The start button could be dimmed while in the event handler.
But Bryan Oakley's solution is probably the better way.

Categories