-
Notifications
You must be signed in to change notification settings - Fork 0
/
asyncconsole.py
130 lines (104 loc) · 3.87 KB
/
asyncconsole.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
# Operation vars and funcs
failsafe = False
import curses
from curses import textpad
# Local imports
import chat
import voice
class AsyncConsole(object):
def __init__(self,screen=None,prompt_string='> '):
self.screen = screen
self.output_window = None
self.prompt_window = None
self.prompt_string=prompt_string
self._initialize()
self.rebuild_prompt()
def _initialize(self):
if not self.screen:
# if wrapper has been used, we don't need this
self.screen = curses.initscr()
curses.noecho()
curses.cbreak()
# get the current size of screen
(y,x) = self.screen.getmaxyx()
# leave last lines for prompt
self.output_window = self.screen.subwin(y-2,x,0,0)
self.prompt_window = self.screen.subwin(1,x,y-2,0)
self.edit = textpad.Textbox(self.prompt_window,insert_mode=True)
# let output_window scroll by itself when number of lines are more than window size
self.output_window.scrollok(True)
self.prompt_window.scrollok(True) #FIX: not working with textpad.Textbox
def rebuild_prompt(self,default_text=None):
self.prompt_window.clear()
self.prompt_window.addstr(self.prompt_string)
if default_text:
self.prompt_window.addstr(default_text)
self.prompt_window.refresh()
def resize(self):
#FIX: leaving garbage behind
# get new size of screen
(y,x)=self.screen.getmaxyx()
self.output_window.resize(y-2,x)
# move the prompt window to the bottom of the output_window
self.prompt_window.mvwin(y-2,0)
self.prompt_window.resize(1,x)
self.output_window.refresh()
self.prompt_window.refresh()
def _validate_input(self,key):
#TODO: handle up and down arrows
# terminate editing when pression enter key
if key == ord('\n'):
return curses.ascii.BEL # this is equivalent to CONTROL+G - terminate editing and return content
if key in (curses.ascii.STX,curses.KEY_LEFT, curses.ascii.BS,curses.KEY_BACKSPACE):
minx = len(self.prompt_string)
(y,x) = self.prompt_window.getyx()
if x == minx:
return None
return key
def readline(self,handle_interrupt=True):
# interpret keypad keys like arrows
self.prompt_window.keypad(1)
try:
self.input_string = self.edit.edit(self._validate_input)[len(self.prompt_string):]
except KeyboardInterrupt:
#TODO: I still don't know if I want to handle this here or not
if handle_interrupt:
return False
else:
raise KeyboardInterrupt
self.rebuild_prompt()
return True
def addline(self,line):
'''
Add a string line to the output screen
'''
#TODO: make this thread safe?
self.output_window.addstr(line+'\n')
self.output_window.refresh()
def restore_screen(self):
# to be used if not using the wrapper module
curses.nocbreak()
curses.echo()
curses.endwin()
def main(stdscr):
# demo code
console = AsyncConsole(stdscr)
exit = False
while not exit and console.readline():
query = console.input_string
console.addline('Q: ' + query)
if query.lower() == 'quit':
exit = True
returned = {}
if failsafe:
try:
returned = chat.process(query)
except:
returned = {'message': 'Unable to perform desired action. Processing error occured.', 'image': 'None'}
else:
returned = chat.process(query)
console.addline('R: ' + returned['message'])
if chat.speak:
voice.speak(returned['message'])
if __name__ == '__main__':
curses.wrapper(main)