-
Notifications
You must be signed in to change notification settings - Fork 18
/
console.py
132 lines (111 loc) · 3.43 KB
/
console.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import os, sys
try:
from msvcrt import getch
special_keys = {
b'H': 'up', b'P': 'down',
b'M': 'right', b'K': 'left',
}
def _get_key():
char = getch()
# Arrow keys are returned as two bytes, starting with 224.
if ord(char) == 224:
return special_keys[getch()]
else:
return char.decode('utf-8')
def _display(text):
os.system('cls')
sys.stdout.write(text)
except ImportError:
import curses, atexit
window = curses.initscr()
window.keypad(True)
curses.noecho()
curses.cbreak()
# Without this the program works fine, but the terminal remains borked
# after exit. If this happens to you, run `reset` to restore (just type and
# press enter, even if you don't see the text).
atexit.register(curses.endwin)
atexit.register(curses.nocbreak)
atexit.register(curses.echo)
special_keys = {
259: 'up', 258: 'down',
261: 'right', 260: 'left',
}
# Note: window.getch() returns int, not str.
def _get_key():
keycode = window.getch()
if keycode > 256:
return special_keys[keycode]
else:
return chr(keycode)
def _display(text):
window.addstr(0, 0, text)
window.clrtobot()
window.refresh()
current_text = ''
def display(text):
"""
Clears the screen and refills it with the given text.
"""
while not isinstance(text, str):
text = ''.join(text)
global current_text
current_text = text
return _display(text)
def set_display(line, column, text):
"""
Changes only a portion of the display, keeping the rest constant.
This function assumes the display has been set by the function `display`.
Multi-character replacements are supported, but multi-line ones not.
"""
lines = current_text.split('\n')
x, y = column, line
lines[y] = lines[y][:x] + text + lines[y][x + len(text):]
display('\n'.join(lines))
"""
Map of keys and functions. Every time one of these keys is pressed, the
respective function will be called. The pressed key is not returned by
get_key() or similar.
"""
hotkeys = {}
def get_key():
"""
Waits for user keyboard input and returns the character typed, or special
key such as "up".
"""
while True:
key = _get_key()
if key in hotkeys:
hotkeys[key]()
continue
else:
return key
def get_valid_key(expected_keys):
"""
Waits until the user presses one of the keys in `expected_keys` and returns
it.
"""
while True:
key = get_key()
if key in expected_keys:
return key
def get_option(option_by_key):
"""
Given a map key -> option, waits until the user selects a valid key and
then returns the associated option.
"""
return option_by_key[get_valid_key(option_by_key)]
def process_input(function_by_key):
"""
Given a map key -> function, loops receiving user input and invoking the
respective functions. Unknown keys are ignored.
To exit the loop, raise an exception from the invoked function.
"""
while True:
get_option(function_by_key)()
if __name__ == '__main__':
while True:
key = get_key()
print(key)
if key == 'q':
exit()