今回はtkinterを駆使してフラッシュカードアプリを作ります。
デジタル単語帳みたいな感じで、表に覚えたい言語、裏に母国語を書いて覚えるというもの。
あんまりこれで覚えても実用的じゃない気もしますが、レッスンとして。
表部分はフランス語、裏は英語としています。
3秒間フランス語が出た後に自動的に裏カードとして英単語が出てきます。
下のバツとチェックのボタンは、覚えたらチェックボタンを押すと
その単語は次から出なくなるという動作をします。
流れとしては
・前回までに覚えていない部分のcsvを読み込む
・初めての場合は全ての単語が入っているcsvを読み込む
・フラッシュカードがスタートし、csvからランダムに単語を選ぶ。
・3秒間覚える単語を表示する
・ボタンを押さずにそのまま何もしない場合は3秒後に母国語を表示する
・チェックボタンを押すと全ての単語が入っているcsvから今表示した単語を削除して前回に覚えていない部分のcsvとして保存をする
・バツボタンを押すと削除はしない
・どちらのボタンを押しても、押した後に次の単語がランダムに表示される。
・ウィンドウを閉じると終了する
こんな感じ。
では始めます。
tkinterでUIをレイアウトする
from tkinter import *
import pandas as pd
import random
BG_COLOR = "#B1DDC6"
window = Tk()
window.title("Flash Card")
window.minsize(width=800, height=650)
window.config(bg=BG_COLOR, pady=50, padx=50)
img_right = PhotoImage(file="right.png")
img_wrong = PhotoImage(file="wrong.png")
img_card_front = PhotoImage(file="card_front.png")
img_card_back = PhotoImage(file="card_back.png")
//
def 入れるところ
//
canvas = Canvas(width=800, height=560, highlightthickness=0, bg=BG_COLOR)
canvas_bg = canvas.create_image(400, 280, image=img_card_front)
canvas.grid(row=0, column=0, columnspan=2)
right_btn = Button(image=img_right, highlightbackground=BG_COLOR, command=delete_word)
right_btn.grid(row=1, column=1)
wrong_btn = Button(image=img_wrong, highlightbackground=BG_COLOR, command=next_word)
wrong_btn.grid(row=1, column=0)
title = canvas.create_text(400, 100, text="", font=("Futura", 30, "italic"))
word = canvas.create_text(400, 250, text="", font=("Futura", 60, "italic"))
card_flip = window.after(3000, func=card_back)
next_word()
window.mainloop()
主にcanvasと2つのボタンのみで、canvasに今回はフランス語・英語のタイトルと
それぞれの単語を表示するようにします。
text=が空白なのは、すぐにランダムで単語を選んで表示するので表示する必要がないため。
チェックボタンにはdelete_word、バツボタンにはnext_wordという関数をいれていて、
delete_wordはcsvから単語を消して続ける機能、
next_wordは消さずに次の単語を表示するというもの。
アニメーションする機能は前回もやったwindow.afterの関数を使う。
3秒= 3000ms を超えたらfunc=card_backを起動します。
最初に読み込むcsvを決めて辞書型リストにする
try:
df = pd.read_csv("to_learn.csv")
except FileNotFoundError:
df = pd.read_csv("french_words.csv")
df_dic = df.to_dict(orient="records")
ここではtry、exceptを使って読み込むcsvを選びます。
最初の場合は覚えた単語を削除したcsvのto_learn.csvはないのでエラーになるので、
そうなった場合はデフォルトcsvのfrench_words.csvをpandasで読み込みます。
orient="records"というのは、通常であれば
{'French': {0: 'partie', 1: 'histoire', 2: 'seulement',...
というような辞書型リストになりますが、これを使うことにより
[{'French': 'partie', 'English': 'part'}, と表の1列ずつにまとまった辞書リストにフォーマットしてくれるオプションです。
単語をランダムに選んで表示する機能 = next_card を実装する
get_word = {}
def next_word():
global get_word, card_flip
window.after_cancel(card_flip)
get_word = random.choice(df_dic)
get_word_fr = get_word["French"]
canvas.itemconfig(title, text="French", fill="#000")
canvas.itemconfig(word, text=get_word_fr, fill="#000")
canvas.itemconfig(canvas_bg, image=img_card_front)
card_flip = window.after(3000, func=card_back)
最初にget_word を空の辞書リストで入れているのは、next_wordだけでなく
他の関数でも使うため。
globalでcard_flipを入れているのはmainloop手前のものを入れています。
というのもそれがないとエラーになってしまうのと
このあとにafter_cancel()を入れてボタンを3秒以内にクリックした時に
その時に起動しているcard_flipを一旦クリアしてから新しい単語で再度3秒カウントするのをバグなく起動したいからです。
ここで単語をランダムに選んでいるのと同時に、カードが裏返っているので
文字の色や背景などを初期状態に戻す処理をcanvas.itemconfigでいれています。
最後に再度3秒カウントするためのcard_flipを入れて終わり。
カードを裏返す機能= card_backを実装する
def card_back():
canvas.itemconfig(title, fill="white", text="English")
canvas.itemconfig(word, fill="white", text=get_word["English"])
canvas.itemconfig(canvas_bg, image=img_card_back)
これは先ほどの初期状態のものを裏返しバージョン。
覚えた単語を削除して新しいcsvに保存する処理を実装する
def delete_word():
df_dic.remove(get_word)
to_learn_df = pd.DataFrame(df_dic)
to_learn_df.to_csv("to_learn.csv", index=False)
next_word()
シンプルにremoveでその時表示している部分 = get_wordをごっそり削除した後に、
その辞書型リストをpandasでデータフレームに整形しなおし、csvで保存。
index=Falseにしているのはこれをしないとindexの数字がどんどん増えていってしまうため。
これが終わったらnext_word()で引き続き次の単語をランダムで選ぶようにしています。
ソースはGitにて。