173 lines
5.4 KiB
Python
Executable file
173 lines
5.4 KiB
Python
Executable file
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
import os
|
|
import time
|
|
import digitalio
|
|
import board
|
|
from PIL import Image, ImageOps
|
|
import numpy # pylint: disable=unused-import
|
|
from adafruit_rgb_display import st7789 # pylint: disable=unused-import
|
|
|
|
# Change to match your display
|
|
BUTTON_NEXT = board.D17
|
|
BUTTON_PREVIOUS = board.D22
|
|
|
|
# Configuration for CS and DC pins (these are PiTFT defaults):
|
|
cs_pin = digitalio.DigitalInOut(board.CE0)
|
|
dc_pin = digitalio.DigitalInOut(board.D25)
|
|
reset_pin = digitalio.DigitalInOut(board.D24)
|
|
|
|
def init_button(pin):
|
|
button = digitalio.DigitalInOut(pin)
|
|
button.switch_to_input()
|
|
button.pull = digitalio.Pull.UP
|
|
return button
|
|
|
|
# pylint: disable=too-few-public-methods
|
|
class Frame:
|
|
def __init__(self, duration=0):
|
|
self.duration = duration
|
|
self.image = None
|
|
|
|
# pylint: enable=too-few-public-methods
|
|
|
|
class AnimatedGif:
|
|
def __init__(self, display, width=None, height=None, folder=None):
|
|
self._frame_count = 0
|
|
self._loop = 0
|
|
self._index = 0
|
|
self._duration = 0
|
|
self._gif_files = []
|
|
self._frames = []
|
|
|
|
if width is not None:
|
|
self._width = width
|
|
else:
|
|
self._width = display.width
|
|
if height is not None:
|
|
self._height = height
|
|
else:
|
|
self._height = display.height
|
|
self.display = display
|
|
self.advance_button = init_button(BUTTON_NEXT)
|
|
self.back_button = init_button(BUTTON_PREVIOUS)
|
|
if folder is not None:
|
|
self.load_files(folder)
|
|
self.run()
|
|
|
|
def advance(self):
|
|
self._index = (self._index + 1) % len(self._gif_files)
|
|
|
|
def back(self):
|
|
self._index = (self._index - 1 + len(self._gif_files)) % len(self._gif_files)
|
|
|
|
def load_files(self, folder):
|
|
gif_files = [f for f in os.listdir(folder) if f.endswith(".gif")]
|
|
for gif_file in gif_files:
|
|
gif_file = os.path.join(folder, gif_file)
|
|
image = Image.open(gif_file)
|
|
# Only add animated Gifs
|
|
if image.is_animated:
|
|
self._gif_files.append(gif_file)
|
|
|
|
print("Found", self._gif_files)
|
|
if not self._gif_files:
|
|
print("No Gif files found in current folder")
|
|
exit() # pylint: disable=consider-using-sys-exit
|
|
|
|
def preload(self):
|
|
image = Image.open(self._gif_files[self._index])
|
|
print("Loading {}...".format(self._gif_files[self._index]))
|
|
if "duration" in image.info:
|
|
self._duration = image.info["duration"]
|
|
else:
|
|
self._duration = 0
|
|
if "loop" in image.info:
|
|
self._loop = image.info["loop"]
|
|
else:
|
|
self._loop = 1
|
|
self._frame_count = image.n_frames
|
|
self._frames.clear()
|
|
for frame in range(self._frame_count):
|
|
image.seek(frame)
|
|
# Create blank image for drawing.
|
|
# Make sure to create image with mode 'RGB' for full color.
|
|
frame_object = Frame(duration=self._duration)
|
|
if "duration" in image.info:
|
|
frame_object.duration = image.info["duration"]
|
|
frame_object.image = ImageOps.pad( # pylint: disable=no-member
|
|
image.convert("RGB"),
|
|
(self._width, self._height),
|
|
method=Image.Resampling.NEAREST,
|
|
color=(0, 0, 0),
|
|
centering=(0.5, 0.5),
|
|
)
|
|
self._frames.append(frame_object)
|
|
|
|
def play(self):
|
|
self.preload()
|
|
|
|
_prev_advance_btn_val = self.advance_button.value
|
|
_prev_back_btn_val = self.back_button.value
|
|
# Check if we have loaded any files first
|
|
if not self._gif_files:
|
|
print("There are no Gif Images loaded to Play")
|
|
return False
|
|
while True:
|
|
for frame_object in self._frames:
|
|
start_time = time.monotonic()
|
|
self.display.image(frame_object.image)
|
|
_cur_advance_btn_val = self.advance_button.value
|
|
_cur_back_btn_val = self.back_button.value
|
|
if not _cur_advance_btn_val and _prev_advance_btn_val:
|
|
self.advance()
|
|
return False
|
|
if not _cur_back_btn_val and _prev_back_btn_val:
|
|
self.back()
|
|
return False
|
|
|
|
_prev_back_btn_val = _cur_back_btn_val
|
|
_prev_advance_btn_val = _cur_advance_btn_val
|
|
while time.monotonic() < (start_time + frame_object.duration / 1000):
|
|
pass
|
|
|
|
if self._loop == 1:
|
|
return True
|
|
if self._loop > 0:
|
|
self._loop -= 1
|
|
|
|
def run(self):
|
|
while True:
|
|
auto_advance = self.play()
|
|
if auto_advance:
|
|
self.advance()
|
|
|
|
|
|
# Config for display baudrate (default max is 64mhz):
|
|
BAUDRATE = 64000000
|
|
|
|
# Setup SPI bus using hardware SPI:
|
|
spi = board.SPI()
|
|
|
|
disp = st7789.ST7789(
|
|
spi,
|
|
rotation=270,
|
|
width=170,
|
|
height=320,
|
|
x_offset=35,
|
|
cs=cs_pin,
|
|
dc=dc_pin,
|
|
rst=reset_pin,
|
|
baudrate=BAUDRATE,
|
|
)
|
|
# pylint: enable=line-too-long
|
|
|
|
if disp.rotation % 180 == 90:
|
|
disp_height = disp.width # we swap height/width to rotate it to landscape!
|
|
disp_width = disp.height
|
|
else:
|
|
disp_width = disp.width
|
|
disp_height = disp.height
|
|
|
|
gif_player = AnimatedGif(disp, width=disp_width, height=disp_height, folder="gifs")
|