From 67c6becadf997d029462bc0fb1d13ed1861d6261 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 25 Mar 2020 09:26:22 -0700 Subject: [PATCH 01/17] initial work on animation --- src/clue/adafruit_slideshow.py | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/clue/adafruit_slideshow.py diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py new file mode 100644 index 000000000..f895af851 --- /dev/null +++ b/src/clue/adafruit_slideshow.py @@ -0,0 +1,49 @@ +import common +from PIL import Image + +import os +import base64 +from io import BytesIO +from base_circuitpython import base_cp_constants as CONSTANTS + + +class SlideShow: + def __init__( + self, + display, + backlight_pwm=None, + *, + folder="/", + order=0, + loop=True, + dwell=3, + fade_effect=True, + auto_advance=True, + direction=1 + ): + self.dirs = os.listdir(folder) + self.img = None + + def show(self): + for d in self.dirs: + for i in range(6): + self.img = Image.open(os.path.join("./pix", d)) + self.img.convert("RGBA") + self.img.putalpha(i * 51) + self.send() + + for i in range(5, -1): + self.img = Image.open(os.path.join("./pix", d)) + self.img.convert("RGBA") + self.img.putalpha(i * 51) + self.send() + + def send(self): + # sends current bmp_img to the frontend + buffered = BytesIO() + img.save(buffered, format="BMP") + byte_base64 = base64.b64encode(buffered.getvalue()) + img_str = str(byte_base64)[2:-1] + + sendable_json = {CONSTANTS.BASE_64: img_str} + common.utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) From d65486cfdf1c0a4cacb41d3767950abc36262e03 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 25 Mar 2020 14:47:54 -0700 Subject: [PATCH 02/17] cleanup on slideshow --- src/base_circuitpython/base_cp_constants.py | 3 ++ src/clue/adafruit_slideshow.py | 54 +++++++++++++++------ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/base_circuitpython/base_cp_constants.py b/src/base_circuitpython/base_cp_constants.py index bd4d99a26..0304c7e36 100644 --- a/src/base_circuitpython/base_cp_constants.py +++ b/src/base_circuitpython/base_cp_constants.py @@ -3,3 +3,6 @@ PIXELS = "pixels" CLUE_PIN = "D18" + +CLUE = "CLUE" +BASE_64 = "display_base64" diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py index f895af851..53d30f6cc 100644 --- a/src/clue/adafruit_slideshow.py +++ b/src/clue/adafruit_slideshow.py @@ -5,9 +5,11 @@ import base64 from io import BytesIO from base_circuitpython import base_cp_constants as CONSTANTS - +import time +import queue class SlideShow: + def __init__( self, display, @@ -19,26 +21,46 @@ def __init__( dwell=3, fade_effect=True, auto_advance=True, - direction=1 + direction=1, ): self.dirs = os.listdir(folder) - self.img = None + self.folder = folder + + self.BASE_DWELL = 0.3 + self.BASE_DWELL_DARK = 0.7 + self.FADE_FRAMES = 10 + + self.dwell = self.BASE_DWELL + dwell + + + # for d in self.dirs: + # self.pic_queue = + # def show(self): + + img = None for d in self.dirs: - for i in range(6): - self.img = Image.open(os.path.join("./pix", d)) - self.img.convert("RGBA") - self.img.putalpha(i * 51) - self.send() - - for i in range(5, -1): - self.img = Image.open(os.path.join("./pix", d)) - self.img.convert("RGBA") - self.img.putalpha(i * 51) - self.send() - - def send(self): + try: + new_path = os.path.join(self.folder, d) + img = Image.open(new_path) + img.convert("RGBA") + + img.putalpha(255) + + black_overlay = Image.new("RGBA", img.size) + except Exception: + continue + for i in range(self.FADE_FRAMES + 1): + new_img = Image.blend(black_overlay, img, i / self.FADE_FRAMES) + self.send(new_img) + time.sleep(self.dwell) + for i in range(self.FADE_FRAMES, -1, -1): + new_img = Image.blend(black_overlay, img, i / self.FADE_FRAMES) + self.send(new_img) + time.sleep(self.BASE_DWELL_DARK) + + def send(self, img): # sends current bmp_img to the frontend buffered = BytesIO() img.save(buffered, format="BMP") From b7f341dd6bb09cbcb7af3f302ad46ec3f33d3e0b Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 25 Mar 2020 21:51:18 -0700 Subject: [PATCH 03/17] slideshow implementation --- src/clue/adafruit_slideshow.py | 168 +++++++++++++++++++++++++++------ src/process_user_code.py | 1 + 2 files changed, 138 insertions(+), 31 deletions(-) diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py index 53d30f6cc..51782ffc3 100644 --- a/src/clue/adafruit_slideshow.py +++ b/src/clue/adafruit_slideshow.py @@ -6,61 +6,155 @@ from io import BytesIO from base_circuitpython import base_cp_constants as CONSTANTS import time -import queue +import collections +from random import shuffle +# taken from adafruit +class PlayBackOrder: + """Defines possible slideshow playback orders.""" + + # pylint: disable=too-few-public-methods + ALPHABETICAL = 0 + """Orders by alphabetical sort of filenames""" + + RANDOM = 1 + """Randomly shuffles the images""" + # pylint: enable=too-few-public-methods + + +class PlayBackDirection: + """Defines possible slideshow playback directions.""" + + # pylint: disable=too-few-public-methods + BACKWARD = -1 + """The next image is before the current image. When alphabetically sorted, this is towards A.""" + + FORWARD = 1 + """The next image is after the current image. When alphabetically sorted, this is towards Z.""" + # pylint: enable=too-few-public-methods + + +# custom class SlideShow: - def __init__( self, display, backlight_pwm=None, *, folder="/", - order=0, + order=PlayBackOrder.ALPHABETICAL, loop=True, dwell=3, fade_effect=True, auto_advance=True, - direction=1, + direction=PlayBackDirection.FORWARD, ): - self.dirs = os.listdir(folder) - self.folder = folder + self.auto_advance = auto_advance + + self.__abs_path_to_code_file = "" + abs_path_parent_dir = os.path.abspath( + os.path.join(self.__abs_path_to_code_file, os.pardir) + ) + abs_path_folder = os.path.normpath(os.path.join(abs_path_parent_dir, folder)) + + self.folder = abs_path_folder + self.dirs = os.listdir(self.folder) + self.loop = loop self.BASE_DWELL = 0.3 self.BASE_DWELL_DARK = 0.7 - self.FADE_FRAMES = 10 - + if fade_effect: + self.fade_frames = 10 + else: + self.fade_frames = 0 + self.brightness = 1.0 self.dwell = self.BASE_DWELL + dwell + self.direction = direction + self._order = order + self._load_pic_queue() + self.update() + self.curr_img = "" - - # for d in self.dirs: + @property + def current_image_name(self): + """Returns the current image name.""" + return self._curr_img - # self.pic_queue = - # - def show(self): + @property + def order(self): + """Specifies the order in which the images are displayed. Options are random (``RANDOM``) or + alphabetical (``ALPHABETICAL``). Default is ``RANDOM``.""" + return self._order - img = None - for d in self.dirs: - try: - new_path = os.path.join(self.folder, d) - img = Image.open(new_path) - img.convert("RGBA") + @order.setter + def order(self, order): + if order not in [PlayBackOrder.ALPHABETICAL, PlayBackOrder.RANDOM]: + raise ValueError("Order must be either 'RANDOM' or 'ALPHABETICAL'") + + self._order = order + self._reorder_images() - img.putalpha(255) + def _reorder_images(self): + self._load_pic_queue() - black_overlay = Image.new("RGBA", img.size) + def _get_img(self): + if self.direction == PlayBackDirection.FORWARD: + return self.pic_queue.popleft() + else: + return self.pic_queue.pop() + + def _load_pic_queue(self): + dir_imgs = [] + for d in self.dirs: + try: + dir_imgs.append(os.path.join(self.folder, d)) except Exception: continue - for i in range(self.FADE_FRAMES + 1): - new_img = Image.blend(black_overlay, img, i / self.FADE_FRAMES) - self.send(new_img) - time.sleep(self.dwell) - for i in range(self.FADE_FRAMES, -1, -1): - new_img = Image.blend(black_overlay, img, i / self.FADE_FRAMES) - self.send(new_img) - time.sleep(self.BASE_DWELL_DARK) - - def send(self, img): + if self._order == PlayBackOrder.RANDOM: + shuffle(dir_imgs) + + self.pic_queue = collections.deque(dir_imgs) + + def update(self): + + successful = False + while self.auto_advance and not successful: + if len(self.pic_queue): + successful = self.advance(self._get_img()) + elif self.loop: + self._load_pic_queue() + else: + return False + + return True + + def advance(self, new_path): + try: + img = Image.open(new_path) + self._curr_img = new_path + img.convert("RGBA") + + img.putalpha(255) + + black_overlay = Image.new("RGBA", img.size) + except Exception: + return False + + time.sleep(self.BASE_DWELL_DARK) + for i in range(self.fade_frames + 1): + new_img = Image.blend( + black_overlay, img, i * self.brightness / self.fade_frames + ) + self._send(new_img) + time.sleep(self.dwell) + for i in range(self.fade_frames, -1, -1): + new_img = Image.blend( + black_overlay, img, i * self.brightness / self.fade_frames + ) + self._send(new_img) + return True + + def _send(self, img): # sends current bmp_img to the frontend buffered = BytesIO() img.save(buffered, format="BMP") @@ -69,3 +163,15 @@ def send(self, img): sendable_json = {CONSTANTS.BASE_64: img_str} common.utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) + + @property + def brightness(self): + return self._brightness + + @brightness.setter + def brightness(self, brightness): + if brightness < 0: + brightness = 0 + elif brightness > 1.0: + brightness = 1.0 + self._brightness = brightness diff --git a/src/process_user_code.py b/src/process_user_code.py index cede54e12..fb303a57f 100644 --- a/src/process_user_code.py +++ b/src/process_user_code.py @@ -100,6 +100,7 @@ def handle_user_prints(): # Execute User Code Thread def execute_user_code(abs_path_to_code_file): cpx._Express__abs_path_to_code_file = abs_path_to_code_file + clue._SlideShow__abs_path_to_code_file = abs_path_to_code_file # Execute the user's code.py file with open(abs_path_to_code_file, encoding="utf8") as user_code_file: user_code = user_code_file.read() From dff99710e23e62a7d3563239d0652e2897de6321 Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 26 Mar 2020 10:20:30 -0700 Subject: [PATCH 04/17] fixed relative path issue --- src/adafruit_circuitplayground/express.py | 3 +-- src/clue/adafruit_slideshow.py | 4 ++-- src/common/utils.py | 2 ++ src/process_user_code.py | 5 ++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/adafruit_circuitplayground/express.py b/src/adafruit_circuitplayground/express.py index d4af22980..cc6f16dae 100644 --- a/src/adafruit_circuitplayground/express.py +++ b/src/adafruit_circuitplayground/express.py @@ -48,7 +48,6 @@ def __init__(self): "shake": False, } self.__debug_mode = False - self.__abs_path_to_code_file = "" self.pixels = Pixel(self.__state, self.__debug_mode) @property @@ -169,7 +168,7 @@ def play_file(self, file_name): telemetry_py.send_telemetry(TelemetryEvent.CPX_API_PLAY_FILE) file_name = utils.remove_leading_slashes(file_name) abs_path_parent_dir = os.path.abspath( - os.path.join(self.__abs_path_to_code_file, os.pardir) + os.path.join(utils.abs_path_to_user_file, os.pardir) ) abs_path_wav_file = os.path.normpath( os.path.join(abs_path_parent_dir, file_name) diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py index 51782ffc3..e8658de32 100644 --- a/src/clue/adafruit_slideshow.py +++ b/src/clue/adafruit_slideshow.py @@ -8,6 +8,7 @@ import time import collections from random import shuffle +from common import utils # taken from adafruit class PlayBackOrder: @@ -52,9 +53,8 @@ def __init__( self.auto_advance = auto_advance - self.__abs_path_to_code_file = "" abs_path_parent_dir = os.path.abspath( - os.path.join(self.__abs_path_to_code_file, os.pardir) + os.path.join(utils.abs_path_to_user_file, os.pardir) ) abs_path_folder = os.path.normpath(os.path.join(abs_path_parent_dir, folder)) diff --git a/src/common/utils.py b/src/common/utils.py index dbd144835..f78dda44d 100644 --- a/src/common/utils.py +++ b/src/common/utils.py @@ -9,6 +9,8 @@ previous_state = {} +abs_path_to_user_file = "" + def update_state_with_device_name(state, device_name): updated_state = dict(state) diff --git a/src/process_user_code.py b/src/process_user_code.py index fb303a57f..d458b0de0 100644 --- a/src/process_user_code.py +++ b/src/process_user_code.py @@ -43,7 +43,7 @@ # This import must happen after the sys.path is modified from common.telemetry import telemetry_py - +from common import utils from adafruit_circuitplayground.express import cpx from adafruit_circuitplayground.constants import CPX @@ -99,8 +99,7 @@ def handle_user_prints(): # Execute User Code Thread def execute_user_code(abs_path_to_code_file): - cpx._Express__abs_path_to_code_file = abs_path_to_code_file - clue._SlideShow__abs_path_to_code_file = abs_path_to_code_file + utils.abs_path_to_user_file = abs_path_to_code_file # Execute the user's code.py file with open(abs_path_to_code_file, encoding="utf8") as user_code_file: user_code = user_code_file.read() From 6031346ea289cdf1a25b12639dea4c3914337aaa Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 26 Mar 2020 13:56:00 -0700 Subject: [PATCH 05/17] more slideshow bug fixes --- src/clue/adafruit_slideshow.py | 45 ++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py index e8658de32..b5d091881 100644 --- a/src/clue/adafruit_slideshow.py +++ b/src/clue/adafruit_slideshow.py @@ -51,6 +51,7 @@ def __init__( direction=PlayBackDirection.FORWARD, ): + self._curr_img_handle = Image.new("RGBA", (240, 240)) self.auto_advance = auto_advance abs_path_parent_dir = os.path.abspath( @@ -63,10 +64,13 @@ def __init__( self.loop = loop self.BASE_DWELL = 0.3 self.BASE_DWELL_DARK = 0.7 + self.TRANSITION_INCREMENTS = 18 + self.fade_frames = 8 if fade_effect: - self.fade_frames = 10 + self.advance = self.advance_with_fade else: - self.fade_frames = 0 + self.advance = self.advance_no_fade + self.brightness = 1.0 self.dwell = self.BASE_DWELL + dwell self.direction = direction @@ -107,8 +111,10 @@ def _load_pic_queue(self): dir_imgs = [] for d in self.dirs: try: - dir_imgs.append(os.path.join(self.folder, d)) - except Exception: + new_path = os.path.join(self.folder, d) + if os.path.splitext(new_path)[1] == ".bmp": + dir_imgs.append(new_path) + except Exception as e: continue if self._order == PlayBackOrder.RANDOM: shuffle(dir_imgs) @@ -128,16 +134,17 @@ def update(self): return True - def advance(self, new_path): + def advance_with_fade(self, new_path): try: img = Image.open(new_path) self._curr_img = new_path - img.convert("RGBA") + img = img.convert("RGBA") + img = img.crop((0, 0, 240, 240)) img.putalpha(255) black_overlay = Image.new("RGBA", img.size) - except Exception: + except Exception as e: return False time.sleep(self.BASE_DWELL_DARK) @@ -154,6 +161,30 @@ def advance(self, new_path): self._send(new_img) return True + def advance_no_fade(self, new_path): + old_img = self._curr_img_handle + + try: + new_img = Image.open(new_path) + new_img = new_img.crop((0, 0, 240, 240)) + self._curr_img = new_path + new_img = new_img.convert("RGBA") + + new_img.putalpha(255) + except Exception as e: + return False + + for i in range(self.TRANSITION_INCREMENTS + 1): + img_piece = new_img.crop((0, 0, 240, i * 240 / self.TRANSITION_INCREMENTS)) + + old_img.paste(img_piece) + self._send(old_img) + + self._curr_img_handle = new_img + + time.sleep(self.dwell) + return True + def _send(self, img): # sends current bmp_img to the frontend buffered = BytesIO() From 73a7d66da65d6661e7e4f34bb6cf207e733e7024 Mon Sep 17 00:00:00 2001 From: andreamah Date: Fri, 27 Mar 2020 15:08:58 -0700 Subject: [PATCH 06/17] cleanup --- src/clue/adafruit_slideshow.py | 215 +++++++++++++++++++++------------ 1 file changed, 139 insertions(+), 76 deletions(-) diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py index b5d091881..2195c674d 100644 --- a/src/clue/adafruit_slideshow.py +++ b/src/clue/adafruit_slideshow.py @@ -50,9 +50,43 @@ def __init__( auto_advance=True, direction=PlayBackDirection.FORWARD, ): + self._BASE_DWELL = 0.3 + self._BASE_DWELL_DARK = 0.7 + self._NO_FADE_TRANSITION_INCREMENTS = 18 - self._curr_img_handle = Image.new("RGBA", (240, 240)) self.auto_advance = auto_advance + """Enable auto-advance based on dwell time. Set to ``False`` to manually control.""" + + self.loop = loop + """Specifies whether to loop through the images continuously or play through the list once. + ``True`` will continue to loop, ``False`` will play only once.""" + + self.fade_frames = 8 + """Whether to include the fade effect between images. ``True`` tells the code to fade the + backlight up and down between image display transitions. ``False`` maintains max + brightness on the backlight between image transitions.""" + + self.dwell = self._BASE_DWELL + dwell + """The number of seconds each image displays, in seconds.""" + + self.direction = direction + """Specify the playback direction. Default is ``PlayBackDirection.FORWARD``. Can also be + ``PlayBackDirection.BACKWARD``.""" + + self.advance = self._advance_no_fade + """Displays the next image. Returns True when a new image was displayed, False otherwise. + """ + + if fade_effect: + self.advance = self._advance_with_fade + + self._img_start = None + + self.brightness = 1.0 + + self._curr_img_handle = Image.new( + "RGBA", (CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH) + ) abs_path_parent_dir = os.path.abspath( os.path.join(utils.abs_path_to_user_file, os.pardir) @@ -60,24 +94,15 @@ def __init__( abs_path_folder = os.path.normpath(os.path.join(abs_path_parent_dir, folder)) self.folder = abs_path_folder + self.dirs = os.listdir(self.folder) - self.loop = loop - self.BASE_DWELL = 0.3 - self.BASE_DWELL_DARK = 0.7 - self.TRANSITION_INCREMENTS = 18 - self.fade_frames = 8 - if fade_effect: - self.advance = self.advance_with_fade - else: - self.advance = self.advance_no_fade - self.brightness = 1.0 - self.dwell = self.BASE_DWELL + dwell - self.direction = direction self._order = order - self._load_pic_queue() - self.update() - self.curr_img = "" + self._curr_img = "" + self._reorder_images() + + # show the first working image + self.advance() @property def current_image_name(self): @@ -98,91 +123,139 @@ def order(self, order): self._order = order self._reorder_images() - def _reorder_images(self): - self._load_pic_queue() + @property + def brightness(self): + """Brightness of the backlight when an image is displaying. Clamps to 0 to 1.0""" + return self._brightness + + @brightness.setter + def brightness(self, brightness): + if brightness < 0: + brightness = 0 + elif brightness > 1.0: + brightness = 1.0 + self._brightness = brightness + + def update(self): + """Updates the slideshow to the next image.""" + now = time.monotonic() + if not self.auto_advance or now - self._img_start < self.dwell: + return True + + return self.advance() + + def _get_next_img(self): + + if not len(self.pic_queue): + if self.loop: + self._reorder_images() + else: + return "" - def _get_img(self): if self.direction == PlayBackDirection.FORWARD: return self.pic_queue.popleft() else: return self.pic_queue.pop() - def _load_pic_queue(self): + def _reorder_images(self): dir_imgs = [] for d in self.dirs: try: new_path = os.path.join(self.folder, d) if os.path.splitext(new_path)[1] == ".bmp": dir_imgs.append(new_path) - except Exception as e: + except Image.UnidentifiedImageError as e: continue if self._order == PlayBackOrder.RANDOM: shuffle(dir_imgs) self.pic_queue = collections.deque(dir_imgs) - def update(self): + def _advance_with_fade(self): - successful = False - while self.auto_advance and not successful: - if len(self.pic_queue): - successful = self.advance(self._get_img()) - elif self.loop: - self._load_pic_queue() - else: + old_img = self._curr_img_handle + + advance_sucessful = False + + while not advance_sucessful: + new_path = self._get_next_img() + if new_path == "": return False - return True + try: + new_img = Image.open(new_path) - def advance_with_fade(self, new_path): - try: - img = Image.open(new_path) - self._curr_img = new_path + new_img = new_img.convert("RGBA") + new_img.putalpha(255) - img = img.convert("RGBA") - img = img.crop((0, 0, 240, 240)) - img.putalpha(255) + new_img = new_img.crop( + (0, 0, CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH) + ) - black_overlay = Image.new("RGBA", img.size) - except Exception as e: - return False + black_overlay = Image.new("RGBA", new_img.size) + advance_sucessful = True + except Image.UnidentifiedImageError as e: + pass - time.sleep(self.BASE_DWELL_DARK) - for i in range(self.fade_frames + 1): - new_img = Image.blend( - black_overlay, img, i * self.brightness / self.fade_frames - ) - self._send(new_img) - time.sleep(self.dwell) for i in range(self.fade_frames, -1, -1): - new_img = Image.blend( - black_overlay, img, i * self.brightness / self.fade_frames + sendable_img = Image.blend( + black_overlay, old_img, i * self.brightness / self.fade_frames ) - self._send(new_img) + self._send(sendable_img) + + time.sleep(self._BASE_DWELL_DARK) + + for i in range(self.fade_frames + 1): + sendable_img = Image.blend( + black_overlay, new_img, i * self.brightness / self.fade_frames + ) + self._send(sendable_img) + + self._curr_img_handle = new_img + self._curr_img = new_path + self._img_start = time.monotonic() return True - def advance_no_fade(self, new_path): - old_img = self._curr_img_handle + def _advance_no_fade(self): - try: - new_img = Image.open(new_path) - new_img = new_img.crop((0, 0, 240, 240)) - self._curr_img = new_path - new_img = new_img.convert("RGBA") + old_img = self._curr_img_handle - new_img.putalpha(255) - except Exception as e: - return False + advance_sucessful = False - for i in range(self.TRANSITION_INCREMENTS + 1): - img_piece = new_img.crop((0, 0, 240, i * 240 / self.TRANSITION_INCREMENTS)) + while not advance_sucessful: + new_path = self._get_next_img() + if new_path == "": + return False + try: + new_img = Image.open(new_path) + new_img = new_img.crop( + (0, 0, CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH) + ) + + self._curr_img = new_path + + new_img = new_img.convert("RGBA") + new_img.putalpha(255) + advance_sucessful = True + except Image.UnidentifiedImageError as e: + pass + + if self.brightness < 1.0: + black_overlay = Image.new("RGBA", new_img.size) + new_img = Image.blend(black_overlay, new_img, self.brightness) + + for i in range(self._NO_FADE_TRANSITION_INCREMENTS + 1): + curr_y = ( + i * CONSTANTS.SCREEN_HEIGHT_WIDTH / self._NO_FADE_TRANSITION_INCREMENTS + ) + img_piece = new_img.crop((0, 0, CONSTANTS.SCREEN_HEIGHT_WIDTH, curr_y)) old_img.paste(img_piece) self._send(old_img) self._curr_img_handle = new_img - - time.sleep(self.dwell) + self._curr_img = new_path + self._img_start = time.monotonic() return True def _send(self, img): @@ -190,19 +263,9 @@ def _send(self, img): buffered = BytesIO() img.save(buffered, format="BMP") byte_base64 = base64.b64encode(buffered.getvalue()) + + # only send the base_64 string contents img_str = str(byte_base64)[2:-1] sendable_json = {CONSTANTS.BASE_64: img_str} common.utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) - - @property - def brightness(self): - return self._brightness - - @brightness.setter - def brightness(self, brightness): - if brightness < 0: - brightness = 0 - elif brightness > 1.0: - brightness = 1.0 - self._brightness = brightness From d8240daec2c47d94cda71ab368b56e3a427d3e3d Mon Sep 17 00:00:00 2001 From: andreamah Date: Fri, 27 Mar 2020 15:28:49 -0700 Subject: [PATCH 07/17] more cleanup --- src/clue/adafruit_slideshow.py | 49 ++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py index 2195c674d..263e39e09 100644 --- a/src/clue/adafruit_slideshow.py +++ b/src/clue/adafruit_slideshow.py @@ -73,21 +73,25 @@ def __init__( """Specify the playback direction. Default is ``PlayBackDirection.FORWARD``. Can also be ``PlayBackDirection.BACKWARD``.""" - self.advance = self._advance_no_fade + self.advance = self._advance_with_fade """Displays the next image. Returns True when a new image was displayed, False otherwise. """ - if fade_effect: - self.advance = self._advance_with_fade + # assign new advance method if fade is disabled + if not fade_effect: + self.advance = self._advance_no_fade self._img_start = None self.brightness = 1.0 + # blank screen for start self._curr_img_handle = Image.new( "RGBA", (CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH) ) + # if path is relative, this makes sure that + # it's relative to the users's code file abs_path_parent_dir = os.path.abspath( os.path.join(utils.abs_path_to_user_file, os.pardir) ) @@ -95,11 +99,14 @@ def __init__( self.folder = abs_path_folder + # get files within specified directory self.dirs = os.listdir(self.folder) self._order = order self._curr_img = "" - self._reorder_images() + + # load images into main queue + self._load_images() # show the first working image self.advance() @@ -121,7 +128,7 @@ def order(self, order): raise ValueError("Order must be either 'RANDOM' or 'ALPHABETICAL'") self._order = order - self._reorder_images() + self._load_images() @property def brightness(self): @@ -146,9 +153,10 @@ def update(self): def _get_next_img(self): + # handle empty queue if not len(self.pic_queue): if self.loop: - self._reorder_images() + self._load_images() else: return "" @@ -157,11 +165,13 @@ def _get_next_img(self): else: return self.pic_queue.pop() - def _reorder_images(self): + def _load_images(self): dir_imgs = [] for d in self.dirs: try: new_path = os.path.join(self.folder, d) + + # only add bmp imgs if os.path.splitext(new_path)[1] == ".bmp": dir_imgs.append(new_path) except Image.UnidentifiedImageError as e: @@ -169,12 +179,13 @@ def _reorder_images(self): if self._order == PlayBackOrder.RANDOM: shuffle(dir_imgs) + # convert list to queue + # (must be list beforehand for potential randomization) self.pic_queue = collections.deque(dir_imgs) def _advance_with_fade(self): old_img = self._curr_img_handle - advance_sucessful = False while not advance_sucessful: @@ -192,11 +203,21 @@ def _advance_with_fade(self): (0, 0, CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH) ) + if new_img.size[0] < 240 or new_img.size[1] < 240: + black_overlay = Image.new( + "RGBA", + CONSTANTS.SCREEN_HEIGHT_WIDTH, + CONSTANTS.SCREEN_HEIGHT_WIDTH, + ) + black_overlay.paste(new_img) + new_img = black_overlay + black_overlay = Image.new("RGBA", new_img.size) advance_sucessful = True except Image.UnidentifiedImageError as e: pass + # fade out old photo for i in range(self.fade_frames, -1, -1): sendable_img = Image.blend( black_overlay, old_img, i * self.brightness / self.fade_frames @@ -205,6 +226,7 @@ def _advance_with_fade(self): time.sleep(self._BASE_DWELL_DARK) + # fade in new photo for i in range(self.fade_frames + 1): sendable_img = Image.blend( black_overlay, new_img, i * self.brightness / self.fade_frames @@ -229,10 +251,20 @@ def _advance_no_fade(self): try: new_img = Image.open(new_path) + new_img = new_img.crop( (0, 0, CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH) ) + if new_img.size[0] < 240 or new_img.size[1] < 240: + black_overlay = Image.new( + "RGBA", + CONSTANTS.SCREEN_HEIGHT_WIDTH, + CONSTANTS.SCREEN_HEIGHT_WIDTH, + ) + black_overlay.paste(new_img) + new_img = black_overlay + self._curr_img = new_path new_img = new_img.convert("RGBA") @@ -245,6 +277,7 @@ def _advance_no_fade(self): black_overlay = Image.new("RGBA", new_img.size) new_img = Image.blend(black_overlay, new_img, self.brightness) + # gradually scroll new img over old img for i in range(self._NO_FADE_TRANSITION_INCREMENTS + 1): curr_y = ( i * CONSTANTS.SCREEN_HEIGHT_WIDTH / self._NO_FADE_TRANSITION_INCREMENTS From fc1de740a8bba0439b66b58d7e2d209be304f6a1 Mon Sep 17 00:00:00 2001 From: andreamah Date: Fri, 27 Mar 2020 18:45:41 -0700 Subject: [PATCH 08/17] added tests --- src/clue/adafruit_slideshow.py | 3 +- src/clue/test/slideshow_pics/pic_1.bmp | Bin 0 -> 113974 bytes src/clue/test/slideshow_pics/pic_2.bmp | Bin 0 -> 113974 bytes src/clue/test/slideshow_pics/pic_3.bmp | Bin 0 -> 330726 bytes src/clue/test/slideshow_pics/pic_4.bmp | Bin 0 -> 327414 bytes src/clue/test/slideshow_pics/pic_5.bmp | Bin 0 -> 225622 bytes src/clue/test/slideshow_pics/pic_6.bmp | Bin 0 -> 115818 bytes src/clue/test/slideshow_pics/pic_7.bmp | Bin 0 -> 93510 bytes src/clue/test/slideshow_pics/pic_8.bmp | Bin 0 -> 193734 bytes src/clue/test/test_adafruit_slideshow.py | 100 +++++++++++++++++++++++ src/clue/test/test_helpers.py | 21 ++++- 11 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 src/clue/test/slideshow_pics/pic_1.bmp create mode 100644 src/clue/test/slideshow_pics/pic_2.bmp create mode 100644 src/clue/test/slideshow_pics/pic_3.bmp create mode 100644 src/clue/test/slideshow_pics/pic_4.bmp create mode 100644 src/clue/test/slideshow_pics/pic_5.bmp create mode 100644 src/clue/test/slideshow_pics/pic_6.bmp create mode 100644 src/clue/test/slideshow_pics/pic_7.bmp create mode 100644 src/clue/test/slideshow_pics/pic_8.bmp create mode 100644 src/clue/test/test_adafruit_slideshow.py diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py index 263e39e09..a285b537f 100644 --- a/src/clue/adafruit_slideshow.py +++ b/src/clue/adafruit_slideshow.py @@ -1,4 +1,3 @@ -import common from PIL import Image import os @@ -301,4 +300,4 @@ def _send(self, img): img_str = str(byte_base64)[2:-1] sendable_json = {CONSTANTS.BASE_64: img_str} - common.utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) + utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) diff --git a/src/clue/test/slideshow_pics/pic_1.bmp b/src/clue/test/slideshow_pics/pic_1.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0d15550078f61eb5e68515958b9c3a79d376bbbb GIT binary patch literal 113974 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 G?EfD(a~m1} literal 0 HcmV?d00001 diff --git a/src/clue/test/slideshow_pics/pic_2.bmp b/src/clue/test/slideshow_pics/pic_2.bmp new file mode 100644 index 0000000000000000000000000000000000000000..345d5820e11be986d573aab47bdf709e48ca9e82 GIT binary patch literal 113974 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 literal 0 HcmV?d00001 diff --git a/src/clue/test/slideshow_pics/pic_3.bmp b/src/clue/test/slideshow_pics/pic_3.bmp new file mode 100644 index 0000000000000000000000000000000000000000..78ef7b8a23d5f92e06cffc87c0795af7f6ef884c GIT binary patch literal 330726 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 literal 0 HcmV?d00001 diff --git a/src/clue/test/slideshow_pics/pic_6.bmp b/src/clue/test/slideshow_pics/pic_6.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d3e2540397b43a06c2c748ffe972047b8141ae8a GIT binary patch literal 115818 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 C7L5@A literal 0 HcmV?d00001 diff --git a/src/clue/test/slideshow_pics/pic_8.bmp b/src/clue/test/slideshow_pics/pic_8.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2746a30f812280f3aff4b5ca6992f753ced09982 GIT binary patch literal 193734 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 C1~?P| literal 0 HcmV?d00001 diff --git a/src/clue/test/test_adafruit_slideshow.py b/src/clue/test/test_adafruit_slideshow.py new file mode 100644 index 000000000..bd4494f9a --- /dev/null +++ b/src/clue/test/test_adafruit_slideshow.py @@ -0,0 +1,100 @@ +from ..adafruit_slideshow import SlideShow, PlayBackDirection, PlayBackOrder +import board +import pathlib +import os + +from PIL import Image +from .test_helpers import helper +from base_circuitpython import base_cp_constants as CONSTANTS + +from unittest import mock + +from common import utils + + +class TestAdafruitSlideShow(object): + def setup_method(self): + self.abs_path = pathlib.Path(__file__).parent.absolute() + + # Create a new black (default) image + self.main_img = Image.new( + "RGBA", + (CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH), + (0, 0, 0, 0), + ) + + utils.send_to_simulator = mock.Mock() + + def test_slideshow(self): + + pic_dir = os.path.join(self.abs_path, "slideshow_pics") + slideshow_images = [] + for i in range(8): + img = Image.open(os.path.join(pic_dir, f"pic_{i+1}.bmp")) + img = img.convert("RGBA") + img.putalpha(255) + + img = img.crop( + (0, 0, CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH) + ) + + if img.size[0] < 240 or img.size[1] < 240: + black_overlay = Image.new( + "RGBA", + CONSTANTS.SCREEN_HEIGHT_WIDTH, + CONSTANTS.SCREEN_HEIGHT_WIDTH, + ) + black_overlay.paste(img) + img = black_overlay + + slideshow_images.append(img) + + # Create the slideshow object that plays through once alphabetically. + slideshow = SlideShow( + board.DISPLAY, + dwell=3, + folder=pic_dir, + loop=True, + fade_effect=True, + auto_advance=True, + order=PlayBackOrder.ALPHABETICAL, + direction=PlayBackDirection.FORWARD, + ) + + slideshow._send = self._send_helper + + # first image's appear time is unstable,since it fades/scrolls in + # can oly predict following ones... + + for i in range(1, 8): + slideshow.advance() + helper._Helper__test_image_equality( + self.main_img.load(), slideshow_images[i].load() + ) + + # Create the slideshow object that plays through once backwards. + slideshow2 = SlideShow( + board.DISPLAY, + dwell=3, + folder=pic_dir, + loop=True, + fade_effect=False, + auto_advance=True, + order=PlayBackOrder.ALPHABETICAL, + direction=PlayBackDirection.BACKWARD, + ) + + slideshow2._send = self._send_helper + + helper._Helper__test_image_equality( + self.main_img.load(), slideshow_images[7].load() + ) + + for i in range(6, -1, -1): + slideshow2.advance() + helper._Helper__test_image_equality( + self.main_img.load(), slideshow_images[i].load() + ) + + def _send_helper(self, image): + self.main_img = image diff --git a/src/clue/test/test_helpers.py b/src/clue/test/test_helpers.py index 5c4a99be6..429965aaf 100644 --- a/src/clue/test/test_helpers.py +++ b/src/clue/test/test_helpers.py @@ -7,7 +7,26 @@ def __test_image_equality(self, image_1, image_2): for j in range(CONSTANTS.SCREEN_HEIGHT_WIDTH): pixel_1 = image_1[j, i] pixel_2 = image_2[j, i] - assert pixel_1 == pixel_2 + + if not isinstance(pixel_1, tuple): + pixel_1 = self.hex2rgba(pixel_1) + + if not isinstance(pixel_2, tuple): + pixel_2 = self.hex2rgba(pixel_2) + assert pixel_1[0:3] == pixel_2[0:3] + + def hex2rgba(self, curr_colour): + + ret_list = [] + + for i in range(3, -1, -1): + val = (curr_colour >> (2 ** (i + 1))) & 255 + if val == 0: + ret_list.append(0) + else: + ret_list.append(val) + + return tuple(ret_list) helper = Helper() From 87e8c8744e4b27eb287925ed8d33565c68d184b9 Mon Sep 17 00:00:00 2001 From: andreamah Date: Sat, 28 Mar 2020 23:19:55 -0700 Subject: [PATCH 09/17] terminal sequences --- ...n-display-text-DSX_CUSTOM_MAR172020.tar.gz | Bin 126240 -> 126256 bytes src/base_circuitpython/board.py | 2 +- src/clue/adafruit_clue.py | 5 +---- src/clue/terminal_handler_test.py | 0 4 files changed, 2 insertions(+), 5 deletions(-) create mode 100644 src/clue/terminal_handler_test.py diff --git a/adafruit-circuitpython-display-text-DSX_CUSTOM_MAR172020.tar.gz b/adafruit-circuitpython-display-text-DSX_CUSTOM_MAR172020.tar.gz index 9907d8851f72d364eb06e839116f0cc196acea28..896dd6c31557780c1e6269abead235d76164a0a0 100644 GIT binary patch delta 15735 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 delta 15719 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 diff --git a/src/base_circuitpython/board.py b/src/base_circuitpython/board.py index 611f20805..c6fdb697a 100644 --- a/src/base_circuitpython/board.py +++ b/src/base_circuitpython/board.py @@ -7,7 +7,7 @@ def __init__(self): pass def show(self, group): - group.draw() + group.draw(show=True) DISPLAY = Display() diff --git a/src/clue/adafruit_clue.py b/src/clue/adafruit_clue.py index bb4ef83b7..910868edd 100644 --- a/src/clue/adafruit_clue.py +++ b/src/clue/adafruit_clue.py @@ -126,7 +126,6 @@ def __init__( max_glyphs=60, color=title_color, scale=title_scale, - auto_write=False, ) title.x = 0 title.y = 8 @@ -151,9 +150,7 @@ def __getitem__(self, item): def add_text_line(self, color=0xFFFFFF): """Adds a line on the display of the specified color and returns the label object.""" - text_label = self._label.Label( - self._font, text="", max_glyphs=45, color=color, auto_write=False - ) + text_label = self._label.Label(self._font, text="", max_glyphs=45, color=color) text_label.x = 0 text_label.y = self._y self._y = text_label.y + 13 diff --git a/src/clue/terminal_handler_test.py b/src/clue/terminal_handler_test.py new file mode 100644 index 000000000..e69de29bb From c546632d66cc84d49e8ecfc39be8be40d41e775c Mon Sep 17 00:00:00 2001 From: andreamah Date: Mon, 30 Mar 2020 16:32:10 -0700 Subject: [PATCH 10/17] nonworking prints, refactored context switching --- ...n-display-text-DSX_CUSTOM_MAR172020.tar.gz | Bin 126256 -> 126248 bytes src/base_circuitpython/board.py | 20 ++- src/base_circuitpython/displayio/__init__.py | 2 +- src/base_circuitpython/displayio/group.py | 100 +++++++++------ src/base_circuitpython/displayio/tile_grid.py | 15 +-- src/base_circuitpython/terminal_handler.py | 119 +++++++++++++++++ src/clue/adafruit_clue.py | 6 +- src/clue/adafruit_slideshow.py | 1 + src/clue/terminal_handler_test.py | 120 ++++++++++++++++++ src/process_user_code.py | 15 +++ 10 files changed, 343 insertions(+), 55 deletions(-) create mode 100644 src/base_circuitpython/terminal_handler.py diff --git a/adafruit-circuitpython-display-text-DSX_CUSTOM_MAR172020.tar.gz b/adafruit-circuitpython-display-text-DSX_CUSTOM_MAR172020.tar.gz index 896dd6c31557780c1e6269abead235d76164a0a0..938961b05dd5194f650b7ebd2f0e46361306a34a 100644 GIT binary patch delta 13447 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 delta 13455 zcmai)Q*h@E%-?^PF>WuHKpVI>}5X$z;CC ze9G2f{;t8m5Jkem4)Xf>!!*Hs!)^nj>@9%yWZ*%7EAWYEJSt}aWPXU^bzOQNc{7|` z)s8?!skfI_5ed~NRyuZ$Qm3?mEn{wHOP7?THld-xt}Z28N`je^glt&Xf#~aa&{19v z2WJw4i8yqJj45Z)pj{^{cge-rubz=o%x=V>33t~#AEE_cJ!~GUMd779<_~-`I!K4r zlXmhajDd;NxjRU7k!t1?*-KjWIVPG3T@RGsU>rs5au`-_ehF&3%d4yniR$!w{k|kB z!pk(s#Y1(=w?I=aA~cjPU!!wjZEPC|Cl@!cRx_&~c}D5Y5_Tfs>yDAr_a~leH8*b! z(XX^dm3$8twr-*Pgf+rOKm_mx$4Jm+D6XR8ZZhz(DX9R*LPwSkZH9P4(c99fj(Jtb z*TO}caC31O>?KI7mM}8HB+yD$5)!0^&&!ZNp`X9e((-XkmEEdk_&1UnV%o@2YbHwq z(WQ_j9(bI48)BWMRHa;Um5w)Rve1tMRot8~$p`n~LejJyTo1VtS6%H{uEQp?+ zE((kml7e0B(e0|t{@qR3kv%@5jM;JdFz}`6aTg3&VYrKAfyBWZi*_)!^Y&%tcR=r8 zjczNvcWOZRjm!);$^wKLar_WzJKdNxf)9%~JGctKGTt41SwuJ7Hytf38|FcY`0M3_ zHA#xSRp4OaYV|90F!1-RIE*qZ-pcn%oY&JJ=HGnL5#~>AUzg+O#2^7mQs-oBG-a4_i&BlEa>fP@|C$E0Kk5bmxQa^Ikx=WcJU#`~n?Xo} zJWEyj1@bdf<+3KpT+$>pDhYkXd;^o{EoXR;bjD>$qSW)ex|TnBF1yfX@NK-%?-HTb z+S0NqBj0`e5?xY<({Avt{Q^~tI-WmeH!wvCuq2hgP!0k7wUF2&EqbjwJ_n=6K{%^# zA7@#d-|{{z-WeBf@s6CA`ftpWQ|XE;BAf$$q}>=NV>DthK_}!t(aU=oq*f4Aoev)< znv^oQh8kNW(BmKwF|0Cvu}>z1iT@%X!WOX|l#C04SxM=BRZ9f78DH=&%PSxq3(91$ zUu(G$=GOv_1abCZku~C=0GgMXH0Ehw3De5}$^xrOt6mFA)#c;HLxQL^fl>~vK0n7Y z4c3ZhXTcCDQ|?Q4g5IEHz2xXl-VB>T%~#p5f3|EfD=%_TB=P}eIt;qRgytI2ZF@;1fiD+aSU%?nR(|1YI+r?wO;O+A1{y|{57`lEkP6A6o!8+%t20?Py< z21nVQ&y71}6iwt^NdZO_KD zssBf~)JuLBk+iJU5V&~?luJNE`r;03+!cQt_>&;on*s5Fi?}(9Bp7)(1^nKAn&WUM z=RS~W&7QC3b*yo&AkS@@sDeyo=XQmp;lNm@@Ip5$n6Ifs%bp_XH_k{f-b+9=QH}K`1cC2E(j3Y^ zmr=PuC!p?dijB)nAl4P}qcXqo`#f=P{vr^f8B8?O&!HbZ9p1QV5Z7jyj~uF^B~xVL zf_`Fry6;EG(CPbyZp%#-Ht@o%+JNjvHJ+w%LI2|CAFMsd=>N^fX?#92uE2PmhBxwp zDsnLU=SBjCFXnPqoV>n(l3_3|0>@MR(2<)OCiKDI(UVWi*N2rqYQb<}Jg*26-z0$} zGkSdsj&ZXIBkCAIw#XbzN{R083oMenVqS)v=V^8F(Bs(mM#s(gl$rx33U-(`b2ZR- zb!gsAfwgiN;Mg<@e`qjPNWOy`(qWqLK5wKg(2cy6yze;-U6PH9^Xt(YY&L$Yy}o+n z69HdXm+Cr25{w^s_})@GM`q>89a03uu)p0X0yImRH>1nr22`b5ti?QySh?3I<&icVW@e zGWm7>PBAW#vbQMkJi|TAy-zH#`Rgltei!-d#*5yLGpL~mgZ4Nhb{q8Z%-aAfWa6>{ z$$FL9it$S_Qv!JQ=qAzJf%^w0h0qYfpfD!A+cNF&y164CSz^2OxrJWa`WP{eGmqHu?c zXV9XlII@^hw6Kg3M=#&qbRC+f+!sG_2Th zz4O(nLH7+;i-|mUPCW4{5{ak zM%vcbRLIC0yIxV(Wq_%orgK<<6c6`AxQ=4=vAB~=Bjz^X-)kf(nYPawtDs^sq~`pG z6iyv}u-#aZ3i3gj&TZJ6*lM$$?BgIhC;VfpU@=-qee?)>dh?h*-k+q>M8ztT<0PY~ zlSK=s8GiC1VgN&N?xtvIV7k@hQ3&0l;kPdg^ZAVbr_H&#%rl|0x_FNO78?fiS%{NI zxE1MEQEaFORvO>7V5KnYbt7VC{alU08~XAfi>U?IF~Q-oXo#8YZtYzvQp6LbS+o ztu}M%nc?@IU&5rPyEmV;Fd+-aPe69Gd2VxEFHpWjb)^T>GhTFi+oETeIyeQt`Z#BY z>!hl6Qxe1@PgDLwlV;0t$l*3Xj}%-@+l7!$t%`<%Cdq0^{-3P)#%G|(06L1(AmWx~ zds6mHjrPkXu}l?%Axd5&EU|7qo*pqb%){txzmb|09Cw3Y^VlbIM^OTufN$u2M=NL$4*Pk{a|L>P3a7zExo{6qC35%CgFuDoL}AI*m}uDPTsc z_DYwVfy|6KDOh0^AK+G2?JpBR5k}+6d&nn)1dX^9gD4y%^FnQJF?>_L?Nwi6|Htzd)Q=DXGl`g{;0E7=C46 zQ?5j$b&>?<(*2W3RF`9Cc1eDD3Ril}y-OJF4i`>2KwbrhzqFcZz87^S}uw@scocqsD|Q zL|5Ly`L9xj5S6yfS$lIkGUk^vm#qDGlB%A^Tg?8R7?QgIo=h>My8tt=TQXtx}6FtcJ?;&1Lb2|g+CncEznS5343kp>Kx>>kj9tu{AMYf{ z1dvhDC1Ogc=B#o`1_3wl15vN1tF}3jI2Kizj&fM&OfXpuR%A!XAab|9N?1}nsuL9z zpRn|=9A~k$#ecnb3nn8T0rud;6aIhE(69Nbf=XCVQt5q@6rr>0qY>Ze@vS(;ReOz5 zU5~^z`?hJ;=I`t#;4)K#vY^(ggoP{{s5$j7nIw^kN0}{pxdCP}I-W(91n?mg{yZfZ zWjO>0O92G3n_$xOMNLgE?4$Fdm~eO{N45t&a{h%JK7&Yg>nLP)9Io8-tz+43~0HdA+M>kqfGh~)&C}^)1ezq<}-~^+5M-R2p zV(i@TNG!}uE80U1<6k}&lAx_Ojoc6Jqu5drCl(V_&3qfCTKqF^b?evArKQ`AbtheS z38JcQdxP9w0GUl#)X-?q>*``Vs_OlBO6JbxOP}=k2jH8=m79g)Q}S~6?p%C+kVB~C z;$E00RH^p+uflXgvSea~DWAZBfk1Z$K?0<;|J)vScJfaCx?$HEGyQX`ob*RAv_+FI zSBAc&{e9oUm>Yz9dv#P=olHGv(eBOZGGy;zkB||-&G)H}8;t&i#JiwX;O_dJg5HXj z8J$(Z5YSSUE&KcJFM(q4ueUrU4;8p2Ck;a0Aavtd3^mjHq$y>B&zh);N`#Jz;wtCl z#>{}Eguw2J2E6jI>JW_>TaJ`$8kfi&i;%v5NGD#VPE3Chjozxam7BIuF2KIA?M84K zX41m%RHzM9hq`4XYVf@8=13pypzbKSz$lC5gaPw5?wM71J@I51i(pxd8%uK=tNYjT zkntmoMiNu1WViJ1vRaq!Pnnp3R#fCw%^3BpNgju+rmF6WN-;7;y{lReV@G32<$`!} zI17ZZnV)u-HJ5*eyTfd z&;X8M&pHO-#p7c3iy;RnRzVhv7E!LcLaK>iraj}U=~2Sp;k{hFbPBIfi4Dij_jWR!x(x2n>~!-0Qp z4*R*)v9we$CKw(&9!615kAl8HGqGUmjRLfEb`aM`<_)c#U8%4U1XI|1d8wE_C|L@h zI+=JRi&lj_sR)hPcCj6k!{~PxeDNA zLdAJF|Ct$6%lJ&yYmK=Ew&bPSh7MLk#hoKXEcRZ+BtjSe+mozz+g^K%hDcH7Mh3Kt zd}{Hn6I4!l;&~Foy<8^@61-u^qa2>N+Xmrbkz31b%%LJx6epIZG8_b!@g!ieW3LC1 zkyJD!DM>DEuL2CpG)YqO6q*VToH`Hxm7f0!KsWto)A&H_HA3P2gZ!7!8x!eoUmKjn zCunE_Ex~y4{3sbKY!i>?%y|O&7FA#*S4~Y#DWjG}Ga*h%&dE{$9D5WI@yE^3l^l8b zaMqz(wI?^RBJas6Pf4`lc!!b=}X<{*pKt>?BD2mu~ z2yG@P<3ji$dBsl%0s#ims-L{kqYD=c48hJ6q{AG^YYI)=8%wn-lsUVozqKg(Ss@#p z@^-BUd1m$*y60>Ab7SCi1Q?Af5uX_XIBDfqF74Sb&?m!kYY>&$+e>GfOj@@xhPc4y5Bp14{nZmIc;3-K9QqOly9Zxhd~v=y0GE`?v~cEd z1^qPilXI>VaTe8N7$`62cFVhGwb0zhwvHDtoaa9g@dLxD&$*{P0LMDj(rnj zqShTgF20904&jmDwNF3HR6Lt&cixjuMQ)R^S)Q&^IyX|z*m}jF)%_&=sP7U9eXxbm z!|#C9=Q=+4tr0)DgAQ}QtWsa8VDKs39{hJQZ*N~Zq)co;9ymlzkTZ1Q{?MX~qthz; zU7QH@Gj;M&?|M;@5nBkfprKu67(@G{vRx`ayTxHP__kOT4l5DvoWp`t9P*CZ*~Ol7 za-W9AfQgum5|&`io#ux0x3-ooPhgXgs((>c2Ve=Zj6D2-u7rxe#%9s+M@)P zB*@+~eAzs{jUI&8T90bHSRo+vb<{$->ZjblkIzy)1G2C%&g&Rzlhh58da7)x6vT@a zvM{%`Q0gDrR=kULQV5j?42h0i)@R_NynN&OI_okto27U2M<~g%`m4;9(WXv$7EP1> zX5pA@uz^Jy5Cy$uvdCL?WV3@^N-jJP*2N}cvKofea3Ra!bS|XTum`n6If^%`yi2Mz zT<0{70NcjEj8}Vc=4+Cz5L}}c86`Pd#fV_EM*dibfnv&j^8<}aSNBYa3LzT~{W|T> zKlHs0>BOo+1a2#gqzcbn9evZ3TnXEssBZ*Q_(n3iPX+a>WKc1!rJC(7l`&3AdQZh* zj&n&yaY79LOjK!pabkJc!taTBU0nppkOlC|0*MQqctr5<5ejf*?xtmV-9v}2)h8w6 zqMnkn4Ct5wB}0sApXi^u9dixWt&A+P)`p4%t*jZ}vvCSTk`SRE*&00RH?djd6e_dB zXzjmGz{0Q~MD{?c5gQOUV`X~QP%fUao zR?ktpyIDew1we7qlDxBAkwz{k*SBjoe*0Vo545U)^UgD%2kji{J`|Hzq}CZYeC1sw z&}^;fo?$wC(;BC=TDD1!ro(A;bxMcW&4ZwjJ zhvZ+`8hMG@g$U0)RRny{Vh&SK1Y&S8E9;a9MwVO4&C@6dEyGIt|9r*K;DtTOqsyvg zq$xC)2y#-cM#%TB@tY#V2;%p!$-5~7es176fcAa)|*S9b@CD z@smJ_iFA;u>Mb4am%j#znt_?FO)C1R@wxiM!c2*Dkd8v972| ze@SiXDTzG|DRc1+Hv=PRG^)l-%(9!6wUvt%Hf#?pk`~-v@i+lP*`gt&0$A{qA^D>e zJY8RpY!Oj1;j+}uYrBH{Icy~rqNwCwuOtWdj5sSfbxb-XpOGd?Q$g)C*M8p2is4_G z(X04ZL_;8zf>7y6)QN2}B+4e_RU}z{>JH>$aV4{6h;YJ)8ByK67b54qoOp2OWYFyeQB zf~7lkr98gbQM#Rsme~QOQF@_6Qc<(0kvOA<5h~39GD|{GsA$me0I;?r6-hB})@3^% zLR@DuL=-!sEXAtwbIKg~-ds1l=~u_egbh5{Y}VHxjg_9Mj1_6l9r26EGu+$T-hoiq z1sDm$k0R|0==$wt`esI|40|xG7H=#>Ms2%tOBP)?$*#)QNW&ysZX;SHct{^<#AL!V zFqUZgKlf?GC+_{DgTTr@Y45DFb0kkuDR+3vjl6bAvV{T)E+yiQWh5qIlMozgr-(5u zqKN-TvWnom&5#(aIW zXz$cADCC#*6@W{K%XQH zUN&(5m5W0_!0Yz)y#0{Z1Iqo+EI;BHD zK>C-*3~?d}FzwK3J~K|zCm8sVigXUv6swf1$Yt2zxbNg)tk(vyg3Mafn~vngA`xkO zcbKsxt2nK#>Y6!Iw_ej^>AQs``!i48vng|s^;<)3y~BERKuMZo>jJh!JS9qDHZFKEsDc#D9;qkp3&6u8+{ zf1f5I^jRYMJn6^Nd&r?2Bz!9l9Zv1xbqfTveReu&^9LSj>V=^8`es}J>LT3-*SZ#TXFkrk=gPMhCK*QpCkMDr*q$=a?EUsejx|Ph57ISgi!|{h6y1?=E&sUh%fu z9gqF|8{oD}Px$yd_+3%*{+VOlC$NS0#Ix3GPqMn*T}pt!t}?(rg^a z?}?RO@_pP=Zm{>cVC#N8RVVVi9jQOg2C3z(6AAV|mfHL7AjJ}ueMFBJ*8=gKFQzTP z6s5lRc_iO^PKnDL+WcY9uTKBF?LGCbvGvnuHBgruIVgs5<0ewS=PD+X|5pT5pqKp< zdooAe{mwf2HIdh+Tc>vMHn(f)^<`JT3m;P}t((IWbfLZ6)uirPkJrWgTo=sNug>=! zQPLHOuE){G5C$&^&~2{x9B|XR`q}+$wKmc!@V@5n`8T!e_!&f{`Ysq0mYqjq-a z+V2v`<94p7>jTe)MD9AkamP&OColi=-M{&FqLyoFqK^M&m%&r+XepKN$cq2vtGF0O zH*34e^WR+N#!+C!2Wdj^w%#E;H&K(GIcp8)!~6u7Tc`8GS)Ci7%}k5$2&>NhJ(0rU zbq~mnzx|QT_}al9U<7>hC%CqH}|EQhFd!~2@ru!0h&F2Bv z=UP(dk;2~Ju0wogIrnQbwdZcZ&Mk2=Q!m}?-^jLF=kpM}I;C&wwccmMaTl%A<5mmf z;}Jhz%5{qd%XQ|eY8N__m^&;_^f3*u7m+NFC?4=}dmEmie%!@9! zFCF{iCH>D;mD3qRAGhjkbsw!69ACSw#GbQw-Dlsg$J*KFC2hUKqg=ZUqo?!pf2uxS zb`K#rEbsW;S2dMDm&HN%?ao&R2$knc_a4(}+s9(>g|YDFhn~+i_kUGta2mnj2l(9r zwSl-{#u+=jzhA!B7cboK9N90BO>Hr`cloQyp3}2V(X`pmY!%nR<@5UJG4GH1H{|ap zQ(NuU$I0DK!{(|CF_&GZ+}Dhki2F1vU1i6Ud_@a{Uau3nUj!T-<1YGj^DQ^kURNCp z$J?JJ=3Vcx{O=d)Tdre6xp}Ppz-Qin86J$QuRf0E0Gm#2x~V<$PS-9IZMBWY2>J}M zlX^-WuZXd)CV@I`$4lEmJYHU3O*1jN+%=1m`qj^uBX-`Qv4mZ7w)|P$Yg1dz$MEXi z$lEnrPER?TN?2s0Dwn^XKXUmuUqA;H6U{Sz_M0GXPlJOj&~p`!wiO8ImHJpS*p8ld z{pdN}ji2<71G$+vpZq^FEGc7PewiBkhUwbBb){S7K>tS%S!>yaO0SN?66bD@H7{#L z^qF?e;>^O+Fu4af8{wm)z}2}_Ir$^3qmp4ps;u0RBQH{GM5L0>LDJvt#02Gm0}{HE zo5{YYGb{LV)*%7%`zL8lXwSO5ODukS&#x;}%>{%rnS`%a=25_TmUX(Jm#Qt7~jS|l8nl6NAN z6$AO2yu-Z&j%7+%f)0AS)gF1wI*S;l%H9+eOGss?SDa!WqjYc$)iODQ?il@t@vQ)( zomAl9G6bv(ux(3T^^+Vsl8zTdzn^gW?$EJg)KEQL!6vi9n~3K$J>TH-&hPDklMY(1 z2eAagEU$tF29Ec7vXae}-*eYfs=xjO!q|SL=r&9rKngr))bB>aQS9c0ZW&bOG#98( zbY;X`SyAPI$V95^PgN6oghNqx+viG!Lx)Ee{F(g##)9#p$E5G2$W3>5u0(MM_?%w3 zbq^1YAJ zlm>uTE0TN%-I#czl{M9F^PvZo?n#q~fl)c#;gvMwvE6TE$d*-^b0SV~!J%jlJ>LEf zV;fotIow=dxiqZBA;dICxSHQlQ7TteRcO`NtcU6?t732}sng;avV#L0Y1KXVd1RKe zF}vebnhsi2?6ZuEP?G95qaMFM4DUM-y)gsPqv!-+JNk7?oVkR$#l*r{kFD&}DwDso z%H|N5-lK%~$X8YTV&_F^Hj9`JnID8)nvDL0QP=Nkx9A`Tfkd1@Q<6ptIxEy{lYf2>#iP4@97Sh z(DVKmoZ9tTzoJAKQ!JwC`x&u9WcP%yKY_J%z|FC%XE_)b-P z+AlQnyQp~2aOIw+uyDULT&henx)Bxlm?0?SryUQs)_$~qkGhv#?~s#$jsdeQjlSR#-l3^BIkf1pg0Lz(6K+bJNtNskLTbp)LL#!ka@fnI-}iwX zvZK=|kR{@0{w?56#o})F@!zeMp7r-txp^Tw&*k^|nnj7T-Sq5I!{?Vg=%?7Z8@Vu= zO&6%}_($j)=w!C-zQ_FdR_1!D`jTF2Mw?|yb+KHjLsIbVzgMk^R4j6hUB1$7nGNo) zG%b2=w9MI;kbeR~uzLmEybTa!SvXpm{!;hgFtvPCfPG3H)ozytp#fZjnG>8W(P6^R zU>1I4^yR{9eLS#PJYX3dpcy$jlf7^w@()`qt+Rcvr$$mO&we>f^TAhvhCNL+O{Nr0 zG0MrVgGuZqZ38bi%rl2H$n0tF{>z!0`&*{MXQOQHvOp5PJ+9MBcM8DG9^X7KfDf66 z;RrnA2YkJ2m>f*JeZQH8E|_N=`3yID%#-vf21qgQNRV-)(b)XLxXH^9TQwG~h8;WF zRw{tF`QfN!W5JZ2XA4W_P2)CjprGr2;Y32;Oo64rsO$FNw^1ArS@HuzKS%@LZtXBl z?gv!;zn5JJ8z{H*nh0PLde% zv4>8TJKB^3Gl}1sza4{OdBkD-eXkpXVElx>Npz~RU-LJqHcsIDcpP+y=e-8Iy_pGBCy0fG$9wvV8}A2HMAkGTuCnV`L1d`0yJ30O zjZpoFlmjUZ88@J*=&KXu-G!*;xO)B{JRHDuyt!q&N|NVOXH3`EGGDL`Kc_tSs5i0r zih9?~=fTppi%uO^3GibX-Kv@NQXA+oXYSFMI-`}EWP)KMURN4*&%-Wu5KBJ{<4FG_ znQ7>35%amfs~^=p->YBO^nQO~(C1 z3_m?7bas#ICZLGJLI62>&rP(Z1xR$NtjQN*xiv#+%m`k z9{}ZQOwLRz$Wj>SPVKY#M@%z%n$FBH9-t1tc>DUhJ6ia<;<`Y}mygm3<_EC+h93A} ze=rlo^yQE6qzxN3vGa!S#Z=H(R66`(?4h^@o=(j^u$cRrvEHjAZvhK37F{qQs@jI3%~;dAj>b zs0x##AT5L=^`1PT_(+RYg{jqTBQ?xW1w#zs(C*!C?vOjYSVQgn)~rg8hKXTgV_fU)#N_BT7OT8f!~ElcNyqUGQsAj=tte4igVlHda$66L zUe->5io+H*M)4@h`Nv&aSe~m83c=uCL-ejZ=glh-e}S=%20E0-q^k6KG4$$utCn|o z1O@T(Nx2w-)C7@z5SW!ZcZg;HV33W9b)IBe#NRFcF0#|CixZbYN3Xh1;tkvDzHZC9 zA;=K%#vB{lj?96f!aQi=LjIT9tmp6`WVa(XVpK*v zHQAX-V9^bKf4}~%;ra`mU0psx%{Z=0uSRBpR3@^t0-CmUx4lQ^go99 z^4XS>L4iFyfEO zDS1FS*O$ORk^8~TY9Uz!K%8-7=N9EthTE@6G|F!zCg4k&^%jGq(4KQIb#<-L1kru4 zK7QPj;Pnlrsk}8V+LB|_p|AO&Q?IAVIG?klvA{Kgww$iB$0lpaZD}x*vP&<#x3Xuv zjyEnmMn5#ph^%{=gf<%6|7jw0{s4vcY%^|yd>39oyplKj+kAAuE%n;%y*X%)%&xz1 zKzO-liT1<#NRMNCqz3fmt9}FO(sS!sxs3l@@dn+8gY*1h8G`8iRnj=#D-KtVcUEP4ini>@E=_e%U+b4%V8lxN4lv3O+YdyzOoDIEScL9B`)WbEq~+M6Yi z8J*iFEk3iCwjsp=!bOXM@ZTwNNi>>5m8e!PNoxtZWP`(^Hz54iDRj0g=h&VT z?m8BI=wK{x`A-5A>)SU5950T#7ZLbyEN~(V~DtfFCJRN4AXc@3yjD1YlTL<>zls9uc!G5p ztOpJ0E5kD2D6Bk$|E{em27y_XSmgRbnc$2v=J7CveU2@Fuh%E}^$mg>e~2+8x?i7Z z7M`(yARzaL-b2H!h#0aoo4Hz*QAoaP7cxmjeRt@kP6iZp{#A}(-`7QD^LW@B=C7d6 z)0Z@16xsU z7LsanFV6ruSPtoGwH72IxWHIVdh*Ar@)Ajp47KmM?!DWSE5mJy)MDuXV(dkCyI&-C?eBRRBhP?}={Oh7?c4Zqwjj@VXRIO5!1Po`_(tt=$_e8m zqz0bHb^VvbpGAXK$KC4AG}U1Gs#-~&H10E#aC{wVTkoa&;zQ~?p1!v~U?xX5tlYfn zfxFCc*nW73&;&k^Ooh;hJDqo?WXMrLjnaRL?$XEK%}OC?CZrc5-0kQhlJ^uJmOD_l atU>Le?r0gaK_IpdNG7;L8n74$u>S*9Hl|(x diff --git a/src/base_circuitpython/board.py b/src/base_circuitpython/board.py index c6fdb697a..2cb506bc2 100644 --- a/src/base_circuitpython/board.py +++ b/src/base_circuitpython/board.py @@ -2,12 +2,26 @@ # https://learn.adafruit.com/arduino-to-circuitpython/the-board-module +import terminal_handler + + class Display: def __init__(self): - pass + self.active_group = None + self.terminal = terminal_handler.Terminal() + + def show(self, group=None): + + self.active_group = group - def show(self, group): - group.draw(show=True) + # show can take a string if context is + # not in the traditional Group + TileGrid style + if not isinstance(group, str): + if group == None: + print("Here") + self.terminal.configure(no_verif=True) + else: + group.draw(show=True) DISPLAY = Display() diff --git a/src/base_circuitpython/displayio/__init__.py b/src/base_circuitpython/displayio/__init__.py index 9f8437b9e..8cfadeee0 100644 --- a/src/base_circuitpython/displayio/__init__.py +++ b/src/base_circuitpython/displayio/__init__.py @@ -9,4 +9,4 @@ from .palette import Palette # references to img and bmp_img are for testing purposes -from .tile_grid import TileGrid, img, bmp_img +from .tile_grid import TileGrid diff --git a/src/base_circuitpython/displayio/group.py b/src/base_circuitpython/displayio/group.py index 2d3f3b8af..1469d4bef 100644 --- a/src/base_circuitpython/displayio/group.py +++ b/src/base_circuitpython/displayio/group.py @@ -3,10 +3,11 @@ from PIL import Image import adafruit_display_text -from .tile_grid import TileGrid, bmp_img, img +from .tile_grid import TileGrid from . import constants as CONSTANTS import common +import board # Group implementation loosely based on the # displayio.Group class in Adafruit CircuitPython @@ -16,7 +17,10 @@ class Group: - def __init__(self, max_size, scale=1, auto_write=True): + def __init__(self, max_size, scale=1, auto_write=True, has_active_group_ref=True): + + self.has_active_group_ref = has_active_group_ref + self.__contents = [] self.max_size = max_size self.scale = scale @@ -42,44 +46,66 @@ def __getitem__(self, index): def __setitem__(self, index, val): self.__contents[index] = val - def draw(self, x=0, y=0, scale=None, show=False): + def draw(self, img=None, x=0, y=0, scale=None, show=True, check_active_ref=True): + if img == None: + img = Image.new( + "RGBA", + (CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH), + (0, 0, 0, 0), + ) # this function is not a part of the orignal implementation # it is what prints itself and its children to the frontend - if scale is None: - scale = self.scale - else: - scale *= self.scale - - try: - if isinstance(self, adafruit_display_text.label.Label): - # adafruit_display_text has some positioning considerations - # that need to be handled. - - # found manually, display must be positioned upwards - # 1 unit (1 unit * scale = scale) - y -= scale - - # group is positioned against anchored_position (default (0,0)), - # which is positioned against anchor_point - - x += self._anchor_point[0] - y += self._anchor_point[1] - if self._boundingbox is not None and self.anchored_position is not None: - x += self.anchored_position[0] - y += self.anchored_position[1] - except AttributeError: - pass - - for elem in self.__contents: - if isinstance(elem, Group): - elem.draw(x, y, scale, False) + if ( + not check_active_ref + or not self.has_active_group_ref + or board.DISPLAY.active_group == self + ): + if scale is None: + scale = self.scale else: - elem.draw(x, y, scale) - - if show: - self.show() - - def show(self): + scale *= self.scale + + try: + if isinstance(self, adafruit_display_text.label.Label): + # adafruit_display_text has some positioning considerations + # that need to be handled. + + # found manually, display must be positioned upwards + # 1 unit (1 unit * scale = scale) + y -= scale + + # group is positioned against anchored_position (default (0,0)), + # which is positioned against anchor_point + + x += self._anchor_point[0] + y += self._anchor_point[1] + if ( + self._boundingbox is not None + and self.anchored_position is not None + ): + x += self.anchored_position[0] + y += self.anchored_position[1] + except AttributeError: + pass + + for elem in self.__contents: + if isinstance(elem, Group): + img = elem.draw( + img=img, + x=x, + y=y, + scale=scale, + show=False, + check_active_ref=False, + ) + else: + img = elem.draw(img=img, x=x, y=y, scale=scale) + + if show: + self.show(img) + return img + + def show(self, img): # sends current bmp_img to the frontend buffered = BytesIO() img.save(buffered, format="BMP") diff --git a/src/base_circuitpython/displayio/tile_grid.py b/src/base_circuitpython/displayio/tile_grid.py index 83b237080..2c570004e 100644 --- a/src/base_circuitpython/displayio/tile_grid.py +++ b/src/base_circuitpython/displayio/tile_grid.py @@ -12,17 +12,6 @@ # https://circuitpython.readthedocs.io/en/5.0.x/shared-bindings/displayio/TileGrid.html -# Create a new black (default) image -img = Image.new( - "RGBA", (CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH), (0, 0, 0, 0) -) - -# Create the pixel map -# All displayio classes can access this -# instance to read and write to the output image. -bmp_img = img.load() - - class TileGrid: def __init__( self, @@ -78,8 +67,7 @@ def __getitem__(self, index): # methods that are not in the origin class: - def draw(self, x, y, scale): - + def draw(self, img, x, y, scale): # draw the current bitmap with # appropriate scale on the global bmp_img x = self.x * scale + x @@ -90,6 +78,7 @@ def draw(self, x, y, scale): ) img.paste(new_shape, (x, y), new_shape) + return img def draw_group(self, x, y, y_start, y_end, x_start, x_end, scale): height = y_end - y_start diff --git a/src/base_circuitpython/terminal_handler.py b/src/base_circuitpython/terminal_handler.py new file mode 100644 index 000000000..4ed0a65e3 --- /dev/null +++ b/src/base_circuitpython/terminal_handler.py @@ -0,0 +1,119 @@ +import displayio +import terminalio +from adafruit_display_text import label +from PIL import Image +import threading + +import os +import base64 +from io import BytesIO +import base_cp_constants as CONSTANTS +import time +import collections +from common import utils +import board + + +class Terminal: + def __init__(self): + self.output_values = collections.deque() + self.__lock = threading.Lock() + # self.add_str_to_terminal("potato") + # self._active = False + + # @property + # def active(self): + # return self._active + + # @active.setter + # def active(self, val): + # self._active = val + # if val: + # self._show() + + def _create_newline(self, str_list): + for string in str_list: + self.output_values.appendleft(string) + + over = len(self.output_values) - 15 + if over > 0: + for i in range(over): + self.output_values.pop() + + def configure(self, no_verif=False): + + self.__lock.acquire() + splash = displayio.Group( + max_size=20, auto_write=False, has_active_group_ref=False + ) + + # reset bmp_img to all black + # splash.img.paste("black", [0, 0, splash.img.size[0], splash.img.size[1]]) + # print(self.output_values) + curr_y = 5 + (16 * (15 - len(self.output_values))) + for o in reversed(self.output_values): + # print(o) + # try: + if len(o): + text_area = label.Label(terminalio.FONT, text=o, line_spacing=1.25) + + text_area.y = curr_y + text_area.x = 15 + splash.append(text_area) + + curr_y += 16 + # except Exception as e: + # # f = open( + # # "C:\\Users\\t-anmah\\Documents\\python_ds_2\\out\\base_circuitpython\\testest.txt", + # # "a", + # # ) + # # f.write("Now the file has more content!") + # # f.write(o) + # # f.close() + # # print(o) + # print(type(e)) + # pass + + splash.draw(show=True) + + self.__lock.release() + + def add_str_to_terminal(self, curr_display_string): + + line_break_amt = 37 + newline_expected_val = line_break_amt + out_str = "" + new_strs = [] + for idx, d in enumerate(curr_display_string): + if d == "\n": + newline_expected_val = line_break_amt + new_strs.append(out_str) + out_str = "" + continue + elif newline_expected_val == 0: + new_strs.append(out_str) + out_str = "" + newline_expected_val = line_break_amt + else: + newline_expected_val -= 1 + out_str += d + new_strs.append(out_str) + + self.__lock.acquire() + self._create_newline(new_strs) + self.__lock.release() + + if board.DISPLAY.active_group == None: + self.configure() + + # def _send(self, img): + # # sends current bmp_img to the frontend + # buffered = BytesIO() + # img.save(buffered, format="BMP") + # byte_base64 = base64.b64encode(buffered.getvalue()) + + # # only send the base_64 string contents + # img_str = str(byte_base64)[2:-1] + + # sendable_json = {CONSTANTS.BASE_64: img_str} + # utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) diff --git a/src/clue/adafruit_clue.py b/src/clue/adafruit_clue.py index 910868edd..eedf31164 100644 --- a/src/clue/adafruit_clue.py +++ b/src/clue/adafruit_clue.py @@ -63,6 +63,7 @@ import pathlib import sys import os +import board abs_path = pathlib.Path(__file__).parent.absolute() sys.path.insert(0, os.path.join(abs_path)) @@ -105,6 +106,7 @@ def __init__( Clue.PURPLE, ) + self._display = board.DISPLAY self._colors = colors self._label = label # self._display = board.DISPLAY @@ -160,11 +162,13 @@ def add_text_line(self, color=0xFFFFFF): def show(self): """Call show() to display the data list.""" - self.text_group.draw(show=True) + self._display.show(self.text_group) # https://stackoverflow.com/questions/31826335/how-to-convert-pil-image-image-object-to-base64-string def show_terminal(self): """Revert to terminalio screen.""" + + self._display.show(None) # TODO: implement terminal for clue screen return diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py index a285b537f..24aea10b4 100644 --- a/src/clue/adafruit_slideshow.py +++ b/src/clue/adafruit_slideshow.py @@ -107,6 +107,7 @@ def __init__( # load images into main queue self._load_images() + display.show(str(self)) # show the first working image self.advance() diff --git a/src/clue/terminal_handler_test.py b/src/clue/terminal_handler_test.py index e69de29bb..925a236ad 100644 --- a/src/clue/terminal_handler_test.py +++ b/src/clue/terminal_handler_test.py @@ -0,0 +1,120 @@ +# import board +# import terminalio + +# import displayio +# from adafruit_display_text import label +# import collections +# from PIL import Image + + +# def create_newline(de, str_list): +# for string in str_list: +# de.appendleft(string) + +# over = len(de) - 15 +# if over > 0: +# for i in range(over): +# de.pop() + + +# def show(output_values): + +# # reset bmp_img to all black +# displayio.img.paste("black", [0, 0, displayio.img.size[0], displayio.img.size[1]]) +# # new_img = Image.new("RGBA", (240,240)) + +# splash = displayio.Group(max_size=20, auto_write=False) +# curr_y = 5 + (16 * (15 - len(output_values))) +# for o in reversed(output_values): +# text_area = label.Label(terminalio.FONT, text=o, line_spacing=1.25) +# text_area.y = curr_y +# curr_y += 16 +# text_area.x = 15 +# splash.append(text_area) + +# board.DISPLAY.show(splash) + + +# def add_str_to_terminal(output_values, curr_display_string): + +# line_break_amt = 37 +# newline_expected_val = line_break_amt +# out_str = "" +# new_strs = [] +# for idx, d in enumerate(curr_display_string): +# if d == "\n": +# newline_expected_val = line_break_amt +# new_strs.append(out_str) +# out_str = "" +# continue +# elif newline_expected_val == 0: +# new_strs.append(out_str) +# out_str = "" +# newline_expected_val = line_break_amt +# else: +# newline_expected_val -= 1 +# out_str += d +# new_strs.append(out_str) +# create_newline(output_values, new_strs) +# show(output_values) + + +# display_strings = [ +# "HELLO", +# "HELLO", +# "test3", +# "test4", +# "test5", +# "test6", +# "test7", +# "test8", +# "test9", +# "test10", +# "test11", +# "test12", +# "test13", +# "test14", +# "test15", +# "test16", +# "potato\nyeee", +# "uwu dcfgvhjbnkml,;.dcfgvhbjnkml,kml,;.dcfgvhbjnkml,h", +# "owo kml,;.dcfgvhbjnkml,kml,;.dcfgvhbjnkml,", +# "kyutegvhbjnkml,kml,;.dcfgvhbjn", +# "animal cgvhbjnkml,kml,;.dcfgvhbjnrossing!!", +# "uwugvhbjnkml,kml,;.dcf\ngvhbjngvhbjnkml,kml,;.dcfgvhbjngvhbjnkml,kml,;.dcfgvhbjngvhbjnkml,kml,;.dcfgvhbjngvhbjnkml,kml,;.dcfgvhbjn", +# "owgvhbjnkml,kml,;.dcfgvhbjno", +# # "kyutgvhbjnkml,kml,;.dcfgvhbjne", +# # "animal gvhbjnkml,kml,;.dcfgvhbjncrossing!!", +# # "uwugvhbjnkml\n,kml,;.dcfgvhbjn", +# # "owogvhb\njnkml,kml,;.dcfgvhbjn", +# # "kyutgvhbjnkml,kml,;.\ndcfgvhbjne", +# # "animal crgvhbjnkml,kml,;.dcfgvhbjnossing!!", +# # "uwugvhbjnkml\n,kml,;.dcfgvhbjn", +# # "owgvhbjnkml,kml,;.dcfgvhbjno", +# # "kyugvhbjnkml,kml,;.dcfgvhbjnte", +# # "animal gvhbjnkml,kml,;.dcfgvhbjncrossing!!", +# # "uwgvhbjnkml,kml,;.dcfgvhbjnu", +# # "owo", +# # "kyute", +# # "animal crossing!!", +# ] + + +# # TERMS FOR LINE BREAKS: + +# # if you take out all newline characters, the number +# # of characters (n) divided by 60 should give you the number +# # of newlines + +# # given an unexpected newline character, the counter resets +# # and the next newline is delayed by as many charactesr as the unexpedcted +# # newline is from a purposely placed newline + +# # initializing deque +# output_values = collections.deque() + +# for d in display_strings: +# add_str_to_terminal(output_values, d) +# # show(output_values) +# while True: +# pass diff --git a/src/process_user_code.py b/src/process_user_code.py index c07f1ddd6..50853f042 100644 --- a/src/process_user_code.py +++ b/src/process_user_code.py @@ -52,8 +52,11 @@ from adafruit_clue import clue from base_circuitpython.base_cp_constants import CLUE +from base_circuitpython import terminal_handler +from base_circuitpython import board +curr_terminal = board.DISPLAY.terminal # Handle User Inputs Thread class UserInput(threading.Thread): def __init__(self): @@ -85,8 +88,10 @@ def run(self): # Handle User's Print Statements Thread def handle_user_prints(): global user_stdout + global curr_terminal while True: if user_stdout.getvalue(): + # curr_terminal.add_str_to_terminal(user_stdout.getvalue()) message = {"type": "print", "data": user_stdout.getvalue()} print(json.dumps(message), file=sys.__stdout__, flush=True) user_stdout.truncate(0) @@ -100,6 +105,9 @@ def handle_user_prints(): # Execute User Code Thread def execute_user_code(abs_path_to_code_file): + global curr_terminal + curr_terminal.add_str_to_terminal("soft reboot") + curr_terminal.add_str_to_terminal("code.py output:") utils.abs_path_to_user_file = abs_path_to_code_file # Execute the user's code.py file with open(abs_path_to_code_file, encoding="utf8") as user_code_file: @@ -117,6 +125,13 @@ def execute_user_code(abs_path_to_code_file): errorMessage += "\t" + str(stackTrace[frameIndex]) print(e, errorMessage, file=sys.stderr, flush=True) + curr_terminal.add_str_to_terminal(errorMessage) + + curr_terminal.add_str_to_terminal("\nCode done running. Waiting for reload.") + board.DISPLAY.show(None) + while True: + pass + user_code = threading.Thread(args=(sys.argv[1],), target=execute_user_code) telemetry_state = json.loads(sys.argv[2]) From b17209adc9349102134a28a0e4d61938a271cab8 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 31 Mar 2020 17:48:03 -0700 Subject: [PATCH 11/17] working terminal with blinka --- src/base_circuitpython/blinka.bmp | Bin 0 -> 172856 bytes src/base_circuitpython/board.py | 1 - src/base_circuitpython/terminal_handler.py | 55 ++++----------------- src/process_user_code.py | 9 ++-- 4 files changed, 14 insertions(+), 51 deletions(-) create mode 100644 src/base_circuitpython/blinka.bmp diff --git a/src/base_circuitpython/blinka.bmp b/src/base_circuitpython/blinka.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0466a02d426e7aa64e8bef4656187276c6f963e0 GIT binary patch literal 172856 zcmeI&F-ikL6adiG*3K$eSxHb31<@c1!CtVjxAF{@dxX@f0)ppA?I}D$ZV>;@m}Nm2 zOK0JQ*=5)X%g@`-=QG)d$JzUS(|xYvn8vaBIrf@K%$jCD)(;PwbfgPT1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0Rn#t++I$v&hCGomq+dL=yf-``JcD)1p@yrus&`FW3`*d zq?@<%ml)5d^N2Gt{NJM$0D-c=V)7CrXK$atIgRsq^Z60gyL=O?1nLpEIX_viz9Mgj z=Sb$1IG53^jzu7{T5%rB!xCp%AQ$S`*08fthO%0}KyEx@ zw^`d<|HBetSs?FqL^7+)Whkrl3-lXz!^%9I*Z&hlNT3&pd`|zN^lxso`Je9o3nm0c zB#`zrV$ZqVZnx`sys*)|k2IqAg-T#3khe9WL`ObH|16n@IOby%NG(TKq9yYfAD@_; F<{QoHq!<7I literal 0 HcmV?d00001 diff --git a/src/base_circuitpython/board.py b/src/base_circuitpython/board.py index 2cb506bc2..1a699ee60 100644 --- a/src/base_circuitpython/board.py +++ b/src/base_circuitpython/board.py @@ -18,7 +18,6 @@ def show(self, group=None): # not in the traditional Group + TileGrid style if not isinstance(group, str): if group == None: - print("Here") self.terminal.configure(no_verif=True) else: group.draw(show=True) diff --git a/src/base_circuitpython/terminal_handler.py b/src/base_circuitpython/terminal_handler.py index 4ed0a65e3..8dfdc05e4 100644 --- a/src/base_circuitpython/terminal_handler.py +++ b/src/base_circuitpython/terminal_handler.py @@ -12,26 +12,19 @@ import collections from common import utils import board +import pathlib class Terminal: def __init__(self): + self.abs_path = pathlib.Path(__file__).parent.absolute() self.output_values = collections.deque() self.__lock = threading.Lock() - # self.add_str_to_terminal("potato") - # self._active = False - - # @property - # def active(self): - # return self._active - - # @active.setter - # def active(self, val): - # self._active = val - # if val: - # self._show() + self.base_img = Image.open(os.path.join(self.abs_path, "blinka.bmp")) def _create_newline(self, str_list): + + self.__lock.acquire() for string in str_list: self.output_values.appendleft(string) @@ -40,6 +33,8 @@ def _create_newline(self, str_list): for i in range(over): self.output_values.pop() + self.__lock.release() + def configure(self, no_verif=False): self.__lock.acquire() @@ -47,13 +42,8 @@ def configure(self, no_verif=False): max_size=20, auto_write=False, has_active_group_ref=False ) - # reset bmp_img to all black - # splash.img.paste("black", [0, 0, splash.img.size[0], splash.img.size[1]]) - # print(self.output_values) curr_y = 5 + (16 * (15 - len(self.output_values))) for o in reversed(self.output_values): - # print(o) - # try: if len(o): text_area = label.Label(terminalio.FONT, text=o, line_spacing=1.25) @@ -62,23 +52,12 @@ def configure(self, no_verif=False): splash.append(text_area) curr_y += 16 - # except Exception as e: - # # f = open( - # # "C:\\Users\\t-anmah\\Documents\\python_ds_2\\out\\base_circuitpython\\testest.txt", - # # "a", - # # ) - # # f.write("Now the file has more content!") - # # f.write(o) - # # f.close() - # # print(o) - # print(type(e)) - # pass - - splash.draw(show=True) + + splash.draw(img=self.base_img.copy(), show=True) self.__lock.release() - def add_str_to_terminal(self, curr_display_string): + def add_str_to_terminal(self, curr_display_string=""): line_break_amt = 37 newline_expected_val = line_break_amt @@ -99,21 +78,7 @@ def add_str_to_terminal(self, curr_display_string): out_str += d new_strs.append(out_str) - self.__lock.acquire() self._create_newline(new_strs) - self.__lock.release() if board.DISPLAY.active_group == None: self.configure() - - # def _send(self, img): - # # sends current bmp_img to the frontend - # buffered = BytesIO() - # img.save(buffered, format="BMP") - # byte_base64 = base64.b64encode(buffered.getvalue()) - - # # only send the base_64 string contents - # img_str = str(byte_base64)[2:-1] - - # sendable_json = {CONSTANTS.BASE_64: img_str} - # utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) diff --git a/src/process_user_code.py b/src/process_user_code.py index 50853f042..52b6f1821 100644 --- a/src/process_user_code.py +++ b/src/process_user_code.py @@ -91,8 +91,9 @@ def handle_user_prints(): global curr_terminal while True: if user_stdout.getvalue(): - # curr_terminal.add_str_to_terminal(user_stdout.getvalue()) + message = {"type": "print", "data": user_stdout.getvalue()} + curr_terminal.add_str_to_terminal(str(message["data"])[:-1]) print(json.dumps(message), file=sys.__stdout__, flush=True) user_stdout.truncate(0) user_stdout.seek(0) @@ -127,10 +128,8 @@ def execute_user_code(abs_path_to_code_file): curr_terminal.add_str_to_terminal(errorMessage) - curr_terminal.add_str_to_terminal("\nCode done running. Waiting for reload.") - board.DISPLAY.show(None) - while True: - pass + curr_terminal.add_str_to_terminal("\nCode done running. Waiting for reload.") + board.DISPLAY.show(None) user_code = threading.Thread(args=(sys.argv[1],), target=execute_user_code) From fdef28d356e52126d89b57c6d380472e94161446 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 1 Apr 2020 11:08:35 -0700 Subject: [PATCH 12/17] fixed existing tests --- src/base_circuitpython/displayio/group.py | 6 ++-- .../displayio/test/test_group.py | 10 ++++--- .../displayio/test/test_tile_grid.py | 16 ++++++++--- src/base_circuitpython/terminal_handler.py | 8 ++++-- src/clue/test/test_adafruit_clue.py | 15 +++++++--- src/clue/test/test_adafruit_display_shapes.py | 28 +++++++++++-------- src/clue/test/test_adafruit_display_text.py | 20 ++++++------- 7 files changed, 63 insertions(+), 40 deletions(-) diff --git a/src/base_circuitpython/displayio/group.py b/src/base_circuitpython/displayio/group.py index 1469d4bef..30ca43295 100644 --- a/src/base_circuitpython/displayio/group.py +++ b/src/base_circuitpython/displayio/group.py @@ -17,9 +17,9 @@ class Group: - def __init__(self, max_size, scale=1, auto_write=True, has_active_group_ref=True): + def __init__(self, max_size, scale=1, auto_write=True, check_active_group_ref=True): - self.has_active_group_ref = has_active_group_ref + self.__check_active_group_ref = check_active_group_ref self.__contents = [] self.max_size = max_size @@ -57,7 +57,7 @@ def draw(self, img=None, x=0, y=0, scale=None, show=True, check_active_ref=True) # it is what prints itself and its children to the frontend if ( not check_active_ref - or not self.has_active_group_ref + or not self.__check_active_group_ref or board.DISPLAY.active_group == self ): if scale is None: diff --git a/src/base_circuitpython/displayio/test/test_group.py b/src/base_circuitpython/displayio/test/test_group.py index ce7bd9f57..48325663d 100644 --- a/src/base_circuitpython/displayio/test/test_group.py +++ b/src/base_circuitpython/displayio/test/test_group.py @@ -7,7 +7,7 @@ from common import utils -from ..tile_grid import TileGrid, img, bmp_img +from ..tile_grid import TileGrid from ..group import Group from ..palette import Palette from ..bitmap import Bitmap @@ -149,20 +149,22 @@ def test_draw_group( tg = TileGrid(bitmap=bmp_1, pixel_shader=palette, position=(0, 0)) tg2 = TileGrid(bitmap=bmp_2, pixel_shader=palette, position=(50, 50)) - group_main = Group(max_size=10, scale=scale_main) + group_main = Group(max_size=10, scale=scale_main, check_active_group_ref=False) group_sub = Group(max_size=10, scale=scale_sub) group_sub.append(tg) group_main.append(group_sub) group_main.append(tg2) + # img = Image.new("RGBA", (240, 240)) + img = group_main.draw() - group_main.draw(0, 0) + img.putalpha(255) expected = Image.open( os.path.join(self.abs_path, "img", "group_test_result.bmp") ) expected.putalpha(255) bmp_img_expected = expected.load() - + bmp_img = img.load() for i in range(CONSTANTS.SCREEN_HEIGHT_WIDTH): for j in range(CONSTANTS.SCREEN_HEIGHT_WIDTH): assert bmp_img_expected[j, i] == bmp_img[j, i] diff --git a/src/base_circuitpython/displayio/test/test_tile_grid.py b/src/base_circuitpython/displayio/test/test_tile_grid.py index c0d1a676b..8c4a591b2 100644 --- a/src/base_circuitpython/displayio/test/test_tile_grid.py +++ b/src/base_circuitpython/displayio/test/test_tile_grid.py @@ -1,5 +1,6 @@ import pytest -from ..tile_grid import TileGrid, img, bmp_img +from PIL import Image +from ..tile_grid import TileGrid from ..palette import Palette from ..bitmap import Bitmap from .. import constants as CONSTANTS @@ -135,9 +136,12 @@ def test_draw( tg = TileGrid(bitmap=bmp, pixel_shader=palette, position=(0, 0)) tg2 = TileGrid(bitmap=bmp, pixel_shader=palette, position=(0, 0)) - + img = Image.new( + "RGBA", (CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH) + ) # without scaling, test output - tg.draw(x_offset, y_offset, 1) + img = tg.draw(img, x_offset, y_offset, 1) + bmp_img = img.load() for i in range(CONSTANTS.SCREEN_HEIGHT_WIDTH): for j in range(CONSTANTS.SCREEN_HEIGHT_WIDTH): if (i in range(y_offset + y, y_offset + y + draw_h)) and ( @@ -149,8 +153,12 @@ def test_draw( ): assert bmp_img[j, i] == bg_color + img = Image.new( + "RGBA", (CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH) + ) # with scaling, test output - tg.draw(x_offset, y_offset, scale) + img = tg.draw(img, x_offset, y_offset, scale) + bmp_img = img.load() for i in range(CONSTANTS.SCREEN_HEIGHT_WIDTH): for j in range(CONSTANTS.SCREEN_HEIGHT_WIDTH): if ( diff --git a/src/base_circuitpython/terminal_handler.py b/src/base_circuitpython/terminal_handler.py index 8dfdc05e4..d37e47534 100644 --- a/src/base_circuitpython/terminal_handler.py +++ b/src/base_circuitpython/terminal_handler.py @@ -1,6 +1,6 @@ import displayio import terminalio -from adafruit_display_text import label +import adafruit_display_text from PIL import Image import threading @@ -39,13 +39,15 @@ def configure(self, no_verif=False): self.__lock.acquire() splash = displayio.Group( - max_size=20, auto_write=False, has_active_group_ref=False + max_size=20, auto_write=False, check_active_group_ref=False ) curr_y = 5 + (16 * (15 - len(self.output_values))) for o in reversed(self.output_values): if len(o): - text_area = label.Label(terminalio.FONT, text=o, line_spacing=1.25) + text_area = adafruit_display_text.label.Label( + terminalio.FONT, text=o, line_spacing=1.25 + ) text_area.y = curr_y text_area.x = 15 diff --git a/src/clue/test/test_adafruit_clue.py b/src/clue/test/test_adafruit_clue.py index 9f516eac7..3ceeed018 100644 --- a/src/clue/test/test_adafruit_clue.py +++ b/src/clue/test/test_adafruit_clue.py @@ -21,9 +21,10 @@ class TestAdafruitClue(object): def setup_method(self): self.abs_path = pathlib.Path(__file__).parent.absolute() - # reset bmp_img to all black - displayio.img.paste( - "black", [0, 0, displayio.img.size[0], displayio.img.size[1]] + self.main_img = Image.new( + "RGBA", + (CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH), + (0, 0, 0, 0), ) utils.send_to_simulator = mock.Mock() @@ -32,10 +33,14 @@ def test_clue_display_text(self): img = Image.open( os.path.join(self.abs_path, CONSTANTS.IMG_DIR_NAME, f"test_clue_text_1.bmp") ) + img.putalpha(255) expected = img.load() clue_data = clue.simple_text_display(title="LET'S TEST!", title_scale=2) + clue_data.text_group.show = self._send_helper + clue_data.text_group._Group__check_active_group_ref = False + clue_data[0].text = "Lorem ipsum" clue_data[1].text = "dolor sit amet, consectetur " clue_data[2].text = "adipiscing:" @@ -51,5 +56,7 @@ def test_clue_display_text(self): clue_data[13].text = "Ut enim ad" clue_data[14].text = "Excepteur sint" clue_data.show() + helper._Helper__test_image_equality(self.main_img.load(), expected) - helper._Helper__test_image_equality(displayio.bmp_img, expected) + def _send_helper(self, image): + self.main_img = image diff --git a/src/clue/test/test_adafruit_display_shapes.py b/src/clue/test/test_adafruit_display_shapes.py index 7ab0ea3f4..0036b2e73 100644 --- a/src/clue/test/test_adafruit_display_shapes.py +++ b/src/clue/test/test_adafruit_display_shapes.py @@ -16,18 +16,19 @@ from .test_helpers import helper from base_circuitpython import base_cp_constants as CONSTANTS +import board class TestAdafruitDisplayShapes(object): def setup_method(self): self.abs_path = pathlib.Path(__file__).parent.absolute() - # reset bmp_img to all black - displayio.img.paste( - "black", [0, 0, displayio.img.size[0], displayio.img.size[1]] - ) - utils.send_to_simulator = mock.Mock() + self.main_img = Image.new( + "RGBA", + (CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH), + (0, 0, 0, 0), + ) def test_shapes(self): @@ -47,31 +48,34 @@ def test_shapes(self): # TAKEN FROM ADAFRUIT'S DISPLAY SHAPES LIBRARY # https://github.com/ladyada/Adafruit_CircuitPython_Display_Shapes/blob/master/examples/display_shapes_simpletest.py splash = displayio.Group(max_size=10) - + splash.show = self._send_helper + board.DISPLAY.show(splash) color_bitmap = displayio.Bitmap(320, 240, 1) color_palette = displayio.Palette(1) color_palette[0] = 0xFFFFFF bg_sprite = displayio.TileGrid( color_bitmap, x=0, y=0, pixel_shader=color_palette ) - splash.append(bg_sprite) - helper._Helper__test_image_equality(displayio.bmp_img, expected_images[0]) + helper._Helper__test_image_equality(self.main_img.load(), expected_images[0]) rect = Rect(80, 20, 41, 41, fill=0x00FF00) splash.append(rect) - helper._Helper__test_image_equality(displayio.bmp_img, expected_images[1]) + helper._Helper__test_image_equality(self.main_img.load(), expected_images[1]) circle = Circle(100, 100, 20, fill=0x00FF00, outline=0xFF00FF) splash.append(circle) - helper._Helper__test_image_equality(displayio.bmp_img, expected_images[2]) + helper._Helper__test_image_equality(self.main_img.load(), expected_images[2]) rect2 = Rect(50, 100, 61, 81, outline=0x0, stroke=3) splash.append(rect2) - helper._Helper__test_image_equality(displayio.bmp_img, expected_images[3]) + helper._Helper__test_image_equality(self.main_img.load(), expected_images[3]) roundrect = RoundRect(10, 10, 61, 81, 10, fill=0x0, outline=0xFF00FF, stroke=6) splash.append(roundrect) - helper._Helper__test_image_equality(displayio.bmp_img, expected_images[4]) + helper._Helper__test_image_equality(self.main_img.load(), expected_images[4]) + + def _send_helper(self, image): + self.main_img = image diff --git a/src/clue/test/test_adafruit_display_text.py b/src/clue/test/test_adafruit_display_text.py index e831cb793..26f190de5 100644 --- a/src/clue/test/test_adafruit_display_text.py +++ b/src/clue/test/test_adafruit_display_text.py @@ -23,12 +23,7 @@ class TestAdafruitDisplayText(object): def setup_method(self): self.abs_path = pathlib.Path(__file__).parent.absolute() - - # reset bmp_img to all black - displayio.img.paste( - "black", [0, 0, displayio.img.size[0], displayio.img.size[1]] - ) - + # Create a new black (default) image utils.send_to_simulator = mock.Mock() @pytest.mark.parametrize( @@ -55,12 +50,17 @@ def test_display_text(self, text, x, y, scale, color): loaded_img = expected_image.load() text_area = label.Label( - terminalio.FONT, text=text, auto_write=False, scale=scale, color=color + terminalio.FONT, + text=text, + auto_write=False, + scale=scale, + color=color, + check_active_group_ref=False, ) text_area.x = x text_area.y = y - text_area.draw(show=True) - helper._Helper__test_image_equality(displayio.bmp_img, loaded_img) - # displayio.img.save(f"test_image_text_{test_count+1}.bmp") + main_img = text_area.draw() + + helper._Helper__test_image_equality(main_img.load(), loaded_img) test_count += 1 From e7e3f89720859c7499b1ed9a02740f461126b3b1 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 1 Apr 2020 14:23:10 -0700 Subject: [PATCH 13/17] terminal testing and further bug fixes --- src/base_circuitpython/base_cp_constants.py | 7 + src/base_circuitpython/board.py | 18 +-- src/base_circuitpython/displayio/group.py | 134 ++++++++++-------- src/base_circuitpython/displayio/tile_grid.py | 6 +- src/base_circuitpython/{ => img}/blinka.bmp | Bin src/base_circuitpython/terminal_handler.py | 75 ++++++---- src/base_circuitpython/test/__init__.py | 0 .../test/test_terminal_handler.py | 61 ++++++++ src/clue/adafruit_clue.py | 4 +- src/clue/adafruit_slideshow.py | 7 +- src/clue/terminal_handler_test.py | 120 ---------------- src/clue/test/test_adafruit_display_text.py | 1 - src/process_user_code.py | 20 ++- src/python_constants.py | 3 + 14 files changed, 229 insertions(+), 227 deletions(-) rename src/base_circuitpython/{ => img}/blinka.bmp (100%) create mode 100644 src/base_circuitpython/test/__init__.py create mode 100644 src/base_circuitpython/test/test_terminal_handler.py delete mode 100644 src/clue/terminal_handler_test.py diff --git a/src/base_circuitpython/base_cp_constants.py b/src/base_circuitpython/base_cp_constants.py index c3de7ffb4..8eff8b11a 100644 --- a/src/base_circuitpython/base_cp_constants.py +++ b/src/base_circuitpython/base_cp_constants.py @@ -8,3 +8,10 @@ BASE_64 = "display_base64" IMG_DIR_NAME = "img" SCREEN_HEIGHT_WIDTH = 240 + +BLINKA_BMP = "blinka.bmp" +CLUE_TERMINAL_LINE_HEIGHT = 16 +CLUE_TERMINAL_LINE_NUM_MAX = 15 +CLUE_TERMINAL_X_OFFSET = 15 +CLUE_TERMINAL_Y_OFFSET = 5 +CLUE_TERMINAL_LINE_BREAK_AMT = 37 \ No newline at end of file diff --git a/src/base_circuitpython/board.py b/src/base_circuitpython/board.py index 1a699ee60..2589e1475 100644 --- a/src/base_circuitpython/board.py +++ b/src/base_circuitpython/board.py @@ -11,16 +11,18 @@ def __init__(self): self.terminal = terminal_handler.Terminal() def show(self, group=None): + if group != self.active_group: + self.active_group = group - self.active_group = group - - # show can take a string if context is - # not in the traditional Group + TileGrid style - if not isinstance(group, str): if group == None: - self.terminal.configure(no_verif=True) - else: - group.draw(show=True) + self.terminal.configure() + return + + # if the group has no attribute called + # "draw", then it is liable for updating itself + # when it calls show + if hasattr(group, "draw"): + group.draw() DISPLAY = Display() diff --git a/src/base_circuitpython/displayio/group.py b/src/base_circuitpython/displayio/group.py index 30ca43295..d1935a01b 100644 --- a/src/base_circuitpython/displayio/group.py +++ b/src/base_circuitpython/displayio/group.py @@ -17,15 +17,18 @@ class Group: - def __init__(self, max_size, scale=1, auto_write=True, check_active_group_ref=True): + def __init__(self, max_size, scale=1, check_active_group_ref=True, auto_write=True): self.__check_active_group_ref = check_active_group_ref - + self.__auto_write = auto_write self.__contents = [] self.max_size = max_size self.scale = scale - self.auto_write = auto_write - self.in_group = False + self.parent = None + + @property + def in_group(self): + return self.parent != None def append(self, item): if len(self.__contents) == self.max_size: @@ -36,77 +39,90 @@ def append(self, item): raise ValueError(CONSTANTS.LAYER_ALREADY_IN_GROUP) self.__contents.append(item) - item.in_group = True - if self.auto_write: - self.draw(show=True) + item.parent = self + + self.__elem_changed() + + def __elem_changed(self): + # ensure that this group is what the board is currently showing + # otherwise, don't bother to draw it + if ( + self.__auto_write + and self.__check_active_group_ref + and board.DISPLAY.active_group == self + ): + self.draw() + + elif self.in_group: + + # if a sub-group is modified, + # propagate to top level to + # see if one of the parents are the + # current active group + self.parent.__elem_changed() def __getitem__(self, index): return self.__contents[index] def __setitem__(self, index, val): + old_val = self.__contents[index] + self.__contents[index] = val + + if old_val != val: + self.__elem_changed() - def draw(self, img=None, x=0, y=0, scale=None, show=True, check_active_ref=True): + def draw(self, img=None, x=0, y=0, scale=None, show=True): + + # this function is not a part of the orignal implementation + # it is what draws itself and its children and potentially shows it to the + # frontend if img == None: img = Image.new( "RGBA", (CONSTANTS.SCREEN_HEIGHT_WIDTH, CONSTANTS.SCREEN_HEIGHT_WIDTH), (0, 0, 0, 0), ) - # this function is not a part of the orignal implementation - # it is what prints itself and its children to the frontend - if ( - not check_active_ref - or not self.__check_active_group_ref - or board.DISPLAY.active_group == self - ): - if scale is None: - scale = self.scale + if scale is None: + scale = self.scale + else: + scale *= self.scale + + try: + if isinstance(self, adafruit_display_text.label.Label): + # adafruit_display_text has some positioning considerations + # that need to be handled. + + # found manually, display must be positioned upwards + # 1 unit (1 unit * scale = scale) + y -= scale + + # group is positioned against anchored_position (default (0,0)), + # which is positioned against anchor_point + + x += self._anchor_point[0] + y += self._anchor_point[1] + if self._boundingbox is not None and self.anchored_position is not None: + x += self.anchored_position[0] + y += self.anchored_position[1] + except AttributeError: + pass + + for elem in self.__contents: + if isinstance(elem, Group): + img = elem.draw(img=img, x=x, y=y, scale=scale, show=False,) else: - scale *= self.scale - - try: - if isinstance(self, adafruit_display_text.label.Label): - # adafruit_display_text has some positioning considerations - # that need to be handled. - - # found manually, display must be positioned upwards - # 1 unit (1 unit * scale = scale) - y -= scale - - # group is positioned against anchored_position (default (0,0)), - # which is positioned against anchor_point - - x += self._anchor_point[0] - y += self._anchor_point[1] - if ( - self._boundingbox is not None - and self.anchored_position is not None - ): - x += self.anchored_position[0] - y += self.anchored_position[1] - except AttributeError: - pass - - for elem in self.__contents: - if isinstance(elem, Group): - img = elem.draw( - img=img, - x=x, - y=y, - scale=scale, - show=False, - check_active_ref=False, - ) - else: - img = elem.draw(img=img, x=x, y=y, scale=scale) - - if show: - self.show(img) - return img + img = elem.draw(img=img, x=x, y=y, scale=scale) + + # show should only be true to the highest parent group + if show: + self.show(img) + + # return value only used if this is within another group + return img def show(self, img): - # sends current bmp_img to the frontend + # sends current img to the frontend buffered = BytesIO() img.save(buffered, format="BMP") byte_base64 = base64.b64encode(buffered.getvalue()) diff --git a/src/base_circuitpython/displayio/tile_grid.py b/src/base_circuitpython/displayio/tile_grid.py index 2c570004e..08610085b 100644 --- a/src/base_circuitpython/displayio/tile_grid.py +++ b/src/base_circuitpython/displayio/tile_grid.py @@ -45,7 +45,11 @@ def __init__( self.bitmap = bitmap self.pixel_shader = pixel_shader self.default_tile = default_tile - self.in_group = False + self.parent = None + + @property + def in_group(self): + return self.parent != None # setitem for an index simply gets the index of the bitmap # rather than the tile index diff --git a/src/base_circuitpython/blinka.bmp b/src/base_circuitpython/img/blinka.bmp similarity index 100% rename from src/base_circuitpython/blinka.bmp rename to src/base_circuitpython/img/blinka.bmp diff --git a/src/base_circuitpython/terminal_handler.py b/src/base_circuitpython/terminal_handler.py index d37e47534..9452e69d4 100644 --- a/src/base_circuitpython/terminal_handler.py +++ b/src/base_circuitpython/terminal_handler.py @@ -1,86 +1,105 @@ -import displayio -import terminalio -import adafruit_display_text + + from PIL import Image import threading - import os import base64 from io import BytesIO -import base_cp_constants as CONSTANTS import time import collections +import pathlib + from common import utils import board -import pathlib +import base_cp_constants as CONSTANTS +import displayio +import terminalio class Terminal: def __init__(self): - self.abs_path = pathlib.Path(__file__).parent.absolute() - self.output_values = collections.deque() + self.__output_values = collections.deque() self.__lock = threading.Lock() - self.base_img = Image.open(os.path.join(self.abs_path, "blinka.bmp")) - - def _create_newline(self, str_list): + self.__abs_path = pathlib.Path(__file__).parent.absolute() + self.__base_img = Image.open( + os.path.join(self.__abs_path, CONSTANTS.IMG_DIR_NAME,CONSTANTS.BLINKA_BMP) + ) + def __create_newline(self, str_list): self.__lock.acquire() for string in str_list: - self.output_values.appendleft(string) + self.__output_values.appendleft(string) - over = len(self.output_values) - 15 + over = len(self.__output_values) - CONSTANTS.CLUE_TERMINAL_LINE_NUM_MAX + + # max CONSTANTS.CLUE_TERMINAL_LINE_NUM_MAX items in output_values if over > 0: for i in range(over): - self.output_values.pop() + self.__output_values.pop() self.__lock.release() def configure(self, no_verif=False): + import adafruit_display_text.label self.__lock.acquire() + + # no need to check the active group within the Group class + # since the caller of configure already did splash = displayio.Group( - max_size=20, auto_write=False, check_active_group_ref=False + max_size=20, check_active_group_ref=False,auto_write=False ) - curr_y = 5 + (16 * (15 - len(self.output_values))) - for o in reversed(self.output_values): + # since the text starts from the bottom, + # we need to find an offset if there are empty spots + + # handling of output_values already ensures that there are + # max CONSTANTS.CLUE_TERMINAL_LINE_NUM_MAX items in output_values deque + num_empty_slots = CONSTANTS.CLUE_TERMINAL_LINE_NUM_MAX - len(self.__output_values) + curr_y = CONSTANTS.CLUE_TERMINAL_Y_OFFSET + (CONSTANTS.CLUE_TERMINAL_LINE_HEIGHT * num_empty_slots) + for o in reversed(self.__output_values): if len(o): text_area = adafruit_display_text.label.Label( terminalio.FONT, text=o, line_spacing=1.25 ) text_area.y = curr_y - text_area.x = 15 + text_area.x = CONSTANTS.CLUE_TERMINAL_X_OFFSET splash.append(text_area) - curr_y += 16 + curr_y += CONSTANTS.CLUE_TERMINAL_LINE_HEIGHT - splash.draw(img=self.base_img.copy(), show=True) + splash.draw(img=self.__base_img.copy()) self.__lock.release() def add_str_to_terminal(self, curr_display_string=""): - line_break_amt = 37 + line_break_amt = CONSTANTS.CLUE_TERMINAL_LINE_BREAK_AMT + + # characters until forced newline newline_expected_val = line_break_amt out_str = "" new_strs = [] for idx, d in enumerate(curr_display_string): - if d == "\n": - newline_expected_val = line_break_amt - new_strs.append(out_str) - out_str = "" - continue - elif newline_expected_val == 0: + # handle custom or forced newline + if d == "\n" or newline_expected_val == 0: new_strs.append(out_str) out_str = "" newline_expected_val = line_break_amt + + # if it was a custom newline, no longer need to + # process the character + if d == "\n": + continue else: newline_expected_val -= 1 out_str += d new_strs.append(out_str) - self._create_newline(new_strs) + self.__create_newline(new_strs) + # only go ahead to configure the screen + # if the terminal is actively on the screen if board.DISPLAY.active_group == None: self.configure() diff --git a/src/base_circuitpython/test/__init__.py b/src/base_circuitpython/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/base_circuitpython/test/test_terminal_handler.py b/src/base_circuitpython/test/test_terminal_handler.py new file mode 100644 index 000000000..22a8986af --- /dev/null +++ b/src/base_circuitpython/test/test_terminal_handler.py @@ -0,0 +1,61 @@ +import pytest + +import base_circuitpython.terminal_handler +from common import utils +from unittest import mock + + +class TestTerminal(object): + def setup_method(self): + + utils.send_to_simulator = mock.Mock() + + @pytest.mark.parametrize( + "str_vals", + [ + (["potato", "Lorem ipsum"]), + ([""]), + ([".......", "123456", "", "test"]), + (["123456789 123456789 123456789 1234567"]), + ], + ) + def test_single_line(self, str_vals): + self.terminal = base_circuitpython.terminal_handler.Terminal() + for s in str_vals: + self.terminal.add_str_to_terminal(s) + + result = list(self.terminal._Terminal__output_values) + result.reverse() + + # output should just be the reversed version since all lines + # don't have newline or exceed 37 characters + assert str_vals == result + + @pytest.mark.parametrize( + "str_vals, expected", + [ + ( + ["\nCode done running. Waiting for reload."], + [".", "Code done running. Waiting for reload", ""], + ), + ( + ["TESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTEST"], + ["ESTTESTTEST", "TESTTESTTESTTESTTESTTESTTESTTESTTESTT"], + ), + ( + ["\nCode done running. Waiting for reload.", "........."], + [".........", ".", "Code done running. Waiting for reload", ""], + ), + ( + ["TEST TEST TEST TEST TEST ", "..."], + ["...", " ", "TEST TEST TEST TEST TEST "], + ), + ], + ) + def test_multiline(self, str_vals, expected): + self.terminal = base_circuitpython.terminal_handler.Terminal() + for s in str_vals: + self.terminal.add_str_to_terminal(s) + + result = list(self.terminal._Terminal__output_values) + assert result == expected diff --git a/src/clue/adafruit_clue.py b/src/clue/adafruit_clue.py index eedf31164..e56d0acdc 100644 --- a/src/clue/adafruit_clue.py +++ b/src/clue/adafruit_clue.py @@ -113,9 +113,7 @@ def __init__( self._font = terminalio.FONT if font: self._font = font - self.text_group = displayio.Group( - max_size=20, scale=text_scale, auto_write=False - ) + self.text_group = displayio.Group(max_size=20, scale=text_scale) if title: # Fail gracefully if title is longer than 60 characters. diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py index 24aea10b4..848ee44d1 100644 --- a/src/clue/adafruit_slideshow.py +++ b/src/clue/adafruit_slideshow.py @@ -8,6 +8,7 @@ import collections from random import shuffle from common import utils +import board # taken from adafruit class PlayBackOrder: @@ -107,7 +108,7 @@ def __init__( # load images into main queue self._load_images() - display.show(str(self)) + display.show(self) # show the first working image self.advance() @@ -184,6 +185,8 @@ def _load_images(self): self.pic_queue = collections.deque(dir_imgs) def _advance_with_fade(self): + if board.DISPLAY.active_group != self: + return old_img = self._curr_img_handle advance_sucessful = False @@ -239,6 +242,8 @@ def _advance_with_fade(self): return True def _advance_no_fade(self): + if board.DISPLAY.active_group != self: + return old_img = self._curr_img_handle diff --git a/src/clue/terminal_handler_test.py b/src/clue/terminal_handler_test.py deleted file mode 100644 index 925a236ad..000000000 --- a/src/clue/terminal_handler_test.py +++ /dev/null @@ -1,120 +0,0 @@ -# import board -# import terminalio - -# import displayio -# from adafruit_display_text import label -# import collections -# from PIL import Image - - -# def create_newline(de, str_list): -# for string in str_list: -# de.appendleft(string) - -# over = len(de) - 15 -# if over > 0: -# for i in range(over): -# de.pop() - - -# def show(output_values): - -# # reset bmp_img to all black -# displayio.img.paste("black", [0, 0, displayio.img.size[0], displayio.img.size[1]]) -# # new_img = Image.new("RGBA", (240,240)) - -# splash = displayio.Group(max_size=20, auto_write=False) -# curr_y = 5 + (16 * (15 - len(output_values))) -# for o in reversed(output_values): -# text_area = label.Label(terminalio.FONT, text=o, line_spacing=1.25) -# text_area.y = curr_y -# curr_y += 16 -# text_area.x = 15 -# splash.append(text_area) - -# board.DISPLAY.show(splash) - - -# def add_str_to_terminal(output_values, curr_display_string): - -# line_break_amt = 37 -# newline_expected_val = line_break_amt -# out_str = "" -# new_strs = [] -# for idx, d in enumerate(curr_display_string): -# if d == "\n": -# newline_expected_val = line_break_amt -# new_strs.append(out_str) -# out_str = "" -# continue -# elif newline_expected_val == 0: -# new_strs.append(out_str) -# out_str = "" -# newline_expected_val = line_break_amt -# else: -# newline_expected_val -= 1 -# out_str += d -# new_strs.append(out_str) -# create_newline(output_values, new_strs) -# show(output_values) - - -# display_strings = [ -# "HELLO", -# "HELLO", -# "test3", -# "test4", -# "test5", -# "test6", -# "test7", -# "test8", -# "test9", -# "test10", -# "test11", -# "test12", -# "test13", -# "test14", -# "test15", -# "test16", -# "potato\nyeee", -# "uwu dcfgvhjbnkml,;.dcfgvhbjnkml,kml,;.dcfgvhbjnkml,h", -# "owo kml,;.dcfgvhbjnkml,kml,;.dcfgvhbjnkml,", -# "kyutegvhbjnkml,kml,;.dcfgvhbjn", -# "animal cgvhbjnkml,kml,;.dcfgvhbjnrossing!!", -# "uwugvhbjnkml,kml,;.dcf\ngvhbjngvhbjnkml,kml,;.dcfgvhbjngvhbjnkml,kml,;.dcfgvhbjngvhbjnkml,kml,;.dcfgvhbjngvhbjnkml,kml,;.dcfgvhbjn", -# "owgvhbjnkml,kml,;.dcfgvhbjno", -# # "kyutgvhbjnkml,kml,;.dcfgvhbjne", -# # "animal gvhbjnkml,kml,;.dcfgvhbjncrossing!!", -# # "uwugvhbjnkml\n,kml,;.dcfgvhbjn", -# # "owogvhb\njnkml,kml,;.dcfgvhbjn", -# # "kyutgvhbjnkml,kml,;.\ndcfgvhbjne", -# # "animal crgvhbjnkml,kml,;.dcfgvhbjnossing!!", -# # "uwugvhbjnkml\n,kml,;.dcfgvhbjn", -# # "owgvhbjnkml,kml,;.dcfgvhbjno", -# # "kyugvhbjnkml,kml,;.dcfgvhbjnte", -# # "animal gvhbjnkml,kml,;.dcfgvhbjncrossing!!", -# # "uwgvhbjnkml,kml,;.dcfgvhbjnu", -# # "owo", -# # "kyute", -# # "animal crossing!!", -# ] - - -# # TERMS FOR LINE BREAKS: - -# # if you take out all newline characters, the number -# # of characters (n) divided by 60 should give you the number -# # of newlines - -# # given an unexpected newline character, the counter resets -# # and the next newline is delayed by as many charactesr as the unexpedcted -# # newline is from a purposely placed newline - -# # initializing deque -# output_values = collections.deque() - -# for d in display_strings: -# add_str_to_terminal(output_values, d) -# # show(output_values) -# while True: -# pass diff --git a/src/clue/test/test_adafruit_display_text.py b/src/clue/test/test_adafruit_display_text.py index 26f190de5..5d6078704 100644 --- a/src/clue/test/test_adafruit_display_text.py +++ b/src/clue/test/test_adafruit_display_text.py @@ -52,7 +52,6 @@ def test_display_text(self, text, x, y, scale, color): text_area = label.Label( terminalio.FONT, text=text, - auto_write=False, scale=scale, color=color, check_active_group_ref=False, diff --git a/src/process_user_code.py b/src/process_user_code.py index 52b6f1821..e5e72d36b 100644 --- a/src/process_user_code.py +++ b/src/process_user_code.py @@ -55,8 +55,9 @@ from base_circuitpython import terminal_handler from base_circuitpython import board - +# get handle to terminal for clue curr_terminal = board.DISPLAY.terminal + # Handle User Inputs Thread class UserInput(threading.Thread): def __init__(self): @@ -91,9 +92,17 @@ def handle_user_prints(): global curr_terminal while True: if user_stdout.getvalue(): - message = {"type": "print", "data": user_stdout.getvalue()} - curr_terminal.add_str_to_terminal(str(message["data"])[:-1]) + + # when I use the value for user_stdout.getvalue() directly + # as the argument for add_str_to_terminal, it only sends the first + # line of the stream. + + # hence, I parse it out of the message dict and take off the + # extra newline at the end. + + data_str = str(message["data"]) + curr_terminal.add_str_to_terminal(data_str[:-1]) print(json.dumps(message), file=sys.__stdout__, flush=True) user_stdout.truncate(0) user_stdout.seek(0) @@ -107,8 +116,7 @@ def handle_user_prints(): # Execute User Code Thread def execute_user_code(abs_path_to_code_file): global curr_terminal - curr_terminal.add_str_to_terminal("soft reboot") - curr_terminal.add_str_to_terminal("code.py output:") + curr_terminal.add_str_to_terminal(CONSTANTS.CODE_START_MSG_CLUE) utils.abs_path_to_user_file = abs_path_to_code_file # Execute the user's code.py file with open(abs_path_to_code_file, encoding="utf8") as user_code_file: @@ -128,7 +136,7 @@ def execute_user_code(abs_path_to_code_file): curr_terminal.add_str_to_terminal(errorMessage) - curr_terminal.add_str_to_terminal("\nCode done running. Waiting for reload.") + curr_terminal.add_str_to_terminal(CONSTANTS.CODE_FINISHED_MSG_CLUE) board.DISPLAY.show(None) diff --git a/src/python_constants.py b/src/python_constants.py index 357b7b06a..c4fcd0d10 100644 --- a/src/python_constants.py +++ b/src/python_constants.py @@ -47,3 +47,6 @@ CLUE = "CLUE" CIRCUITPYTHON = "base_circuitpython" + +CODE_START_MSG_CLUE = "soft reboot\ncode.py output:" +CODE_FINISHED_MSG_CLUE = "\nCode done running. Waiting for reload." From 45dd0fc83910fa67a5787e8705e211e708a7e6e3 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 1 Apr 2020 14:23:35 -0700 Subject: [PATCH 14/17] formatting --- src/base_circuitpython/base_cp_constants.py | 2 +- src/base_circuitpython/displayio/group.py | 4 ++-- src/base_circuitpython/terminal_handler.py | 19 +++++++++++-------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/base_circuitpython/base_cp_constants.py b/src/base_circuitpython/base_cp_constants.py index 8eff8b11a..898ff3594 100644 --- a/src/base_circuitpython/base_cp_constants.py +++ b/src/base_circuitpython/base_cp_constants.py @@ -14,4 +14,4 @@ CLUE_TERMINAL_LINE_NUM_MAX = 15 CLUE_TERMINAL_X_OFFSET = 15 CLUE_TERMINAL_Y_OFFSET = 5 -CLUE_TERMINAL_LINE_BREAK_AMT = 37 \ No newline at end of file +CLUE_TERMINAL_LINE_BREAK_AMT = 37 diff --git a/src/base_circuitpython/displayio/group.py b/src/base_circuitpython/displayio/group.py index d1935a01b..f29b53472 100644 --- a/src/base_circuitpython/displayio/group.py +++ b/src/base_circuitpython/displayio/group.py @@ -65,10 +65,10 @@ def __getitem__(self, index): return self.__contents[index] def __setitem__(self, index, val): - old_val = self.__contents[index] + old_val = self.__contents[index] self.__contents[index] = val - + if old_val != val: self.__elem_changed() diff --git a/src/base_circuitpython/terminal_handler.py b/src/base_circuitpython/terminal_handler.py index 9452e69d4..829cadc42 100644 --- a/src/base_circuitpython/terminal_handler.py +++ b/src/base_circuitpython/terminal_handler.py @@ -1,5 +1,3 @@ - - from PIL import Image import threading import os @@ -22,7 +20,7 @@ def __init__(self): self.__lock = threading.Lock() self.__abs_path = pathlib.Path(__file__).parent.absolute() self.__base_img = Image.open( - os.path.join(self.__abs_path, CONSTANTS.IMG_DIR_NAME,CONSTANTS.BLINKA_BMP) + os.path.join(self.__abs_path, CONSTANTS.IMG_DIR_NAME, CONSTANTS.BLINKA_BMP) ) def __create_newline(self, str_list): @@ -42,21 +40,26 @@ def __create_newline(self, str_list): def configure(self, no_verif=False): import adafruit_display_text.label + self.__lock.acquire() # no need to check the active group within the Group class # since the caller of configure already did splash = displayio.Group( - max_size=20, check_active_group_ref=False,auto_write=False + max_size=20, check_active_group_ref=False, auto_write=False ) # since the text starts from the bottom, # we need to find an offset if there are empty spots - # handling of output_values already ensures that there are + # handling of output_values already ensures that there are # max CONSTANTS.CLUE_TERMINAL_LINE_NUM_MAX items in output_values deque - num_empty_slots = CONSTANTS.CLUE_TERMINAL_LINE_NUM_MAX - len(self.__output_values) - curr_y = CONSTANTS.CLUE_TERMINAL_Y_OFFSET + (CONSTANTS.CLUE_TERMINAL_LINE_HEIGHT * num_empty_slots) + num_empty_slots = CONSTANTS.CLUE_TERMINAL_LINE_NUM_MAX - len( + self.__output_values + ) + curr_y = CONSTANTS.CLUE_TERMINAL_Y_OFFSET + ( + CONSTANTS.CLUE_TERMINAL_LINE_HEIGHT * num_empty_slots + ) for o in reversed(self.__output_values): if len(o): text_area = adafruit_display_text.label.Label( @@ -88,7 +91,7 @@ def add_str_to_terminal(self, curr_display_string=""): out_str = "" newline_expected_val = line_break_amt - # if it was a custom newline, no longer need to + # if it was a custom newline, no longer need to # process the character if d == "\n": continue From c9177b41bc63d23f2895469bc958d10a1421de1c Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 2 Apr 2020 10:51:47 -0700 Subject: [PATCH 15/17] pr feedback and pop updated --- src/base_circuitpython/board.py | 2 +- src/base_circuitpython/displayio/group.py | 21 ++++++++++----------- src/base_circuitpython/terminal_handler.py | 8 ++++---- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/base_circuitpython/board.py b/src/base_circuitpython/board.py index 2589e1475..62de38b1e 100644 --- a/src/base_circuitpython/board.py +++ b/src/base_circuitpython/board.py @@ -15,7 +15,7 @@ def show(self, group=None): self.active_group = group if group == None: - self.terminal.configure() + self.terminal.draw() return # if the group has no attribute called diff --git a/src/base_circuitpython/displayio/group.py b/src/base_circuitpython/displayio/group.py index f0c7bc960..b1c6000b1 100644 --- a/src/base_circuitpython/displayio/group.py +++ b/src/base_circuitpython/displayio/group.py @@ -18,7 +18,6 @@ class Group: def __init__(self, max_size, scale=1, check_active_group_ref=True, auto_write=True): - self.__check_active_group_ref = check_active_group_ref self.__auto_write = auto_write self.__contents = [] @@ -44,8 +43,8 @@ def append(self, item): self.__elem_changed() def __elem_changed(self): - # ensure that this group is what the board is currently showing - # otherwise, don't bother to draw it + # Ensure that this group is what the board is currently showing. + # Otherwise, don't bother to draw it. if ( self.__auto_write and self.__check_active_group_ref @@ -55,10 +54,8 @@ def __elem_changed(self): elif self.in_group: - # if a sub-group is modified, - # propagate to top level to - # see if one of the parents are the - # current active group + # If a sub-group is modified, propagate to top level to + # see if one of the parents are the current active group. self.parent.__elem_changed() def __getitem__(self, index): @@ -73,7 +70,6 @@ def __setitem__(self, index, val): self.__elem_changed() def draw(self, img=None, x=0, y=0, scale=None, show=True): - # this function is not a part of the orignal implementation # it is what draws itself and its children and potentially shows it to the # frontend @@ -93,11 +89,11 @@ def draw(self, img=None, x=0, y=0, scale=None, show=True): # adafruit_display_text has some positioning considerations # that need to be handled. - # found manually, display must be positioned upwards + # This was found manually, display must be positioned upwards # 1 unit (1 unit * scale = scale) y -= scale - # group is positioned against anchored_position (default (0,0)), + # Group is positioned against anchored_position (default (0,0)), # which is positioned against anchor_point x += self._anchor_point[0] @@ -138,4 +134,7 @@ def __len__(self): return len(self.__contents) def pop(self, i=-1): - return self.__contents.pop(i) + item = self.__contents.pop(i) + item.parent = None + self.__elem_changed() + return item diff --git a/src/base_circuitpython/terminal_handler.py b/src/base_circuitpython/terminal_handler.py index 829cadc42..473d7db93 100644 --- a/src/base_circuitpython/terminal_handler.py +++ b/src/base_circuitpython/terminal_handler.py @@ -37,14 +37,14 @@ def __create_newline(self, str_list): self.__lock.release() - def configure(self, no_verif=False): + def draw(self, no_verif=False): import adafruit_display_text.label self.__lock.acquire() # no need to check the active group within the Group class - # since the caller of configure already did + # since the caller of draw already did splash = displayio.Group( max_size=20, check_active_group_ref=False, auto_write=False ) @@ -102,7 +102,7 @@ def add_str_to_terminal(self, curr_display_string=""): self.__create_newline(new_strs) - # only go ahead to configure the screen + # only go ahead to draw the screen # if the terminal is actively on the screen if board.DISPLAY.active_group == None: - self.configure() + self.draw() From 5ff31639d8f02383a37fad9a7c776c869b0defff Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 2 Apr 2020 11:31:24 -0700 Subject: [PATCH 16/17] PR feedback --- src/base_circuitpython/terminal_handler.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/base_circuitpython/terminal_handler.py b/src/base_circuitpython/terminal_handler.py index 473d7db93..f3f3a0468 100644 --- a/src/base_circuitpython/terminal_handler.py +++ b/src/base_circuitpython/terminal_handler.py @@ -60,10 +60,10 @@ def draw(self, no_verif=False): curr_y = CONSTANTS.CLUE_TERMINAL_Y_OFFSET + ( CONSTANTS.CLUE_TERMINAL_LINE_HEIGHT * num_empty_slots ) - for o in reversed(self.__output_values): - if len(o): + for output_val in reversed(self.__output_values): + if len(output_val): text_area = adafruit_display_text.label.Label( - terminalio.FONT, text=o, line_spacing=1.25 + terminalio.FONT, text=output_val, line_spacing=1.25 ) text_area.y = curr_y @@ -72,10 +72,10 @@ def draw(self, no_verif=False): curr_y += CONSTANTS.CLUE_TERMINAL_LINE_HEIGHT - splash.draw(img=self.__base_img.copy()) - self.__lock.release() + splash.draw(img=self.__base_img.copy()) + def add_str_to_terminal(self, curr_display_string=""): line_break_amt = CONSTANTS.CLUE_TERMINAL_LINE_BREAK_AMT From a794ac31112a86d982ae78e8cea08aa600240f8a Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 2 Apr 2020 11:33:15 -0700 Subject: [PATCH 17/17] more pr fixes --- src/base_circuitpython/terminal_handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base_circuitpython/terminal_handler.py b/src/base_circuitpython/terminal_handler.py index f3f3a0468..2fff004e5 100644 --- a/src/base_circuitpython/terminal_handler.py +++ b/src/base_circuitpython/terminal_handler.py @@ -41,14 +41,14 @@ def draw(self, no_verif=False): import adafruit_display_text.label - self.__lock.acquire() - # no need to check the active group within the Group class # since the caller of draw already did splash = displayio.Group( max_size=20, check_active_group_ref=False, auto_write=False ) + self.__lock.acquire() + # since the text starts from the bottom, # we need to find an offset if there are empty spots