I'm writing a simple Python 3 script and I want to be able to intercept key presses inside the terminal window in order to do something depending on the returned value, for example.
I also want a cross-platform solution.
I would like to reproduce something like this:
import msvcrt
key = ord(msvcrt.getch()) # Wait for a key to be pressed.
if key == 27: # The ESC key
print("You have pressed the ESC key!")
But msvcrt
is a Windows-specific module according to the Python docs (and my tests):
These functions provide access to some useful capabilities on Windows platforms.
I've found the keyboard
module which is quite simple to use (and more cross-platform) but I didn't manage to "catch" only the keys pressed inside the terminal window.
For example:
import keyboard as kb
key = kb.read_hotkey()
if key == "esc": # The ESC key
print("You have pressed the ESC key!")
The code given above intercepts key presses not only when the terminal window where the script is executed is focused, but also when it is not.
So, to conclude, do you know a pythonic way to intercept key presses inside the terminal window (and not outside) where the script is executed (something like an input()
without having to press Enter), and which is cross-platform (at least compatible with GNU/Linux and Windows)?
Thank you in advance for your answers,
Regards,
Alexis.
Have a look at the curses module. It's in the python standard library, but does not support windows out of the box. There is a regularly maintained project called "windows-curses" you can have a look at. I have not tested it, but it supposedly will allow you to use the python curses module on windows. https://pypi.org/project/windows-curses/
import curses
def listen(window):
while True:
key = window.getch()
window.addstr(f'You pressed the "{key}" key!\n')
if key == 'q':
break
handle_keypress(key)
curses.wrapper(listen)
If the curses approach doesn't work for you, or you still need a bit more granularity, then you can roll your own cross-platform approach fairly easily. You can try something like this:
from sys import platform
class Keyboard:
def __new__(cls):
if platform in ['Windows', 'win32', 'cygwin']:
cls = winKeyboard
elif platform in ['Mac', 'darwin', 'os2', 'os2emx']:
cls = MacKeyboard
else:
raise Exception(f'Unrecognized platform, {platform}')
return super(Keyboard, cls).__new__(cls)
def listen(self):
while True:
key = self.getch()
print(f'You pressed the "{key}" key!')
if key == 27:
break
return self.handle_key(key)
class MacKeyboard(Keyboard):
def getch(self):
implement_mac_logic()
class WinKeyboard(Keyboard):
def getch(self):
implement_win_logic()
keyboard = Keyboard()
keyboard.listen()
Keyboard.__new__ does the work of providing the appropriate solution at runtime for the current os.
This approach will still register key-presses regardless of the active window.
In order to do this, you will need access to the active window, which will be another os-specific procedure.
Have a look at this: https://stackoverflow.com/a/36419702/1420455
You could implement a function that checked the name of the current window
class Keyboard:
...
def listen(self):
while True:
if self.get_active_window() != desired_window:
continue
key = self.getch()
print(f'You pressed the "{key}" key!')
if key == 27:
break
return self.handle_key(key)
Then you can just implement the approate logic in WinKeyboard.get_active_window and MacKeyboard.get_active_window This wont take into account being in different tabs. This may be possible, but I am not familiar enough with the apis to tell you.
There are also options such as pygame that will require you to create and manage your own windows but will meet your requirements.
Edit: Changed WinKeyboard and MacKeyboard to inherit from Keyboard.