From 806576be5f9d34c68415abe9103c33f23401de07 Mon Sep 17 00:00:00 2001 From: andreamah Date: Fri, 24 Jan 2020 16:34:34 -0800 Subject: [PATCH 01/27] shim design example --- gulpfile.js | 271 ++++++++++++++------------- src/microbit/__init__.py | 1 + src/microbit/code_processing_shim.py | 13 ++ src/microbit/display.py | 6 + src/microbit/microbit_model.py | 15 ++ 5 files changed, 171 insertions(+), 135 deletions(-) create mode 100644 src/microbit/__init__.py create mode 100644 src/microbit/code_processing_shim.py create mode 100644 src/microbit/display.py create mode 100644 src/microbit/microbit_model.py diff --git a/gulpfile.js b/gulpfile.js index 3c106798d..c642abfed 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,135 +1,136 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -const gulp = require("gulp"); - -const ts = require("gulp-typescript"); -const sourcemaps = require("gulp-sourcemaps"); -const typescript = require("typescript"); -const del = require("del"); -const es = require("event-stream"); -const vsce = require("vsce"); -const nls = require("vscode-nls-dev"); - -const tsProject = ts.createProject("./tsconfig.json", { typescript }); - -const inlineMap = true; -const inlineSource = false; -const outDest = "out"; - -// A list of all locales supported by VSCode can be found here: https://code.visualstudio.com/docs/getstarted/locales -const languages = [{ folderName: "en", id: "en" }]; - -gulp.task("clean", () => { - return del( - [ - "out/*", - "package.nls.*.json", - "../../dist/*0.0.0-UNTRACKEDVERSION.vsix" - ], - { force: true } - ); -}); - -const pythonToMove = [ - "./src/adafruit_circuitplayground/*.*", - "./src/*.py", - "./src/requirements.txt", -]; - -gulp.task("python-compile", () => { - // the base option sets the relative root for the set of files, - // preserving the folder structure - return gulp.src(pythonToMove, { base: "./src/" }).pipe(gulp.dest("out")); -}); - -gulp.task("internal-compile", () => { - return compile(false); -}); - -gulp.task("internal-nls-compile", () => { - return compile(true); -}); - -gulp.task("add-locales", () => { - return gulp - .src(["package.nls.json"]) - .pipe(nls.createAdditionalLanguageFiles(languages, "locales")) - .pipe(gulp.dest(".")); -}); - -gulp.task("vsce:publish", () => { - return vsce.publish(); -}); - -gulp.task("vsce:package", () => { - return vsce.createVSIX({ - packagePath: "../../dist/deviceSimulatorExpress-0.0.0-UNTRACKEDVERSION.vsix" - }); -}); - -gulp.task( - "compile", - gulp.series("clean", "internal-compile", "python-compile", callback => { - callback(); - }) -); - -gulp.task( - "build", - gulp.series( - "clean", - "internal-nls-compile", - "python-compile", - "add-locales", - callback => { - callback(); - } - ) -); - -gulp.task( - "publish", - gulp.series("compile", "vsce:publish", callback => { - callback(); - }) -); - -gulp.task( - "package", - gulp.series("compile", "vsce:package", callback => { - callback(); - }) -); - -//---- internal - -function compile(buildNls) { - var r = tsProject - .src() - .pipe(sourcemaps.init()) - .pipe(tsProject()) - .js.pipe(buildNls ? nls.rewriteLocalizeCalls() : es.through()) - .pipe( - buildNls - ? nls.createAdditionalLanguageFiles(languages, "locales", "out") - : es.through() - ); - - if (inlineMap && inlineSource) { - r = r.pipe(sourcemaps.write()); - } else { - r = r.pipe( - sourcemaps.write("../out", { - // no inlined source - includeContent: inlineSource, - // Return relative source map root directories per file. - sourceRoot: "../src" - }) - ); - } - - return r.pipe(gulp.dest(outDest)); -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const gulp = require("gulp"); + +const ts = require("gulp-typescript"); +const sourcemaps = require("gulp-sourcemaps"); +const typescript = require("typescript"); +const del = require("del"); +const es = require("event-stream"); +const vsce = require("vsce"); +const nls = require("vscode-nls-dev"); + +const tsProject = ts.createProject("./tsconfig.json", { typescript }); + +const inlineMap = true; +const inlineSource = false; +const outDest = "out"; + +// A list of all locales supported by VSCode can be found here: https://code.visualstudio.com/docs/getstarted/locales +const languages = [{ folderName: "en", id: "en" }]; + +gulp.task("clean", () => { + return del( + [ + "out/*", + "package.nls.*.json", + "../../dist/*0.0.0-UNTRACKEDVERSION.vsix" + ], + { force: true } + ); +}); + +const pythonToMove = [ + "./src/adafruit_circuitplayground/*.*", + "./src/microbit/*.*", + "./src/*.py", + "./src/requirements.txt", +]; + +gulp.task("python-compile", () => { + // the base option sets the relative root for the set of files, + // preserving the folder structure + return gulp.src(pythonToMove, { base: "./src/" }).pipe(gulp.dest("out")); +}); + +gulp.task("internal-compile", () => { + return compile(false); +}); + +gulp.task("internal-nls-compile", () => { + return compile(true); +}); + +gulp.task("add-locales", () => { + return gulp + .src(["package.nls.json"]) + .pipe(nls.createAdditionalLanguageFiles(languages, "locales")) + .pipe(gulp.dest(".")); +}); + +gulp.task("vsce:publish", () => { + return vsce.publish(); +}); + +gulp.task("vsce:package", () => { + return vsce.createVSIX({ + packagePath: "../../dist/deviceSimulatorExpress-0.0.0-UNTRACKEDVERSION.vsix" + }); +}); + +gulp.task( + "compile", + gulp.series("clean", "internal-compile", "python-compile", callback => { + callback(); + }) +); + +gulp.task( + "build", + gulp.series( + "clean", + "internal-nls-compile", + "python-compile", + "add-locales", + callback => { + callback(); + } + ) +); + +gulp.task( + "publish", + gulp.series("compile", "vsce:publish", callback => { + callback(); + }) +); + +gulp.task( + "package", + gulp.series("compile", "vsce:package", callback => { + callback(); + }) +); + +//---- internal + +function compile(buildNls) { + var r = tsProject + .src() + .pipe(sourcemaps.init()) + .pipe(tsProject()) + .js.pipe(buildNls ? nls.rewriteLocalizeCalls() : es.through()) + .pipe( + buildNls + ? nls.createAdditionalLanguageFiles(languages, "locales", "out") + : es.through() + ); + + if (inlineMap && inlineSource) { + r = r.pipe(sourcemaps.write()); + } else { + r = r.pipe( + sourcemaps.write("../out", { + // no inlined source + includeContent: inlineSource, + // Return relative source map root directories per file. + sourceRoot: "../src" + }) + ); + } + + return r.pipe(gulp.dest(outDest)); +} diff --git a/src/microbit/__init__.py b/src/microbit/__init__.py new file mode 100644 index 000000000..3f308d8fb --- /dev/null +++ b/src/microbit/__init__.py @@ -0,0 +1 @@ +from .code_processing_shim import * \ No newline at end of file diff --git a/src/microbit/code_processing_shim.py b/src/microbit/code_processing_shim.py new file mode 100644 index 000000000..a3996c21f --- /dev/null +++ b/src/microbit/code_processing_shim.py @@ -0,0 +1,13 @@ +from . import microbit_model + +# EXAMPLE +# can be called simply as "show_message("string")" +def show_message(message): + microbit_model.mb.show_message(message) + +# EXAMPLE +# can be called with display.scroll("string") +class display: + @staticmethod + def scroll(self, message): + microbit_model.mb.display.scroll(self,message) \ No newline at end of file diff --git a/src/microbit/display.py b/src/microbit/display.py new file mode 100644 index 000000000..699c4397b --- /dev/null +++ b/src/microbit/display.py @@ -0,0 +1,6 @@ +# class for microbit led display +class Display: + + # SAMPLE FUNCTION + def scroll(self, message): + print("scroll!! " + message) \ No newline at end of file diff --git a/src/microbit/microbit_model.py b/src/microbit/microbit_model.py new file mode 100644 index 000000000..1d6ebec95 --- /dev/null +++ b/src/microbit/microbit_model.py @@ -0,0 +1,15 @@ +from .display import Display + +class MicrobitModel: + def __init__(self): + # State in the Python process + self.display = Display() + self.__state = { } + self.__debug_mode = False + self.__abs_path_to_code_file = '' + + # SAMPLE FUNCTION + def show_message(self, message): + print("message!! " + message) + +mb = MicrobitModel() \ No newline at end of file From deceeb75debae7df4e92f0e6306ad28273b15bfc Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 28 Jan 2020 10:27:16 -0800 Subject: [PATCH 02/27] some effort on image object design --- src/microbit/code_processing_shim.py | 22 ++++++++---- src/microbit/constants.py | 5 +++ src/microbit/display.py | 7 +++- src/microbit/image.py | 52 ++++++++++++++++++++++++++++ src/test_code/code.py | 38 ++++++++++---------- 5 files changed, 98 insertions(+), 26 deletions(-) create mode 100644 src/microbit/constants.py create mode 100644 src/microbit/image.py diff --git a/src/microbit/code_processing_shim.py b/src/microbit/code_processing_shim.py index a3996c21f..c4b5d6146 100644 --- a/src/microbit/code_processing_shim.py +++ b/src/microbit/code_processing_shim.py @@ -1,13 +1,23 @@ from . import microbit_model +from . import image +from . import constants as CONSTANTS # EXAMPLE # can be called simply as "show_message("string")" def show_message(message): microbit_model.mb.show_message(message) -# EXAMPLE -# can be called with display.scroll("string") -class display: - @staticmethod - def scroll(self, message): - microbit_model.mb.display.scroll(self,message) \ No newline at end of file +# def Image(pattern = CONSTANTS.BLANK): +# img = image.Image(pattern) +# assign_constants(img) + +def assign_constants(obj): + obj.BOAT = image.MicrobitImage(CONSTANTS.BOAT) + +display = microbit_model.mb.display +Image = image.Image + + +# define "constants" here +# Image.BOAT = image.Image(CONSTANTS.BOAT) + diff --git a/src/microbit/constants.py b/src/microbit/constants.py new file mode 100644 index 000000000..9ac1e2858 --- /dev/null +++ b/src/microbit/constants.py @@ -0,0 +1,5 @@ +BOAT = ("05050:","05050:","05050:","99999:","09990") + +BLANK= "00000:00000:00000:00000:00000" + +COPY_ERR_MESSAGE = "please copy() first" \ No newline at end of file diff --git a/src/microbit/display.py b/src/microbit/display.py index 699c4397b..6ce2043bd 100644 --- a/src/microbit/display.py +++ b/src/microbit/display.py @@ -1,6 +1,11 @@ # class for microbit led display class Display: + def __init__(self): + # State in the Python process + self.count = 4 + # SAMPLE FUNCTION def scroll(self, message): - print("scroll!! " + message) \ No newline at end of file + print("scroll!! " + str(self.count)) + self.count = self.count + 1 \ No newline at end of file diff --git a/src/microbit/image.py b/src/microbit/image.py new file mode 100644 index 000000000..f9b1d727c --- /dev/null +++ b/src/microbit/image.py @@ -0,0 +1,52 @@ +from . import microbit_model +from . import constants as CONSTANTS +from . import display + + +class Image: + _BOAT = None + def __init__(self, pattern = CONSTANTS.BLANK, width=5,height=5): + # State in the Python process + self.width = width + self.height = height + if type(pattern) is str: + self.LED = self.convert_to_array(pattern) + else: + self.LED = pattern + + + def convert_to_array(self, pattern): + arr = [] + sub_str = "" + for elem in pattern: + sub_str= sub_str + elem + if elem == ":": + arr.append(sub_str) + sub_str = "" + + + arr.append(sub_str) + return arr + + + def set_pixel(self,x,y,value): + + sub_arr = self.LED[y] + + new_list = list(sub_arr) + new_list[x] = value + + self.LED[y] = "".join(new_list) + + + def get_pixel(self,x,y): + return self.LED[y][x] + + def copy(self): + return Image(list(self.LED)) + + def getvalue(self): + self._BOAT = Image(CONSTANTS.BOAT) + return self._BOAT + + BOAT = property(getvalue) diff --git a/src/test_code/code.py b/src/test_code/code.py index 369561cc5..15f94fb04 100644 --- a/src/test_code/code.py +++ b/src/test_code/code.py @@ -1,19 +1,19 @@ -from adafruit_circuitplayground.express import cpx -import time - -cpx.pixels.brightness = 0.3 -cpx.pixels.fill((0, 0, 0)) # Turn off the NeoPixels if they're on! -cpx.pixels.show() - -while True: - if cpx.button_a: - cpx.pixels[2] = (0, 255, 0) - else: - cpx.pixels[2] = (0, 0, 0) - - if cpx.button_b: - cpx.pixels[7] = (0, 0, 255) - else: - cpx.pixels[7] = (0, 0, 0) - cpx.pixels.show() - +# from adafruit_circuitplayground.express import cpx +# import time + +# cpx.pixels.brightness = 0.3 +# cpx.pixels.fill((0, 0, 0)) # Turn off the NeoPixels if they're on! +# cpx.pixels.show() + +# while True: +# if cpx.button_a: +# cpx.pixels[2] = (0, 255, 0) +# else: +# cpx.pixels[2] = (0, 0, 0) + +# if cpx.button_b: +# cpx.pixels[7] = (0, 0, 255) +# else: +# cpx.pixels[7] = (0, 0, 0) +# cpx.pixels.show() + From 8ed9e5b9db4a19e11415ab36c2e506762f2e8a0b Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 28 Jan 2020 14:02:42 -0800 Subject: [PATCH 03/27] initial look at image class --- src/microbit/code_processing_shim.py | 11 +---- src/microbit/constants.py | 18 ++++++-- src/microbit/image.py | 61 +++++++++++++++++----------- 3 files changed, 54 insertions(+), 36 deletions(-) diff --git a/src/microbit/code_processing_shim.py b/src/microbit/code_processing_shim.py index c4b5d6146..eb53e10ab 100644 --- a/src/microbit/code_processing_shim.py +++ b/src/microbit/code_processing_shim.py @@ -7,17 +7,10 @@ def show_message(message): microbit_model.mb.show_message(message) -# def Image(pattern = CONSTANTS.BLANK): -# img = image.Image(pattern) -# assign_constants(img) - -def assign_constants(obj): - obj.BOAT = image.MicrobitImage(CONSTANTS.BOAT) display = microbit_model.mb.display -Image = image.Image +microbit = microbit_model.mb +Image = image.Image -# define "constants" here -# Image.BOAT = image.Image(CONSTANTS.BOAT) diff --git a/src/microbit/constants.py b/src/microbit/constants.py index 9ac1e2858..71d5b297b 100644 --- a/src/microbit/constants.py +++ b/src/microbit/constants.py @@ -1,5 +1,17 @@ -BOAT = ("05050:","05050:","05050:","99999:","09990") -BLANK= "00000:00000:00000:00000:00000" +BOAT = ([0, 5, 0, 5, 0], + [0, 5, 0, 5, 0], + [0, 5, 0, 5, 0], + [9, 9, 9, 9, 9], + [0, 9, 9, 9, 0]) -COPY_ERR_MESSAGE = "please copy() first" \ No newline at end of file +BLANK= [[0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0]] + + +COPY_ERR_MESSAGE = "please copy() first" + +LED_MAX = 5 \ No newline at end of file diff --git a/src/microbit/image.py b/src/microbit/image.py index f9b1d727c..58a670af7 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -2,13 +2,9 @@ from . import constants as CONSTANTS from . import display - class Image: - _BOAT = None - def __init__(self, pattern = CONSTANTS.BLANK, width=5,height=5): + def __init__(self, pattern = CONSTANTS.BLANK): # State in the Python process - self.width = width - self.height = height if type(pattern) is str: self.LED = self.convert_to_array(pattern) else: @@ -17,26 +13,21 @@ def __init__(self, pattern = CONSTANTS.BLANK, width=5,height=5): def convert_to_array(self, pattern): arr = [] - sub_str = "" - for elem in pattern: - sub_str= sub_str + elem + sub_arr = [] + for elem in pattern: + sub_arr.append(elem) if elem == ":": - arr.append(sub_str) - sub_str = "" - - - arr.append(sub_str) + arr.append(sub_arr) + sub_arr = [] + arr.append(sub_arr) return arr def set_pixel(self,x,y,value): - - sub_arr = self.LED[y] - - new_list = list(sub_arr) - new_list[x] = value - - self.LED[y] = "".join(new_list) + try: + self.LED[y][x] = value + except TypeError: + print(CONSTANTS.COPY_ERR_MESSAGE) def get_pixel(self,x,y): @@ -45,8 +36,30 @@ def get_pixel(self,x,y): def copy(self): return Image(list(self.LED)) - def getvalue(self): - self._BOAT = Image(CONSTANTS.BOAT) - return self._BOAT + def fill(self,value): + for y in range(0,self.height): + for x in range(0,self.width): + self.LED[y][x] = value - BOAT = property(getvalue) + @property + def width(self): + if len(self.LED): + return len(self.LED[0]) + else: + return 0 + + @width.setter + def width(self): + # will name exception later + raise Exception + + @property + def height(self): + return len(self.LED) + + @height.setter + def height(self): + # will name exception later + raise Exception + + From 32060ceb9c443613bd8e67ff1207de6983ed3889 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 28 Jan 2020 14:05:14 -0800 Subject: [PATCH 04/27] fixes to image --- src/microbit/image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/microbit/image.py b/src/microbit/image.py index 58a670af7..15ad3e6a8 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -34,8 +34,8 @@ def get_pixel(self,x,y): return self.LED[y][x] def copy(self): - return Image(list(self.LED)) - + return Image(self.LED) + def fill(self,value): for y in range(0,self.height): for x in range(0,self.width): From ef6498cf02e11eb90cf0f9ab018a9e6bca5af2a0 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 28 Jan 2020 15:56:15 -0800 Subject: [PATCH 05/27] finished first draft of most image methods --- src/microbit/code_processing_shim.py | 8 +++ src/microbit/constants.py | 6 ++ src/microbit/image.py | 101 +++++++++++++++++++++++++-- 3 files changed, 109 insertions(+), 6 deletions(-) diff --git a/src/microbit/code_processing_shim.py b/src/microbit/code_processing_shim.py index eb53e10ab..6ba6366a7 100644 --- a/src/microbit/code_processing_shim.py +++ b/src/microbit/code_processing_shim.py @@ -13,4 +13,12 @@ def show_message(message): microbit = microbit_model.mb Image = image.Image +def repr(image): + + ret_str = "Image(\'" + for index_y in range(0,image.height): + ret_str += image.row_to_str(index_y) + + ret_str = ret_str + "\')" + return ret_str \ No newline at end of file diff --git a/src/microbit/constants.py b/src/microbit/constants.py index 71d5b297b..a156233f0 100644 --- a/src/microbit/constants.py +++ b/src/microbit/constants.py @@ -5,6 +5,12 @@ [9, 9, 9, 9, 9], [0, 9, 9, 9, 0]) +HEART = [[0, 9, 0, 9, 0], + [9, 9, 9, 9, 9], + [9, 9, 9, 9, 9], + [0, 9, 9, 9, 0], + [0, 0, 9, 0, 0]] + BLANK= [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], diff --git a/src/microbit/image.py b/src/microbit/image.py index 15ad3e6a8..95e62fd9f 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -3,14 +3,25 @@ from . import display class Image: - def __init__(self, pattern = CONSTANTS.BLANK): + def __init__(self, *args, **kwargs): + print("args") + print(args) # State in the Python process - if type(pattern) is str: - self.LED = self.convert_to_array(pattern) + if (len(args)==0): + self.LED = CONSTANTS.BLANK + elif (len(args)==1): + pattern = args[0] + if type(pattern) is str: + self.LED = self.convert_to_array(pattern) + else: + self.LED = pattern else: - self.LED = pattern + width = args[0] + height = args[1] + self.LED = self.create_leds(width,height) + + - def convert_to_array(self, pattern): arr = [] sub_arr = [] @@ -22,6 +33,17 @@ def convert_to_array(self, pattern): arr.append(sub_arr) return arr + def create_leds(self, w, h): + arr = [] + for _ in range(0,h): + sub_arr = [] + for _ in range(0,w): + sub_arr.append(0) + + arr.append(sub_arr) + + return arr + def set_pixel(self,x,y,value): try: @@ -36,10 +58,16 @@ def get_pixel(self,x,y): def copy(self): return Image(self.LED) + def invert(self,value): + for y in range(0,self.height): + for x in range(0,self.width): + self.set_pixel(x, y, 9-value) + + def fill(self,value): for y in range(0,self.height): for x in range(0,self.width): - self.LED[y][x] = value + self.set_pixel(x, y, value) @property def width(self): @@ -62,4 +90,65 @@ def height(self): # will name exception later raise Exception + def blit(self, src, x, y, w, h, xdest=0, ydest=0): + for count_y in range(0, h): + for count_x in range(0, w): + if (ydest + count_y < self.height and + xdest + count_x < self.width and + y + count_y < src.height and + x + count_x < src.width): + transfer_pixel = src.get_pixel(x + count_x, y + count_y) + self.set_pixel(xdest + count_x, ydest + count_y, transfer_pixel) + + def crop(self, x, y, w, h): + res = Image(w, h) + res.blit(self, x, y, w, h) + return res + + def shift_vertical(self,n): + + res = Image(self.width, self.height) + if n > 0: + # up + res.blit(self, 0, n, self.width, self.height-n, 0, 0) + else: + # down + res.blit(self, 0, 0, self.width, self.height-abs(n), 0, abs(n)) + + return res + + def shift_horizontal(self,n): + res = Image(self.width, self.height) + if n > 0: + # right + res.blit(self, 0, 0, self.width-n, self.height, n, 0) + else: + # left + res.blit(self, n, 0, self.width-n, self.height, 0, 0) + + return res + + + def shift_up(self,n): + return self.shift_vertical(n) + + def shift_down(self,n): + return self.shift_vertical(n*-1) + + def shift_right(self,n): + return self.shift_horizontal(n) + + def shift_left(self,n): + return self.shift_horizontal(n*-1) + + + def row_to_str(self, y): + new_str = "" + for x in range(0,self.width): + new_str = new_str + str(self.get_pixel(x,y)) + + new_str = new_str + ":" + + return new_str + From d2a76721b7b647fb1d13ed1988615279c9350aaf Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 28 Jan 2020 16:00:38 -0800 Subject: [PATCH 06/27] width and height modifications --- src/microbit/code_processing_shim.py | 2 +- src/microbit/image.py | 43 ++++++++++------------------ 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/microbit/code_processing_shim.py b/src/microbit/code_processing_shim.py index 6ba6366a7..d4a5c1115 100644 --- a/src/microbit/code_processing_shim.py +++ b/src/microbit/code_processing_shim.py @@ -16,7 +16,7 @@ def show_message(message): def repr(image): ret_str = "Image(\'" - for index_y in range(0,image.height): + for index_y in range(0,image.height()): ret_str += image.row_to_str(index_y) ret_str = ret_str + "\')" diff --git a/src/microbit/image.py b/src/microbit/image.py index 95e62fd9f..f24248b6a 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -59,44 +59,32 @@ def copy(self): return Image(self.LED) def invert(self,value): - for y in range(0,self.height): - for x in range(0,self.width): + for y in range(0,self.height()): + for x in range(0,self.width()): self.set_pixel(x, y, 9-value) def fill(self,value): - for y in range(0,self.height): - for x in range(0,self.width): + for y in range(0,self.height()): + for x in range(0,self.width()): self.set_pixel(x, y, value) - @property def width(self): if len(self.LED): return len(self.LED[0]) else: return 0 - @width.setter - def width(self): - # will name exception later - raise Exception - - @property def height(self): return len(self.LED) - - @height.setter - def height(self): - # will name exception later - raise Exception def blit(self, src, x, y, w, h, xdest=0, ydest=0): for count_y in range(0, h): for count_x in range(0, w): - if (ydest + count_y < self.height and - xdest + count_x < self.width and - y + count_y < src.height and - x + count_x < src.width): + if (ydest + count_y < self.height() and + xdest + count_x < self.width() and + y + count_y < src.height() and + x + count_x < src.width()): transfer_pixel = src.get_pixel(x + count_x, y + count_y) self.set_pixel(xdest + count_x, ydest + count_y, transfer_pixel) @@ -107,25 +95,25 @@ def crop(self, x, y, w, h): def shift_vertical(self,n): - res = Image(self.width, self.height) + res = Image(self.width(), self.height()) if n > 0: # up - res.blit(self, 0, n, self.width, self.height-n, 0, 0) + res.blit(self, 0, n, self.width(), self.height()-n, 0, 0) else: # down - res.blit(self, 0, 0, self.width, self.height-abs(n), 0, abs(n)) + res.blit(self, 0, 0, self.width(), self.height()-abs(n), 0, abs(n)) return res def shift_horizontal(self,n): - res = Image(self.width, self.height) + res = Image(self.width(), self.height()) if n > 0: # right - res.blit(self, 0, 0, self.width-n, self.height, n, 0) + res.blit(self, 0, 0, self.width()-n, self.height(), n, 0) else: # left - res.blit(self, n, 0, self.width-n, self.height, 0, 0) + res.blit(self, n, 0, self.width()-n, self.height(), 0, 0) return res @@ -145,10 +133,9 @@ def shift_left(self,n): def row_to_str(self, y): new_str = "" - for x in range(0,self.width): + for x in range(0,self.width()): new_str = new_str + str(self.get_pixel(x,y)) new_str = new_str + ":" return new_str - From 16beee34091b58bf23a1756de1e81d6cc6b40e40 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 28 Jan 2020 17:36:04 -0800 Subject: [PATCH 07/27] more additions to image --- src/microbit/code_processing_shim.py | 16 ++++++- src/microbit/image.py | 66 +++++++++++++++++++++++----- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/microbit/code_processing_shim.py b/src/microbit/code_processing_shim.py index d4a5c1115..231f5e9b3 100644 --- a/src/microbit/code_processing_shim.py +++ b/src/microbit/code_processing_shim.py @@ -21,4 +21,18 @@ def repr(image): ret_str = ret_str + "\')" - return ret_str \ No newline at end of file + return ret_str + + +def str(image): + if type(image) is Image: + ret_str = "Image(\'\n" + for index_y in range(0,image.height()): + ret_str += "\t" + image.row_to_str(index_y) + "\n" + + ret_str = ret_str + "\')" + + return ret_str + else: + # if not image, call regular str class + return image.__str__() \ No newline at end of file diff --git a/src/microbit/image.py b/src/microbit/image.py index f24248b6a..5eee4c3c9 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -8,17 +8,22 @@ def __init__(self, *args, **kwargs): print(args) # State in the Python process if (len(args)==0): - self.LED = CONSTANTS.BLANK + self.__LED = CONSTANTS.BLANK elif (len(args)==1): pattern = args[0] if type(pattern) is str: - self.LED = self.convert_to_array(pattern) + self.__LED = self.convert_to_array(pattern) else: - self.LED = pattern + self.__LED = pattern else: + width = args[0] height = args[1] - self.LED = self.create_leds(width,height) + + if (width < 0 or height < 0): + raise Exception + + self.__LED = self.create_leds(width,height) @@ -26,11 +31,11 @@ def convert_to_array(self, pattern): arr = [] sub_arr = [] for elem in pattern: - sub_arr.append(elem) if elem == ":": arr.append(sub_arr) sub_arr = [] - arr.append(sub_arr) + else: + sub_arr.append(int(elem)) return arr def create_leds(self, w, h): @@ -47,16 +52,16 @@ def create_leds(self, w, h): def set_pixel(self,x,y,value): try: - self.LED[y][x] = value + self.__LED[y][x] = value except TypeError: print(CONSTANTS.COPY_ERR_MESSAGE) def get_pixel(self,x,y): - return self.LED[y][x] + return self.__LED[y][x] def copy(self): - return Image(self.LED) + return Image(self.__LED) def invert(self,value): for y in range(0,self.height()): @@ -70,13 +75,13 @@ def fill(self,value): self.set_pixel(x, y, value) def width(self): - if len(self.LED): - return len(self.LED[0]) + if len(self.__LED): + return len(self.__LED[0]) else: return 0 def height(self): - return len(self.LED) + return len(self.__LED) def blit(self, src, x, y, w, h, xdest=0, ydest=0): for count_y in range(0, h): @@ -139,3 +144,40 @@ def row_to_str(self, y): new_str = new_str + ":" return new_str + + + def limit_result(self,limit,result): + if (result > limit): + return limit + else: + return result + + def __add__(self, other): + if not (type(other) is Image): + raise TypeError(f"unsupported types for __add__: '{type(self)}', '{type(other)}'") + elif not (other.height() == self.height() and + other.width() == self.width()): + raise ValueError("images must be the same size") + else: + res = Image(self.width(), self.height()) + + for y in range(0,self.height()): + for x in range(0,self.width()): + sum = other.get_pixel(x,y) + self.get_pixel(x,y) + display_result = self.limit_result(9, sum) + res.set_pixel(x, y, display_result) + + return res + + + def __mul__(self, other): + float_val = float(other) + res = Image(self.width(), self.height()) + + for y in range(0,self.height()): + for x in range(0,self.width()): + product = self.get_pixel(x,y) * float_val + res.set_pixel(x, y, self.limit_result(9, product)) + + return res + \ No newline at end of file From 52ba07ebb21dc650c9bcbbc76ca791f23904d87b Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Tue, 28 Jan 2020 17:37:38 -0800 Subject: [PATCH 08/27] display microbit library --- src/microbit/constants.py | 15 ++- src/microbit/display.py | 76 +++++++++++++-- src/microbit/image.py | 147 ++++++++++++++++++++++++------ src/microbit/test/__init__.py | 0 src/microbit/test/test_display.py | 76 +++++++++++++++ 5 files changed, 276 insertions(+), 38 deletions(-) create mode 100644 src/microbit/test/__init__.py create mode 100644 src/microbit/test/test_display.py diff --git a/src/microbit/constants.py b/src/microbit/constants.py index 9ac1e2858..c64e085ee 100644 --- a/src/microbit/constants.py +++ b/src/microbit/constants.py @@ -1,5 +1,14 @@ -BOAT = ("05050:","05050:","05050:","99999:","09990") +BOAT = ("05050:", "05050:", "05050:", "99999:", "09990") -BLANK= "00000:00000:00000:00000:00000" +BLANK = "00000:00000:00000:00000:00000" -COPY_ERR_MESSAGE = "please copy() first" \ No newline at end of file +COPY_ERR_MESSAGE = "please copy() first" + +INDEX_ERR = "index out of bounds" + +BRIGHTNESS_ERR = "brightness out of bounds" + +LED_WIDTH = 5 +LED_HEIGHT = 5 + +NOT_IMPLEMENTED_ERROR = "This method is not implemented by the simulator" diff --git a/src/microbit/display.py b/src/microbit/display.py index 6ce2043bd..ec45ee2f1 100644 --- a/src/microbit/display.py +++ b/src/microbit/display.py @@ -1,11 +1,75 @@ -# class for microbit led display -class Display: +from . import constants as CONSTANTS +from .image import Image + +class Display: def __init__(self): # State in the Python process - self.count = 4 + self.__LEDs = [[0] * 5] * 5 + self.__on = True - # SAMPLE FUNCTION def scroll(self, message): - print("scroll!! " + str(self.count)) - self.count = self.count + 1 \ No newline at end of file + raise NotImplementedError(CONSTANTS.NOT_IMPLEMENTED_ERROR) + + def show(self, value, delay=400, wait=True, loop=False, clear=False): + if isinstance(value, Image): + width = ( + value.width() + if value.width() <= CONSTANTS.LED_WIDTH + else CONSTANTS.LED_WIDTH + ) + height = ( + value.height() + if value.height() <= CONSTANTS.LED_HEIGHT + else CONSTANTS.LED_HEIGHT + ) + self.__LEDs = value.LED.copy() + self.__print() + elif isinstance(value, str): + pass + elif isinstance(value, float): + pass + elif isinstance(value, int): + pass + + def get_pixel(self, x, y): + if self.__valid_pos(x, y): + return self.__LEDs[y][x] + else: + raise ValueError(CONSTANTS.INDEX_ERR) + + def set_pixel(self, x, y, value): + if not self.__valid_pos(x, y): + raise ValueError(CONSTANTS.INDEX_ERR) + elif not self.__valid_brightness(value): + raise ValueError(CONSTANTS.BRIGHTNESS_ERR) + else: + self.__LEDs[y][x] = value + + def clear(self): + for y in range(CONSTANTS.LED_WIDTH): + for x in range(CONSTANTS.LED_HEIGHT): + self.__LEDs[y][x] = 0 + + 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 __valid_pos(self, x, y): + return 0 <= x and x <= 4 and 0 <= y and y <= 4 + + def __valid_brightness(self, value): + return 0 <= value and value <= 9 + + def __print(self): + for i in range(5): + print(self.__LEDs[i]) diff --git a/src/microbit/image.py b/src/microbit/image.py index f9b1d727c..e9fcf4e40 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -2,51 +2,140 @@ from . import constants as CONSTANTS from . import display - class Image: - _BOAT = None - def __init__(self, pattern = CONSTANTS.BLANK, width=5,height=5): + def __init__(self, *args, **kwargs): + print("args") + print(args) # State in the Python process - self.width = width - self.height = height - if type(pattern) is str: - self.LED = self.convert_to_array(pattern) + if (len(args)==0): + self.LED = CONSTANTS.BLANK + elif (len(args)==1): + pattern = args[0] + if type(pattern) is str: + self.LED = self.convert_to_array(pattern) + else: + self.LED = pattern else: - self.LED = pattern + width = args[0] + height = args[1] + self.LED = self.create_leds(width,height) + + - def convert_to_array(self, pattern): arr = [] - sub_str = "" - for elem in pattern: - sub_str= sub_str + elem + sub_arr = [] + for elem in pattern: + sub_arr.append(elem) if elem == ":": - arr.append(sub_str) - sub_str = "" - - - arr.append(sub_str) + arr.append(sub_arr) + sub_arr = [] + arr.append(sub_arr) return arr + def create_leds(self, w, h): + arr = [] + for _ in range(0,h): + sub_arr = [] + for _ in range(0,w): + sub_arr.append(0) - def set_pixel(self,x,y,value): + arr.append(sub_arr) + + return arr - sub_arr = self.LED[y] - new_list = list(sub_arr) - new_list[x] = value + def set_pixel(self,x,y,value): + try: + self.LED[y][x] = value + except TypeError: + print(CONSTANTS.COPY_ERR_MESSAGE) - self.LED[y] = "".join(new_list) - def get_pixel(self,x,y): return self.LED[y][x] - + def copy(self): - return Image(list(self.LED)) + return Image(self.LED) + + def invert(self,value): + for y in range(0,self.height()): + for x in range(0,self.width()): + self.set_pixel(x, y, 9-value) + + + def fill(self,value): + for y in range(0,self.height()): + for x in range(0,self.width()): + self.set_pixel(x, y, value) + + def width(self): + if len(self.LED): + return len(self.LED[0]) + else: + return 0 + + def height(self): + return len(self.LED) + + def blit(self, src, x, y, w, h, xdest=0, ydest=0): + for count_y in range(0, h): + for count_x in range(0, w): + if (ydest + count_y < self.height() and + xdest + count_x < self.width() and + y + count_y < src.height() and + x + count_x < src.width()): + transfer_pixel = src.get_pixel(x + count_x, y + count_y) + self.set_pixel(xdest + count_x, ydest + count_y, transfer_pixel) + + def crop(self, x, y, w, h): + res = Image(w, h) + res.blit(self, x, y, w, h) + return res + + def shift_vertical(self,n): + + res = Image(self.width(), self.height()) + if n > 0: + # up + res.blit(self, 0, n, self.width(), self.height()-n, 0, 0) + else: + # down + res.blit(self, 0, 0, self.width(), self.height()-abs(n), 0, abs(n)) + + return res + + + def shift_horizontal(self,n): + res = Image(self.width(), self.height()) + if n > 0: + # right + res.blit(self, 0, 0, self.width()-n, self.height(), n, 0) + else: + # left + res.blit(self, n, 0, self.width()-n, self.height(), 0, 0) + + return res + + + def shift_up(self,n): + return self.shift_vertical(n) + + def shift_down(self,n): + return self.shift_vertical(n*-1) + + def shift_right(self,n): + return self.shift_horizontal(n) + + def shift_left(self,n): + return self.shift_horizontal(n*-1) + + + def row_to_str(self, y): + new_str = "" + for x in range(0,self.width()): + new_str = new_str + str(self.get_pixel(x,y)) - def getvalue(self): - self._BOAT = Image(CONSTANTS.BOAT) - return self._BOAT + new_str = new_str + ":" - BOAT = property(getvalue) + return new_str \ No newline at end of file diff --git a/src/microbit/test/__init__.py b/src/microbit/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/microbit/test/test_display.py b/src/microbit/test/test_display.py new file mode 100644 index 000000000..582347bf0 --- /dev/null +++ b/src/microbit/test/test_display.py @@ -0,0 +1,76 @@ +import pytest + +from .. import constants as CONSTANTS +from ..display import Display +from ..image import Image + + +class TestDisplay(object): + def setup_method(self): + self.display = Display() + + @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) + def test_get_pixel(self, x, y, brightness): + self.display._Display__LEDs[y][x] = brightness + assert brightness == self.display.get_pixel(x, y) + + @pytest.mark.parametrize("x, y", [(5, 0), (0, -1), (0, 5)]) + def test_get_pixel_error(self, x, y): + with pytest.raises(ValueError, match=CONSTANTS.INDEX_ERR): + self.display.get_pixel(x, y) + + @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) + def test_set_pixel(self, x, y, brightness): + self.display.set_pixel(x, y, brightness) + assert brightness == self.display._Display__LEDs[y][x] + + @pytest.mark.parametrize( + "x, y, brightness, err_msg", + [ + (5, 0, 0, CONSTANTS.INDEX_ERR), + (0, -1, 0, CONSTANTS.INDEX_ERR), + (0, 0, -1, CONSTANTS.BRIGHTNESS_ERR), + ], + ) + def test_set_pixel_error(self, x, y, brightness, err_msg): + with pytest.raises(ValueError, match=err_msg): + self.display.set_pixel(x, y, brightness) + + def test_clear(self): + self.display._Display__LEDs[0][0] = 7 + self.display._Display__LEDs[3][4] = 6 + self.display._Display__LEDs[4][4] = 9 + assert not self.__is_clear() + self.display.clear() + assert self.__is_clear() + + def test_on(self): + self.display._Display__on = False + self.display.on() + assert self.display._Display__on + + def test_off(self): + self.display._Display__on = True + self.display.off() + assert False == self.display._Display__on + + @pytest.mark.parametrize("on", [True, False]) + def test_is_on(self, on): + self.display._Display__on = on + assert on == self.display.is_on() + + # Helpers + def __is_clear(self): + for y in range(CONSTANTS.LED_WIDTH): + for x in range(CONSTANTS.LED_HEIGHT): + if 0 != self.display._Display__LEDs[y][x]: + return False + return True + + def test_use_me(self): + img = Image(5, 5) + img.set_pixel(0, 0, 8) + img.set_pixel(0, 1, 9) + img.set_pixel(0, 2, 7) + img.set_pixel(2, 2, 6) + self.display.show(img) From cc0ac13660bff7e1556fa311db36bfe3060e1303 Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Tue, 28 Jan 2020 17:41:44 -0800 Subject: [PATCH 09/27] update dusplay --- src/microbit/display.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/microbit/display.py b/src/microbit/display.py index ec45ee2f1..85bfd437e 100644 --- a/src/microbit/display.py +++ b/src/microbit/display.py @@ -23,7 +23,6 @@ def show(self, value, delay=400, wait=True, loop=False, clear=False): if value.height() <= CONSTANTS.LED_HEIGHT else CONSTANTS.LED_HEIGHT ) - self.__LEDs = value.LED.copy() self.__print() elif isinstance(value, str): pass From c761e8b209c3f6a6626c4a91c365d0c1028e72e9 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 29 Jan 2020 10:29:49 -0800 Subject: [PATCH 10/27] integrated image into display class --- src/microbit/constants.py | 1 - src/microbit/display.py | 17 +++-------------- src/microbit/image.py | 34 +++++++++++++++++++++------------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/microbit/constants.py b/src/microbit/constants.py index f3a1f20bb..ba0bd789f 100644 --- a/src/microbit/constants.py +++ b/src/microbit/constants.py @@ -1,5 +1,4 @@ INDEX_ERR = "index out of bounds" - BRIGHTNESS_ERR = "brightness out of bounds" LED_WIDTH = 5 diff --git a/src/microbit/display.py b/src/microbit/display.py index 85bfd437e..250e4de50 100644 --- a/src/microbit/display.py +++ b/src/microbit/display.py @@ -5,7 +5,7 @@ class Display: def __init__(self): # State in the Python process - self.__LEDs = [[0] * 5] * 5 + self.__image = Image() self.__on = True def scroll(self, message): @@ -32,18 +32,10 @@ def show(self, value, delay=400, wait=True, loop=False, clear=False): pass def get_pixel(self, x, y): - if self.__valid_pos(x, y): - return self.__LEDs[y][x] - else: - raise ValueError(CONSTANTS.INDEX_ERR) + return self.__image.get_pixel(x,y) def set_pixel(self, x, y, value): - if not self.__valid_pos(x, y): - raise ValueError(CONSTANTS.INDEX_ERR) - elif not self.__valid_brightness(value): - raise ValueError(CONSTANTS.BRIGHTNESS_ERR) - else: - self.__LEDs[y][x] = value + self.__image.set_pixel(x, y, value) def clear(self): for y in range(CONSTANTS.LED_WIDTH): @@ -66,9 +58,6 @@ def read_light_level(self): def __valid_pos(self, x, y): return 0 <= x and x <= 4 and 0 <= y and y <= 4 - def __valid_brightness(self, value): - return 0 <= value and value <= 9 - def __print(self): for i in range(5): print(self.__LEDs[i]) diff --git a/src/microbit/image.py b/src/microbit/image.py index dd99f2198..cc906ace3 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -24,7 +24,7 @@ def __init__(self, *args, **kwargs): if width < 0 or height < 0: raise Exception - self.__LED = self.create_leds(width, height) + self.__LED = [[0] * width] * height def convert_to_array(self, pattern): arr = [] @@ -37,25 +37,24 @@ def convert_to_array(self, pattern): sub_arr.append(int(elem)) return arr - def create_leds(self, w, h): - arr = [] - for _ in range(0, h): - sub_arr = [] - for _ in range(0, w): - sub_arr.append(0) - - arr.append(sub_arr) - - return arr def set_pixel(self, x, y, value): try: - self.__LED[y][x] = value + if not self.__valid_pos(x, y): + raise ValueError(CONSTANTS.INDEX_ERR) + elif not self.__valid_brightness(value): + raise ValueError(CONSTANTS.BRIGHTNESS_ERR) + else: + self.__LED[y][x] = value except TypeError: print(CONSTANTS.COPY_ERR_MESSAGE) def get_pixel(self, x, y): - return self.__LED[y][x] + if self.__valid_pos(x,y): + return self.__LED[y][x] + else: + raise ValueError(CONSTANTS.INDEX_ERR) + def copy(self): return Image(self.__LED) @@ -96,6 +95,10 @@ def crop(self, x, y, w, h): res.blit(self, x, y, w, h) return res + + def __valid_pos(self, x, y): + return 0 <= x and x < self.width() and 0 <= y and y < self.height() + def shift_vertical(self, n): res = Image(self.width(), self.height()) @@ -174,3 +177,8 @@ def __mul__(self, other): res.set_pixel(x, y, self.limit_result(9, product)) return res + + def __valid_brightness(self, value): + return 0 <= value and value <= 9 + + \ No newline at end of file From 4cbda2bded2becb9b58de91d36a19034c40b9760 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 29 Jan 2020 11:01:49 -0800 Subject: [PATCH 11/27] fixed LED array reference issue --- src/microbit/code_processing_shim.py | 18 ++- src/microbit/constants.py | 3 +- src/microbit/image.py | 184 +++++++++++++-------------- 3 files changed, 106 insertions(+), 99 deletions(-) diff --git a/src/microbit/code_processing_shim.py b/src/microbit/code_processing_shim.py index 231f5e9b3..8479d5a18 100644 --- a/src/microbit/code_processing_shim.py +++ b/src/microbit/code_processing_shim.py @@ -17,7 +17,7 @@ def repr(image): ret_str = "Image(\'" for index_y in range(0,image.height()): - ret_str += image.row_to_str(index_y) + ret_str += row_to_str(image, index_y) ret_str = ret_str + "\')" @@ -28,11 +28,23 @@ def str(image): if type(image) is Image: ret_str = "Image(\'\n" for index_y in range(0,image.height()): - ret_str += "\t" + image.row_to_str(index_y) + "\n" + ret_str += "\t" + row_to_str(image,index_y) + "\n" ret_str = ret_str + "\')" return ret_str else: # if not image, call regular str class - return image.__str__() \ No newline at end of file + return image.__str__() + + + +# method to help with string formation +def row_to_str(image, y): + new_str = "" + for x in range(0, image.width()): + new_str = new_str + str(image.get_pixel(x, y)) + + new_str = new_str + ":" + + return new_str \ No newline at end of file diff --git a/src/microbit/constants.py b/src/microbit/constants.py index ba0bd789f..61705fbef 100644 --- a/src/microbit/constants.py +++ b/src/microbit/constants.py @@ -1,6 +1,7 @@ INDEX_ERR = "index out of bounds" BRIGHTNESS_ERR = "brightness out of bounds" - +SAME_SIZE_ERR = "images must be the same size" +UNSUPPORTED_ADD_TYPE = "unsupported types for __add__:" LED_WIDTH = 5 LED_HEIGHT = 5 diff --git a/src/microbit/image.py b/src/microbit/image.py index cc906ace3..3788ecc5b 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -5,15 +5,12 @@ class Image: def __init__(self, *args, **kwargs): - print("args") - print(args) - # State in the Python process if len(args) == 0: self.__LED = CONSTANTS.BLANK elif len(args) == 1: pattern = args[0] if type(pattern) is str: - self.__LED = self.convert_to_array(pattern) + self.__LED = self.__string_to_array(pattern) else: self.__LED = pattern else: @@ -22,21 +19,20 @@ def __init__(self, *args, **kwargs): height = args[1] if width < 0 or height < 0: - raise Exception + # not in original, but ideally, + # image should fail non-silently + raise ValueError(CONSTANTS.INDEX_ERR) - self.__LED = [[0] * width] * height + self.__LED = self.__create_leds(width,height) - def convert_to_array(self, pattern): - arr = [] - sub_arr = [] - for elem in pattern: - if elem == ":": - arr.append(sub_arr) - sub_arr = [] - else: - sub_arr.append(int(elem)) - return arr + def width(self): + if len(self.__LED): + return len(self.__LED[0]) + else: + return 0 + def height(self): + return len(self.__LED) def set_pixel(self, x, y, value): try: @@ -55,6 +51,22 @@ def get_pixel(self, x, y): else: raise ValueError(CONSTANTS.INDEX_ERR) + def shift_up(self, n): + return self.__shift_vertical(n) + + def shift_down(self, n): + return self.__shift_vertical(n * -1) + + def shift_right(self, n): + return self.__shift_horizontal(n) + + def shift_left(self, n): + return self.__shift_horizontal(n * -1) + + def crop(self, x, y, w, h): + res = Image(w, h) + res.blit(self, x, y, w, h) + return res def copy(self): return Image(self.__LED) @@ -69,100 +81,29 @@ def fill(self, value): for x in range(0, self.width()): self.set_pixel(x, y, value) - def width(self): - if len(self.__LED): - return len(self.__LED[0]) - else: - return 0 - - def height(self): - return len(self.__LED) def blit(self, src, x, y, w, h, xdest=0, ydest=0): for count_y in range(0, h): for count_x in range(0, w): - if ( - ydest + count_y < self.height() - and xdest + count_x < self.width() - and y + count_y < src.height() - and x + count_x < src.width() - ): + if (self.__valid_pos(xdest + count_x, ydest + count_y) and + src.__valid_pos(x + count_x, y + count_y)): transfer_pixel = src.get_pixel(x + count_x, y + count_y) self.set_pixel(xdest + count_x, ydest + count_y, transfer_pixel) - def crop(self, x, y, w, h): - res = Image(w, h) - res.blit(self, x, y, w, h) - return res - - - def __valid_pos(self, x, y): - return 0 <= x and x < self.width() and 0 <= y and y < self.height() - - def shift_vertical(self, n): - - res = Image(self.width(), self.height()) - if n > 0: - # up - res.blit(self, 0, n, self.width(), self.height() - n, 0, 0) - else: - # down - res.blit(self, 0, 0, self.width(), self.height() - abs(n), 0, abs(n)) - - return res - - def shift_horizontal(self, n): - res = Image(self.width(), self.height()) - if n > 0: - # right - res.blit(self, 0, 0, self.width() - n, self.height(), n, 0) - else: - # left - res.blit(self, n, 0, self.width() - n, self.height(), 0, 0) - - return res - - def shift_up(self, n): - return self.shift_vertical(n) - - def shift_down(self, n): - return self.shift_vertical(n * -1) - - def shift_right(self, n): - return self.shift_horizontal(n) - - def shift_left(self, n): - return self.shift_horizontal(n * -1) - - def row_to_str(self, y): - new_str = "" - for x in range(0, self.width()): - new_str = new_str + str(self.get_pixel(x, y)) - - new_str = new_str + ":" - - return new_str - - def limit_result(self, limit, result): - if result > limit: - return limit - else: - return result - def __add__(self, other): if not (type(other) is Image): raise TypeError( - f"unsupported types for __add__: '{type(self)}', '{type(other)}'" + CONSTANTS.UNSUPPORTED_ADD_TYPE + f"'{type(self)}', '{type(other)}'" ) elif not (other.height() == self.height() and other.width() == self.width()): - raise ValueError("images must be the same size") + raise ValueError(CONSTANTS.SAME_SIZE_ERR) else: res = Image(self.width(), self.height()) - + for y in range(0, self.height()): for x in range(0, self.width()): sum = other.get_pixel(x, y) + self.get_pixel(x, y) - display_result = self.limit_result(9, sum) + display_result = self.__limit_result(9, sum) res.set_pixel(x, y, display_result) return res @@ -174,11 +115,64 @@ def __mul__(self, other): for y in range(0, self.height()): for x in range(0, self.width()): product = self.get_pixel(x, y) * float_val - res.set_pixel(x, y, self.limit_result(9, product)) + res.set_pixel(x, y, self.__limit_result(9, product)) return res + # helpers! + + def __create_leds(self, w, h): + arr = [] + for _ in range(0,h): + sub_arr = [] + for _ in range(0,w): + sub_arr.append(0) + arr.append(sub_arr) + return arr + + def __string_to_array(self, pattern): + arr = [] + sub_arr = [] + for elem in pattern: + if elem == ":": + arr.append(sub_arr) + sub_arr = [] + else: + sub_arr.append(int(elem)) + return arr + + def __limit_result(self, limit, result): + if result > limit: + return limit + else: + return result + def __valid_brightness(self, value): return 0 <= value and value <= 9 - \ No newline at end of file + + def __valid_pos(self, x, y): + return 0 <= x and x < self.width() and 0 <= y and y < self.height() + + def __shift_vertical(self, n): + + res = Image(self.width(), self.height()) + if n > 0: + # up + res.blit(self, 0, n, self.width(), self.height() - n, 0, 0) + else: + # down + res.blit(self, 0, 0, self.width(), self.height() - abs(n), 0, abs(n)) + + return res + + def __shift_horizontal(self, n): + res = Image(self.width(), self.height()) + if n > 0: + # right + res.blit(self, 0, 0, self.width() - n, self.height(), n, 0) + else: + # left + res.blit(self, n, 0, self.width() - n, self.height(), 0, 0) + + return res \ No newline at end of file From 355704735d78d8b1587f7d370ab0c46c65d97be8 Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Wed, 29 Jan 2020 11:02:50 -0800 Subject: [PATCH 12/27] changes to display --- src/microbit/display.py | 16 ++++++---------- src/microbit/image.py | 8 +------- src/microbit/test/test_display.py | 30 +++++++++++++++++------------- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/microbit/display.py b/src/microbit/display.py index 250e4de50..7b41a9bf6 100644 --- a/src/microbit/display.py +++ b/src/microbit/display.py @@ -1,5 +1,6 @@ from . import constants as CONSTANTS from .image import Image +from . import code_processing_shim class Display: @@ -23,7 +24,7 @@ def show(self, value, delay=400, wait=True, loop=False, clear=False): if value.height() <= CONSTANTS.LED_HEIGHT else CONSTANTS.LED_HEIGHT ) - self.__print() + self.__image = value elif isinstance(value, str): pass elif isinstance(value, float): @@ -32,15 +33,13 @@ def show(self, value, delay=400, wait=True, loop=False, clear=False): pass def get_pixel(self, x, y): - return self.__image.get_pixel(x,y) + return self.__image.get_pixel(x, y) def set_pixel(self, x, y, value): self.__image.set_pixel(x, y, value) def clear(self): - for y in range(CONSTANTS.LED_WIDTH): - for x in range(CONSTANTS.LED_HEIGHT): - self.__LEDs[y][x] = 0 + self.__image = Image() def on(self): self.__on = True @@ -54,10 +53,7 @@ def is_on(self): def read_light_level(self): raise NotImplementedError(CONSTANTS.NOT_IMPLEMENTED_ERROR) - # Helpers - def __valid_pos(self, x, y): - return 0 <= x and x <= 4 and 0 <= y and y <= 4 - def __print(self): + print("") for i in range(5): - print(self.__LEDs[i]) + print(self._Display__image[i]) diff --git a/src/microbit/image.py b/src/microbit/image.py index cc906ace3..c5464947e 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -5,8 +5,6 @@ class Image: def __init__(self, *args, **kwargs): - print("args") - print(args) # State in the Python process if len(args) == 0: self.__LED = CONSTANTS.BLANK @@ -37,7 +35,6 @@ def convert_to_array(self, pattern): sub_arr.append(int(elem)) return arr - def set_pixel(self, x, y, value): try: if not self.__valid_pos(x, y): @@ -50,12 +47,11 @@ def set_pixel(self, x, y, value): print(CONSTANTS.COPY_ERR_MESSAGE) def get_pixel(self, x, y): - if self.__valid_pos(x,y): + if self.__valid_pos(x, y): return self.__LED[y][x] else: raise ValueError(CONSTANTS.INDEX_ERR) - def copy(self): return Image(self.__LED) @@ -95,7 +91,6 @@ def crop(self, x, y, w, h): res.blit(self, x, y, w, h) return res - def __valid_pos(self, x, y): return 0 <= x and x < self.width() and 0 <= y and y < self.height() @@ -181,4 +176,3 @@ def __mul__(self, other): def __valid_brightness(self, value): return 0 <= value and value <= 9 - \ No newline at end of file diff --git a/src/microbit/test/test_display.py b/src/microbit/test/test_display.py index 582347bf0..b6f8b8d4f 100644 --- a/src/microbit/test/test_display.py +++ b/src/microbit/test/test_display.py @@ -3,6 +3,7 @@ from .. import constants as CONSTANTS from ..display import Display from ..image import Image +from .. import code_processing_shim class TestDisplay(object): @@ -11,7 +12,7 @@ def setup_method(self): @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) def test_get_pixel(self, x, y, brightness): - self.display._Display__LEDs[y][x] = brightness + self.display._Display__image._Image__LED[y][x] = brightness assert brightness == self.display.get_pixel(x, y) @pytest.mark.parametrize("x, y", [(5, 0), (0, -1), (0, 5)]) @@ -22,7 +23,7 @@ def test_get_pixel_error(self, x, y): @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) def test_set_pixel(self, x, y, brightness): self.display.set_pixel(x, y, brightness) - assert brightness == self.display._Display__LEDs[y][x] + assert brightness == self.display._Display__image._Image__LED[y][x] @pytest.mark.parametrize( "x, y, brightness, err_msg", @@ -37,9 +38,9 @@ def test_set_pixel_error(self, x, y, brightness, err_msg): self.display.set_pixel(x, y, brightness) def test_clear(self): - self.display._Display__LEDs[0][0] = 7 - self.display._Display__LEDs[3][4] = 6 - self.display._Display__LEDs[4][4] = 9 + self.display._Display__image._Image__LED[2][3] = 7 + self.display._Display__image._Image__LED[3][4] = 6 + self.display._Display__image._Image__LED[4][4] = 9 assert not self.__is_clear() self.display.clear() assert self.__is_clear() @@ -59,18 +60,21 @@ def test_is_on(self, on): self.display._Display__on = on assert on == self.display.is_on() + def test_show_one_image(self): + img = Image(CONSTANTS.BOAT) + img.set_pixel(0, 0, 8) + img.set_pixel(0, 1, 9) + img.set_pixel(0, 2, 7) + img.set_pixel(2, 2, 6) + self.display.show(img) + assert img == self.display._Display__image + # Helpers def __is_clear(self): for y in range(CONSTANTS.LED_WIDTH): for x in range(CONSTANTS.LED_HEIGHT): - if 0 != self.display._Display__LEDs[y][x]: + if 0 != self.display._Display__image._Image__LED[y][x]: + print(f"Not clear at x: {x}, y: {y}") return False return True - def test_use_me(self): - img = Image(5, 5) - img.set_pixel(0, 0, 8) - img.set_pixel(0, 1, 9) - img.set_pixel(0, 2, 7) - img.set_pixel(2, 2, 6) - self.display.show(img) From ecd96e0cc476968d038b51ff207e14e1c7966c00 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 29 Jan 2020 14:55:46 -0800 Subject: [PATCH 13/27] added bytearray compatability --- src/microbit/constants.py | 1 + src/microbit/image.py | 36 ++++++++- src/microbit/test/test_display.py | 126 +++++++++++++++--------------- src/microbit/test/test_image.py | 94 ++++++++++++++++++++++ 4 files changed, 191 insertions(+), 66 deletions(-) create mode 100644 src/microbit/test/test_image.py diff --git a/src/microbit/constants.py b/src/microbit/constants.py index 61705fbef..9492554cc 100644 --- a/src/microbit/constants.py +++ b/src/microbit/constants.py @@ -2,6 +2,7 @@ BRIGHTNESS_ERR = "brightness out of bounds" SAME_SIZE_ERR = "images must be the same size" UNSUPPORTED_ADD_TYPE = "unsupported types for __add__:" +INCORR_IMAGE_SIZE = "image data is incorrect size" LED_WIDTH = 5 LED_HEIGHT = 5 diff --git a/src/microbit/image.py b/src/microbit/image.py index 3788ecc5b..f91b5e40b 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -1,12 +1,12 @@ from . import microbit_model from . import constants as CONSTANTS from . import display - +import copy class Image: def __init__(self, *args, **kwargs): if len(args) == 0: - self.__LED = CONSTANTS.BLANK + self.__LED = copy.deepcopy(CONSTANTS.BLANK) elif len(args) == 1: pattern = args[0] if type(pattern) is str: @@ -22,8 +22,12 @@ def __init__(self, *args, **kwargs): # not in original, but ideally, # image should fail non-silently raise ValueError(CONSTANTS.INDEX_ERR) + if (len(args) == 3): + byte_arr = args[2] + self.__LED = self.__bytes_to_array(width,height,byte_arr) + else: + self.__LED = self.__create_leds(width,height) - self.__LED = self.__create_leds(width,height) def width(self): if len(self.__LED): @@ -83,6 +87,10 @@ def fill(self, value): def blit(self, src, x, y, w, h, xdest=0, ydest=0): + + if (not self.__valid_pos(x,y) or not src.__valid_pos(xdest, ydest)): + raise ValueError(CONSTANTS.INDEX_ERR) + for count_y in range(0, h): for count_x in range(0, w): if (self.__valid_pos(xdest + count_x, ydest + count_y) and @@ -130,6 +138,28 @@ def __create_leds(self, w, h): arr.append(sub_arr) return arr + + def __bytes_to_array(self, height, width, byte_arr): + bytes_translated = bytes(byte_arr) + + if (not (len(bytes_translated)) == height*width): + raise ValueError(CONSTANTS.INCORR_IMAGE_SIZE) + + arr = [] + sub_arr = [] + + for index,elem in enumerate(bytes_translated): + if index % width == 0 and not index is 0: + arr.append(sub_arr) + sub_arr = [] + + sub_arr.append(elem) + + arr.append(sub_arr) + return arr + + + def __string_to_array(self, pattern): arr = [] sub_arr = [] diff --git a/src/microbit/test/test_display.py b/src/microbit/test/test_display.py index 582347bf0..3f3a27119 100644 --- a/src/microbit/test/test_display.py +++ b/src/microbit/test/test_display.py @@ -1,76 +1,76 @@ -import pytest +# import pytest -from .. import constants as CONSTANTS -from ..display import Display -from ..image import Image +# from .. import constants as CONSTANTS +# from ..display import Display +# from ..image import Image -class TestDisplay(object): - def setup_method(self): - self.display = Display() +# class TestDisplay(object): +# def setup_method(self): +# self.display = Display() - @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) - def test_get_pixel(self, x, y, brightness): - self.display._Display__LEDs[y][x] = brightness - assert brightness == self.display.get_pixel(x, y) +# @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) +# def test_get_pixel(self, x, y, brightness): +# self.display._Display__LEDs[y][x] = brightness +# assert brightness == self.display.get_pixel(x, y) - @pytest.mark.parametrize("x, y", [(5, 0), (0, -1), (0, 5)]) - def test_get_pixel_error(self, x, y): - with pytest.raises(ValueError, match=CONSTANTS.INDEX_ERR): - self.display.get_pixel(x, y) +# @pytest.mark.parametrize("x, y", [(5, 0), (0, -1), (0, 5)]) +# def test_get_pixel_error(self, x, y): +# with pytest.raises(ValueError, match=CONSTANTS.INDEX_ERR): +# self.display.get_pixel(x, y) - @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) - def test_set_pixel(self, x, y, brightness): - self.display.set_pixel(x, y, brightness) - assert brightness == self.display._Display__LEDs[y][x] +# @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) +# def test_set_pixel(self, x, y, brightness): +# self.display.set_pixel(x, y, brightness) +# assert brightness == self.display._Display__LEDs[y][x] - @pytest.mark.parametrize( - "x, y, brightness, err_msg", - [ - (5, 0, 0, CONSTANTS.INDEX_ERR), - (0, -1, 0, CONSTANTS.INDEX_ERR), - (0, 0, -1, CONSTANTS.BRIGHTNESS_ERR), - ], - ) - def test_set_pixel_error(self, x, y, brightness, err_msg): - with pytest.raises(ValueError, match=err_msg): - self.display.set_pixel(x, y, brightness) +# @pytest.mark.parametrize( +# "x, y, brightness, err_msg", +# [ +# (5, 0, 0, CONSTANTS.INDEX_ERR), +# (0, -1, 0, CONSTANTS.INDEX_ERR), +# (0, 0, -1, CONSTANTS.BRIGHTNESS_ERR), +# ], +# ) +# def test_set_pixel_error(self, x, y, brightness, err_msg): +# with pytest.raises(ValueError, match=err_msg): +# self.display.set_pixel(x, y, brightness) - def test_clear(self): - self.display._Display__LEDs[0][0] = 7 - self.display._Display__LEDs[3][4] = 6 - self.display._Display__LEDs[4][4] = 9 - assert not self.__is_clear() - self.display.clear() - assert self.__is_clear() +# def test_clear(self): +# self.display._Display__LEDs[0][0] = 7 +# self.display._Display__LEDs[3][4] = 6 +# self.display._Display__LEDs[4][4] = 9 +# assert not self.__is_clear() +# self.display.clear() +# assert self.__is_clear() - def test_on(self): - self.display._Display__on = False - self.display.on() - assert self.display._Display__on +# def test_on(self): +# self.display._Display__on = False +# self.display.on() +# assert self.display._Display__on - def test_off(self): - self.display._Display__on = True - self.display.off() - assert False == self.display._Display__on +# def test_off(self): +# self.display._Display__on = True +# self.display.off() +# assert False == self.display._Display__on - @pytest.mark.parametrize("on", [True, False]) - def test_is_on(self, on): - self.display._Display__on = on - assert on == self.display.is_on() +# @pytest.mark.parametrize("on", [True, False]) +# def test_is_on(self, on): +# self.display._Display__on = on +# assert on == self.display.is_on() - # Helpers - def __is_clear(self): - for y in range(CONSTANTS.LED_WIDTH): - for x in range(CONSTANTS.LED_HEIGHT): - if 0 != self.display._Display__LEDs[y][x]: - return False - return True +# # Helpers +# def __is_clear(self): +# for y in range(CONSTANTS.LED_WIDTH): +# for x in range(CONSTANTS.LED_HEIGHT): +# if 0 != self.display._Display__LEDs[y][x]: +# return False +# return True - def test_use_me(self): - img = Image(5, 5) - img.set_pixel(0, 0, 8) - img.set_pixel(0, 1, 9) - img.set_pixel(0, 2, 7) - img.set_pixel(2, 2, 6) - self.display.show(img) +# def test_use_me(self): +# img = Image(5, 5) +# img.set_pixel(0, 0, 8) +# img.set_pixel(0, 1, 9) +# img.set_pixel(0, 2, 7) +# img.set_pixel(2, 2, 6) +# self.display.show(img) diff --git a/src/microbit/test/test_image.py b/src/microbit/test/test_image.py new file mode 100644 index 000000000..3b0fac63a --- /dev/null +++ b/src/microbit/test/test_image.py @@ -0,0 +1,94 @@ +import pytest + +from .. import constants as CONSTANTS +from .. import code_processing_shim +from ..display import Display +from ..image import Image + + + + +class TestImage(object): + def setup_method(self): + self.image = Image() + self.image_heart = Image(CONSTANTS.HEART) + # self.image_3x3 = + # self.image_empty = Image("") + + # GET PIXEL + @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) + def test_get_pixel(self, x, y, brightness): + self.image._Image__LED[y][x] = brightness + assert brightness == self.image.get_pixel(x, y) + + # SET PIXEL + @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) + def test_set_pixel(self, x, y, brightness): + self.image.set_pixel(x, y, brightness) + assert brightness == self.image._Image__LED[y][x] + + + # GET PIXEL - INDEX ERROR + @pytest.mark.parametrize("x, y", [(5, 0), (0, -1), (0, 5)]) + def test_get_pixel_error(self, x, y): + with pytest.raises(ValueError, match=CONSTANTS.INDEX_ERR): + self.image.get_pixel(x, y) + + # SET PIXEL - VARIOUS ERRORS + @pytest.mark.parametrize( + "x, y, brightness, err_msg", + [ + (5, 0, 0, CONSTANTS.INDEX_ERR), + (0, -1, 0, CONSTANTS.INDEX_ERR), + (0, 0, -1, CONSTANTS.BRIGHTNESS_ERR), + ], + ) + def test_set_pixel_error(self, x, y, brightness, err_msg): + with pytest.raises(ValueError, match=err_msg): + self.image.set_pixel(x, y, brightness) + + # WIDTH & HEIGHT + @pytest.mark.parametrize("image", [(Image()), (Image(3,3)), (Image(""))]) + def test_width_and_height(self, image): + assert image.height() == len(image._Image__LED) + if len(image._Image__LED) == 0: + assert image.width() == 0 + else: + assert image.width() == len(image._Image__LED[0]) + + assert image.height() == image.width() + + # BLIT + # @pytest.mark.parametrize("x, y, w, h, x_dest, y_dest", [(0,0,3,3,4,3),(1,1,2,4,0,1),(1,3,1,2,0,2)]) + # def test_blit(self, x, y, w, h, x_dest, y_dest): + # x_offset = x_dest-x + # y_offset = y_dest-y + # result = Image() + + # print("here") + # result.blit(self.image_heart, x, y, w, h, x_dest, y_dest) + # self.__check_blit(result, self.image_heart, x, y, w, h, x_offset, y_offset) + + # # helper! :D + # def __check_value(self,src,x,y,value): + # if src._Image__valid_pos(x,y): + # assert(src._Image__LED[y][x] == value) + # def __check_blit(self,target, src,x,y, w, h, x_offset,y_offset): + # for index_y, val_y in enumerate(src._Image__LED[y:]): + # if index_y >= h: + # break + # for index_x,val_x in enumerate(val_y[x:]): + # print(f"{index_x} {val_x}") + # if index_x >= w: + # break + # if (src._Image__valid_pos(index_x+x_offset,index_y+y_offset)): + # try: + # self.__check_value(target,index_x+x_offset, index_y+y_offset, val_x) + # except AssertionError as e: + # print("uuuwu") + # print(f"{index_x} {index_y} {w} {h} {x_offset} {y_offset}") + # print(code_processing_shim.str(src)) + # print(code_processing_shim.str(target)) + # print(f"{index_x+x_offset} {index_y+y_offset} {val_x}") + # print(e) + From 9652a23f746c2a742e363cedfd6fd49cd15dd218 Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Wed, 29 Jan 2020 15:10:04 -0800 Subject: [PATCH 14/27] Before merge --- src/microbit/button.py | 26 +++++++++++++++ src/microbit/constants.py | 8 +++-- src/microbit/display.py | 53 ++++++++++++++++++++----------- src/microbit/image.py | 24 +++++++------- src/microbit/microbit_model.py | 11 +++++-- src/microbit/test/test_button.py | 41 ++++++++++++++++++++++++ src/microbit/test/test_display.py | 38 ++++++++++++++++++---- 7 files changed, 160 insertions(+), 41 deletions(-) create mode 100644 src/microbit/button.py create mode 100644 src/microbit/test/test_button.py diff --git a/src/microbit/button.py b/src/microbit/button.py new file mode 100644 index 000000000..a28d13ed9 --- /dev/null +++ b/src/microbit/button.py @@ -0,0 +1,26 @@ +class Button: + def __init__(self): + self.__pressed = False + self.__presses = 0 + self.__prev_pressed = False + + def is_pressed(self): + return self.__pressed + + def was_pressed(self): + res = self.__prev_pressed + self.__prev_pressed = False + return res + + def get_presses(self): + res = self.__presses + self.__presses = 0 + return res + + def __press_down(self): + self.__pressed = True + self.__presses += 1 + + def __release(self): + self.__pressed = False + self.__prev_pressed = True diff --git a/src/microbit/constants.py b/src/microbit/constants.py index 61705fbef..4af98d262 100644 --- a/src/microbit/constants.py +++ b/src/microbit/constants.py @@ -4,9 +4,10 @@ UNSUPPORTED_ADD_TYPE = "unsupported types for __add__:" LED_WIDTH = 5 LED_HEIGHT = 5 - +MAX_BRIGHTNESS = 9 NOT_IMPLEMENTED_ERROR = "This method is not implemented by the simulator" - +ASCII_START = 32 +ASCII_END = 126 BOAT = ( [0, 5, 0, 5, 0], [0, 5, 0, 5, 0], @@ -35,3 +36,6 @@ COPY_ERR_MESSAGE = "please copy() first" LED_MAX = 5 + +ALPHABET = b"\x00\x00\x00\x00\x00\x08\x08\x08\x00\x08\x0a\x4a\x40\x00\x00\x0a\x5f\xea\x5f\xea\x0e\xd9\x2e\xd3\x6e\x19\x32\x44\x89\x33\x0c\x92\x4c\x92\x4d\x08\x08\x00\x00\x00\x04\x88\x08\x08\x04\x08\x04\x84\x84\x88\x00\x0a\x44\x8a\x40\x00\x04\x8e\xc4\x80\x00\x00\x00\x04\x88\x00\x00\x0e\xc0\x00\x00\x00\x00\x08\x00\x01\x22\x44\x88\x10\x0c\x92\x52\x52\x4c\x04\x8c\x84\x84\x8e\x1c\x82\x4c\x90\x1e\x1e\xc2\x44\x92\x4c\x06\xca\x52\x5f\xe2\x1f\xf0\x1e\xc1\x3e\x02\x44\x8e\xd1\x2e\x1f\xe2\x44\x88\x10\x0e\xd1\x2e\xd1\x2e\x0e\xd1\x2e\xc4\x88\x00\x08\x00\x08\x00\x00\x04\x80\x04\x88\x02\x44\x88\x04\x82\x00\x0e\xc0\x0e\xc0\x08\x04\x82\x44\x88\x0e\xd1\x26\xc0\x04\x0e\xd1\x35\xb3\x6c\x0c\x92\x5e\xd2\x52\x1c\x92\x5c\x92\x5c\x0e\xd0\x10\x10\x0e\x1c\x92\x52\x52\x5c\x1e\xd0\x1c\x90\x1e\x1e\xd0\x1c\x90\x10\x0e\xd0\x13\x71\x2e\x12\x52\x5e\xd2\x52\x1c\x88\x08\x08\x1c\x1f\xe2\x42\x52\x4c\x12\x54\x98\x14\x92\x10\x10\x10\x10\x1e\x11\x3b\x75\xb1\x31\x11\x39\x35\xb3\x71\x0c\x92\x52\x52\x4c\x1c\x92\x5c\x90\x10\x0c\x92\x52\x4c\x86\x1c\x92\x5c\x92\x51\x0e\xd0\x0c\x82\x5c\x1f\xe4\x84\x84\x84\x12\x52\x52\x52\x4c\x11\x31\x31\x2a\x44\x11\x31\x35\xbb\x71\x12\x52\x4c\x92\x52\x11\x2a\x44\x84\x84\x1e\xc4\x88\x10\x1e\x0e\xc8\x08\x08\x0e\x10\x08\x04\x82\x41\x0e\xc2\x42\x42\x4e\x04\x8a\x40\x00\x00\x00\x00\x00\x00\x1f\x08\x04\x80\x00\x00\x00\x0e\xd2\x52\x4f\x10\x10\x1c\x92\x5c\x00\x0e\xd0\x10\x0e\x02\x42\x4e\xd2\x4e\x0c\x92\x5c\x90\x0e\x06\xc8\x1c\x88\x08\x0e\xd2\x4e\xc2\x4c\x10\x10\x1c\x92\x52\x08\x00\x08\x08\x08\x02\x40\x02\x42\x4c\x10\x14\x98\x14\x92\x08\x08\x08\x08\x06\x00\x1b\x75\xb1\x31\x00\x1c\x92\x52\x52\x00\x0c\x92\x52\x4c\x00\x1c\x92\x5c\x90\x00\x0e\xd2\x4e\xc2\x00\x0e\xd0\x10\x10\x00\x06\xc8\x04\x98\x08\x08\x0e\xc8\x07\x00\x12\x52\x52\x4f\x00\x11\x31\x2a\x44\x00\x11\x31\x35\xbb\x00\x12\x4c\x8c\x92\x00\x11\x2a\x44\x98\x00\x1e\xc4\x88\x1e\x06\xc4\x8c\x84\x86\x08\x08\x08\x08\x08\x18\x08\x0c\x88\x18\x00\x00\x0c\x83\x60" + diff --git a/src/microbit/display.py b/src/microbit/display.py index 7b41a9bf6..815b83b18 100644 --- a/src/microbit/display.py +++ b/src/microbit/display.py @@ -1,3 +1,5 @@ +import time + from . import constants as CONSTANTS from .image import Image from . import code_processing_shim @@ -13,24 +15,39 @@ def scroll(self, message): raise NotImplementedError(CONSTANTS.NOT_IMPLEMENTED_ERROR) def show(self, value, delay=400, wait=True, loop=False, clear=False): - if isinstance(value, Image): - width = ( - value.width() - if value.width() <= CONSTANTS.LED_WIDTH - else CONSTANTS.LED_WIDTH - ) - height = ( - value.height() - if value.height() <= CONSTANTS.LED_HEIGHT - else CONSTANTS.LED_HEIGHT - ) - self.__image = value - elif isinstance(value, str): - pass - elif isinstance(value, float): - pass - elif isinstance(value, int): + # wait has no effect + while True: + # Need to check if iterable + # if iterable: + # for c in value: + # if isinstance(c, image): + # self.__image = value.crop(0, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT) + # elif isinstance(c, str) and len(c) == 1: + # show letter + # else: + # break + + # if isinstance(value, Image): + # self.__image = value.crop(0, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT) + # elif isinstance(value, str): + # chars = list(value) + # for c in chars: + # if c < CONSTANTS.ASCII_START or c > ASCII_END: + # c = "?" + # offset = (c - ASCII_START) * 5 + # representative_bytes = CONSTANTS.ALPHABET[offset : offset + 25] + # representative_image = Image(5, 5, representative_bytes) + # self.__image = representative_image + # time.sleep(delay / 1000) + # elif isinstance(value, float): + # pass + # elif isinstance(value, int): + # pass + # if not loop: + # break pass + if clear: + self.clear() def get_pixel(self, x, y): return self.__image.get_pixel(x, y) @@ -56,4 +73,4 @@ def read_light_level(self): def __print(self): print("") for i in range(5): - print(self._Display__image[i]) + print(self._Display__image._Image__LED[i]) diff --git a/src/microbit/image.py b/src/microbit/image.py index 3788ecc5b..f4116adcb 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -1,12 +1,13 @@ from . import microbit_model from . import constants as CONSTANTS from . import display +import copy class Image: def __init__(self, *args, **kwargs): if len(args) == 0: - self.__LED = CONSTANTS.BLANK + self.__LED = copy.deepcopy(CONSTANTS.BLANK) elif len(args) == 1: pattern = args[0] if type(pattern) is str: @@ -23,7 +24,7 @@ def __init__(self, *args, **kwargs): # image should fail non-silently raise ValueError(CONSTANTS.INDEX_ERR) - self.__LED = self.__create_leds(width,height) + self.__LED = self.__create_leds(width, height) def width(self): if len(self.__LED): @@ -46,7 +47,7 @@ def set_pixel(self, x, y, value): print(CONSTANTS.COPY_ERR_MESSAGE) def get_pixel(self, x, y): - if self.__valid_pos(x,y): + if self.__valid_pos(x, y): return self.__LED[y][x] else: raise ValueError(CONSTANTS.INDEX_ERR) @@ -81,12 +82,12 @@ def fill(self, value): for x in range(0, self.width()): self.set_pixel(x, y, value) - def blit(self, src, x, y, w, h, xdest=0, ydest=0): for count_y in range(0, h): for count_x in range(0, w): - if (self.__valid_pos(xdest + count_x, ydest + count_y) and - src.__valid_pos(x + count_x, y + count_y)): + if self.__valid_pos( + xdest + count_x, ydest + count_y + ) and src.__valid_pos(x + count_x, y + count_y): transfer_pixel = src.get_pixel(x + count_x, y + count_y) self.set_pixel(xdest + count_x, ydest + count_y, transfer_pixel) @@ -99,7 +100,7 @@ def __add__(self, other): raise ValueError(CONSTANTS.SAME_SIZE_ERR) else: res = Image(self.width(), self.height()) - + for y in range(0, self.height()): for x in range(0, self.width()): sum = other.get_pixel(x, y) + self.get_pixel(x, y) @@ -123,13 +124,13 @@ def __mul__(self, other): def __create_leds(self, w, h): arr = [] - for _ in range(0,h): + for _ in range(0, h): sub_arr = [] - for _ in range(0,w): + for _ in range(0, w): sub_arr.append(0) arr.append(sub_arr) return arr - + def __string_to_array(self, pattern): arr = [] sub_arr = [] @@ -150,7 +151,6 @@ def __limit_result(self, limit, result): def __valid_brightness(self, value): return 0 <= value and value <= 9 - def __valid_pos(self, x, y): return 0 <= x and x < self.width() and 0 <= y and y < self.height() @@ -175,4 +175,4 @@ def __shift_horizontal(self, n): # left res.blit(self, n, 0, self.width() - n, self.height(), 0, 0) - return res \ No newline at end of file + return res diff --git a/src/microbit/microbit_model.py b/src/microbit/microbit_model.py index 1d6ebec95..a8fa7815c 100644 --- a/src/microbit/microbit_model.py +++ b/src/microbit/microbit_model.py @@ -1,15 +1,20 @@ from .display import Display +from .button import Button + class MicrobitModel: def __init__(self): # State in the Python process self.display = Display() - self.__state = { } + self.button_a = Button() + self.button_b = Button() + self.__state = {} self.__debug_mode = False - self.__abs_path_to_code_file = '' + self.__abs_path_to_code_file = "" # SAMPLE FUNCTION def show_message(self, message): print("message!! " + message) -mb = MicrobitModel() \ No newline at end of file + +mb = MicrobitModel() diff --git a/src/microbit/test/test_button.py b/src/microbit/test/test_button.py new file mode 100644 index 000000000..acd7826d0 --- /dev/null +++ b/src/microbit/test/test_button.py @@ -0,0 +1,41 @@ +import pytest +from ..button import Button + + +class TestButton(object): + def setup_method(self): + self.button = Button() + + @pytest.mark.parametrize("pressed", [True, False]) + def test_is_pressed(self, pressed): + self.button._Button__pressed = pressed + assert pressed == self.button.is_pressed() + + @pytest.mark.parametrize("was_pressed", [True, False]) + def test_was_pressed(self, was_pressed): + self.button._Button__prev_pressed = was_pressed + assert was_pressed == self.button.was_pressed() + # Button resets prev pressed after was_pressed() is called + assert not self.button.was_pressed() + + @pytest.mark.parametrize("presses", [0, 2, 4]) + def test_get_presses(self, presses): + self.button._Button__presses = presses + assert presses == self.button.get_presses() + # Presses is reset to 0 after get_presses() is called + assert 0 == self.button.get_presses() + + def test_press_down(self): + self.button._Button__press_down() + assert self.button._Button__presses == 1 + assert self.button._Button__pressed + self.button._Button__press_down() + assert self.button._Button__presses == 2 + assert self.button._Button__pressed + + def test_release(self): + self.button._Button__pressed = True + self.button._Button__prev_pressed = False + self.button._Button__release() + assert not self.button._Button__pressed + assert self.button._Button__prev_pressed diff --git a/src/microbit/test/test_display.py b/src/microbit/test/test_display.py index b6f8b8d4f..71966dadc 100644 --- a/src/microbit/test/test_display.py +++ b/src/microbit/test/test_display.py @@ -61,20 +61,46 @@ def test_is_on(self, on): assert on == self.display.is_on() def test_show_one_image(self): - img = Image(CONSTANTS.BOAT) + img = Image() img.set_pixel(0, 0, 8) img.set_pixel(0, 1, 9) img.set_pixel(0, 2, 7) img.set_pixel(2, 2, 6) self.display.show(img) - assert img == self.display._Display__image + assert self.__same_image(img, self.display._Display__image) + + def test_show_different_size_image(self): + img = Image(3, 7) + img.set_pixel(1, 1, 9) + img.set_pixel(2, 6, 9) # Will not be on display + expected = Image(5, 5) + expected.set_pixel(1, 1, 9) + self.display.show(img) + assert self.__same_image(expected, self.display._Display__image) + + def test_show_smaller_image(self): + img = Image(2, 2) + img.set_pixel(1, 1, 9) + expected = Image(5, 5) + expected.set_pixel(1, 1, 9) + self.display.show(img) + assert self.__same_image(expected, self.display._Display__image) # Helpers def __is_clear(self): - for y in range(CONSTANTS.LED_WIDTH): - for x in range(CONSTANTS.LED_HEIGHT): - if 0 != self.display._Display__image._Image__LED[y][x]: - print(f"Not clear at x: {x}, y: {y}") + i = Image() + return self.__same_image(i, self.display._Display__image) + + def __same_image(self, i1, i2): + if i1.width() != i2.width() or i1.height() != i2.height(): + return False + for y in range(i1.height()): + for x in range(i1.width()): + if i1.get_pixel(x, y) != i2.get_pixel(x, y): return False return True + def __print(self, img): + print("") + for i in range(5): + print(img._Image__LED[i]) From 20690b29b617553219fe433ab4ebdf52342618e5 Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Wed, 29 Jan 2020 17:21:45 -0800 Subject: [PATCH 15/27] display show working, still need to add tests --- src/microbit/constants.py | 1 - src/microbit/display.py | 90 +++++++++++++++++++++---------- src/microbit/image.py | 2 +- src/microbit/test/test_display.py | 20 +++++++ 4 files changed, 82 insertions(+), 31 deletions(-) diff --git a/src/microbit/constants.py b/src/microbit/constants.py index 919582b9a..c1c1177a7 100644 --- a/src/microbit/constants.py +++ b/src/microbit/constants.py @@ -33,7 +33,6 @@ [0, 0, 0, 0, 0], ] - COPY_ERR_MESSAGE = "please copy() first" LED_MAX = 5 diff --git a/src/microbit/display.py b/src/microbit/display.py index 10e0e7d25..531722baa 100644 --- a/src/microbit/display.py +++ b/src/microbit/display.py @@ -17,35 +17,37 @@ def scroll(self, message): def show(self, value, delay=400, wait=True, loop=False, clear=False): # wait has no effect while True: - # Need to check if iterable - # if iterable: - # for c in value: - # if isinstance(c, image): - # self.__image = value.crop(0, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT) - # elif isinstance(c, str) and len(c) == 1: - # show letter - # else: - # break - - # if isinstance(value, Image): - # self.__image = value.crop(0, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT) - # elif isinstance(value, str): - # chars = list(value) - # for c in chars: - # if c < CONSTANTS.ASCII_START or c > ASCII_END: - # c = "?" - # offset = (c - ASCII_START) * 5 - # representative_bytes = CONSTANTS.ALPHABET[offset : offset + 25] - # representative_image = Image(5, 5, representative_bytes) - # self.__image = representative_image - # time.sleep(delay / 1000) - # elif isinstance(value, float): - # pass - # elif isinstance(value, int): - # pass - # if not loop: - # break - break + if isinstance(value, Image): + self.__image = value.crop( + 0, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT + ) + elif isinstance(value, (str, int, float)): + if isinstance(value, str): + chars = list(value) + else: + chars = list(str(value)) + for c in chars: + self.__image = self.__get_image_from_char(c) + self.__print() + time.sleep(delay / 1000) + else: + # Check if iterable + try: + _ = iter(value) + for elem in value: + if isinstance(elem, Image): + self.__image = elem.crop( + 0, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT + ) + elif isinstance(elem, str) and len(elem) == 1: + self.__image = self.__get_image_from_char(elem) + else: + break + self.__print() + except TypeError: + pass # Not iterable + if not loop: + break if clear: self.clear() @@ -70,7 +72,37 @@ def is_on(self): def read_light_level(self): raise NotImplementedError(CONSTANTS.NOT_IMPLEMENTED_ERROR) + # Helpers + def __print(self): print("") for i in range(5): print(self._Display__image._Image__LED[i]) + + def __get_image_from_char(self, 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) * 5 + representative_bytes = CONSTANTS.ALPHABET[offset : offset + 5] + return Image(self.__convert_bytearray_to_image_array(representative_bytes)) + + def __convert_bytearray_to_image_array(self, byte_array): + 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 recursively + # If there is a 1 at x, then the pixel at column x is lit + for bit in b_as_bits[::-1]: + if len(sub_arr) < 5: + sub_arr.insert(0, int(bit) * CONSTANTS.MAX_BRIGHTNESS) + 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) + return arr diff --git a/src/microbit/image.py b/src/microbit/image.py index 59b071424..56f20d2b7 100644 --- a/src/microbit/image.py +++ b/src/microbit/image.py @@ -148,7 +148,7 @@ def __bytes_to_array(self, height, width, byte_arr): sub_arr = [] for index, elem in enumerate(bytes_translated): - if index % width == 0 and not index is 0: + if index % width == 0 and index != 0: arr.append(sub_arr) sub_arr = [] diff --git a/src/microbit/test/test_display.py b/src/microbit/test/test_display.py index 71966dadc..34ec2f3dc 100644 --- a/src/microbit/test/test_display.py +++ b/src/microbit/test/test_display.py @@ -86,6 +86,19 @@ def test_show_smaller_image(self): self.display.show(img) assert self.__same_image(expected, self.display._Display__image) + def test_show_char(self): + expected = Image( + [ + [0, 9, 0, 0, 0], + [0, 9, 0, 0, 0], + [0, 9, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 9, 0, 0, 0], + ] + ) + self.display.show("!") + assert self.__same_image(expected, self.display._Display__image) + # Helpers def __is_clear(self): i = Image() @@ -104,3 +117,10 @@ def __print(self, img): print("") for i in range(5): print(img._Image__LED[i]) + + # def __convert_bytearray_to_image(self, byte_array): + # print(byte_array) + # arr = [] + # for b in byte_array: + # print(f"b: {b} type: {type(b)}") + # return Image(arr) From 1f4d0dd52b924f490e2347aa0e29bed206cce599 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 29 Jan 2020 20:54:22 -0800 Subject: [PATCH 16/27] first commit --- src/microbit/__init__.py | 2 +- src/microbit/code_processing_shim.py | 50 ------- src/microbit/constants.py | 41 ------ src/microbit/display.py | 108 -------------- src/microbit/image.py | 204 --------------------------- src/microbit/microbit_model.py | 20 --- src/microbit/{ => model}/button.py | 52 +++---- src/microbit/model/microbit.py | 10 ++ src/microbit/shim.py | 3 + src/microbit/test/test_button.py | 2 +- src/microbit/test/test_display.py | 126 ----------------- src/microbit/test/test_image.py | 94 ------------ 12 files changed, 41 insertions(+), 671 deletions(-) delete mode 100644 src/microbit/code_processing_shim.py delete mode 100644 src/microbit/constants.py delete mode 100644 src/microbit/display.py delete mode 100644 src/microbit/image.py delete mode 100644 src/microbit/microbit_model.py rename src/microbit/{ => model}/button.py (95%) create mode 100644 src/microbit/model/microbit.py create mode 100644 src/microbit/shim.py delete mode 100644 src/microbit/test/test_display.py delete mode 100644 src/microbit/test/test_image.py diff --git a/src/microbit/__init__.py b/src/microbit/__init__.py index 3f308d8fb..5cb079215 100644 --- a/src/microbit/__init__.py +++ b/src/microbit/__init__.py @@ -1 +1 @@ -from .code_processing_shim import * \ No newline at end of file +from .shim import * \ No newline at end of file diff --git a/src/microbit/code_processing_shim.py b/src/microbit/code_processing_shim.py deleted file mode 100644 index 8479d5a18..000000000 --- a/src/microbit/code_processing_shim.py +++ /dev/null @@ -1,50 +0,0 @@ -from . import microbit_model -from . import image -from . import constants as CONSTANTS - -# EXAMPLE -# can be called simply as "show_message("string")" -def show_message(message): - microbit_model.mb.show_message(message) - - -display = microbit_model.mb.display - -microbit = microbit_model.mb -Image = image.Image - -def repr(image): - - ret_str = "Image(\'" - for index_y in range(0,image.height()): - ret_str += row_to_str(image, index_y) - - ret_str = ret_str + "\')" - - return ret_str - - -def str(image): - if type(image) is Image: - ret_str = "Image(\'\n" - for index_y in range(0,image.height()): - ret_str += "\t" + row_to_str(image,index_y) + "\n" - - ret_str = ret_str + "\')" - - return ret_str - else: - # if not image, call regular str class - return image.__str__() - - - -# method to help with string formation -def row_to_str(image, y): - new_str = "" - for x in range(0, image.width()): - new_str = new_str + str(image.get_pixel(x, y)) - - new_str = new_str + ":" - - return new_str \ No newline at end of file diff --git a/src/microbit/constants.py b/src/microbit/constants.py deleted file mode 100644 index c1c1177a7..000000000 --- a/src/microbit/constants.py +++ /dev/null @@ -1,41 +0,0 @@ -INDEX_ERR = "index out of bounds" -BRIGHTNESS_ERR = "brightness out of bounds" -SAME_SIZE_ERR = "images must be the same size" -UNSUPPORTED_ADD_TYPE = "unsupported types for __add__:" -INCORR_IMAGE_SIZE = "image data is incorrect size" -LED_WIDTH = 5 -LED_HEIGHT = 5 -MAX_BRIGHTNESS = 9 -NOT_IMPLEMENTED_ERROR = "This method is not implemented by the simulator" -ASCII_START = 32 -ASCII_END = 126 -BOAT = ( - [0, 5, 0, 5, 0], - [0, 5, 0, 5, 0], - [0, 5, 0, 5, 0], - [9, 9, 9, 9, 9], - [0, 9, 9, 9, 0], -) - -HEART = [ - [0, 9, 0, 9, 0], - [9, 9, 9, 9, 9], - [9, 9, 9, 9, 9], - [0, 9, 9, 9, 0], - [0, 0, 9, 0, 0], -] - -BLANK = [ - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], -] - -COPY_ERR_MESSAGE = "please copy() first" - -LED_MAX = 5 - -ALPHABET = b"\x00\x00\x00\x00\x00\x08\x08\x08\x00\x08\x0a\x4a\x40\x00\x00\x0a\x5f\xea\x5f\xea\x0e\xd9\x2e\xd3\x6e\x19\x32\x44\x89\x33\x0c\x92\x4c\x92\x4d\x08\x08\x00\x00\x00\x04\x88\x08\x08\x04\x08\x04\x84\x84\x88\x00\x0a\x44\x8a\x40\x00\x04\x8e\xc4\x80\x00\x00\x00\x04\x88\x00\x00\x0e\xc0\x00\x00\x00\x00\x08\x00\x01\x22\x44\x88\x10\x0c\x92\x52\x52\x4c\x04\x8c\x84\x84\x8e\x1c\x82\x4c\x90\x1e\x1e\xc2\x44\x92\x4c\x06\xca\x52\x5f\xe2\x1f\xf0\x1e\xc1\x3e\x02\x44\x8e\xd1\x2e\x1f\xe2\x44\x88\x10\x0e\xd1\x2e\xd1\x2e\x0e\xd1\x2e\xc4\x88\x00\x08\x00\x08\x00\x00\x04\x80\x04\x88\x02\x44\x88\x04\x82\x00\x0e\xc0\x0e\xc0\x08\x04\x82\x44\x88\x0e\xd1\x26\xc0\x04\x0e\xd1\x35\xb3\x6c\x0c\x92\x5e\xd2\x52\x1c\x92\x5c\x92\x5c\x0e\xd0\x10\x10\x0e\x1c\x92\x52\x52\x5c\x1e\xd0\x1c\x90\x1e\x1e\xd0\x1c\x90\x10\x0e\xd0\x13\x71\x2e\x12\x52\x5e\xd2\x52\x1c\x88\x08\x08\x1c\x1f\xe2\x42\x52\x4c\x12\x54\x98\x14\x92\x10\x10\x10\x10\x1e\x11\x3b\x75\xb1\x31\x11\x39\x35\xb3\x71\x0c\x92\x52\x52\x4c\x1c\x92\x5c\x90\x10\x0c\x92\x52\x4c\x86\x1c\x92\x5c\x92\x51\x0e\xd0\x0c\x82\x5c\x1f\xe4\x84\x84\x84\x12\x52\x52\x52\x4c\x11\x31\x31\x2a\x44\x11\x31\x35\xbb\x71\x12\x52\x4c\x92\x52\x11\x2a\x44\x84\x84\x1e\xc4\x88\x10\x1e\x0e\xc8\x08\x08\x0e\x10\x08\x04\x82\x41\x0e\xc2\x42\x42\x4e\x04\x8a\x40\x00\x00\x00\x00\x00\x00\x1f\x08\x04\x80\x00\x00\x00\x0e\xd2\x52\x4f\x10\x10\x1c\x92\x5c\x00\x0e\xd0\x10\x0e\x02\x42\x4e\xd2\x4e\x0c\x92\x5c\x90\x0e\x06\xc8\x1c\x88\x08\x0e\xd2\x4e\xc2\x4c\x10\x10\x1c\x92\x52\x08\x00\x08\x08\x08\x02\x40\x02\x42\x4c\x10\x14\x98\x14\x92\x08\x08\x08\x08\x06\x00\x1b\x75\xb1\x31\x00\x1c\x92\x52\x52\x00\x0c\x92\x52\x4c\x00\x1c\x92\x5c\x90\x00\x0e\xd2\x4e\xc2\x00\x0e\xd0\x10\x10\x00\x06\xc8\x04\x98\x08\x08\x0e\xc8\x07\x00\x12\x52\x52\x4f\x00\x11\x31\x2a\x44\x00\x11\x31\x35\xbb\x00\x12\x4c\x8c\x92\x00\x11\x2a\x44\x98\x00\x1e\xc4\x88\x1e\x06\xc4\x8c\x84\x86\x08\x08\x08\x08\x08\x18\x08\x0c\x88\x18\x00\x00\x0c\x83\x60" - diff --git a/src/microbit/display.py b/src/microbit/display.py deleted file mode 100644 index 531722baa..000000000 --- a/src/microbit/display.py +++ /dev/null @@ -1,108 +0,0 @@ -import time - -from . import constants as CONSTANTS -from .image import Image -from . import code_processing_shim - - -class Display: - def __init__(self): - # State in the Python process - self.__image = Image() - self.__on = True - - def scroll(self, message): - raise NotImplementedError(CONSTANTS.NOT_IMPLEMENTED_ERROR) - - def show(self, value, delay=400, wait=True, loop=False, clear=False): - # wait has no effect - while True: - if isinstance(value, Image): - self.__image = value.crop( - 0, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT - ) - elif isinstance(value, (str, int, float)): - if isinstance(value, str): - chars = list(value) - else: - chars = list(str(value)) - for c in chars: - self.__image = self.__get_image_from_char(c) - self.__print() - time.sleep(delay / 1000) - else: - # Check if iterable - try: - _ = iter(value) - for elem in value: - if isinstance(elem, Image): - self.__image = elem.crop( - 0, 0, CONSTANTS.LED_WIDTH, CONSTANTS.LED_HEIGHT - ) - elif isinstance(elem, str) and len(elem) == 1: - self.__image = self.__get_image_from_char(elem) - else: - break - self.__print() - except TypeError: - pass # Not iterable - if not loop: - break - if clear: - self.clear() - - def get_pixel(self, x, y): - return self.__image.get_pixel(x, y) - - def set_pixel(self, x, y, value): - self.__image.set_pixel(x, y, value) - - def clear(self): - self.__image = Image() - - 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 __print(self): - print("") - for i in range(5): - print(self._Display__image._Image__LED[i]) - - def __get_image_from_char(self, 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) * 5 - representative_bytes = CONSTANTS.ALPHABET[offset : offset + 5] - return Image(self.__convert_bytearray_to_image_array(representative_bytes)) - - def __convert_bytearray_to_image_array(self, byte_array): - 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 recursively - # If there is a 1 at x, then the pixel at column x is lit - for bit in b_as_bits[::-1]: - if len(sub_arr) < 5: - sub_arr.insert(0, int(bit) * CONSTANTS.MAX_BRIGHTNESS) - 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) - return arr diff --git a/src/microbit/image.py b/src/microbit/image.py deleted file mode 100644 index 56f20d2b7..000000000 --- a/src/microbit/image.py +++ /dev/null @@ -1,204 +0,0 @@ -from . import microbit_model -from . import constants as CONSTANTS -from . import display -import copy - - -class Image: - def __init__(self, *args, **kwargs): - if len(args) == 0: - self.__LED = copy.deepcopy(CONSTANTS.BLANK) - elif len(args) == 1: - pattern = args[0] - if type(pattern) is str: - self.__LED = self.__string_to_array(pattern) - else: - self.__LED = pattern - else: - - width = args[0] - height = args[1] - - if width < 0 or height < 0: - # not in original, but ideally, - # image should fail non-silently - raise ValueError(CONSTANTS.INDEX_ERR) - if len(args) == 3: - byte_arr = args[2] - self.__LED = self.__bytes_to_array(width, height, byte_arr) - else: - self.__LED = self.__create_leds(width, height) - - def width(self): - if len(self.__LED): - return len(self.__LED[0]) - else: - return 0 - - def height(self): - return len(self.__LED) - - def set_pixel(self, x, y, value): - try: - if not self.__valid_pos(x, y): - raise ValueError(CONSTANTS.INDEX_ERR) - elif not self.__valid_brightness(value): - raise ValueError(CONSTANTS.BRIGHTNESS_ERR) - else: - self.__LED[y][x] = value - except TypeError: - print(CONSTANTS.COPY_ERR_MESSAGE) - - def get_pixel(self, x, y): - if self.__valid_pos(x, y): - return self.__LED[y][x] - else: - raise ValueError(CONSTANTS.INDEX_ERR) - - def shift_up(self, n): - return self.__shift_vertical(n) - - def shift_down(self, n): - return self.__shift_vertical(n * -1) - - def shift_right(self, n): - return self.__shift_horizontal(n) - - def shift_left(self, n): - return self.__shift_horizontal(n * -1) - - def crop(self, x, y, w, h): - res = Image(w, h) - res.blit(self, x, y, w, h) - return res - - def copy(self): - return Image(self.__LED) - - def invert(self, value): - for y in range(0, self.height()): - for x in range(0, self.width()): - self.set_pixel(x, y, 9 - value) - - def fill(self, value): - for y in range(0, self.height()): - for x in range(0, self.width()): - self.set_pixel(x, y, value) - - def blit(self, src, x, y, w, h, xdest=0, ydest=0): - - if not self.__valid_pos(x, y) or not src.__valid_pos(xdest, ydest): - raise ValueError(CONSTANTS.INDEX_ERR) - - for count_y in range(0, h): - for count_x in range(0, w): - if self.__valid_pos( - xdest + count_x, ydest + count_y - ) and src.__valid_pos(x + count_x, y + count_y): - transfer_pixel = src.get_pixel(x + count_x, y + count_y) - self.set_pixel(xdest + count_x, ydest + count_y, transfer_pixel) - - def __add__(self, other): - if not (type(other) is Image): - raise TypeError( - CONSTANTS.UNSUPPORTED_ADD_TYPE + f"'{type(self)}', '{type(other)}'" - ) - elif not (other.height() == self.height() and other.width() == self.width()): - raise ValueError(CONSTANTS.SAME_SIZE_ERR) - else: - res = Image(self.width(), self.height()) - - for y in range(0, self.height()): - for x in range(0, self.width()): - sum = other.get_pixel(x, y) + self.get_pixel(x, y) - display_result = self.__limit_result(9, sum) - res.set_pixel(x, y, display_result) - - return res - - def __mul__(self, other): - float_val = float(other) - res = Image(self.width(), self.height()) - - for y in range(0, self.height()): - for x in range(0, self.width()): - product = self.get_pixel(x, y) * float_val - res.set_pixel(x, y, self.__limit_result(9, product)) - - return res - - # helpers! - - def __create_leds(self, w, h): - arr = [] - for _ in range(0, h): - sub_arr = [] - for _ in range(0, w): - sub_arr.append(0) - arr.append(sub_arr) - return arr - - def __bytes_to_array(self, height, width, byte_arr): - bytes_translated = bytes(byte_arr) - - if not (len(bytes_translated)) == height * width: - raise ValueError(CONSTANTS.INCORR_IMAGE_SIZE) - - arr = [] - sub_arr = [] - - for index, elem in enumerate(bytes_translated): - if index % width == 0 and index != 0: - arr.append(sub_arr) - sub_arr = [] - - sub_arr.append(elem) - - arr.append(sub_arr) - return arr - - def __string_to_array(self, pattern): - arr = [] - sub_arr = [] - for elem in pattern: - if elem == ":": - arr.append(sub_arr) - sub_arr = [] - else: - sub_arr.append(int(elem)) - return arr - - def __limit_result(self, limit, result): - if result > limit: - return limit - else: - return result - - def __valid_brightness(self, value): - return 0 <= value and value <= 9 - - def __valid_pos(self, x, y): - return 0 <= x and x < self.width() and 0 <= y and y < self.height() - - def __shift_vertical(self, n): - - res = Image(self.width(), self.height()) - if n > 0: - # up - res.blit(self, 0, n, self.width(), self.height() - n, 0, 0) - else: - # down - res.blit(self, 0, 0, self.width(), self.height() - abs(n), 0, abs(n)) - - return res - - def __shift_horizontal(self, n): - res = Image(self.width(), self.height()) - if n > 0: - # right - res.blit(self, 0, 0, self.width() - n, self.height(), n, 0) - else: - # left - res.blit(self, n, 0, self.width() - n, self.height(), 0, 0) - - return res diff --git a/src/microbit/microbit_model.py b/src/microbit/microbit_model.py deleted file mode 100644 index a8fa7815c..000000000 --- a/src/microbit/microbit_model.py +++ /dev/null @@ -1,20 +0,0 @@ -from .display import Display -from .button import Button - - -class MicrobitModel: - def __init__(self): - # State in the Python process - self.display = Display() - self.button_a = Button() - self.button_b = Button() - self.__state = {} - self.__debug_mode = False - self.__abs_path_to_code_file = "" - - # SAMPLE FUNCTION - def show_message(self, message): - print("message!! " + message) - - -mb = MicrobitModel() diff --git a/src/microbit/button.py b/src/microbit/model/button.py similarity index 95% rename from src/microbit/button.py rename to src/microbit/model/button.py index a28d13ed9..46cae1653 100644 --- a/src/microbit/button.py +++ b/src/microbit/model/button.py @@ -1,26 +1,26 @@ -class Button: - def __init__(self): - self.__pressed = False - self.__presses = 0 - self.__prev_pressed = False - - def is_pressed(self): - return self.__pressed - - def was_pressed(self): - res = self.__prev_pressed - self.__prev_pressed = False - return res - - def get_presses(self): - res = self.__presses - self.__presses = 0 - return res - - def __press_down(self): - self.__pressed = True - self.__presses += 1 - - def __release(self): - self.__pressed = False - self.__prev_pressed = True +class Button: + def __init__(self): + self.__pressed = False + self.__presses = 0 + self.__prev_pressed = False + + def is_pressed(self): + return self.__pressed + + def was_pressed(self): + res = self.__prev_pressed + self.__prev_pressed = False + return res + + def get_presses(self): + res = self.__presses + self.__presses = 0 + return res + + def __press_down(self): + self.__pressed = True + self.__presses += 1 + + def __release(self): + self.__pressed = False + self.__prev_pressed = True diff --git a/src/microbit/model/microbit.py b/src/microbit/model/microbit.py new file mode 100644 index 000000000..f03f2c808 --- /dev/null +++ b/src/microbit/model/microbit.py @@ -0,0 +1,10 @@ +from .button import Button + + +class MicrobitModel: + def __init__(self): + # State in the Python process + self.button_a = Button() + self.button_b = Button() + +mb = MicrobitModel() diff --git a/src/microbit/shim.py b/src/microbit/shim.py new file mode 100644 index 000000000..83f1fe409 --- /dev/null +++ b/src/microbit/shim.py @@ -0,0 +1,3 @@ +from .model import microbit + +microbit = microbit.mb \ No newline at end of file diff --git a/src/microbit/test/test_button.py b/src/microbit/test/test_button.py index acd7826d0..4c712e46b 100644 --- a/src/microbit/test/test_button.py +++ b/src/microbit/test/test_button.py @@ -1,5 +1,5 @@ import pytest -from ..button import Button +from ..model.button import Button class TestButton(object): diff --git a/src/microbit/test/test_display.py b/src/microbit/test/test_display.py deleted file mode 100644 index 34ec2f3dc..000000000 --- a/src/microbit/test/test_display.py +++ /dev/null @@ -1,126 +0,0 @@ -import pytest - -from .. import constants as CONSTANTS -from ..display import Display -from ..image import Image -from .. import code_processing_shim - - -class TestDisplay(object): - def setup_method(self): - self.display = Display() - - @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) - def test_get_pixel(self, x, y, brightness): - self.display._Display__image._Image__LED[y][x] = brightness - assert brightness == self.display.get_pixel(x, y) - - @pytest.mark.parametrize("x, y", [(5, 0), (0, -1), (0, 5)]) - def test_get_pixel_error(self, x, y): - with pytest.raises(ValueError, match=CONSTANTS.INDEX_ERR): - self.display.get_pixel(x, y) - - @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) - def test_set_pixel(self, x, y, brightness): - self.display.set_pixel(x, y, brightness) - assert brightness == self.display._Display__image._Image__LED[y][x] - - @pytest.mark.parametrize( - "x, y, brightness, err_msg", - [ - (5, 0, 0, CONSTANTS.INDEX_ERR), - (0, -1, 0, CONSTANTS.INDEX_ERR), - (0, 0, -1, CONSTANTS.BRIGHTNESS_ERR), - ], - ) - def test_set_pixel_error(self, x, y, brightness, err_msg): - with pytest.raises(ValueError, match=err_msg): - self.display.set_pixel(x, y, brightness) - - def test_clear(self): - self.display._Display__image._Image__LED[2][3] = 7 - self.display._Display__image._Image__LED[3][4] = 6 - self.display._Display__image._Image__LED[4][4] = 9 - assert not self.__is_clear() - self.display.clear() - assert self.__is_clear() - - def test_on(self): - self.display._Display__on = False - self.display.on() - assert self.display._Display__on - - def test_off(self): - self.display._Display__on = True - self.display.off() - assert False == self.display._Display__on - - @pytest.mark.parametrize("on", [True, False]) - def test_is_on(self, on): - self.display._Display__on = on - assert on == self.display.is_on() - - def test_show_one_image(self): - img = Image() - img.set_pixel(0, 0, 8) - img.set_pixel(0, 1, 9) - img.set_pixel(0, 2, 7) - img.set_pixel(2, 2, 6) - self.display.show(img) - assert self.__same_image(img, self.display._Display__image) - - def test_show_different_size_image(self): - img = Image(3, 7) - img.set_pixel(1, 1, 9) - img.set_pixel(2, 6, 9) # Will not be on display - expected = Image(5, 5) - expected.set_pixel(1, 1, 9) - self.display.show(img) - assert self.__same_image(expected, self.display._Display__image) - - def test_show_smaller_image(self): - img = Image(2, 2) - img.set_pixel(1, 1, 9) - expected = Image(5, 5) - expected.set_pixel(1, 1, 9) - self.display.show(img) - assert self.__same_image(expected, self.display._Display__image) - - def test_show_char(self): - expected = Image( - [ - [0, 9, 0, 0, 0], - [0, 9, 0, 0, 0], - [0, 9, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 9, 0, 0, 0], - ] - ) - self.display.show("!") - assert self.__same_image(expected, self.display._Display__image) - - # Helpers - def __is_clear(self): - i = Image() - return self.__same_image(i, self.display._Display__image) - - def __same_image(self, i1, i2): - if i1.width() != i2.width() or i1.height() != i2.height(): - return False - for y in range(i1.height()): - for x in range(i1.width()): - if i1.get_pixel(x, y) != i2.get_pixel(x, y): - return False - return True - - def __print(self, img): - print("") - for i in range(5): - print(img._Image__LED[i]) - - # def __convert_bytearray_to_image(self, byte_array): - # print(byte_array) - # arr = [] - # for b in byte_array: - # print(f"b: {b} type: {type(b)}") - # return Image(arr) diff --git a/src/microbit/test/test_image.py b/src/microbit/test/test_image.py deleted file mode 100644 index 3b0fac63a..000000000 --- a/src/microbit/test/test_image.py +++ /dev/null @@ -1,94 +0,0 @@ -import pytest - -from .. import constants as CONSTANTS -from .. import code_processing_shim -from ..display import Display -from ..image import Image - - - - -class TestImage(object): - def setup_method(self): - self.image = Image() - self.image_heart = Image(CONSTANTS.HEART) - # self.image_3x3 = - # self.image_empty = Image("") - - # GET PIXEL - @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) - def test_get_pixel(self, x, y, brightness): - self.image._Image__LED[y][x] = brightness - assert brightness == self.image.get_pixel(x, y) - - # SET PIXEL - @pytest.mark.parametrize("x, y, brightness", [(1, 1, 4), (2, 3, 6), (4, 4, 9)]) - def test_set_pixel(self, x, y, brightness): - self.image.set_pixel(x, y, brightness) - assert brightness == self.image._Image__LED[y][x] - - - # GET PIXEL - INDEX ERROR - @pytest.mark.parametrize("x, y", [(5, 0), (0, -1), (0, 5)]) - def test_get_pixel_error(self, x, y): - with pytest.raises(ValueError, match=CONSTANTS.INDEX_ERR): - self.image.get_pixel(x, y) - - # SET PIXEL - VARIOUS ERRORS - @pytest.mark.parametrize( - "x, y, brightness, err_msg", - [ - (5, 0, 0, CONSTANTS.INDEX_ERR), - (0, -1, 0, CONSTANTS.INDEX_ERR), - (0, 0, -1, CONSTANTS.BRIGHTNESS_ERR), - ], - ) - def test_set_pixel_error(self, x, y, brightness, err_msg): - with pytest.raises(ValueError, match=err_msg): - self.image.set_pixel(x, y, brightness) - - # WIDTH & HEIGHT - @pytest.mark.parametrize("image", [(Image()), (Image(3,3)), (Image(""))]) - def test_width_and_height(self, image): - assert image.height() == len(image._Image__LED) - if len(image._Image__LED) == 0: - assert image.width() == 0 - else: - assert image.width() == len(image._Image__LED[0]) - - assert image.height() == image.width() - - # BLIT - # @pytest.mark.parametrize("x, y, w, h, x_dest, y_dest", [(0,0,3,3,4,3),(1,1,2,4,0,1),(1,3,1,2,0,2)]) - # def test_blit(self, x, y, w, h, x_dest, y_dest): - # x_offset = x_dest-x - # y_offset = y_dest-y - # result = Image() - - # print("here") - # result.blit(self.image_heart, x, y, w, h, x_dest, y_dest) - # self.__check_blit(result, self.image_heart, x, y, w, h, x_offset, y_offset) - - # # helper! :D - # def __check_value(self,src,x,y,value): - # if src._Image__valid_pos(x,y): - # assert(src._Image__LED[y][x] == value) - # def __check_blit(self,target, src,x,y, w, h, x_offset,y_offset): - # for index_y, val_y in enumerate(src._Image__LED[y:]): - # if index_y >= h: - # break - # for index_x,val_x in enumerate(val_y[x:]): - # print(f"{index_x} {val_x}") - # if index_x >= w: - # break - # if (src._Image__valid_pos(index_x+x_offset,index_y+y_offset)): - # try: - # self.__check_value(target,index_x+x_offset, index_y+y_offset, val_x) - # except AssertionError as e: - # print("uuuwu") - # print(f"{index_x} {index_y} {w} {h} {x_offset} {y_offset}") - # print(code_processing_shim.str(src)) - # print(code_processing_shim.str(target)) - # print(f"{index_x+x_offset} {index_y+y_offset} {val_x}") - # print(e) - From c4a1ca2ab8e113baaec83312cb5f6e2e2d5c4f09 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 29 Jan 2020 21:10:31 -0800 Subject: [PATCH 17/27] updated folder hierarchy --- gulpfile.js | 1 + src/microbit/model/microbit.py | 3 +++ src/microbit/shim.py | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index c642abfed..4c5cc8780 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -36,6 +36,7 @@ gulp.task("clean", () => { const pythonToMove = [ "./src/adafruit_circuitplayground/*.*", "./src/microbit/*.*", + "./src/microbit/model/*.*", "./src/*.py", "./src/requirements.txt", ]; diff --git a/src/microbit/model/microbit.py b/src/microbit/model/microbit.py index f03f2c808..cd57576ba 100644 --- a/src/microbit/model/microbit.py +++ b/src/microbit/model/microbit.py @@ -7,4 +7,7 @@ def __init__(self): self.button_a = Button() self.button_b = Button() + def show_message(self, message): + print("message!! " + message) + mb = MicrobitModel() diff --git a/src/microbit/shim.py b/src/microbit/shim.py index 83f1fe409..d9e3178fb 100644 --- a/src/microbit/shim.py +++ b/src/microbit/shim.py @@ -1,3 +1,4 @@ from .model import microbit -microbit = microbit.mb \ No newline at end of file +def show_message(message): + microbit.mb.show_message(message) \ No newline at end of file From 73a826c069af4ec324e98491721551d16cc5d7af Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 29 Jan 2020 21:17:49 -0800 Subject: [PATCH 18/27] formatted with black --- src/microbit/__init__.py | 2 +- src/microbit/model/microbit.py | 27 ++++++++++++++------------- src/microbit/shim.py | 9 +++++---- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/microbit/__init__.py b/src/microbit/__init__.py index 5cb079215..a0d5418e9 100644 --- a/src/microbit/__init__.py +++ b/src/microbit/__init__.py @@ -1 +1 @@ -from .shim import * \ No newline at end of file +from .shim import * diff --git a/src/microbit/model/microbit.py b/src/microbit/model/microbit.py index cd57576ba..bdf4094c6 100644 --- a/src/microbit/model/microbit.py +++ b/src/microbit/model/microbit.py @@ -1,13 +1,14 @@ -from .button import Button - - -class MicrobitModel: - def __init__(self): - # State in the Python process - self.button_a = Button() - self.button_b = Button() - - def show_message(self, message): - print("message!! " + message) - -mb = MicrobitModel() +from .button import Button + + +class MicrobitModel: + def __init__(self): + # State in the Python process + self.button_a = Button() + self.button_b = Button() + + def show_message(self, message): + print("message!! " + message) + + +mb = MicrobitModel() diff --git a/src/microbit/shim.py b/src/microbit/shim.py index d9e3178fb..dcdecd37c 100644 --- a/src/microbit/shim.py +++ b/src/microbit/shim.py @@ -1,4 +1,5 @@ -from .model import microbit - -def show_message(message): - microbit.mb.show_message(message) \ No newline at end of file +from .model import microbit + + +def show_message(message): + microbit.mb.show_message(message) From dfe868b89412722f711b02d54ac199b86d9ce5bf Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 29 Jan 2020 21:21:16 -0800 Subject: [PATCH 19/27] added comments --- src/microbit/model/microbit.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/microbit/model/microbit.py b/src/microbit/model/microbit.py index bdf4094c6..fa583d881 100644 --- a/src/microbit/model/microbit.py +++ b/src/microbit/model/microbit.py @@ -7,8 +7,9 @@ def __init__(self): self.button_a = Button() self.button_b = Button() + # Will be removed once functions added to shim def show_message(self, message): - print("message!! " + message) + print("message: " + message) mb = MicrobitModel() From 154bc5847ff091e16153dd3304ffbb5716ab0705 Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Thu, 30 Jan 2020 11:59:57 -0800 Subject: [PATCH 20/27] Updated functions that are actually in microbit lib --- src/microbit/model/microbit.py | 15 --------------- src/microbit/model/microbit_model.py | 20 ++++++++++++++++++++ src/microbit/shim.py | 10 +++++++--- 3 files changed, 27 insertions(+), 18 deletions(-) delete mode 100644 src/microbit/model/microbit.py create mode 100644 src/microbit/model/microbit_model.py diff --git a/src/microbit/model/microbit.py b/src/microbit/model/microbit.py deleted file mode 100644 index fa583d881..000000000 --- a/src/microbit/model/microbit.py +++ /dev/null @@ -1,15 +0,0 @@ -from .button import Button - - -class MicrobitModel: - def __init__(self): - # State in the Python process - self.button_a = Button() - self.button_b = Button() - - # Will be removed once functions added to shim - def show_message(self, message): - print("message: " + message) - - -mb = MicrobitModel() diff --git a/src/microbit/model/microbit_model.py b/src/microbit/model/microbit_model.py new file mode 100644 index 000000000..2b52de097 --- /dev/null +++ b/src/microbit/model/microbit_model.py @@ -0,0 +1,20 @@ +from .button import Button +import time + + +class MicrobitModel: + def __init__(self): + # State in the Python process + self.button_a = Button() + self.button_b = Button() + self.start_time = time.time() + + def sleep(self, n): + # time.sleep(n / 1000) + print("sleeping") + + def running_time(self): + return time.time() - start_time() + + +mb = MicrobitModel() diff --git a/src/microbit/shim.py b/src/microbit/shim.py index dcdecd37c..298ac5f85 100644 --- a/src/microbit/shim.py +++ b/src/microbit/shim.py @@ -1,5 +1,9 @@ -from .model import microbit +from .model import microbit_model -def show_message(message): - microbit.mb.show_message(message) +def sleep(n): + microbit_model.mb.sleep(n) + + +def running_time(n): + microbit_model.mb.running_time(n) From c7d39cef9bddd35c1bc1cbba653a84eab06e428a Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Thu, 30 Jan 2020 12:02:55 -0800 Subject: [PATCH 21/27] fixed running_time bug --- .vscode/settings.json | 3 ++- src/microbit/model/microbit_model.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 15f262be7..6ab228442 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,6 @@ }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts "typescript.tsc.autoDetect": "off", - "python.formatting.provider": "black" + "python.formatting.provider": "black", + "editor.fontSize": 12 } \ No newline at end of file diff --git a/src/microbit/model/microbit_model.py b/src/microbit/model/microbit_model.py index 2b52de097..5ab606930 100644 --- a/src/microbit/model/microbit_model.py +++ b/src/microbit/model/microbit_model.py @@ -1,6 +1,7 @@ -from .button import Button import time +from .button import Button + class MicrobitModel: def __init__(self): @@ -10,11 +11,10 @@ def __init__(self): self.start_time = time.time() def sleep(self, n): - # time.sleep(n / 1000) - print("sleeping") + time.sleep(n / 1000) def running_time(self): - return time.time() - start_time() + return time.time() - self.start_time mb = MicrobitModel() From 72e0f2ffa5739c6bc1f20c986f14bca91dbb7caa Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Thu, 30 Jan 2020 12:20:35 -0800 Subject: [PATCH 22/27] Added unit tests --- src/microbit/model/microbit_model.py | 5 +++-- src/microbit/shim.py | 4 ++-- src/microbit/test/test_microbit_model.py | 24 ++++++++++++++++++++++++ src/microbit/test/test_shim.py | 19 +++++++++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 src/microbit/test/test_microbit_model.py create mode 100644 src/microbit/test/test_shim.py diff --git a/src/microbit/model/microbit_model.py b/src/microbit/model/microbit_model.py index 5ab606930..f8e2572da 100644 --- a/src/microbit/model/microbit_model.py +++ b/src/microbit/model/microbit_model.py @@ -8,13 +8,14 @@ def __init__(self): # State in the Python process self.button_a = Button() self.button_b = Button() - self.start_time = time.time() + self.__start_time = time.time() def sleep(self, n): time.sleep(n / 1000) def running_time(self): - return time.time() - self.start_time + print(f"time. time: {time.time()}") + return time.time() - self.__start_time mb = MicrobitModel() diff --git a/src/microbit/shim.py b/src/microbit/shim.py index 298ac5f85..ac8f544f0 100644 --- a/src/microbit/shim.py +++ b/src/microbit/shim.py @@ -5,5 +5,5 @@ def sleep(n): microbit_model.mb.sleep(n) -def running_time(n): - microbit_model.mb.running_time(n) +def running_time(): + microbit_model.mb.running_time() diff --git a/src/microbit/test/test_microbit_model.py b/src/microbit/test/test_microbit_model.py new file mode 100644 index 000000000..e37df6019 --- /dev/null +++ b/src/microbit/test/test_microbit_model.py @@ -0,0 +1,24 @@ +import time + +import pytest +from unittest import mock +from ..model.microbit_model import MicrobitModel + + +class TestMicrobitModel(object): + def setup_method(self): + self.mb = MicrobitModel() + + @pytest.mark.parametrize("value", [9, 30, 1999]) + def test_sleep(self, value): + time.sleep = mock.Mock() + self.mb.sleep(value) + time.sleep.assert_called_with(value / 1000) + + def test_running_time(self): + mock_start_time = 10 + mock_end_time = 300 + self.mb._MicrobitModel__start_time = mock_start_time + time.time = mock.MagicMock(return_value=mock_end_time) + print(time.time()) + assert mock_end_time - mock_start_time == pytest.approx(self.mb.running_time()) diff --git a/src/microbit/test/test_shim.py b/src/microbit/test/test_shim.py new file mode 100644 index 000000000..bd4333d18 --- /dev/null +++ b/src/microbit/test/test_shim.py @@ -0,0 +1,19 @@ +import time + +import pytest +from unittest import mock +from .. import shim +from ..model import microbit_model + + +class TestShim(object): + def test_sleep(self): + n = 100 + microbit_model.mb.sleep = mock.Mock() + shim.sleep(n) + microbit_model.mb.sleep.assert_called_with(n) + + def test_running_time(self): + microbit_model.mb.running_time = mock.Mock() + shim.running_time() + microbit_model.mb.running_time.assert_called_once() From 2484fda2bb38a917c01f3b7a8543e17eec7dc72c Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Thu, 30 Jan 2020 13:18:48 -0800 Subject: [PATCH 23/27] reverted settings.json --- .vscode/settings.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 6ab228442..fa0a10487 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,5 @@ "out": true // set this to false to include "out" folder in search results }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts - "typescript.tsc.autoDetect": "off", - "python.formatting.provider": "black", - "editor.fontSize": 12 -} \ No newline at end of file + "typescript.tsc.autoDetect": "off" +} From b9b5e72aa167be2c68f39d3c729bf66e8b7f786b Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Thu, 30 Jan 2020 17:07:34 -0800 Subject: [PATCH 24/27] updated gulp file --- gulpfile.js | 145 ++++++++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 72 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 4c5cc8780..5a4a494a8 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -23,115 +23,116 @@ const outDest = "out"; const languages = [{ folderName: "en", id: "en" }]; gulp.task("clean", () => { - return del( - [ - "out/*", - "package.nls.*.json", - "../../dist/*0.0.0-UNTRACKEDVERSION.vsix" - ], - { force: true } - ); + return del( + [ + "out/*", + "package.nls.*.json", + "../../dist/*0.0.0-UNTRACKEDVERSION.vsix", + ], + { force: true } + ); }); const pythonToMove = [ - "./src/adafruit_circuitplayground/*.*", - "./src/microbit/*.*", - "./src/microbit/model/*.*", - "./src/*.py", - "./src/requirements.txt", + "./src/adafruit_circuitplayground/*.*", + "./src/microbit/*.*", + "./src/microbit/!(test)/**/*", + "./src/*.py", + "./src/requirements.txt", ]; gulp.task("python-compile", () => { - // the base option sets the relative root for the set of files, - // preserving the folder structure - return gulp.src(pythonToMove, { base: "./src/" }).pipe(gulp.dest("out")); + // the base option sets the relative root for the set of files, + // preserving the folder structure + return gulp.src(pythonToMove, { base: "./src/" }).pipe(gulp.dest("out")); }); gulp.task("internal-compile", () => { - return compile(false); + return compile(false); }); gulp.task("internal-nls-compile", () => { - return compile(true); + return compile(true); }); gulp.task("add-locales", () => { - return gulp - .src(["package.nls.json"]) - .pipe(nls.createAdditionalLanguageFiles(languages, "locales")) - .pipe(gulp.dest(".")); + return gulp + .src(["package.nls.json"]) + .pipe(nls.createAdditionalLanguageFiles(languages, "locales")) + .pipe(gulp.dest(".")); }); gulp.task("vsce:publish", () => { - return vsce.publish(); + return vsce.publish(); }); gulp.task("vsce:package", () => { - return vsce.createVSIX({ - packagePath: "../../dist/deviceSimulatorExpress-0.0.0-UNTRACKEDVERSION.vsix" - }); + return vsce.createVSIX({ + packagePath: + "../../dist/deviceSimulatorExpress-0.0.0-UNTRACKEDVERSION.vsix", + }); }); gulp.task( - "compile", - gulp.series("clean", "internal-compile", "python-compile", callback => { - callback(); - }) + "compile", + gulp.series("clean", "internal-compile", "python-compile", callback => { + callback(); + }) ); gulp.task( - "build", - gulp.series( - "clean", - "internal-nls-compile", - "python-compile", - "add-locales", - callback => { - callback(); - } - ) + "build", + gulp.series( + "clean", + "internal-nls-compile", + "python-compile", + "add-locales", + callback => { + callback(); + } + ) ); gulp.task( - "publish", - gulp.series("compile", "vsce:publish", callback => { - callback(); - }) + "publish", + gulp.series("compile", "vsce:publish", callback => { + callback(); + }) ); gulp.task( - "package", - gulp.series("compile", "vsce:package", callback => { - callback(); - }) + "package", + gulp.series("compile", "vsce:package", callback => { + callback(); + }) ); //---- internal function compile(buildNls) { - var r = tsProject - .src() - .pipe(sourcemaps.init()) - .pipe(tsProject()) - .js.pipe(buildNls ? nls.rewriteLocalizeCalls() : es.through()) - .pipe( - buildNls - ? nls.createAdditionalLanguageFiles(languages, "locales", "out") - : es.through() - ); - - if (inlineMap && inlineSource) { - r = r.pipe(sourcemaps.write()); - } else { - r = r.pipe( - sourcemaps.write("../out", { - // no inlined source - includeContent: inlineSource, - // Return relative source map root directories per file. - sourceRoot: "../src" - }) - ); - } + var r = tsProject + .src() + .pipe(sourcemaps.init()) + .pipe(tsProject()) + .js.pipe(buildNls ? nls.rewriteLocalizeCalls() : es.through()) + .pipe( + buildNls + ? nls.createAdditionalLanguageFiles(languages, "locales", "out") + : es.through() + ); + + if (inlineMap && inlineSource) { + r = r.pipe(sourcemaps.write()); + } else { + r = r.pipe( + sourcemaps.write("../out", { + // no inlined source + includeContent: inlineSource, + // Return relative source map root directories per file. + sourceRoot: "../src", + }) + ); + } - return r.pipe(gulp.dest(outDest)); + return r.pipe(gulp.dest(outDest)); } From 113024d59e354eb059abf45a37e545606d1e863d Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Fri, 31 Jan 2020 09:25:46 -0800 Subject: [PATCH 25/27] addressed comments --- src/microbit/model/button.py | 1 + src/microbit/test/test_button.py | 51 ++++++++++++++++++++------------ src/microbit/test/test_shim.py | 6 ++-- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/microbit/model/button.py b/src/microbit/model/button.py index 46cae1653..6c5c67eae 100644 --- a/src/microbit/model/button.py +++ b/src/microbit/model/button.py @@ -1,4 +1,5 @@ class Button: + # The implementation is based off of https://github.com/bbcmicrobit/micropython/blob/master/docs/button.rst. def __init__(self): self.__pressed = False self.__presses = 0 diff --git a/src/microbit/test/test_button.py b/src/microbit/test/test_button.py index 4c712e46b..014694bd5 100644 --- a/src/microbit/test/test_button.py +++ b/src/microbit/test/test_button.py @@ -6,25 +6,6 @@ class TestButton(object): def setup_method(self): self.button = Button() - @pytest.mark.parametrize("pressed", [True, False]) - def test_is_pressed(self, pressed): - self.button._Button__pressed = pressed - assert pressed == self.button.is_pressed() - - @pytest.mark.parametrize("was_pressed", [True, False]) - def test_was_pressed(self, was_pressed): - self.button._Button__prev_pressed = was_pressed - assert was_pressed == self.button.was_pressed() - # Button resets prev pressed after was_pressed() is called - assert not self.button.was_pressed() - - @pytest.mark.parametrize("presses", [0, 2, 4]) - def test_get_presses(self, presses): - self.button._Button__presses = presses - assert presses == self.button.get_presses() - # Presses is reset to 0 after get_presses() is called - assert 0 == self.button.get_presses() - def test_press_down(self): self.button._Button__press_down() assert self.button._Button__presses == 1 @@ -39,3 +20,35 @@ def test_release(self): self.button._Button__release() assert not self.button._Button__pressed assert self.button._Button__prev_pressed + + def test_is_pressed(self): + assert not self.button.is_pressed() + self.button._Button__press_down() + assert self.button.is_pressed() + + def test_was_pressed(self): + assert not self.button.was_pressed() + self.button._Button__press_down() + self.button._Button__release() + assert self.button.was_pressed() + # Button resets __prev_pressed after was_pressed() is called. + assert not self.button.was_pressed() + + def test_get_presses_one(self): + assert 0 == self.button.get_presses() + self.button._Button__press_down() + self.button._Button__release() + assert 1 == self.button.get_presses() + # Presses is reset to 0 after get_presses() is called. + assert 0 == self.button.get_presses() + + def test_get_presses_two(self): + assert 0 == self.button.get_presses() + self.button._Button__press_down() + self.button._Button__release() + self.button._Button__press_down() + self.button._Button__release() + assert 2 == self.button.get_presses() + # Presses is reset to 0 after get_presses() is called. + assert 0 == self.button.get_presses() + diff --git a/src/microbit/test/test_shim.py b/src/microbit/test/test_shim.py index bd4333d18..68853ec9b 100644 --- a/src/microbit/test/test_shim.py +++ b/src/microbit/test/test_shim.py @@ -8,10 +8,10 @@ class TestShim(object): def test_sleep(self): - n = 100 + milliseconds = 100 microbit_model.mb.sleep = mock.Mock() - shim.sleep(n) - microbit_model.mb.sleep.assert_called_with(n) + shim.sleep(milliseconds) + microbit_model.mb.sleep.assert_called_with(milliseconds) def test_running_time(self): microbit_model.mb.running_time = mock.Mock() From 55e011a76bf4ce79b7bd248960f532b5b15d2d02 Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Fri, 31 Jan 2020 09:33:17 -0800 Subject: [PATCH 26/27] Formatted with black --- src/microbit/test/test_button.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/microbit/test/test_button.py b/src/microbit/test/test_button.py index 014694bd5..f41f84da9 100644 --- a/src/microbit/test/test_button.py +++ b/src/microbit/test/test_button.py @@ -51,4 +51,3 @@ def test_get_presses_two(self): assert 2 == self.button.get_presses() # Presses is reset to 0 after get_presses() is called. assert 0 == self.button.get_presses() - From da46cc880469c4dcde23b4a681083445c4e7a8dc Mon Sep 17 00:00:00 2001 From: Vandy Liu Date: Fri, 31 Jan 2020 10:58:11 -0800 Subject: [PATCH 27/27] Addressed comments --- src/microbit/model/button.py | 2 +- src/microbit/test/test_button.py | 23 ++++++++--------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/microbit/model/button.py b/src/microbit/model/button.py index 6c5c67eae..54a60e8a4 100644 --- a/src/microbit/model/button.py +++ b/src/microbit/model/button.py @@ -20,8 +20,8 @@ def get_presses(self): def __press_down(self): self.__pressed = True + self.__prev_pressed = True self.__presses += 1 def __release(self): self.__pressed = False - self.__prev_pressed = True diff --git a/src/microbit/test/test_button.py b/src/microbit/test/test_button.py index f41f84da9..aee9cbb43 100644 --- a/src/microbit/test/test_button.py +++ b/src/microbit/test/test_button.py @@ -10,16 +10,17 @@ def test_press_down(self): self.button._Button__press_down() assert self.button._Button__presses == 1 assert self.button._Button__pressed + assert self.button._Button__prev_pressed self.button._Button__press_down() assert self.button._Button__presses == 2 assert self.button._Button__pressed + assert self.button._Button__prev_pressed def test_release(self): self.button._Button__pressed = True self.button._Button__prev_pressed = False self.button._Button__release() assert not self.button._Button__pressed - assert self.button._Button__prev_pressed def test_is_pressed(self): assert not self.button.is_pressed() @@ -34,20 +35,12 @@ def test_was_pressed(self): # Button resets __prev_pressed after was_pressed() is called. assert not self.button.was_pressed() - def test_get_presses_one(self): - assert 0 == self.button.get_presses() - self.button._Button__press_down() - self.button._Button__release() - assert 1 == self.button.get_presses() - # Presses is reset to 0 after get_presses() is called. + @pytest.mark.parametrize("presses", [1, 2, 4]) + def test_get_presses(self, presses): assert 0 == self.button.get_presses() - - def test_get_presses_two(self): - assert 0 == self.button.get_presses() - self.button._Button__press_down() - self.button._Button__release() - self.button._Button__press_down() - self.button._Button__release() - assert 2 == self.button.get_presses() + for i in range(presses): + self.button._Button__press_down() + self.button._Button__release() + assert presses == self.button.get_presses() # Presses is reset to 0 after get_presses() is called. assert 0 == self.button.get_presses()