Skip to content

Commit 32097cd

Browse files
committed
implement statuslien and echo
1 parent 3f29a61 commit 32097cd

File tree

2 files changed

+221
-49
lines changed

2 files changed

+221
-49
lines changed

examples/nonblocking_serialinput_statusline_dev.py

Lines changed: 58 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import board
1212
import digitalio
1313
import nonblocking_serialinput as nb_serialin
14+
import ansi_escape_code as terminal
1415

1516
##########################################
1617
# globals
@@ -31,31 +32,63 @@ def main():
3132
# wait for serial terminal to get ready..
3233
time.sleep(1)
3334
print("")
34-
print("nonblocking_serialinput_simpletest.py")
35-
print(42 * "*")
36-
37-
runtime_print_next = time.monotonic()
38-
runtime_print_intervall = 1.0
39-
running = True
40-
while running:
41-
# input handling
42-
my_input.update()
43-
input_string = my_input.input()
44-
if input_string is not None:
45-
# print("input_string: {}".format(repr(input_string)))
46-
# we have at least a empty string.
47-
if "exit" in input_string:
48-
print("Stop Program running.")
49-
running = False
50-
elif "hello" in input_string:
51-
print("World :-)")
52-
else:
53-
print("type 'exit' to stop the program.")
54-
# live sign
55-
if runtime_print_next < time.monotonic():
56-
runtime_print_next = time.monotonic() + runtime_print_intervall
57-
print("{: > 7.2f}s".format(time.monotonic()))
58-
led.value = not led.value
35+
print("nonblocking_serialinput_statusline_dev.py")
36+
print("*" * 42)
37+
test_string_colors = (
38+
terminal.ANSIColors.fg.lightblue
39+
+ "Hello "
40+
+ terminal.ANSIColors.fg.green
41+
+ "World "
42+
+ terminal.ANSIColors.fg.orange
43+
+ ":-)"
44+
+ terminal.ANSIColors.reset
45+
)
46+
# print("test_string_colors", test_string_colors)
47+
# print("test_string_colors", test_string_colors)
48+
# print("test_string_colors", test_string_colors)
49+
# time.sleep(1)
50+
# test_string_move = (
51+
# terminal.ANSIControl.cursor.previous_line(2)
52+
# + terminal.ANSIColors.fg.red
53+
# + "WOOO"
54+
# + terminal.ANSIColors.reset
55+
# + terminal.ANSIControl.cursor.next_line(1)
56+
# + terminal.ANSIControl.erase_line()
57+
# + ":-)"
58+
# )
59+
# print(test_string_move)
60+
# time.sleep(1)
61+
62+
print("*" * 42)
63+
print("\n" * 5)
64+
print("1", "dub di dub")
65+
print("2", test_string_colors)
66+
print("3", "This is a beautifull day!")
67+
time.sleep(1)
68+
69+
print(">> ")
70+
print("this is a status line - it should stay as last line.")
71+
time.sleep(5)
72+
73+
move = ""
74+
# earese statusline
75+
move += terminal.ANSIControl.cursor.previous_line(1)
76+
move += terminal.ANSIControl.erase_line(0)
77+
# earese inputline
78+
move += terminal.ANSIControl.cursor.previous_line(1)
79+
move += terminal.ANSIControl.erase_line(0)
80+
print(move, end="")
81+
time.sleep(1)
82+
# output print values
83+
print("enjoy your life!")
84+
# time.sleep(2)
85+
# now we have to reprint the echo & statusline.
86+
print(">> ")
87+
print("this is a status line - it should stay as last line.")
88+
89+
# print("oh... the program just did a print statement... and with this another one..")
90+
91+
time.sleep(10)
5992

6093

6194
##########################################

nonblocking_serialinput.py

Lines changed: 163 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,17 @@
2626
<https://circuitpython.readthedocs.io/en/latest/shared-bindings/usb_cdc/index.html>`_
2727
"""
2828

29+
import time
30+
2931
# import supervisor
3032
import usb_cdc
33+
import ansi_escape_code as terminal
3134

3235
__version__ = "0.0.0-auto.0"
3336
__repo__ = "https://github.com/s-light/CircuitPython_nonblocking_serialinput.git"
3437

38+
# pylint: disable=too-many-instance-attributes
39+
3540
##########################################
3641
# NonBlockingSerialInput Class
3742

@@ -56,9 +61,16 @@ class NonBlockingSerialInput:
5661
Default: usb_cdc.console
5762
:param bool echo: enable/disable remote echo
5863
Default: True
59-
:param bool statusline: enable/disable status line handling - `Not implemented yet - issue #1:
64+
:param string echo_pre_text: Text to put on line start if echo is active
65+
Default: ">> "
66+
:param string statusline: enable/disable status line handling - `Not implemented yet - issue #1:
6067
<https://github.com/s-light/CircuitPython_nonblocking_serialinput/issues/1>`_
61-
Default: False
68+
Default: None
69+
:param function statusline_fn: callback function for statusline output.
70+
must return the string to use as statusline. ``def statusline_fn() string:``
71+
Default: "uptime:{runtime}"
72+
:param string statusline_intervall: time intervall in seconds to update the statusline
73+
Default: 1s
6274
:param string encoding: input string encoding
6375
Default: "utf-8"
6476
:param string, list line_end_custom: set custom line ends
@@ -80,7 +92,10 @@ def __init__(
8092
print_help_fn=None,
8193
serial=usb_cdc.console,
8294
echo=True,
95+
echo_pre_text=">> ",
8396
statusline=False,
97+
statusline_fn=None,
98+
statusline_intervall=1,
8499
encoding="utf-8",
85100
line_end_custom=None,
86101
use_universal_line_end_basic=True,
@@ -92,7 +107,14 @@ def __init__(
92107
self.print_help_fn = print_help_fn
93108
self.serial = serial
94109
self.echo = echo
110+
self.echo_pre_text = echo_pre_text
95111
self.statusline = statusline
112+
if statusline_fn:
113+
self.statusline_fn = statusline_fn
114+
else:
115+
self.statusline_fn = self._statusline_fn_default
116+
self.statusline_intervall = statusline_intervall
117+
self.statusline_next_update = time.monotonic()
96118
self.encoding = encoding
97119
self.line_end_list = []
98120
if line_end_custom:
@@ -108,6 +130,136 @@ def __init__(
108130
self.input_buffer = ""
109131
self.input_list = []
110132

133+
##########################################
134+
# output handling
135+
@staticmethod
136+
def _statusline_fn_default():
137+
"""Default statusline"""
138+
return "uptime:{uptime: >8.2f}".format(uptime=time.monotonic)
139+
140+
def _statusline_update_check_intervall(self):
141+
"""Update the Statusline if intervall is over."""
142+
if self.statusline_next_update <= time.monotonic():
143+
self.statusline_next_update = time.monotonic() + self.statusline_intervall
144+
self.statusline_print()
145+
146+
def _get_statusline(self):
147+
return self.statusline_fn()
148+
149+
def _get_echo_line(self):
150+
text = "{echo_pre_text}{input_buffer}".format(
151+
echo_pre_text=self.echo_pre_text,
152+
input_buffer=self.input_buffer,
153+
)
154+
return text
155+
156+
def statusline_print(self):
157+
"""Update the Statusline."""
158+
if self.statusline:
159+
move = ""
160+
# earease line
161+
move += terminal.ANSIControl.cursor.previous_line(1)
162+
move += terminal.ANSIControl.erase_line(0)
163+
164+
# reprint echo
165+
line = self._get_statusline()
166+
167+
# move back to bottom of screen
168+
moveback = terminal.ANSIControl.cursor.next_line(1)
169+
170+
# execute all the things ;-)
171+
print(
172+
"{move}"
173+
"{line}"
174+
"{moveback}"
175+
"".format(
176+
move=move,
177+
line=line,
178+
moveback=moveback,
179+
),
180+
end="",
181+
)
182+
183+
def echo_print(self):
184+
"""Update the echho line."""
185+
if self.echo:
186+
187+
move = ""
188+
line_count = 1
189+
if self.statusline:
190+
# jump over statusline
191+
line_count += 1
192+
# eareas
193+
move += terminal.ANSIControl.cursor.previous_line(line_count)
194+
move += terminal.ANSIControl.erase_line(0)
195+
196+
# reprint line
197+
line = self._get_echo_line()
198+
199+
# move back to bottom of screen
200+
line_count = 1
201+
if self.statusline:
202+
# jump over statusline
203+
line_count += 1
204+
moveback = terminal.ANSIControl.cursor.next_line(line_count)
205+
206+
# execute all the things ;-)
207+
print(
208+
"{move}"
209+
"{line}"
210+
"{moveback}"
211+
"".format(
212+
move=move,
213+
line=line,
214+
moveback=moveback,
215+
),
216+
end="",
217+
)
218+
219+
def print(self, *args):
220+
# def print(self, *args, end="\n"):
221+
"""
222+
Print information & variables to the connected serial.
223+
224+
This is a *drop in replacement* for the global print function.
225+
it is needed for the statusline handling to work.
226+
(we need to move the cursor...)
227+
228+
currently it is not supported to print without newline at end.
229+
230+
:param object *args: things to print
231+
"""
232+
# :param bool end: line end character to print. Default: "\n"
233+
if self.echo or self.statusline:
234+
move = ""
235+
if self.statusline:
236+
# earease statusline
237+
move += terminal.ANSIControl.cursor.previous_line(1)
238+
move += terminal.ANSIControl.erase_line(0)
239+
if self.echo:
240+
# earease echoline
241+
move += terminal.ANSIControl.cursor.previous_line(1)
242+
move += terminal.ANSIControl.erase_line(0)
243+
print(move, end="")
244+
# *normally print output
245+
print(*args)
246+
# print(*args, end=end)
247+
# print statement is finished.
248+
# now we have to reprint echo & statusline
249+
if self.echo:
250+
print(self._get_echo_line())
251+
if self.statusline:
252+
print(self._get_statusline())
253+
else:
254+
# print(*args, end)
255+
print(*args)
256+
257+
# def out(self):
258+
# pass
259+
260+
##########################################
261+
# input handling
262+
111263
def _buffer_count_line_ends(self):
112264
result = 0
113265
for end in self.line_end_list:
@@ -152,20 +304,24 @@ def input(self):
152304
try:
153305
result = self.input_list.pop(0)
154306
if self.verbose:
155-
print("result: {}".format(repr(result)))
307+
self.print("result: {}".format(repr(result)))
156308
except IndexError:
157309
result = None
158310
return result
159311

312+
##########################################
313+
# main handling
314+
160315
def update(self):
161316
"""Main update funciton. please call as often as possible."""
162317
if self.serial.connected:
163318
available = self.serial.in_waiting
164319
while available:
165320
raw = self.serial.read(available)
166-
if self.echo and not self.statusline:
167-
self.serial.write(raw)
168321
self.input_buffer += raw.decode(self.encoding)
322+
if self.echo:
323+
# self.serial.write(raw)
324+
self.echo_print()
169325
# decode: keyword argeuments and errors not supported by CircuitPython
170326
# encoding=self.encoding,
171327
# errors="strict",
@@ -180,6 +336,8 @@ def update(self):
180336
parsed_input = True
181337
if parsed_input and self.print_help_fn:
182338
self.print_help_fn()
339+
if self.statusline:
340+
self._statusline_update_check_intervall()
183341

184342

185343
##########################################
@@ -222,25 +380,6 @@ def update(self):
222380
]
223381

224382

225-
# def find_first_line_end(input_string, line_end_list=universal_line_end_basic, start=0):
226-
# result = -1
227-
# # print("input_string: {}".format(repr(input_string)))
228-
# i = iter(line_end_list)
229-
# while result is -1:
230-
# try:
231-
# line_end = next(i)
232-
# except StopIteration:
233-
# result = False
234-
# # print("StopIteration")
235-
# else:
236-
# # print("line_end: {}".format(repr(line_end)))
237-
# result = input_string.find(line_end, start)
238-
# # print("result: {}".format(repr(result)))
239-
# if result is False:
240-
# result = -1
241-
# return result
242-
243-
244383
# def find_first_line_end(input_string, line_end_list=None, start=0, return_line_end=False):
245384
def find_first_line_end(input_string, line_end_list=None, start=0):
246385
"""

0 commit comments

Comments
 (0)