Saturday, October 29, 2022

Python - Project - Flash Card (Day 31)

This is a 100 Days challenge to learn a new language (Python). 100 Days of Code - The Complete Python Pro Bootcamp 

I will post some notes to motivate myself to finish this challenge.


Goal



1. Using Tkinter to build a GUI
a. Using canvas to mix images and texts
b. Binding functions to buttons
c. Utilizing grid layout for the better look
d. Lean how to update Tkinter elements

2. Deal with csv file through pandas
a. Support reading and writting
b. Studying some parameters when transform DataFrame to other DataType

DataFrame.to_dict(orient="records")

3. Using after and after_cancel to manage events

4. Try to use Error Handling as we learned on Day 30


Requirements



1. Build a flash card app which read data from csv file.

2. After 3 seconds, app should flip the card to see the translated word.

3. Next word should be shown after both buttons got clicked.

4. Once users click 'mark' button (or I knew button), then we should not let users see this word anymore.

5. Save the progress to csv file and read data from it from the next play.


Project - Flash Card





Ex:
import tkinter as tk
import pandas as pd
import random
import os

BACKGROUND_COLOR = "#B1DDC6"
FONT = "Ariel"
ALL_WORDS_CSV_FILE = "data/1000 words (en-to-zh-tw).csv"
WORDS_TO_LEARN_CSV_FILE = "data/words_to_learn.csv"

# Variables
words_dic = {}
selected_word = {}
filp_timer = ""

# Read a comma-separated values (csv) file into DataFrame.
# NOTE:
# Using Exception handling here for practice only becuse it is easy
# to be abused
# It is better to find a proper way
# (such as adding some if-else condition)
try:
    words_to_learn_data_frame = pd.read_csv(WORDS_TO_LEARN_CSV_FILE)
except FileNotFoundError:
    # If we cannot find the historical data,
     # then we read data from raw csv
    all_words_data_frame = pd.read_csv(ALL_WORDS_CSV_FILE)
    words_dic = all_words_data_frame.to_dict(orient="records")
else:
    words_dic = words_to_learn_data_frame.to_dict(orient="records")


def game_over():
    """Show game over message, and remvoe the historical data"""

    # Clear title and word text
    canvas.delete(title_text)
    canvas.delete(word_text)

    # Add game over message
    canvas.create_text(
        400,
        170,
        text="You have learned all words",
        fill="black",
        font=(FONT, 40, "bold"),
    )

    # Remove 'words_to_learn.csv'
    if os.path.exists(WORDS_TO_LEARN_CSV_FILE):
        os.remove(WORDS_TO_LEARN_CSV_FILE)


def right_btn_clicked():
    """Need to remove the current word from the list and
dump the updated list to csv file, then generate the next word"""

    # Remove the previous word since users knew it
    global selected_word
    if selected_word:
        words_dic.remove(selected_word)
        # Reset it
        selected_word = {}

    # Export current words collection to csv
    words_to_learn_data_frame = pd.DataFrame.from_dict(words_dic)
    words_to_learn_data_frame.to_csv(
"data/words_to_learn.csv",
index=False
)

    next_word()


def next_word():
    """Randomly select a word and show it in card"""

    # cancel previous timer if needed
    global filp_timer
    if not filp_timer == "":
        root.after_cancel(filp_timer)

    # Game Over - No more words to learn
    if len(words_dic) == 0:
        game_over()

        return

    # Change the canvas image to front_image
    canvas.itemconfig(canvas_image, image=front_image)

    # Randomly select a word and save it to the
# global variable selected_word
    global selected_word
    selected_word = random.choice(words_dic)

    # Change title and word
    canvas.itemconfig(title_text, fill="black", text="English")
    canvas.itemconfig(word_text, fill="black",
text=selected_word.get("English"))

    # Update global variable filter_timer
    filp_timer = root.after(3000, flip_card)


def flip_card():
    """Show Chinese Word"""

    # Change the canvas image to back_image
    canvas.itemconfig(canvas_image, image=back_image)

    # Change title and word
    canvas.itemconfig(title_text, fill="white", text="Chinese")
    canvas.itemconfig(
        word_text, fill="white",
text=selected_word.get("Traditional Chinese")
    )


# Init Tkinter
root = tk.Tk()
root.title("Flash Card")
root.config(padx=50, pady=50, background=BACKGROUND_COLOR)

# Init canvas
canvas = tk.Canvas(
    width=800, height=526, background=BACKGROUND_COLOR,
highlightthickness=0
)

# Add card_front image and set it to the center of the canvas
front_image = tk.PhotoImage(file="images/card_front.png")
back_image = tk.PhotoImage(file="images/card_back.png")
canvas_image = canvas.create_image(400, 263, image=front_image)

# Add Title Text, and set it to the middle and a bit up of the canvas
title_text = canvas.create_text(
    400, 150, text="", fill="black", font=(FONT, 40, "italic")
)
# Add Word Text, and set it to the middle and a bit down of the canvas
word_text = canvas.create_text(400, 253, text="", fill="black",
font=(FONT, 60, "bold"))

canvas.grid(column=0, row=0, columnspan=2)

# Wrong Btn
wrong_image = tk.PhotoImage(file="images/wrong.png")
wrong_btn = tk.Button(image=wrong_image, command=next_word)
wrong_btn.config(background=BACKGROUND_COLOR,
activebackground=BACKGROUND_COLOR)
wrong_btn.grid(sticky="ew", column=0, row=1)

# Right Btn
right_image = tk.PhotoImage(file="images/right.png")
right_btn = tk.Button(image=right_image, command=right_btn_clicked)
right_btn.config(background=BACKGROUND_COLOR,
activebackground=BACKGROUND_COLOR)
right_btn.grid(sticky="ew", column=1, row=1)

# Get the first Word Card
next_word()

# Start the Event Loop
root.mainloop()

No comments:

Post a Comment