Last active
August 2, 2023 03:53
-
-
Save michelbl/efda48b19d3e587685e3441a74457024 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
''' | |
A Python class implementing KBHIT, the standard keyboard-interrupt poller. | |
Works transparently on Windows and Posix (Linux, Mac OS X). Doesn't work | |
with IDLE. | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU Lesser General Public License as | |
published by the Free Software Foundation, either version 3 of the | |
License, or (at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
''' | |
import os | |
# Windows | |
if os.name == 'nt': | |
import msvcrt | |
# Posix (Linux, OS X) | |
else: | |
import sys | |
import termios | |
import atexit | |
from select import select | |
class KBHit: | |
def __init__(self): | |
'''Creates a KBHit object that you can call to do various keyboard things. | |
''' | |
if os.name == 'nt': | |
pass | |
else: | |
# Save the terminal settings | |
self.fd = sys.stdin.fileno() | |
self.new_term = termios.tcgetattr(self.fd) | |
self.old_term = termios.tcgetattr(self.fd) | |
# New terminal setting unbuffered | |
self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO) | |
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term) | |
# Support normal-terminal reset at exit | |
atexit.register(self.set_normal_term) | |
def set_normal_term(self): | |
''' Resets to normal terminal. On Windows this is a no-op. | |
''' | |
if os.name == 'nt': | |
pass | |
else: | |
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term) | |
def getch(self): | |
''' Returns a keyboard character after kbhit() has been called. | |
Should not be called in the same program as getarrow(). | |
''' | |
s = '' | |
if os.name == 'nt': | |
return msvcrt.getch().decode('utf-8') | |
else: | |
return sys.stdin.read(1) | |
def getarrow(self): | |
''' Returns an arrow-key code after kbhit() has been called. Codes are | |
0 : up | |
1 : right | |
2 : down | |
3 : left | |
Should not be called in the same program as getch(). | |
''' | |
if os.name == 'nt': | |
msvcrt.getch() # skip 0xE0 | |
c = msvcrt.getch() | |
vals = [72, 77, 80, 75] | |
else: | |
c = sys.stdin.read(3)[2] | |
vals = [65, 67, 66, 68] | |
return vals.index(ord(c.decode('utf-8'))) | |
def kbhit(self): | |
''' Returns True if keyboard character was hit, False otherwise. | |
''' | |
if os.name == 'nt': | |
return msvcrt.kbhit() | |
else: | |
dr,dw,de = select([sys.stdin], [], [], 0) | |
return dr != [] | |
# Test | |
if __name__ == "__main__": | |
kb = KBHit() | |
print('Hit any key, or ESC to exit') | |
while True: | |
if kb.kbhit(): | |
c = kb.getch() | |
c_ord = ord(c) | |
print(c) | |
print(c_ord) | |
if c_ord == 27: # ESC | |
break | |
print(c) | |
kb.set_normal_term() |
Is there a possibilty to get on Windows and Linux the same KeyCodes. e.g. F1 behaves different ...
Hello, great work! Can I be able to use the code for my research work? I will site your github. Thank you!
You can do whatever you want with this piece of code.
Is it possible to get 0x03
when pressing ctrl-c?
How to modify the code?
import os
if os.name == 'nt':
import msvcrt
else:
import sys
import termios
import atexit
from select import select
class KBHit:
def __init__(self):
'''
Creates a KBHit object that you can call to do various keyboard things.
'''
if os.name != 'nt':
self._setup_posix()
def _setup_posix(self):
'''
Sets up the terminal to read non-blocking input.
'''
# Save the terminal settings
self.fd = sys.stdin.fileno()
self.new_term = termios.tcgetattr(self.fd)
self.old_term = termios.tcgetattr(self.fd)
# New terminal setting unbuffered
self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
# Support normal-terminal reset at exit
atexit.register(self._reset_posix_term)
def _reset_posix_term(self):
# Resets to normal terminal. On Windows, this is a no-op.
if os.name != 'nt':
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)
def getch(self):
'''
Returns a keyboard character after kbhit() has been called.
'''
if os.name == 'nt':
return msvcrt.getch().decode('utf-8')
else:
return sys.stdin.read(1)
def get_arrow(self):
'''
Returns an arrow-key code after kbhit() has been called. Codes are:
0: up, 1: right, 2: down, 3: left
'''
if os.name == 'nt':
msvcrt.getch() # skip 0xE0
c = msvcrt.getch()
vals = [72, 77, 80, 75]
else:
c = sys.stdin.read(3)[2]
vals = [65, 67, 66, 68]
return vals.index(ord(c.decode('utf-8')))
def kbhit(self):
'''
Returns True if a keyboard character was hit, False otherwise.
'''
if os.name == 'nt':
return msvcrt.kbhit()
else:
dr, dw, de = select([sys.stdin], [], [], 0)
return dr != []
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if os.name != 'nt':
self._reset_posix_term()
# Test
if __name__ == "__main__":
kb = KBHit()
print('Hit any key, or ESC to exit')
with KBHit() as kb:
try:
while True:
if kb.kbhit():
c = kb.getch()
c_ord = ord(c)
print(c)
print(c_ord)
if c_ord == 27: # ESC
break
print(c)
except KeyboardInterrupt:
pass
A lil' refactor, nothing much
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
this doesn't work in python 3
windows10 understands unicode quite well, so you can use msvcrt.getwch
a small example