Skip to content

Commit 2f1a99a

Browse files
authored
Merge pull request #752 from makermelissa/master
Added PyPortal Calculator
2 parents 8c27d6a + 537b51e commit 2f1a99a

4 files changed

Lines changed: 6297 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
*~
22
Hue_Controller/secrets.h
33
.idea
4-
4+
*.DS_Store
55
CircuitPython_Logger/secrets\.py

PyPortal_Calculator/calculator.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"""
2+
CircuitPython library to handle the input and calculations
3+
4+
* Author(s): Melissa LeBlanc-Williams
5+
"""
6+
7+
# pylint: disable=eval-used
8+
def calculate(number_one, operator, number_two):
9+
result = eval(number_one + operator + number_two)
10+
if int(result) == result:
11+
result = int(result)
12+
return str(result)
13+
14+
class Calculator:
15+
def __init__(self, calc_display, clear_button, label_offset):
16+
self._calc_display = calc_display
17+
self._clear_button = clear_button
18+
self._label_offset = label_offset
19+
self._accumulator = "0"
20+
self._operator = None
21+
self._equal_pressed = False
22+
self._operand = None
23+
self._all_clear()
24+
25+
def _all_clear(self):
26+
self._accumulator = "0"
27+
self._operator = None
28+
self._equal_pressed = False
29+
self._clear_entry()
30+
31+
def _clear_entry(self):
32+
self._operand = None
33+
self._set_button_ce(False)
34+
self._set_text("0")
35+
36+
def _set_button_ce(self, entry_only):
37+
if entry_only:
38+
self._clear_button.label = "CE"
39+
else:
40+
self._clear_button.label = "AC"
41+
42+
def _set_text(self, text):
43+
self._calc_display.text = text
44+
_, _, screen_w, _ = self._calc_display.bounding_box
45+
self._calc_display.x = self._label_offset - screen_w
46+
47+
def _get_text(self):
48+
return self._calc_display.text
49+
50+
def _handle_number(self, input_key):
51+
display_text = self._get_text()
52+
if self._operand is None and self._operator is not None:
53+
display_text = ""
54+
elif self._operand is not None and self._operator is not None and self._equal_pressed:
55+
self._accumulator = self._operand
56+
self._operator = None
57+
self._operand = None
58+
display_text = ""
59+
elif display_text == "0":
60+
display_text = ""
61+
display_text += input_key
62+
self._set_text(display_text)
63+
if self._operator is not None:
64+
self._operand = display_text
65+
self._set_button_ce(True)
66+
self._equal_pressed = False
67+
68+
def _handle_operator(self, input_key):
69+
if input_key == "x":
70+
input_key = "*"
71+
if self._equal_pressed:
72+
self._operand = None
73+
if self._operator is None:
74+
self._operator = input_key
75+
else:
76+
# Perform current calculation before changing input_keys
77+
if self._operand is not None:
78+
self._accumulator = calculate(self._accumulator, self._operator, self._operand)
79+
self._set_text(self._accumulator)
80+
self._operand = None
81+
self._operator = input_key
82+
self._accumulator = self._get_text()
83+
self._equal_pressed = False
84+
85+
def _handle_equal(self):
86+
if self._operator is not None:
87+
if self._operand is None:
88+
self._operand = self._get_text()
89+
self._accumulator = calculate(self._accumulator, self._operator, self._operand)
90+
self._set_text(self._accumulator)
91+
self._equal_pressed = True
92+
93+
def add_input(self, input_key):
94+
try:
95+
if input_key == "AC":
96+
self._all_clear()
97+
elif input_key == "CE":
98+
self._clear_entry()
99+
elif self._operator is None and input_key == "0":
100+
pass
101+
elif len(input_key) == 1 and 48 <= ord(input_key) <= 57:
102+
self._handle_number(input_key)
103+
elif input_key in ('+', '-', '/', 'x'):
104+
self._handle_operator(input_key)
105+
elif input_key == ".":
106+
if not input_key in self._get_text():
107+
self._set_text(self._get_text() + input_key)
108+
self._set_button_ce(True)
109+
self._equal_pressed = False
110+
elif input_key == "+/-":
111+
self._set_text(calculate(self._get_text(), "*", "-1"))
112+
elif input_key == "%":
113+
self._set_text(calculate(self._get_text(), "/", "100"))
114+
elif input_key == "=":
115+
self._handle_equal()
116+
except (ZeroDivisionError, RuntimeError):
117+
self._all_clear()
118+
self._set_text("Error")

PyPortal_Calculator/code.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"""
2+
PyPortal Calculator Demo
3+
"""
4+
import time
5+
from collections import namedtuple
6+
import board
7+
import displayio
8+
from adafruit_display_text.label import Label
9+
from adafruit_bitmap_font import bitmap_font
10+
from adafruit_display_shapes.rect import Rect
11+
from adafruit_button import Button
12+
from calculator import Calculator
13+
import adafruit_touchscreen
14+
Coords = namedtuple("Point", "x y")
15+
16+
ts = adafruit_touchscreen.Touchscreen(board.TOUCH_XL, board.TOUCH_XR,
17+
board.TOUCH_YD, board.TOUCH_YU,
18+
calibration=((5200, 59000), (5800, 57000)),
19+
size=(320, 240))
20+
21+
# Settings
22+
BUTTON_WIDTH = 60
23+
BUTTON_HEIGHT = 30
24+
BUTTON_MARGIN = 8
25+
MAX_DIGITS = 29
26+
BLACK = 0x0
27+
ORANGE = 0xFF8800
28+
WHITE = 0xFFFFFF
29+
GRAY = 0x888888
30+
LABEL_OFFSET = 290
31+
32+
# Make the display context
33+
calc_group = displayio.Group(max_size=25)
34+
board.DISPLAY.show(calc_group)
35+
36+
# Make a background color fill
37+
color_bitmap = displayio.Bitmap(320, 240, 1)
38+
color_palette = displayio.Palette(1)
39+
color_palette[0] = GRAY
40+
bg_sprite = displayio.TileGrid(color_bitmap,
41+
pixel_shader=color_palette,
42+
x=0, y=0)
43+
calc_group.append(bg_sprite)
44+
45+
# Load the font
46+
font = bitmap_font.load_font("/fonts/Arial-12.bdf")
47+
buttons = []
48+
49+
# Some button placement functions
50+
def button_grid(row, col):
51+
return Coords(BUTTON_MARGIN * (row + 1) + BUTTON_WIDTH * row + 20,
52+
BUTTON_MARGIN * (col + 1) + BUTTON_HEIGHT * col + 40)
53+
54+
def make_button(row, col, label, width=1, color=WHITE, text_color=BLACK):
55+
pos = button_grid(row, col)
56+
new_button = Button(x=pos.x, y=pos.y,
57+
width=BUTTON_WIDTH * width + BUTTON_MARGIN * (width - 1),
58+
height=BUTTON_HEIGHT, label=label, label_font=font,
59+
label_color=text_color, fill_color=color, style=Button.ROUNDRECT)
60+
buttons.append(new_button)
61+
return new_button
62+
63+
border = Rect(20, 8, 280, 35, fill=WHITE, outline=BLACK, stroke=2)
64+
calc_display = Label(font, text="0", color=BLACK, max_glyphs=MAX_DIGITS)
65+
calc_display.y = 25
66+
67+
clear_button = make_button(0, 0, "AC")
68+
make_button(1, 0, "+/-")
69+
make_button(2, 0, "%")
70+
make_button(3, 0, "/", 1, ORANGE, WHITE)
71+
make_button(0, 1, "7")
72+
make_button(1, 1, "8")
73+
make_button(2, 1, "9")
74+
make_button(3, 1, "x", 1, ORANGE, WHITE)
75+
make_button(0, 2, "4")
76+
make_button(1, 2, "5")
77+
make_button(2, 2, "6")
78+
make_button(3, 2, "-", 1, ORANGE, WHITE)
79+
make_button(0, 3, "1")
80+
make_button(1, 3, "2")
81+
make_button(2, 3, "3")
82+
make_button(3, 3, "+", 1, ORANGE, WHITE)
83+
make_button(0, 4, "0", 2)
84+
make_button(2, 4, ".")
85+
make_button(3, 4, "=", 1, ORANGE, WHITE)
86+
87+
# Add the display and buttons to the main calc group
88+
calc_group.append(border)
89+
calc_group.append(calc_display)
90+
for b in buttons:
91+
calc_group.append(b.group)
92+
93+
calculator = Calculator(calc_display, clear_button, LABEL_OFFSET)
94+
95+
button = ""
96+
while True:
97+
point = ts.touch_point
98+
if point is not None:
99+
for i, b in enumerate(buttons):
100+
if b.contains(point) and button == "":
101+
b.selected = True
102+
button = b.label
103+
time.sleep(0.1)
104+
b.selected = False
105+
break
106+
else:
107+
if button != "":
108+
calculator.add_input(button)
109+
button = ""
110+
time.sleep(0.05)

0 commit comments

Comments
 (0)