Displaying more than one GIF isn't too much different than a single GIF. This page provides some tips. Memory management becomes more important.
This example also shows writing directly to the built-in display, which can save time in displaying frames for a smoother feel. The trade-off is researching how to write directly to the display as that can vary from one display type to another.
Download the Project Bundle
The example uses one code file and three sample animated GIF images. To get everything you need, click on the Download Project Bundle link below, and uncompress the .zip file.
Hook the PyPortal to your computer via a known good USB data+power cable. It should show up as a thumb drive named CIRCUITPY.
Using File Explorer/Finder (depending on your Operating System), drag the contents of the uncompressed bundle directory onto your board's CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.
The code below displays multiple GIFs, sequentially as you tap the screen. Copy the file code-multi-pyportal-direct.py to code.py. You can leave the names of the GIF files as they are.
# SPDX-FileCopyrightText: 2023 Anne Barela for Adafruit Industries # # SPDX-License-Identifier: MIT # # Play Multiple GIF files on a PyPortal # Requires CircuitPython 8.1.0-beta.1 or later # Updated 4/4/2023 import time import gc import os import struct import board import gifio import adafruit_touchscreen # Get a dictionary of GIF filenames at the passed base directory def get_files(base): allfiles = os.listdir(base) file_names = [] for _, filetext in enumerate(allfiles): if not filetext.startswith("."): if filetext not in ('boot_out.txt', 'System Volume Information'): if filetext.endswith(".gif"): file_names.append(filetext) return file_names display = board.DISPLAY # Take over display to drive directly display.auto_refresh = False display_bus = display.bus # Pyportal has a touchscreen, a touch advances to next GIF file WIDTH = board.DISPLAY.width HEIGHT = board.DISPLAY.height ts = adafruit_touchscreen.Touchscreen(board.TOUCH_XL, board.TOUCH_XR, board.TOUCH_YD, board.TOUCH_YU, calibration=((5200, 59000), (5800, 57000)), size=(WIDTH, HEIGHT)) files = get_files("/") for i in range(len(files)): odg = gifio.OnDiskGif(files[i]) # Skip Feather GIFs if put on PyPortal if odg.width != WIDTH: print("File "+files[i]+" not right width, skipping\n") continue start = time.monotonic() next_delay = odg.next_frame() # Load the first frame end = time.monotonic() call_delay = end - start # Display GIF file frames until screen touched (for PyPortal) while True: sleeptime = max(0, next_delay - call_delay) time.sleep(sleeptime) if ts.touch_point is not None: break next_delay = odg.next_frame() display_bus.send(42, struct.pack(">hh", 0, odg.bitmap.width - 1)) display_bus.send(43, struct.pack(">hh", 0, odg.bitmap.height - 1)) display_bus.send(44, odg.bitmap) # End while # Clean up memory odg.deinit() odg = None gc.collect() # End for
How It Works
First the code imports all the libraries needed for the example. Only the Adafruit_Touchscreen
library needs an external file, the rest are included inside CircuitPython. Ensure adafruit_touchscreen.mpy is in the /lib folder. The display is addressed directly through display_bus
. The screen width of 320 px and height 240 px is retrieved through the internal board definition for the touchscreen definition.
The function get_files()
gets the names of all the GIF files in the root (main) directory on the device. This is a convenience - if you only want a predefined list, make a dictionary named files with the filenames, like this:
files = ['/file1.gif', '/file2.gif']
To register user input, the touchscreen is used. This isn't necessary in most applications but having a convenient input method can be handy, especially if the PyPortal is in a case.
Each GIF file found on the PyPortal is opened via gifio
. The first frame of the current file is loaded using odg.next_frame()
. The time it takes to call the function is noted to make an adjustment for the time between frames later.
A While
loop constantly fetches frames and displays them with a file-defined pause between frames. As calling the next_frame
takes time (measured earlier), that time is subtracted from the frame rate specified in the file.
The frame contents are sent direct to the display via calls to display_bus.send
rather than using displayio
. This makes displaying frames faster, making for smoother playback.
If the screen is touched, the memory is cleaned up and the next file on the PyPortal is played until they all have been played and the program ends.
If you wish to put the GIF files in a subdirectory, change the line files = get_files("/"
)
to your preferred directory name, for example files = get_files("/Images")
.
Text editor powered by tinymce.