This repository was archived by the owner on Dec 23, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 49
Display module for microbit library #189
Merged
Merged
Changes from all commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
806576b
shim design example
andreamah deceeb7
some effort on image object design
andreamah 8ed9e5b
initial look at image class
andreamah 32060ce
fixes to image
andreamah ef6498c
finished first draft of most image methods
andreamah d2a7672
width and height modifications
andreamah 16beee3
more additions to image
andreamah 52ba07e
display microbit library
91352a0
resolve merge conflicts
cc0ac13
update dusplay
c761e8b
integrated image into display class
andreamah 4cbda2b
fixed LED array reference issue
andreamah 3557047
changes to display
0717e6b
fixed merge conflicts
ecd96e0
added bytearray compatability
andreamah 9652a23
Before merge
ddcf389
reserve merge conflicts
20690b2
display show working, still need to add tests
b627989
updated display tests and refactored some code
2a6553e
started scroll method for display
7593b04
before merge
ccc752c
fixed merge conflicts
5a11e1c
Changed comment
39643f4
new test
6f0cbe9
test
88a1226
Done unit tests
8a76684
Removed unused file
a6d24b9
Deleted comments
fa53c7d
black formatting
dee99d4
Updated microbit model to include display
6be2586
Addressed comments
4dfd964
Merge branch 'dev' into users/t-vali/display
vandyliu e940be0
added comment
d5c5b58
Merge branch 'users/t-vali/display' of https://github.com/microsoft/v…
e8105f1
Updated how display on and off functionality work
e35a11b
incorporated PR feedback
cd5386f
Refactored code
vandyliu 972057f
formatted with black
vandyliu 164a481
Lock acquisition added
fcb741a
Lock acquisition added
0a62a0c
Added image lock
975ab1d
updated image lock
1e502f9
Updated locks
c2ca23c
Updated locks
8fc9b3b
Updated locks
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,269 @@ | ||
import copy | ||
import time | ||
import threading | ||
|
||
from . import constants as CONSTANTS | ||
from .image import Image | ||
from .. import shim | ||
|
||
|
||
class Display: | ||
# The implementation based off of https://github.com/bbcmicrobit/micropython/blob/master/docs/display.rst. | ||
|
||
def __init__(self): | ||
self.__image = Image() | ||
self.__on = True | ||
self.__current_pid = None | ||
self.__blank_image = Image() | ||
|
||
self.__lock = threading.Lock() | ||
|
||
def scroll(self, value, delay=150, wait=True, loop=False, monospace=False): | ||
vandyliu marked this conversation as resolved.
Show resolved
Hide resolved
vandyliu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if not wait: | ||
thread = threading.Thread( | ||
target=self.scroll, args=(value, delay, True, loop, monospace) | ||
) | ||
thread.start() | ||
return | ||
|
||
# Set current_pid to the thread's identifier | ||
self.__lock.acquire() | ||
self.__current_pid = threading.get_ident() | ||
self.__lock.release() | ||
|
||
if isinstance(value, (str, int, float)): | ||
value = str(value) | ||
else: | ||
raise TypeError(f"can't convert {type(value)} object to str implicitly") | ||
|
||
letters = [] | ||
for c in value: | ||
if monospace: | ||
letters.append(Display.__get_image_from_char(c)) | ||
letters.append( | ||
Image(CONSTANTS.SPACE_BETWEEN_LETTERS_WIDTH, CONSTANTS.LED_HEIGHT) | ||
) | ||
else: | ||
if c == " ": | ||
letters.append( | ||
Image(CONSTANTS.WHITESPACE_WIDTH, CONSTANTS.LED_HEIGHT) | ||
) | ||
else: | ||
letters.append( | ||
Display.__strip_unlit_columns(Display.__get_image_from_char(c)) | ||
) | ||
letters.append( | ||
Image( | ||
CONSTANTS.SPACE_BETWEEN_LETTERS_WIDTH, CONSTANTS.LED_HEIGHT, | ||
) | ||
) | ||
appended_image = Display.__create_scroll_image(letters) | ||
|
||
while True: | ||
# Show the scrolled image one square at a time. | ||
for x in range(appended_image.width() - CONSTANTS.LED_WIDTH + 1): | ||
self.__lock.acquire() | ||
|
||
# If show or scroll is called again, there will be a different pid and break | ||
if self.__current_pid != threading.get_ident(): | ||
self.__lock.release() | ||
break | ||
|
||
self.__image.blit( | ||
appended_image, x, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT | ||
) | ||
self.__lock.release() | ||
|
||
Display.sleep_ms(delay) | ||
|
||
if not loop: | ||
break | ||
|
||
def show(self, value, delay=400, wait=True, loop=False, clear=False): | ||
if not wait: | ||
thread = threading.Thread( | ||
target=self.show, args=(value, delay, True, loop, clear) | ||
) | ||
thread.start() | ||
return | ||
|
||
# Set current_pid to the thread's identifier | ||
self.__lock.acquire() | ||
self.__current_pid = threading.get_ident() | ||
self.__lock.release() | ||
|
||
images = [] | ||
use_delay = False | ||
if isinstance(value, Image): | ||
images.append(value.crop(0, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT)) | ||
elif isinstance(value, (str, int, float)): | ||
chars = list(str(value)) | ||
for c in chars: | ||
images.append(Display.__get_image_from_char(c)) | ||
if len(chars) > 1: | ||
use_delay = True | ||
else: | ||
# Check if iterable | ||
try: | ||
_ = iter(value) | ||
except TypeError as e: | ||
raise e | ||
|
||
for elem in value: | ||
if isinstance(elem, Image): | ||
images.append( | ||
elem.crop(0, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT) | ||
) | ||
elif isinstance(elem, str) and len(elem) == 1: | ||
images.append(Display.__get_image_from_char(elem)) | ||
# If elem is not char or image, break without iterating through rest of list | ||
else: | ||
break | ||
use_delay = True | ||
|
||
while True: | ||
for image in images: | ||
self.__lock.acquire() | ||
|
||
# If show or scroll is called again, there will be a different pid and break | ||
if self.__current_pid != threading.get_ident(): | ||
self.__lock.release() | ||
break | ||
|
||
self.__image = image | ||
self.__lock.release() | ||
|
||
if use_delay: | ||
Display.sleep_ms(delay) | ||
|
||
if not loop: | ||
break | ||
if clear: | ||
self.clear() | ||
|
||
def get_pixel(self, x, y): | ||
self.__lock.acquire() | ||
pixel = self.__image.get_pixel(x, y) | ||
self.__lock.release() | ||
return pixel | ||
|
||
def set_pixel(self, x, y, value): | ||
self.__lock.acquire() | ||
self.__image.set_pixel(x, y, value) | ||
self.__lock.release() | ||
|
||
def clear(self): | ||
self.__lock.acquire() | ||
self.__image = Image() | ||
self.__lock.release() | ||
|
||
def on(self): | ||
self.__on = True | ||
|
||
def off(self): | ||
self.__on = False | ||
|
||
def is_on(self): | ||
return self.__on | ||
|
||
def read_light_level(self): | ||
raise NotImplementedError(CONSTANTS.NOT_IMPLEMENTED_ERROR) | ||
|
||
# Helpers | ||
|
||
def __get_array(self): | ||
vandyliu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if self.is_on(): | ||
self.__lock.acquire() | ||
leds = copy.deepcopy(self.__image._Image__LED) | ||
self.__lock.release() | ||
return leds | ||
else: | ||
return self.__blank_image._Image__LED | ||
|
||
@staticmethod | ||
def __get_image_from_char(c): | ||
# If c is not between the ASCII alphabet we cover, make it a question mark | ||
if ord(c) < CONSTANTS.ASCII_START or ord(c) > CONSTANTS.ASCII_END: | ||
c = "?" | ||
offset = (ord(c) - CONSTANTS.ASCII_START) * CONSTANTS.LED_WIDTH | ||
representative_bytes = CONSTANTS.ALPHABET[ | ||
offset : offset + CONSTANTS.LED_HEIGHT | ||
] | ||
return Image(Display.__convert_bytearray_to_image_str(representative_bytes)) | ||
|
||
# Removes columns that are not lit | ||
vandyliu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@staticmethod | ||
def __strip_unlit_columns(image): | ||
min_index = CONSTANTS.LED_WIDTH - 1 | ||
max_index = 0 | ||
for row in image._Image__LED: | ||
for index, bit in enumerate(row): | ||
if bit > 0: | ||
min_index = min(min_index, index) | ||
max_index = max(max_index, index) | ||
return image.crop(min_index, 0, max_index - min_index + 1, CONSTANTS.LED_HEIGHT) | ||
|
||
# This method is different from Image's __bytes_to_array. | ||
# This one requires a conversion from binary of the ALPHABET constant to an image. | ||
@staticmethod | ||
def __convert_bytearray_to_image_str(byte_array): | ||
andreamah marked this conversation as resolved.
Show resolved
Hide resolved
|
||
arr = [] | ||
for b in byte_array: | ||
# Convert byte to binary | ||
b_as_bits = str(bin(b))[2:] | ||
sub_arr = [] | ||
while len(sub_arr) < 5: | ||
# Iterate throught bits | ||
# If there is a 1 at b, then the pixel at column b is lit | ||
for bit in b_as_bits[::-1]: | ||
if len(sub_arr) < 5: | ||
sub_arr.insert(0, int(bit) * CONSTANTS.BRIGHTNESS_MAX) | ||
else: | ||
break | ||
# Add 0s to the front until the list is 5 long | ||
while len(sub_arr) < 5: | ||
sub_arr.insert(0, 0) | ||
arr.append(sub_arr) | ||
image_str = "" | ||
for row in arr: | ||
for elem in row: | ||
image_str += str(elem) | ||
image_str += ":" | ||
return image_str | ||
|
||
@staticmethod | ||
def __insert_blank_column(image): | ||
for row in image._Image__LED: | ||
row.append(0) | ||
|
||
@staticmethod | ||
def __create_scroll_image(images): | ||
blank_5x5_image = Image() | ||
front_of_scroll_image = Image(4, 5) | ||
images.insert(0, front_of_scroll_image) | ||
|
||
scroll_image = Image._Image__append_images(images) | ||
end_of_scroll_image = Image() | ||
# Insert columns of 0s until the ending is a 5x5 blank | ||
end_of_scroll_image.blit( | ||
scroll_image, | ||
scroll_image.width() - CONSTANTS.LED_WIDTH, | ||
0, | ||
CONSTANTS.LED_WIDTH, | ||
CONSTANTS.LED_HEIGHT, | ||
) | ||
while not Image._Image__same_image(end_of_scroll_image, blank_5x5_image): | ||
Display.__insert_blank_column(scroll_image) | ||
end_of_scroll_image.blit( | ||
scroll_image, | ||
scroll_image.width() - CONSTANTS.LED_WIDTH, | ||
0, | ||
CONSTANTS.LED_WIDTH, | ||
CONSTANTS.LED_HEIGHT, | ||
) | ||
|
||
return scroll_image | ||
|
||
@staticmethod | ||
def sleep_ms(ms): | ||
time.sleep(ms / 1000) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.