Merge pull request #2727 from FoamyGuy/add_text_editor
Adding Text editor project code
This commit is contained in:
commit
ae08e5704c
7 changed files with 632 additions and 0 deletions
3
CircuitPython_Text_Editor/adafruit_editor/__init__.py
Normal file
3
CircuitPython_Text_Editor/adafruit_editor/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-FileCopyrightText: 2024 Tim Cocks
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
124
CircuitPython_Text_Editor/adafruit_editor/dang.py
Normal file
124
CircuitPython_Text_Editor/adafruit_editor/dang.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import select
|
||||
import sys
|
||||
# pylint: disable=no-self-use
|
||||
|
||||
try:
|
||||
import termios
|
||||
|
||||
_orig_attr = None # pylint: disable=invalid-name
|
||||
|
||||
def _nonblocking():
|
||||
global _orig_attr # pylint: disable=global-statement
|
||||
_orig_attr = termios.tcgetattr(sys.stdin)
|
||||
attr = termios.tcgetattr(sys.stdin)
|
||||
attr[3] &= ~(termios.ECHO | termios.ICANON)
|
||||
attr[6][termios.VMIN] = 1
|
||||
attr[6][termios.VTIME] = 0
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr)
|
||||
|
||||
def _blocking():
|
||||
if _orig_attr is not None:
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, _orig_attr)
|
||||
|
||||
except ImportError:
|
||||
|
||||
def _nonblocking():
|
||||
pass
|
||||
|
||||
def _blocking():
|
||||
pass
|
||||
|
||||
|
||||
LINES = 24
|
||||
COLS = 80
|
||||
|
||||
special_keys = {
|
||||
"\x1b": ..., # all prefixes of special keys must be entered as Ellipsis
|
||||
"\x1b[": ...,
|
||||
"\x1b[5": ...,
|
||||
"\x1b[6": ...,
|
||||
"\x1b[A": "KEY_UP",
|
||||
"\x1b[B": "KEY_DOWN",
|
||||
"\x1b[C": "KEY_RIGHT",
|
||||
"\x1b[D": "KEY_LEFT",
|
||||
"\x1b[H": "KEY_HOME",
|
||||
"\x1b[F": "KEY_END",
|
||||
"\x1b[5~": "KEY_PGUP",
|
||||
"\x1b[6~": "KEY_PGDN",
|
||||
"\x1b[3~": "KEY_DELETE",
|
||||
}
|
||||
|
||||
|
||||
class Screen:
|
||||
def __init__(self):
|
||||
self._poll = select.poll()
|
||||
self._poll.register(sys.stdin, select.POLLIN)
|
||||
self._pending = ""
|
||||
|
||||
def _sys_stdin_readable(self):
|
||||
return hasattr(sys.stdin, "readable") and sys.stdin.readable()
|
||||
|
||||
def _sys_stdout_flush(self):
|
||||
if hasattr(sys.stdout, "flush"):
|
||||
sys.stdout.flush()
|
||||
|
||||
def _terminal_read_blocking(self):
|
||||
return sys.stdin.read(1)
|
||||
|
||||
def _terminal_read_timeout(self, timeout):
|
||||
if self._sys_stdin_readable() or self._poll.poll(timeout):
|
||||
r = sys.stdin.read(1)
|
||||
return r
|
||||
return None
|
||||
|
||||
def move(self, y, x):
|
||||
print(end=f"\033[{y+1};{x+1}H")
|
||||
|
||||
def erase(self):
|
||||
print(end="\033H\033[2J")
|
||||
|
||||
def addstr(self, y, x, text):
|
||||
self.move(y, x)
|
||||
print(end=text)
|
||||
|
||||
def getkey(self):
|
||||
self._sys_stdout_flush()
|
||||
pending = self._pending
|
||||
if pending and (code := special_keys.get(pending)) is None:
|
||||
self._pending = pending[1:]
|
||||
return pending[0]
|
||||
|
||||
while True:
|
||||
if pending:
|
||||
c = self._terminal_read_timeout(50)
|
||||
if c is None:
|
||||
self._pending = pending[1:]
|
||||
return pending[0]
|
||||
else:
|
||||
c = self._terminal_read_blocking()
|
||||
c = pending + c
|
||||
|
||||
code = special_keys.get(c)
|
||||
|
||||
if code is None:
|
||||
self._pending = c[1:]
|
||||
return c[0]
|
||||
if code is not Ellipsis:
|
||||
return code
|
||||
|
||||
pending = c
|
||||
|
||||
|
||||
def wrapper(func, *args, **kwds):
|
||||
stdscr = Screen()
|
||||
try:
|
||||
_nonblocking()
|
||||
return func(stdscr, *args, **kwds)
|
||||
finally:
|
||||
_blocking()
|
||||
stdscr.move(LINES - 1, 0)
|
||||
print("\n")
|
||||
333
CircuitPython_Text_Editor/adafruit_editor/editor.py
Normal file
333
CircuitPython_Text_Editor/adafruit_editor/editor.py
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
# SPDX-FileCopyrightText: 2020 Wasim Lorgat
|
||||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import gc
|
||||
import os
|
||||
import usb_cdc
|
||||
from . import dang as curses
|
||||
from . import util
|
||||
#pylint: disable=redefined-builtin
|
||||
|
||||
def print(message):
|
||||
usb_cdc.data.write(f"{message}\r\n".encode("utf-8"))
|
||||
|
||||
|
||||
class MaybeDisableReload:
|
||||
def __enter__(self):
|
||||
try:
|
||||
from supervisor import runtime # pylint: disable=import-outside-toplevel
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
self._old_autoreload = ( # pylint: disable=attribute-defined-outside-init
|
||||
runtime.autoreload
|
||||
)
|
||||
runtime.autoreload = False
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
try:
|
||||
from supervisor import runtime # pylint: disable=import-outside-toplevel
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
runtime.autoreload = self._old_autoreload
|
||||
|
||||
|
||||
def os_exists(filename):
|
||||
try:
|
||||
os.stat(filename)
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def gc_mem_free_hint():
|
||||
if hasattr(gc, "mem_free"):
|
||||
gc.collect()
|
||||
return f" | free: {gc.mem_free()}"
|
||||
return ""
|
||||
|
||||
|
||||
class Buffer:
|
||||
def __init__(self, lines):
|
||||
self.lines = lines
|
||||
|
||||
def __len__(self):
|
||||
return len(self.lines)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.lines[index]
|
||||
|
||||
@property
|
||||
def bottom(self):
|
||||
return len(self) - 1
|
||||
|
||||
def insert(self, cursor, string):
|
||||
row, col = cursor.row, cursor.col
|
||||
# print(f"len: {len(self.lines)}")
|
||||
# print(f"row: {row}")
|
||||
try:
|
||||
current = self.lines.pop(row)
|
||||
except IndexError:
|
||||
current = ""
|
||||
new = current[:col] + string + current[col:]
|
||||
self.lines.insert(row, new)
|
||||
|
||||
def split(self, cursor):
|
||||
row, col = cursor.row, cursor.col
|
||||
current = self.lines.pop(row)
|
||||
self.lines.insert(row, current[:col])
|
||||
self.lines.insert(row + 1, current[col:])
|
||||
|
||||
def delete(self, cursor):
|
||||
row, col = cursor.row, cursor.col
|
||||
if (row, col) < (self.bottom, len(self[row])):
|
||||
current = self.lines.pop(row)
|
||||
if col < len(current):
|
||||
new = current[:col] + current[col + 1:]
|
||||
self.lines.insert(row, new)
|
||||
else:
|
||||
nextline = self.lines.pop(row)
|
||||
new = current + nextline
|
||||
self.lines.insert(row, new)
|
||||
|
||||
|
||||
def clamp(x, lower, upper):
|
||||
if x < lower:
|
||||
return lower
|
||||
if x > upper:
|
||||
return upper
|
||||
return x
|
||||
|
||||
|
||||
class Cursor:
|
||||
def __init__(self, row=0, col=0, col_hint=None):
|
||||
self.row = row
|
||||
self._col = col
|
||||
self._col_hint = col if col_hint is None else col_hint
|
||||
|
||||
@property
|
||||
def col(self):
|
||||
return self._col
|
||||
|
||||
@col.setter
|
||||
def col(self, col):
|
||||
self._col = col
|
||||
self._col_hint = col
|
||||
|
||||
def _clamp_col(self, buffer):
|
||||
self._col = min(self._col_hint, len(buffer[self.row]))
|
||||
|
||||
def up(self, buffer): # pylint: disable=invalid-name
|
||||
if self.row > 0:
|
||||
self.row -= 1
|
||||
self._clamp_col(buffer)
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
|
||||
def down(self, buffer):
|
||||
if self.row < len(buffer) - 1:
|
||||
self.row += 1
|
||||
self._clamp_col(buffer)
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
|
||||
def left(self, buffer):
|
||||
if self.col > 0:
|
||||
self.col -= 1
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
elif self.row > 0:
|
||||
self.row -= 1
|
||||
self.col = len(buffer[self.row])
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
|
||||
def right(self, buffer):
|
||||
print(f"len: {len(buffer)}")
|
||||
if len(buffer) > 0 and self.col < len(buffer[self.row]):
|
||||
self.col += 1
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
elif self.row < len(buffer) - 1:
|
||||
self.row += 1
|
||||
self.col = 0
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
|
||||
|
||||
def end(self, buffer):
|
||||
self.col = len(buffer[self.row])
|
||||
# print(f"cursor pos: {self.row}, {self.col}")
|
||||
|
||||
|
||||
class Window:
|
||||
def __init__(self, n_rows, n_cols, row=0, col=0):
|
||||
self.n_rows = n_rows
|
||||
self.n_cols = n_cols
|
||||
self.row = row
|
||||
self.col = col
|
||||
|
||||
@property
|
||||
def bottom(self):
|
||||
return self.row + self.n_rows - 1
|
||||
|
||||
def up(self, cursor): # pylint: disable=invalid-name
|
||||
if cursor.row == self.row - 1 and self.row > 0:
|
||||
self.row -= 1
|
||||
|
||||
def down(self, buffer, cursor):
|
||||
if cursor.row == self.bottom + 1 and self.bottom < len(buffer) - 1:
|
||||
self.row += 1
|
||||
|
||||
def horizontal_scroll(self, cursor, left_margin=5, right_margin=2):
|
||||
n_pages = cursor.col // (self.n_cols - right_margin)
|
||||
self.col = max(n_pages * self.n_cols - right_margin - left_margin, 0)
|
||||
|
||||
def translate(self, cursor):
|
||||
return cursor.row - self.row, cursor.col - self.col
|
||||
|
||||
|
||||
def left(window, buffer, cursor):
|
||||
cursor.left(buffer)
|
||||
window.up(cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def right(window, buffer, cursor):
|
||||
cursor.right(buffer)
|
||||
window.down(buffer, cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def home(window, buffer, cursor): # pylint: disable=unused-argument
|
||||
cursor.col = 0
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def end(window, buffer, cursor):
|
||||
cursor.end(buffer)
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def editor(stdscr, filename, visible_cursor): # pylint: disable=too-many-branches,too-many-statements
|
||||
if os_exists(filename):
|
||||
with open(filename, "r", encoding="utf-8") as f:
|
||||
buffer = Buffer(f.read().splitlines())
|
||||
else:
|
||||
buffer = Buffer([""])
|
||||
|
||||
window = Window(curses.LINES - 1, curses.COLS - 1)
|
||||
cursor = Cursor()
|
||||
|
||||
# print("updating visible cursor")
|
||||
visible_cursor.anchored_position = ((0 * 6) - 1, (0 * 12) + 20)
|
||||
try:
|
||||
visible_cursor.text = buffer.lines[0][0]
|
||||
except IndexError:
|
||||
visible_cursor.text = " "
|
||||
|
||||
stdscr.erase()
|
||||
|
||||
img = [None] * curses.LINES
|
||||
|
||||
def setline(row, line):
|
||||
if img[row] == line:
|
||||
return
|
||||
img[row] = line
|
||||
line += " " * (window.n_cols - len(line))
|
||||
stdscr.addstr(row, 0, line)
|
||||
|
||||
while True:
|
||||
lastrow = 0
|
||||
for row, line in enumerate(buffer[window.row: window.row + window.n_rows]):
|
||||
lastrow = row
|
||||
if row == cursor.row - window.row and window.col > 0:
|
||||
line = "«" + line[window.col + 1:]
|
||||
if len(line) > window.n_cols:
|
||||
line = line[: window.n_cols - 1] + "»"
|
||||
setline(row, line)
|
||||
for row in range(lastrow + 1, window.n_rows):
|
||||
setline(row, "~~ EOF ~~")
|
||||
row = curses.LINES - 1
|
||||
if util.readonly():
|
||||
line = f"{filename:12} (readonly) | ^C: quit{gc_mem_free_hint()}"
|
||||
else:
|
||||
line = f"{filename:12} | ^X: write & exit | ^C: quit w/o save{gc_mem_free_hint()}"
|
||||
setline(row, line)
|
||||
|
||||
stdscr.move(*window.translate(cursor))
|
||||
|
||||
k = stdscr.getkey()
|
||||
if len(k) == 1 and " " <= k <= "~":
|
||||
buffer.insert(cursor, k)
|
||||
for _ in k:
|
||||
right(window, buffer, cursor)
|
||||
elif k == "\x18": # ctrl-x
|
||||
if not util.readonly():
|
||||
with open(filename, "w", encoding="utf-8") as f:
|
||||
for row in buffer:
|
||||
f.write(f"{row}\n")
|
||||
return
|
||||
else:
|
||||
print("Unable to Save due to readonly mode! File Contents:")
|
||||
print("---- begin file contents ----")
|
||||
for row in buffer:
|
||||
print(row)
|
||||
print("---- end file contents ----")
|
||||
elif k == "\x11": # Ctrl-Q
|
||||
print("ctrl-Q")
|
||||
for row in buffer:
|
||||
print(row)
|
||||
elif k == "KEY_HOME":
|
||||
home(window, buffer, cursor)
|
||||
elif k == "KEY_END":
|
||||
end(window, buffer, cursor)
|
||||
elif k == "KEY_LEFT":
|
||||
left(window, buffer, cursor)
|
||||
elif k == "KEY_DOWN":
|
||||
cursor.down(buffer)
|
||||
window.down(buffer, cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_PGDN":
|
||||
for _ in range(window.n_rows):
|
||||
cursor.down(buffer)
|
||||
window.down(buffer, cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_UP":
|
||||
cursor.up(buffer)
|
||||
window.up(cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_PGUP":
|
||||
for _ in range(window.n_rows):
|
||||
cursor.up(buffer)
|
||||
window.up(cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_RIGHT":
|
||||
right(window, buffer, cursor)
|
||||
elif k == "\n":
|
||||
buffer.split(cursor)
|
||||
right(window, buffer, cursor)
|
||||
elif k in ("KEY_DELETE", "\x04"):
|
||||
print("delete")
|
||||
buffer.delete(cursor)
|
||||
|
||||
elif k in ("KEY_BACKSPACE", "\x7f", "\x08"):
|
||||
print(f"backspace {bytes(k, 'utf-8')}")
|
||||
if (cursor.row, cursor.col) > (0, 0):
|
||||
left(window, buffer, cursor)
|
||||
buffer.delete(cursor)
|
||||
else:
|
||||
print(f"unhandled k: {k}")
|
||||
print(f"unhandled K: {ord(k)}")
|
||||
print(f"unhandled k: {bytes(k, 'utf-8')}")
|
||||
# print("updating visible cursor")
|
||||
# print(f"anchored pos: {((cursor.col * 6) - 1, (cursor.row * 12) + 20)}")
|
||||
visible_cursor.anchored_position = ((cursor.col * 6) - 1, (cursor.row * 12) + 20)
|
||||
|
||||
try:
|
||||
visible_cursor.text = buffer.lines[cursor.row][cursor.col]
|
||||
except IndexError:
|
||||
visible_cursor.text = " "
|
||||
|
||||
|
||||
def edit(filename, visible_cursor):
|
||||
with MaybeDisableReload():
|
||||
return curses.wrapper(editor, filename, visible_cursor)
|
||||
98
CircuitPython_Text_Editor/adafruit_editor/picker.py
Normal file
98
CircuitPython_Text_Editor/adafruit_editor/picker.py
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import os
|
||||
import usb_cdc
|
||||
from . import dang as curses
|
||||
from . import util
|
||||
#pylint: disable=redefined-builtin
|
||||
|
||||
def print(message):
|
||||
usb_cdc.data.write(f"{message}\r\n".encode("utf-8"))
|
||||
|
||||
|
||||
always = ["code.py", "boot.py", "settings.toml", "boot_out.txt"]
|
||||
good_extensions = [".py", ".toml", ".txt", ".json"]
|
||||
|
||||
|
||||
def os_exists(filename):
|
||||
try:
|
||||
os.stat(filename)
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def isdir(filename):
|
||||
return os.stat(filename)[0] & 0o40_000
|
||||
|
||||
|
||||
def has_good_extension(filename):
|
||||
for g in good_extensions:
|
||||
if filename.endswith(g):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def picker(stdscr, options, notes=(), start_idx=0):
|
||||
stdscr.erase()
|
||||
stdscr.addstr(curses.LINES - 1, 0, "Enter: select | ^C: quit | ^N: New")
|
||||
del options[curses.LINES - 1:]
|
||||
for row, option in enumerate(options):
|
||||
if row < len(notes) and (note := notes[row]):
|
||||
option = f"{option} {note}"
|
||||
|
||||
stdscr.addstr(row, 3, option)
|
||||
|
||||
old_idx = None
|
||||
idx = start_idx
|
||||
while True:
|
||||
if idx != old_idx:
|
||||
if old_idx is not None:
|
||||
stdscr.addstr(old_idx, 0, " ")
|
||||
stdscr.addstr(idx, 0, "=>")
|
||||
old_idx = idx
|
||||
|
||||
k = stdscr.getkey()
|
||||
|
||||
if k == "KEY_DOWN":
|
||||
idx = min(idx + 1, len(options) - 1)
|
||||
elif k == "KEY_UP":
|
||||
idx = max(idx - 1, 0)
|
||||
elif k == "\n":
|
||||
return options[idx]
|
||||
|
||||
# ctrl-N
|
||||
elif k == "\x0E":
|
||||
if not util.readonly():
|
||||
new_file_name = new_file(stdscr)
|
||||
if new_file_name is not None:
|
||||
return new_file_name
|
||||
|
||||
|
||||
# pylint: disable=inconsistent-return-statements
|
||||
def new_file(stdscr):
|
||||
stdscr.erase()
|
||||
new_file_name = input("New File Name: ")
|
||||
if os_exists(new_file_name):
|
||||
print("Error: File Already Exists")
|
||||
return
|
||||
with open(new_file_name, "w") as f:
|
||||
f.write("")
|
||||
|
||||
return new_file_name
|
||||
|
||||
|
||||
def pick_file():
|
||||
options = sorted(
|
||||
(
|
||||
g
|
||||
for g in os.listdir(".")
|
||||
if g not in always and not isdir(g) and not g.startswith(".")
|
||||
),
|
||||
key=lambda filename: (not has_good_extension(filename), filename),
|
||||
) + always[:]
|
||||
notes = [None if os_exists(filename) else "(NEW)" for filename in options]
|
||||
return curses.wrapper(picker, options, notes)
|
||||
10
CircuitPython_Text_Editor/adafruit_editor/util.py
Normal file
10
CircuitPython_Text_Editor/adafruit_editor/util.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
def readonly():
|
||||
try:
|
||||
import storage # pylint: disable=import-outside-toplevel
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
return storage.getmount("/").readonly
|
||||
15
CircuitPython_Text_Editor/boot.py
Normal file
15
CircuitPython_Text_Editor/boot.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# SPDX-FileCopyrightText: 2024 Tim Cocks
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
import usb_cdc
|
||||
import board
|
||||
import digitalio
|
||||
import storage
|
||||
|
||||
usb_cdc.enable(console=True, data=True) # Enable console and data
|
||||
|
||||
write_mode_btn = digitalio.DigitalInOut(board.D9)
|
||||
write_mode_btn.direction = digitalio.Direction.INPUT
|
||||
write_mode_btn.pull = digitalio.Pull.UP
|
||||
|
||||
storage.remount("/", readonly=write_mode_btn.value)
|
||||
49
CircuitPython_Text_Editor/code.py
Normal file
49
CircuitPython_Text_Editor/code.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# SPDX-FileCopyrightText: 2024 Tim Cocks
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
import traceback
|
||||
from adafruit_editor import editor, picker
|
||||
from adafruit_featherwing import tft_featherwing_35
|
||||
import terminalio
|
||||
import displayio
|
||||
from adafruit_display_text.bitmap_label import Label
|
||||
import usb_cdc
|
||||
#pylint: disable=redefined-builtin,broad-except
|
||||
|
||||
def print(message):
|
||||
usb_cdc.data.write(f"{message}\r\n".encode("utf-8"))
|
||||
|
||||
|
||||
tft_featherwing = tft_featherwing_35.TFTFeatherWing35V2()
|
||||
display = tft_featherwing.display
|
||||
display.rotation = 180
|
||||
|
||||
customized_console_group = displayio.Group()
|
||||
display.root_group = customized_console_group
|
||||
customized_console_group.append(displayio.CIRCUITPYTHON_TERMINAL)
|
||||
|
||||
visible_cursor = Label(terminalio.FONT, text="",
|
||||
color=0x000000, background_color=0xeeeeee, padding_left=1)
|
||||
visible_cursor.hidden = True
|
||||
visible_cursor.anchor_point = (0, 0)
|
||||
customized_console_group.append(visible_cursor)
|
||||
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
visible_cursor.hidden = True
|
||||
filename = picker.pick_file()
|
||||
except KeyboardInterrupt:
|
||||
customized_console_group.remove(displayio.CIRCUITPYTHON_TERMINAL)
|
||||
break
|
||||
|
||||
try:
|
||||
visible_cursor.hidden = False
|
||||
editor.edit(filename, visible_cursor)
|
||||
except KeyboardInterrupt:
|
||||
visible_cursor.hidden = True
|
||||
|
||||
# Any Exception, including Keyboard Interrupt
|
||||
except Exception as e:
|
||||
print("\n".join(traceback.format_exception(e)))
|
||||
customized_console_group.remove(displayio.CIRCUITPYTHON_TERMINAL)
|
||||
Loading…
Reference in a new issue