Flashcards for iPad using Pythonista
Since I’ve started using Duolingo, I occasionally save a phrase that I think might be worth remembering. I save it very simply: by taking a screenshot. I do this because I think it will be helpful, for the learning process, to memorize some phrases as well as run through the app normally.
What I’ve been doing is occasionally going into the Photos app on the iPad and scanning through the “flashcards” I have thus created. As the number of phrases increases, it has occurred to me that a random flashcard app might be useful.
This is, of course, easy to create in Pythonista.
In the interface builder, two elements are required, a button to show the image and a button to choose the album to get the images from.
button name | x, y | size | flex | border | radius | border color | title | action |
---|---|---|---|---|---|---|---|---|
flasher | 6, 12 | 756, 880 | WH | 2 | 0 | #6e6e6e | Tap To Start | showCard |
albumChooser | 166, 900 | 440, 30 | WLRT | 1 | 8 | #6e6e6e | No Albums Found | chooseAlbum |
The overall view I’ve set to 768x960. The two titles—Tap To Start and No Albums Found—are defaults; the image where the flashcards go will initially not have an image in it and will say “Tap To Start”. Once an image is chosen, that title will go away, by setting its title to an empty string.
The very first function that it runs is:
[toggle code]
-
def getAlbums():
- albums = {}
-
for album in photos.get_albums():
-
if 'phrases' in album.title:
- albums[album.title] = album
-
if 'phrases' in album.title:
- return albums
This creates a dictionary of album names, from all albums that have the word “phrases” in their title. In my case, these are:
- French phrases
- French phrases (Good)
- French phrases (Very Good)
- Italian phrases
- Italian phrases (Good)
- Italian phrases (Very Good)
- Italian phrases (Great)
On my Mac, these albums are smart albums: I mark all Duolingo screenshots as either French or Italian and as flashcards using keywords. I also use ratings keywords for 1 star, 2 star, 3 star, 4 star, and 5 star. The latter three correspond to Good, Very Good, and Great, which means I can focus in on those phrases I most want to learn.
The drop-down (or, in this case, pop-up) menu for choosing which album to pull flashcards from is simple. When setting up the view, it creates an albumDrop and sets its hidden to True, so that it isn’t visible. When I press the button to choose a new album, it just unhides it:
[toggle code]
-
def chooseAlbum(chooser):
- albumDrop.selected_row = 0, albumList.items.index(view['albumChooser'].title)
- albumDrop.hidden = False
The first line selects the current album, and the second line unhides the drop-down. When an album is chosen, the delegate hides the drop-down again.
A new flashcard is displayed whenever I tap on the flashcard display area. That’s why I made it a ui.Button rather than a ui.ImageView.
[toggle code]
-
def showCard(flasher):
- album = albums[view['albumChooser'].title]
- card = random.choice(album.assets)
- #if there are more than one card in this album, make sure we aren't repeating the current card
-
if view.currentCard and len(album.assets) > 1:
-
while card == view.currentCard:
- card = random.choice(album.assets)
-
while card == view.currentCard:
- view.newCard(flasher, card)
- flasher.title = ''
First, it gets the album, using the title of the albumChooser button as the key for the albums dictionary. Then, it chooses an image at random from that album’s assets. If (as should be the case in a flashcard album) there are more than one image in the album, it also makes sure that the new image isn’t the same as the old image. Finally, it asks the view to replace the image with the new card and makes sure that the flashcard display area no longer has a title.
Here is the full code:
[toggle code]
- import ui, photos
- import random
-
class FlashView(ui.View):
-
def __init__(self):
- self.currentCard = None
-
def draw(self):
- self.frameCard()
-
def resetFlasher(self, flasher):
- flasher.x = 6
- flasher.y = 12
- flasher.width = self.width - 12
- flasher.height = self.height - 80
-
def frameCard(self):
- card = self.currentCard
-
if not card:
- return
- flasher = self['flasher']
- self.resetFlasher(flasher)
-
if card.pixel_width/card.pixel_height > flasher.width/flasher.height:
- #need to reduce the height of the flashcard display
- oldHeight = flasher.height
- newHeight = flasher.width*card.pixel_height/card.pixel_width
- flasher.height = newHeight
- flasher.y += (oldHeight-newHeight)/2
-
elif card.pixel_width/card.pixel_height < flasher.width/flasher.height:
- #need to reduce the width of the flashcard display
- oldWidth = flasher.width
- newWidth = flasher.height*card.pixel_width/card.pixel_height
- flasher.width = newWidth
- flasher.x += (oldWidth-newWidth)/2
-
def newCard(self, flasher, card):
- view.currentCard = card
- flasher.background_image = card.get_ui_image()
- self.frameCard()
-
def __init__(self):
-
def getAlbums():
- albums = {}
-
for album in photos.get_albums():
-
if 'phrases' in album.title:
- albums[album.title] = album
-
if 'phrases' in album.title:
- return albums
-
def chooseAlbum(chooser):
- albumDrop.selected_row = 0, albumList.items.index(view['albumChooser'].title)
- albumDrop.hidden = False
-
class AlbumSelector():
-
def tableview_did_select(self, tableview, section, row):
- #highlight the selection
- tableview.reload()
- #change the button text
- view['albumChooser'].title = albumList.items[row]
- #hide the drop-down again
- albumDrop.hidden = True
-
def tableview_did_select(self, tableview, section, row):
-
def showCard(flasher):
- album = albums[view['albumChooser'].title]
- card = random.choice(album.assets)
- #if there are more than one card in this album, make sure we aren't repeating the current card
-
if view.currentCard and len(album.assets) > 1:
-
while card == view.currentCard:
- card = random.choice(album.assets)
-
while card == view.currentCard:
- view.newCard(flasher, card)
- flasher.title = ''
- #populate list of albums
- albums = getAlbums()
- #drop-down for choosing an album
- albumList = ui.ListDataSource(sorted(albums.keys()))
- albumList.delete_enabled = False
- albumDrop = ui.TableView()
- albumDrop.delegate = AlbumSelector()
- albumDrop.hidden = True
- #display app
- view = ui.load_view()
- view.add_subview(albumDrop)
- #set default album as the first one in the list
- view['albumChooser'].title = albumList.items[0]
- view.present()
- #align album list dropdown to button
- albumDrop.width = view['albumChooser'].width*1.8
- albumDrop.row_height = view['albumChooser'].height
- albumDrop.height = albumDrop.row_height*(len(albums))
- albumDrop.x = view['albumChooser'].x
- albumDrop.y = view['albumChooser'].y - albumDrop.height
- albumDrop.border_width = view['albumChooser'].border_width
- albumDrop.border_color = view['albumChooser'].border_color
- albumDrop.data_source = albumList
- albumDrop.reload()
You can, of course, download the zip file and install it yourself in Pythonista (Zip file, 2.9 KB) if you wish.
This code can, of course, be used to display the photos from any album at random. If you’re using it for something other than phrases, you’ll need to change the way that the app collects names of albums from your photo albums. As long as getAlbums returns a dictionary of albums, keyed off of the album title, the rest of the app won’t care.
I had a strong sense of nostalgia while building this app. The very first program that I wrote for public consumption was a Spanish flashcard program in BASIC on the TRS-80 Model I. It was “RS-80Tay, Aysay, Hatway?”1 in the February, 1982 issue of 80 microcomputing. It was the same basic idea, albeit with scoring and quizzing: input phrases, and then have the computer quiz you on them.
Pythonista elicits, for me, a lot of the same sense of wonder, excitement, and immediacy that programming with BASIC did in the eighties, and programming with HyperCard did in the nineties. Whenever I think of something really useful that I ought to be able to do with the iPad, and there’s no app for that yet, it’s fun to make it in Pythonista. Whether it’s on-the-fly data collection and analysis or augmenting everyday blog reading, I can think of a potential solution, fool around with it without hurting anything, and see useful results immediately.
No, I did not choose the title. I wanted to call it “La Computadora Internacional”.
↑
- FlashCard for Pythonista (Zip file, 2.9 KB)
- A flashcard program for the iPad, using Pythonista. It displays images at random from specific photo albums.
- 80 Microcomputing Magazine February 1982 at Internet Archive
- A Special Education Issue of 80-Microcomputing: “Will Computers Replace Teachers?”
- DieSquare for iOS
- Are your dice biased? Perform on-the-fly chi-square tests using your iPad or iPhone and Pythonista.
- Preflighting blog comments in the Pythonista share screen
- I now use Pythonista’s sharing extension to ensure that comments on other people’s blogs are appropriately formatted.
- Pythonista
- “Bring the Zen of Python to iOS.”
More iOS programming environments
- Preflighting blog comments in the Pythonista share screen
- I now use Pythonista’s sharing extension to ensure that comments on other people’s blogs are appropriately formatted.
- DieSquare for iOS
- Are your dice biased? Perform on-the-fly chi-square tests using your iPad or iPhone and Pythonista.
- HotPaw Basic on iOS
- Looks like there’s a minor renaissance in programming languages on the iPhone and iPad. HotPaw BASIC is one of the first.
More languages
- The First Language
- Scholars once believed that, or seriously discussed whether, Hebrew was the first language of mankind. In a fantasy game, there really can have been a first, holy language of the gods.
More Pythonista
- Preflighting blog comments in the Pythonista share screen
- I now use Pythonista’s sharing extension to ensure that comments on other people’s blogs are appropriately formatted.
- DieSquare for iOS
- Are your dice biased? Perform on-the-fly chi-square tests using your iPad or iPhone and Pythonista.
- Draw a circle on an iPad map from three points in Pythonista
- Use Pythonista on the iPad to import a map screenshot, tap three points, and draw a circle from those three points. This simple shell can be used for other on-the-fly programming, too.