Skip to content
This repository was archived by the owner on Dec 23, 2021. It is now read-only.

Commit 64a97be

Browse files
authored
Working Backend and Frontend for CLUE Screen (#266)
Initial backend and frontend for CLUE screen work
1 parent 813b285 commit 64a97be

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+28454
-68
lines changed
Binary file not shown.

gulpfile.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ gulp.task("clean", () => {
3535

3636
const pythonToMove = [
3737
"./src/adafruit_circuitplayground/*.*",
38+
"./src/clue/*.*",
39+
"./src/clue/!(test)/**/*",
40+
"./src/base_circuitpython/**/*",
3841
"./src/micropython/*.*",
3942
"./src/micropython/microbit/*.*",
4043
"./src/micropython/microbit/!(test)/**/*",

locales/en/package.i18n.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"deviceSimulatorExpressExtension.commands.common.label": "Device Simulator Express",
44
"deviceSimulatorExpressExtension.commands.common.runSimulator": "Run Simulator",
55
"deviceSimulatorExpressExtension.commands.common.gettingStarted": "Getting Started",
6-
76
"deviceSimulatorExpressExtension.commands.common.changeBaudRate": "Change Baud Rate",
87
"deviceSimulatorExpressExtension.commands.common.closeSerialMonitor": "Close Serial Monitor",
98
"deviceSimulatorExpressExtension.commands.common.openSerialMonitor": "Open Serial Monitor",
@@ -14,8 +13,10 @@
1413
"deviceSimulatorExpressExtension.commands.microbit.deployToDevice": "[micro:bit] Deploy to Device",
1514
"deviceSimulatorExpressExtension.commands.microbit.openSimulator": "[micro:bit] Open Simulator",
1615
"deviceSimulatorExpressExtension.commands.microbit.newFile": "[micro:bit] New File",
16+
"deviceSimulatorExpressExtension.commands.clue.openSimulator": "[Clue] Open Simulator",
17+
"deviceSimulatorExpressExtension.commands.clue.newFile": "[Clue] New File",
1718
"deviceSimulatorExpressExtension.configuration.title": "Device Simulator Express configuration",
1819
"deviceSimulatorExpressExtension.configuration.properties.configEnvOnChange": "When you change the Python interpreter, the Device Simulator Express will automatically configure itself for the required dependencies.",
1920
"deviceSimulatorExpressExtension.configuration.properties.debuggerPort": "The port the Server will listen on for communication with the debugger.",
2021
"deviceSimulatorExpressExtension.configuration.properties.dependencyChecker": "Whether or not to ask if we can download dependencies. If unchecked, the extension will default to never download dependencies, except when automatically creating a virtual environment in the extension files."
21-
}
22+
}

package.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
"onCommand:deviceSimulatorExpress.microbit.deployToDevice",
4242
"onCommand:deviceSimulatorExpress.microbit.newFile",
4343
"onCommand:deviceSimulatorExpress.microbit.openSimulator",
44+
"onCommand:deviceSimulatorExpress.clue.newFile",
45+
"onCommand:deviceSimulatorExpress.clue.openSimulator",
4446
"onDebug"
4547
],
4648
"main": "./out/extension.js",
@@ -110,6 +112,16 @@
110112
"command": "deviceSimulatorExpress.microbit.newFile",
111113
"title": "%deviceSimulatorExpressExtension.commands.microbit.newFile%",
112114
"category": "%deviceSimulatorExpressExtension.commands.common.label%"
115+
},
116+
{
117+
"command": "deviceSimulatorExpress.clue.openSimulator",
118+
"title": "%deviceSimulatorExpressExtension.commands.clue.openSimulator%",
119+
"category": "%deviceSimulatorExpressExtension.commands.common.label%"
120+
},
121+
{
122+
"command": "deviceSimulatorExpress.clue.newFile",
123+
"title": "%deviceSimulatorExpressExtension.commands.clue.newFile%",
124+
"category": "%deviceSimulatorExpressExtension.commands.common.label%"
113125
}
114126
],
115127
"colors": [
@@ -342,4 +354,4 @@
342354
"extensionDependencies": [
343355
"ms-python.python"
344356
]
345-
}
357+
}

package.nls.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"deviceSimulatorExpressExtension.commands.common.label": "Device Simulator Express",
44
"deviceSimulatorExpressExtension.commands.common.runSimulator": "Run Simulator",
55
"deviceSimulatorExpressExtension.commands.common.gettingStarted": "Getting Started",
6-
76
"deviceSimulatorExpressExtension.commands.common.changeBaudRate": "Change Baud Rate",
87
"deviceSimulatorExpressExtension.commands.common.closeSerialMonitor": "Close Serial Monitor",
98
"deviceSimulatorExpressExtension.commands.common.openSerialMonitor": "Open Serial Monitor",
@@ -14,8 +13,10 @@
1413
"deviceSimulatorExpressExtension.commands.microbit.deployToDevice": "[micro:bit] Deploy to Device",
1514
"deviceSimulatorExpressExtension.commands.microbit.openSimulator": "[micro:bit] Open Simulator",
1615
"deviceSimulatorExpressExtension.commands.microbit.newFile": "[micro:bit] New File",
16+
"deviceSimulatorExpressExtension.commands.clue.openSimulator": "[Clue] Open Simulator",
17+
"deviceSimulatorExpressExtension.commands.clue.newFile": "[Clue] New File",
1718
"deviceSimulatorExpressExtension.configuration.title": "Device Simulator Express configuration",
1819
"deviceSimulatorExpressExtension.configuration.properties.configEnvOnChange": "When you change the Python interpreter, the Device Simulator Express will automatically configure itself for the required dependencies.",
1920
"deviceSimulatorExpressExtension.configuration.properties.debuggerPort": "The port the Server will listen on for communication with the debugger.",
2021
"deviceSimulatorExpressExtension.configuration.properties.dependencyChecker": "Whether or not to ask for dependency downloads. If unchecked, the extension will default to never download dependencies, except when automatically creating a virtual environment in the extension files."
21-
}
22+
}

src/base_circuitpython/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import pathlib
2+
import os
3+
import sys
4+
5+
abs_path = pathlib.Path(__file__).parent.absolute()
6+
sys.path.insert(0, os.path.join(abs_path))
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CPX = "CPX"
2+
CLUE = "CLUE"
3+
PIXELS = "pixels"
4+
5+
CLUE_PIN = "D18"
6+
7+
IMG_DIR_NAME = "img"
8+
SCREEN_HEIGHT_WIDTH = 240

src/base_circuitpython/board.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# dummy class for references to board display to work
2+
# https://learn.adafruit.com/arduino-to-circuitpython/the-board-module
3+
4+
5+
class Display:
6+
def __init__(self):
7+
pass
8+
9+
def show(self, group):
10+
group.draw()
11+
12+
13+
DISPLAY = Display()
14+
15+
# deafult pin,
16+
# shows that this could
17+
# refer to the CPX
18+
# or CLUE neopixel pin
19+
NEOPIXEL = "D00"

src/base_circuitpython/digitalio.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# dummy class for neopixel library to work
2+
3+
# original implementation docs for digitalio:
4+
# https://circuitpython.readthedocs.io/en/5.0.x/shared-bindings/digitalio/__init__.html
5+
6+
7+
class DigitalInOut:
8+
def __init__(self, pin):
9+
self.pin = pin
10+
pass
11+
12+
def deinit(self):
13+
pass
14+
15+
16+
class Direction:
17+
OUTPUT = 0
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Displayio implementation loosely based on the
2+
# displayio package in Adafruit CircuitPython
3+
4+
# https://circuitpython.readthedocs.io/en/5.0.x/shared-bindings/displayio/__init__.html
5+
6+
from .bitmap import Bitmap
7+
from .color_type import ColorType
8+
from .group import Group
9+
from .palette import Palette
10+
11+
# references to img and bmp_img are for testing purposes
12+
from .tile_grid import TileGrid, img, bmp_img
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from . import constants as CONSTANTS
2+
3+
# Bitmap implementation loosely based on the
4+
# displayio.Bitmap class in Adafruit CircuitPython
5+
6+
# https://circuitpython.readthedocs.io/en/5.0.x/shared-bindings/displayio/Bitmap.html
7+
8+
# The colour of a certain pixel is interpreted
9+
# within the TileGrid instance that the object
10+
# lives within. Each pixel is an integer value
11+
# that refers to the colours in the palette via index.
12+
13+
14+
class Bitmap:
15+
def __init__(self, width, height, value_count=255):
16+
self.__width = width
17+
self.__height = height
18+
self.values = bytearray(width * height)
19+
20+
@property
21+
def width(self):
22+
return self.__width
23+
24+
@property
25+
def height(self):
26+
return self.__height
27+
28+
def __setitem__(self, index, value):
29+
if isinstance(index, tuple):
30+
if index[0] >= self.width or index[1] >= self.height:
31+
raise IndexError(CONSTANTS.PIXEL_OUT_OF_BOUNDS)
32+
33+
index = index[0] + index[1] * self.width
34+
35+
try:
36+
self.values[index] = value
37+
except IndexError:
38+
raise IndexError(CONSTANTS.PIXEL_OUT_OF_BOUNDS)
39+
40+
def __getitem__(self, index):
41+
42+
if isinstance(index, tuple):
43+
if index[0] >= self.width or index[1] >= self.height:
44+
raise IndexError(CONSTANTS.PIXEL_OUT_OF_BOUNDS)
45+
46+
index = index[0] + index[1] * self.width
47+
48+
try:
49+
return self.values[index]
50+
except IndexError:
51+
raise IndexError(CONSTANTS.PIXEL_OUT_OF_BOUNDS)
52+
53+
def __len__(self):
54+
return self.width * self.height
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Datatype for the items within a palette
2+
class ColorType:
3+
def __init__(self, rgb888):
4+
self.rgb888 = rgb888
5+
self.transparent = False
6+
7+
def __eq__(self, other):
8+
return (
9+
isinstance(other, ColorType)
10+
and self.rgb888 == other.rgb888
11+
and self.transparent == other.transparent
12+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
PIXEL_OUT_OF_BOUNDS = "pixel coordinates out of bounds"
2+
PALETTE_OUT_OF_RANGE = "Palette index out of range"
3+
TILE_OUT_OF_BOUNDS = "Tile index out of bounds"
4+
INCORR_SUBCLASS = "Layer must be a Group or TileGrid subclass."
5+
LAYER_ALREADY_IN_GROUP = "Layer already in a group."
6+
GROUP_FULL = "Group Full"
7+
8+
SCREEN_HEIGHT_WIDTH = 240
9+
10+
CLUE = "CLUE"
11+
BASE_64 = "display_base64"
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import base64
2+
from io import BytesIO
3+
from PIL import Image
4+
import adafruit_display_text
5+
6+
from .tile_grid import TileGrid, bmp_img, img
7+
from . import constants as CONSTANTS
8+
9+
import common
10+
11+
# Group implementation loosely based on the
12+
# displayio.Group class in Adafruit CircuitPython
13+
# (with only the functions needed for the CLUE)
14+
15+
# https://circuitpython.readthedocs.io/en/5.0.x/shared-bindings/displayio/Group.html
16+
17+
18+
class Group:
19+
def __init__(self, max_size, scale=1, auto_write=True):
20+
self.__contents = []
21+
self.max_size = max_size
22+
self.scale = scale
23+
self.auto_write = auto_write
24+
self.in_group = False
25+
26+
def append(self, item):
27+
if len(self.__contents) == self.max_size:
28+
raise RuntimeError(CONSTANTS.GROUP_FULL)
29+
elif not isinstance(item, TileGrid) and not isinstance(item, Group):
30+
raise ValueError(CONSTANTS.INCORR_SUBCLASS)
31+
elif item.in_group:
32+
raise ValueError(CONSTANTS.LAYER_ALREADY_IN_GROUP)
33+
34+
self.__contents.append(item)
35+
item.in_group = True
36+
if self.auto_write:
37+
self.draw(show=True)
38+
39+
def __getitem__(self, index):
40+
return self.__contents[index]
41+
42+
def __setitem__(self, index, val):
43+
self.__contents[index] = val
44+
45+
def draw(self, x=0, y=0, scale=None, show=False):
46+
# this function is not a part of the orignal implementation
47+
# it is what prints itself and its children to the frontend
48+
if scale is None:
49+
scale = self.scale
50+
else:
51+
scale *= self.scale
52+
53+
try:
54+
if isinstance(self, adafruit_display_text.label.Label):
55+
# adafruit_display_text has some positioning considerations
56+
# that need to be handled.
57+
58+
# found manually, display must be positioned upwards
59+
# 1 unit (1 unit * scale = scale)
60+
y -= scale
61+
62+
# group is positioned against anchored_position (default (0,0)),
63+
# which is positioned against anchor_point
64+
65+
x += self._anchor_point[0]
66+
y += self._anchor_point[1]
67+
if self._boundingbox is not None and self.anchored_position is not None:
68+
x += self.anchored_position[0]
69+
y += self.anchored_position[1]
70+
except AttributeError:
71+
pass
72+
73+
for elem in self.__contents:
74+
if isinstance(elem, Group):
75+
elem.draw(x, y, scale, False)
76+
else:
77+
elem.draw(x, y, scale)
78+
79+
if show:
80+
self.show()
81+
82+
def show(self):
83+
# sends current bmp_img to the frontend
84+
buffered = BytesIO()
85+
img.save(buffered, format="BMP")
86+
byte_base64 = base64.b64encode(buffered.getvalue())
87+
img_str = str(byte_base64)[2:-1]
88+
89+
sendable_json = {CONSTANTS.BASE_64: img_str}
90+
common.utils.send_to_simulator(sendable_json, CONSTANTS.CLUE)
91+
92+
def __len__(self):
93+
if not self.__contents:
94+
return 0
95+
else:
96+
return len(self.__contents)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from .color_type import ColorType
2+
from . import constants as CONSTANTS
3+
4+
# Palette implementation loosely based on the
5+
# displayio.Palette class in Adafruit CircuitPython
6+
# (with only the functions needed for the CLUE)
7+
8+
# https://circuitpython.readthedocs.io/en/5.0.x/shared-bindings/displayio/Palette.html
9+
10+
11+
class Palette:
12+
def __init__(self, color_count):
13+
self.color_count = color_count
14+
self.__contents = []
15+
16+
# set all colours to black by default
17+
for i in range(self.color_count):
18+
self.__contents.append(ColorType((0, 0, 0)))
19+
20+
def __getitem__(self, index):
21+
if index >= self.color_count:
22+
raise IndexError(CONSTANTS.PALETTE_OUT_OF_RANGE)
23+
24+
return self.__contents[index].rgb888
25+
26+
def __setitem__(self, index, value):
27+
if index >= self.color_count:
28+
raise IndexError(CONSTANTS.PALETTE_OUT_OF_RANGE)
29+
30+
self.__contents[index].rgb888 = value
31+
32+
def make_transparent(self, index):
33+
self.__toggle_transparency(index, True)
34+
35+
def make_opaque(self, index):
36+
self.__toggle_transparency(index, False)
37+
38+
def __toggle_transparency(self, index, transparency):
39+
if self.__contents[index]:
40+
self.__contents[index].transparent = transparency

src/base_circuitpython/displayio/test/__init__.py

Whitespace-only changes.
Binary file not shown.

0 commit comments

Comments
 (0)