2626 <https://circuitpython.readthedocs.io/en/latest/shared-bindings/usb_cdc/index.html>`_
2727"""
2828
29+ import time
30+
2931# import supervisor
3032import 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):
245384def find_first_line_end (input_string , line_end_list = None , start = 0 ):
246385 """
0 commit comments