Reputation: 345
I need to do roughly the following:
while True:
if *a key is pressed within 5 seconds of some prior event*:
print(*the specific key that was pressed)
elif *5 seconds pass with no key press*
print("No key pressed")
I posted about my specific needs in another question (Read specific key with msvcrt.getch() OR move on after set time with no input), but I figure this format is much more approachable. The critical part here is that I must know what key was pressed. I have been trying to use msvcrt.getch() amd msvcrt.kbhit()
, but it seems I need some sort of hybrid.
Can anyone figure this out?
Windows 11, Python 3.11.0
Upvotes: 1
Views: 61
Reputation: 35
Here's a potential solution, tested on Windows 11 with Python 3.11 and 3.12. It works by checking how long ago the event of interest was fired. See docstrings and comments below:
import msvcrt
import time
class KeyboardChecker:
"""Checks for input."""
def __init__(self):
self.last_event_trigger_time = None
def event_triggered(self):
"""Call this when your event is triggered."""
self.last_event_trigger_time = time.perf_counter()
print(
round(self.last_event_trigger_time, 3),
'[KeyboardChecker]: Event triggered.',
)
def _read_all_input(self):
"""Read all available input."""
input_data = b''
while msvcrt.kbhit():
# Read and store input until no more is available.
input_data += msvcrt.getch()
return input_data
def check_input(self):
"""Check if we have received input within 5 seconds of the last event."""
if self.last_event_trigger_time is None:
# If the trigger time is still `None`, the
# event has not been triggered yet.
# Don't run the next part of the code.
return None # This is the same as `return`.
current_time = time.perf_counter()
if current_time - self.last_event_trigger_time > 5:
# If the event was last triggered more than 5
# seconds ago, don't do anything with the input.
# OPTIONAL: Read input but don't use it. This
# means you won't have anything waiting to be
# read the next time you want to read any
# input, which may be useful.
_not_used = self._read_all_input()
# Don't run the next part of the code.
return None
# By the time we reach this point (if at all), we
# know that the event was triggered and that it was
# 5 seconds ago or less. We can now read our input,
# if we have any.
if msvcrt.kbhit():
# If we have received input, read it all.
return self._read_all_input()
# If we do not have any input, return an empty
# bytestring to distinguish from the rejection
# cases above.
return b''
You can call .event_triggered()
whenever you fire your event, and call .check_input()
to check that we have input and that it's not too late to get it (based on when the event was last fired).
Here is a usage example...
# Create a `KeyboardChecker` instance so we can do all of
# our checking.
keyboard_checker = KeyboardChecker()
# This gets called when the event is triggered. We will
# call it here for demonstration.
keyboard_checker.event_triggered()
# Now, we run a while loop that loops every half-second
# until calls to `keyboard_checker.check_input()` are
# rejected (when it starts reurning None).
while True:
# Record the time.
timestamp = round(time.perf_counter(), 3)
# Check for input.
received_input = keyboard_checker.check_input()
# Handle all cases.
if received_input is None:
# Event was triggered more than 5 seconds ago.
print(timestamp, 'The call to `.check_input()` was rejected! Stopping loop...')
break # Stop the loop.
elif received_input == b'':
# Input was checked, but none was received.
print(timestamp, 'No characters received.')
else:
# Input was checked and data was received.
print(timestamp, f'Received the following data: {received_input}.')
# Wait for half a second.
time.sleep(0.5)
...and the output it generated when I ran it.
1491860.076 [KeyboardChecker]: Event triggered.
1491860.076 Received the following data: b'f'.
1491860.578 Received the following data: b'o'.
1491861.081 Received the following data: b'o'.
1491861.584 No characters received.
1491862.085 Received the following data: b'b'.
1491862.588 Received the following data: b'a'.
1491863.091 Received the following data: b'r'.
1491863.593 No characters received.
1491864.095 No characters received.
1491864.597 No characters received.
1491865.098 The call to `.check_input()` was rejected! Stopping loop...
Notice that calls to .check_input()
are rejected once we pass the 5-second mark.
Upvotes: 0