Skip to content

Instantly share code, notes, and snippets.

@kmatch98
Last active September 25, 2020 16:39
Show Gist options
  • Save kmatch98/bffd7f9097dc1b797b2f140b480bd63d to your computer and use it in GitHub Desktop.
Save kmatch98/bffd7f9097dc1b797b2f140b480bd63d to your computer and use it in GitHub Desktop.
Button layout simplification using a grid layout.\
import os
import board
import displayio
from adafruit_bitmap_font import bitmap_font
from adafruit_button import Button
import adafruit_touchscreen
# These pins are used as both analog and digital! XL, XR and YU must be analog
# and digital capable. YD just need to be digital
ts = adafruit_touchscreen.Touchscreen(
board.TOUCH_XL,
board.TOUCH_XR,
board.TOUCH_YD,
board.TOUCH_YU,
calibration=((5200, 59000), (5800, 57000)),
size=(320, 240),
)
# the current working directory (where this file is)
cwd = ("/" + __file__).rsplit("/", 1)[0]
fonts = [
file
for file in os.listdir(cwd + "/fonts/")
if (file.endswith(".bdf") and not file.startswith("._"))
]
for i, filename in enumerate(fonts):
fonts[i] = cwd + "/fonts/" + filename
print(fonts)
THE_FONT = "/fonts/Arial-16.bdf"
DISPLAY_STRING = "Button Text"
# Make the display context
splash = displayio.Group(max_size=20)
board.DISPLAY.auto_refresh=False
board.DISPLAY.show(splash)
BUTTON_WIDTH = 80
BUTTON_HEIGHT = 40
BUTTON_MARGIN = 20
##########################################################################
# Make a background color fill
color_bitmap = displayio.Bitmap(320, 240, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x404040
bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
print(bg_sprite.x, bg_sprite.y)
splash.append(bg_sprite)
##########################################################################
# Load the font
font = bitmap_font.load_font(THE_FONT)
buttons = []
# Default button styling:
button_0 = Button(
x=BUTTON_MARGIN,
y=BUTTON_MARGIN,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
label="butn0",
label_font=font,
)
buttons.append(button_0)
# a button with no indicators at all
button_1 = Button(
x=BUTTON_MARGIN * 2 + BUTTON_WIDTH,
y=BUTTON_MARGIN,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
label_font=font,
label="butn1",
label_color=0x00FF00,
fill_color=0x0,
outline_color=None,
)
buttons.append(button_1)
# various colorings
button_2 = Button(
x=BUTTON_MARGIN * 3 + 2 * BUTTON_WIDTH,
y=BUTTON_MARGIN,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
label="butn2",
label_font=font,
label_color=0x0000FF,
fill_color=0x00FF00,
outline_color=0xFF0000,
)
buttons.append(button_2)
# Transparent button with text
button_3 = Button(
x=BUTTON_MARGIN,
y=BUTTON_MARGIN * 2 + BUTTON_HEIGHT,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
label="butn3",
label_font=font,
label_color=0x0,
fill_color=None,
outline_color=None,
)
buttons.append(button_3)
# a roundrect
button_4 = Button(
x=BUTTON_MARGIN * 2 + BUTTON_WIDTH,
y=BUTTON_MARGIN * 2 + BUTTON_HEIGHT,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
label="butn4",
label_font=font,
style=Button.ROUNDRECT,
)
buttons.append(button_4)
# a shadowrect
button_5 = Button(
x=BUTTON_MARGIN * 3 + BUTTON_WIDTH * 2,
y=BUTTON_MARGIN * 2 + BUTTON_HEIGHT,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
label="butn5",
label_font=font,
style=Button.SHADOWRECT,
)
buttons.append(button_5)
# a shadowroundrect
button_6 = Button(
x=BUTTON_MARGIN,
y=BUTTON_MARGIN * 3 + BUTTON_HEIGHT * 2,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
label="butn6",
label_font=font,
style=Button.SHADOWROUNDRECT,
selected_label=0x0,
selected_fill=0x707070,
selected_outline=0xa0a0a0,
)
buttons.append(button_6)
for b in buttons:
splash.append(b)
print("width: {}".format(board.DISPLAY.width))
def button_grid_layout(button, grid_size, display=None, window_size=None, window_offset=(0,0), position=(0,0), button_size=(1,1), padding=10):
if display is not None:
window_size=(display.width, display.height)
elif window_size is None:
print('Must provide either a display or window_size=(width,height) entry')
grid_size_x=grid_size[0]
grid_size_y=grid_size[1]
grid_position_x=position[0]
grid_position_y=position[1]
button_size_x=button_size[0]
button_size_y=button_size[1]
button.width=int(button_size_x*window_size[0]/grid_size_x)-2*padding
button.height=int(button_size_y*window_size[1]/grid_size_y)-2*padding
button.x=window_offset[0] + int(grid_position_x*window_size[0]/grid_size_x)+padding
button.y=window_offset[1] + int(grid_position_y*window_size[1]/grid_size_y)+padding
my_grid_size=3,4
my_padding=8
my_window_size=(240,200)
my_window_offset=(320-240, 240-200)
button_grid_layout(button_0, window_size=my_window_size, window_offset=my_window_offset, grid_size=my_grid_size, position=(0,0), button_size=(1,1), padding=my_padding)
button_grid_layout(button_1, window_size=my_window_size, window_offset=my_window_offset, grid_size=my_grid_size, position=(1,0), button_size=(1,1), padding=my_padding)
button_grid_layout(button_2, window_size=my_window_size, window_offset=my_window_offset, grid_size=my_grid_size, position=(2,0), button_size=(1,1), padding=my_padding)
button_grid_layout(button_3, window_size=my_window_size, window_offset=my_window_offset, grid_size=my_grid_size, position=(0,1), button_size=(1,1), padding=my_padding)
button_grid_layout(button_4, window_size=my_window_size, window_offset=my_window_offset, grid_size=my_grid_size, position=(1,1), button_size=(2,1), padding=my_padding)
button_grid_layout(button_5, window_size=my_window_size, window_offset=my_window_offset, grid_size=my_grid_size, position=(0,2), button_size=(1,2), padding=my_padding)
button_grid_layout(button_6, window_size=my_window_size, window_offset=my_window_offset, grid_size=my_grid_size, position=(1,2), button_size=(2,2), padding=my_padding)
board.DISPLAY.refresh()
board.DISPLAY.auto_refresh=True
while True:
p = ts.touch_point
if p:
print(p)
for i, b in enumerate(buttons):
if b.contains(p):
print("Button %d pressed" % i)
b.selected = True
else:
b.selected = False
# The MIT License (MIT)
#
# Copyright (c) 2019 Limor Fried for Adafruit Industries
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_button`
================================================================================
UI Buttons for displayio
* Author(s): Limor Fried
Implementation Notes
--------------------
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
"""
from micropython import const
import displayio
from adafruit_display_text.label import Label
from adafruit_display_shapes.rect import Rect
from adafruit_display_shapes.roundrect import RoundRect
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Button.git"
def _check_color(color):
# if a tuple is supplied, convert it to a RGB number
if isinstance(color, tuple):
r, g, b = color
return int((r << 16) + (g << 8) + (b & 0xFF))
return color
class Button(displayio.Group):
# pylint: disable=too-many-instance-attributes, too-many-locals
"""Helper class for creating UI buttons for ``displayio``.
:param x: The x position of the button.
:param y: The y position of the button.
:param width: The width of the button in pixels.
:param height: The height of the button in pixels.
:param name: The name of the button.
:param style: The style of the button. Can be RECT, ROUNDRECT, SHADOWRECT, SHADOWROUNDRECT.
Defaults to RECT.
:param fill_color: The color to fill the button. Defaults to 0xFFFFFF.
:param outline_color: The color of the outline of the button.
:param label: The text that appears inside the button. Defaults to not displaying the label.
:param label_font: The button label font.
:param label_color: The color of the button label text. Defaults to 0x0.
:param selected_fill: Inverts the fill color.
:param selected_outline: Inverts the outline color.
:param selected_label: Inverts the label color.
"""
RECT = const(0)
ROUNDRECT = const(1)
SHADOWRECT = const(2)
SHADOWROUNDRECT = const(3)
def __init__(
self,
*,
x,
y,
width,
height,
name=None,
style=RECT,
fill_color=0xFFFFFF,
outline_color=0x0,
label=None,
label_font=None,
label_color=0x0,
selected_fill=None,
selected_outline=None,
selected_label=None
):
super().__init__(x=x, y=y)
self.x = x
self.y = y
self._width = width
self._height = height
self._font = label_font
self._selected = False
self.name = name
#self._label = label
self._label_object = None
self.body = self.fill = self.shadow = None
self.fill_color = _check_color(fill_color)
self.outline_color = _check_color(outline_color)
self._label_color = label_color
self._label_font = label_font
# Selecting inverts the button colors!
self.selected_fill = _check_color(selected_fill)
self.selected_outline = _check_color(selected_outline)
self.selected_label = _check_color(selected_label)
self.style=style
self._update_button(label) # Create the button elements.
def _update_button(self, label):
# store any existing text if there is not a new label
if self._label_object and self and (self[-1] == self._label_object) and (label is None):
label=self._label_object.text
for _ in range(len(self)): # clear any existing items in the current button Group
self.pop()
print("len self: {}".format(len(self)))
if self.selected_fill is None and self.fill_color is not None:
self.selected_fill = (~self.fill_color) & 0xFFFFFF
if self.selected_outline is None and self.outline_color is not None:
self.selected_outline = (~self.outline_color) & 0xFFFFFF
if (self.outline_color is not None) or (self.fill_color is not None):
if self.style == Button.RECT:
self.body = Rect(
0,
0,
self._width,
self._height,
fill=self.fill_color,
outline=self.outline_color,
)
elif self.style == Button.ROUNDRECT:
self.body = RoundRect(
0,
0,
self._width,
self._height,
r=10,
fill=self.fill_color,
outline=self.outline_color,
)
elif self.style == Button.SHADOWRECT:
self.shadow = Rect(2, 2, self._width - 2, self._height - 2, fill=self.outline_color)
self.body = Rect(
0,
0,
self._width - 2,
self._height - 2,
fill=self.fill_color,
outline=self.outline_color,
)
elif self.style == Button.SHADOWROUNDRECT:
self.shadow = RoundRect(
2, 2, self._width - 2, self._height - 2, r=10, fill=self.outline_color
)
self.body = RoundRect(
0,
0,
self._width - 2,
self._height - 2,
r=10,
fill=self.fill_color,
outline=self.outline_color,
)
if self.shadow:
self.append(self.shadow)
self.append(self.body)
self.label=label
@property
def width(self):
return self._width
@width.setter
def width(self, width):
self._width=width
self._update_button(None)
@property
def height(self):
return self._height
@height.setter
def height(self, height):
self._height=height
self._update_button(None)
@property
def label(self):
"""The text label of the button"""
return self._label_object.text
@label.setter
def label(self, newtext):
print("newtext: {}".format(newtext))
if self._label_object and self and (self[-1] == self._label_object):
self.pop()
self._label_object = None
if not newtext or (self._label_color is None): # no new text
return # nothing to do!
if not self._label_font:
raise RuntimeError("Please provide label font")
# This is odd usage, since the input value of label was a text string, and now it is defined as a Label object.
self._label_object = Label(self._label_font, text=newtext)
dims = self._label_object.bounding_box
print("self.width: {}, self.height: {} dims2: {}, dims3: {}".format(self.width, self.height, dims[2], dims[3]))
if dims[2] >= self.width or dims[3] >= self.height:
raise RuntimeError("Button not large enough for label")
self._label_object.x = (self.width - dims[2]) // 2
self._label_object.y = self.height // 2
self._label_object.color = self._label_color
self.append(self._label_object)
if (self.selected_label is None) and (self._label_color is not None):
self.selected_label = (~self._label_color) & 0xFFFFFF
@property
def selected(self):
"""Selected inverts the colors."""
return self._selected
@selected.setter
def selected(self, value):
if value == self._selected:
return # bail now, nothing more to do
self._selected = value
if self._selected:
new_fill = self.selected_fill
new_out = self.selected_outline
new_label = self.selected_label
if self.style == Button.SHADOWROUNDRECT:
self.shadow.x=0
self.shadow.y=0
self.shadow.fill=self.selected_outline
self.body.x=2
self.body.y=2
else:
new_fill = self.fill_color
new_out = self.outline_color
new_label = self._label_color
if self.style == Button.SHADOWROUNDRECT:
self.shadow.x=2
self.shadow.y=2
self.shadow.fill=self.outline_color
self.shadow.outline=self.outline_color
self.body.x=0
self.body.y=0
# update all relevant colors!
if self.body is not None:
self.body.fill = new_fill
self.body.outline = new_out
if self._label_object is not None:
self._label_object.color = new_label
@property
def group(self):
"""Return self for compatibility with old API."""
print(
"Warning: The group property is being deprecated. "
"User code should be updated to add the Button directly to the "
"Display or other Groups."
)
return self
def contains(self, point):
"""Used to determine if a point is contained within a button. For example,
``button.contains(touch)`` where ``touch`` is the touch point on the screen will allow for
determining that a button has been touched.
"""
return (self.x <= point[0] <= self.x + self.width) and (
self.y <= point[1] <= self.y + self.height
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment