We’ll start off by working through a quick app that just does the very basics we need to study. It’ll load up some cards out of a JSON file, shuffle them, and go through them one at a time as you press the D button on the MagTag. In the next section, we’ll add some fancier features, like sorting your cards into categories, and letting you pick which ones you’d like to study per session.
In this code, we’ll be using a PCF font for international character sets. PCF fonts offer a little extra efficiency compared to raw BDF (bitmap) fonts, so they’re easier to fit on CircuitPython drives. We’ll be using one today for the Japanese Hiragana and Katakana characters used in this deck.
Installing Project Code
CIRCUITPY drive. Then you need to update code.py with the example script.
Thankfully, we can do this in one go. In the example below, click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, open the directory MagTag_Flashcards/basic/ and then click on the directory that matches the version of CircuitPython you're using and copy the contents of that directory to your CIRCUITPY drive.
CIRCUITPY
# SPDX-FileCopyrightText: 2021 Anne Barela for Adafruit Industries # # SPDX-License-Identifier: MIT import time import json import terminalio import digitalio import random from adafruit_magtag.magtag import MagTag # Set up the magtag print("Magtag Basic Flashcards") magtag = MagTag() # Import cards cards = {} with open("deck.json") as fp: cards = json.load(fp) # Create a text area magtag.add_text( text_font="yasashi20.pcf", text_position=( magtag.graphics.display.width // 2, magtag.graphics.display.height // 2, ), line_spacing=0.85, text_anchor_point=(0.5, 0.5), ) # Set up buttons cur_btn = False prev_btn = False while True: # Shuffle the deck cards = sorted(cards, key=lambda _: random.random()) for card in cards: # Show the first side and wait for the D button text = ''.join(magtag.wrap_nicely(card[0], 20)) magtag.set_text(text) while True: cur_btn = magtag.peripherals.button_d_pressed if cur_btn and not prev_btn: print("Show Result") time.sleep(0.1) break prev_btn = cur_btn # Show the second side and wait for the D button text = '\n'.join(magtag.wrap_nicely(card[1], 11)) text += '\n' text += '\n'.join(magtag.wrap_nicely(card[2], 20)) print(text) magtag.set_text(text) while True: cur_btn = magtag.peripherals.button_d_pressed if cur_btn and not prev_btn: print("Next Card") time.sleep(0.1) break prev_btn = cur_btn
Example Deck
In addition, here's an example "deck" of cards, written in JSON. In case you've never used JSON before, it stands for JavaScript Object Notation: it's a format of writing data (like arrays and key-value pairs) that was originally derived from Javascript but is now commonly used across many different programming languages. In this example deck file, we create a list of lists, using square brackets []
to enclose the lists and commas ,
to separate list elements.
[ ["Monday","げつ ようび","Getsu yōbi"], ["Tuesday","か ようび","Ka yōbi"], ["Wednesday","すい ようび","Sui yōbi"], ["Thursday","もく ようび","Moku yōbi"], ["Friday","きん ようび","Kin yōbi"], ["Saturday","ど ようび","Do yōbi"], ["Sunday","にち ようび","Nichi yōbi"] ]
This particular example is written for studying Japanese, but it should work fine for any topic you want - Spanish, geography, code reference, obscure Star Trek trivia, whatever. Just make sure to tweak the text-displaying parts of the code if you add or remove “sections” of the card structure.
How does it work?
Let's walk through this code step by step. First, we import all the required libraries, and create an object for the magtag that contains all the library features, like detecting button presses. We'll also tell the Serial Port we've started up the program.
import time import json import terminalio import digitalio import random from adafruit_magtag.magtag import MagTag # Set up the magtag print("Magtag Basic Flashcards") magtag = MagTag()
Then, we use the json
library to open our deck file, and interpret the JSON syntax into a list-of-lists that we can use in Python.
# Import cards cards = {} with open("deck.json") as fp: cards = json.load(fp)
As the last part of our setup, we create a text object to hold the sides of our flashcard. This uses the Yasashi 20 point PCF font, which contains both english and Japanese Hira and Katakana characters (no Kanji, though, sadly - that's a bit too much for the size of the Magtag's memory).
We'll put it right in the center of the screen.
# Create a text area magtag.add_text( text_font="yasashi20.pcf", text_position=( magtag.graphics.display.width // 2, magtag.graphics.display.height // 2, ), line_spacing=0.85, text_anchor_point=(0.5, 0.5), )
To actually change the sides of the card, and to move to the next card, we'll need to detect when a magtag button is pressed.
The magtag library contains the attribute magtag.peripherals.button_d_pressed
, which will tell us whether the D button is currently up or down. But this value by itself isn't able to detect when the button is pressed. To do that, we'll create a couple of memory variables:
cur_btn = False prev_btn = False
Whenever we want to wait for a button press, we'll enter a short while
loop, and do nothing until we see the button change from an "up" state to a "down" state.
while True: cur_btn = magtag.peripherals.button_d_pressed if cur_btn and not prev_btn: print("Show Result") time.sleep(0.1) break prev_btn = cur_btn
Before we actually start using the deck, we want to make sure it isn't in the same order every time. Circuitpython doesn't have access to the random.shuffle()
function, but we can fake it by using the built in function sorted()
and indexes from random.random()
to achieve the same effect.
cards = sorted(cards, key=lambda _: random.random())
When we want to display text, we use the built in magtag set_text
function. By using wrap_nicely
, we can make sure that the card text won't ever go off the side of the magtag - note that characters from other languages (like Hiragana) can be larger than roman ones, so expect to change the length parameter based on the kind of text you want to display.
# Example Hira wrapping (fewer characters) text = '\n'.join(magtag.wrap_nicely(card[1], 11)) # Example Roman wrapping (more characters) text += '\n'.join(magtag.wrap_nicely(card[2], 20)) # Set the contents of the text field magtag.set_text(text)
We combine all these elements into the program's final main loop, which will run through the cards in a random order forever, waiting on user button presses to move forward.
while True: # Shuffle the deck cards = sorted(cards, key=lambda _: random.random()) for card in cards: # Show the first side and wait for the D button text = ''.join(magtag.wrap_nicely(card[0], 20)) magtag.set_text(text) while True: cur_btn = magtag.peripherals.button_d_pressed if cur_btn and not prev_btn: print("Show Result") time.sleep(0.1) break prev_btn = cur_btn # Show the second side and wait for the D button text = '\n'.join(magtag.wrap_nicely(card[1], 11)) text += '\n' text += '\n'.join(magtag.wrap_nicely(card[2], 20)) print(text) magtag.set_text(text) while True: cur_btn = magtag.peripherals.button_d_pressed if cur_btn and not prev_btn: print("Next Card") time.sleep(0.1) break prev_btn = cur_btn
Page last edited January 21, 2025
Text editor powered by tinymce.