Udemy Python Notes

Day 31 Flash Card App Capstone Project

헤일리유 2022. 12. 7. 15:53

Day 31

 

What I've been learing:

  • Catching exceptions
  • Using the Json data format
  • Passing and reading cvs using Pandas
  • Opening and writing files

 


 

1. Creating the UI

 

Instruction

1. Download the starting files from the course resources. 

2. Use the images in the image folder, to create the following user interface. The ❌ and ✅ are buttons. You can add images to buttons like this:
my_image = PhotoImage(file="path/to/image_file.png")button = Button(image=my_image, highlightthickness=0)

3. Here are some hints for the fonts, measurements and positioning. 

 

from tkinter import *
import pandas
import random

BACKGROUND_COLOR = "#B1DDC6"
FR_FONT = ("Ariel", 40, 'italic')
EN_FONT = ("Ariel", 60, "bold")
window = Tk()
window.title("Flashy")
window.config(padx=50, pady=50, bg=BACKGROUND_COLOR)

canvas = Canvas(width=800, height=526, bg=BACKGROUND_COLOR, highlightthickness=0)
canvas.grid(column=0, row=0, columnspan=2)
flash_card_img = PhotoImage(file='images/card_front.png')
canvas.create_image(400, 263, image=flash_card_img)
french = canvas.create_text(400, 150, text="fr", font=FR_FONT)
english = canvas.create_text(400, 263, text="en", font=EN_FONT)

random_word()

check_image = PhotoImage(file="images/right.png")
cross_image = PhotoImage(file="images/wrong.png")

right_button = Button(image=check_image, highlightthickness=0, command=random_word)
right_button.grid(column=1, row=1)
wrong_button = Button(image=cross_image, highlightthickness=0, command=random_word)
wrong_button.grid(column=0, row=1)


window.mainloop()

 

 

 

2. New flash card

 

Get a random french word from the csv file.

Instruction

1. Read the data from the french_words.csv file in the datafolder. 

2. Pick a random French word/translation and put the word into the flashcard. Every time you press the ❌ or ✅ buttons, it should generate a new random word to display. 

 

내가 해본 것

data = pandas.read_csv("data/french_words.csv")
# print(data)
word_dict = {row.French: row.English for (index, row) in data.iterrows()}
# print(word_dict)
# print(random.choice(list(word_dict.keys())))
# pick a random key from word_dict
# random.choice(list(word_dict.keys()))
def next_card():
    random_fr = random.choice(list(word_dict.keys()))
    random_en = word_dict[random_fr]
    canvas.itemconfig(french, text=random_fr)
    canvas.itemconfig(english, text=random_en)

 

Angela's solution

data = pandas.read_csv("data/french_words.csv")
to_learn = data.to_dict(orient="records")
def next_card():
    current_card = random.choice(to_learn)
    canvas.itemconfig(card_title, text="French")
    canvas.itemconfig(card_word, text=current_card["french"])

 

일단 내가 설명을 잘 안 읽고 시작해서 영어랑 프랑스어랑 카드 앞 쪽에 다 넣어버렸다.

방식은 다르지만 데이터에서 랜덤으로 단어를 가져와서 텍스트를 바꾸는 건 성공했다고 볼 수 있다.

안젤라의 방식이 훨씬 깔끔하기 때문에 안젤라 코드로 수정했다.

 

 

 

 

3. Flipping the card

 

3초 후에 카드가 뒷면으로 바뀌면서 영어가 나오도록

Instruction

1. After a delay of 3s (3000ms), the card should flip and display the English translation for the current word. 

2. The card image should change to the card_back.png and the text colour should change to white. The title of the card should change to "English" from "French".

 

data = pandas.read_csv("data/french_words.csv")
to_learn = data.to_dict(orient="records")
current_card = {}


def next_card():
    global current_card
    current_card = random.choice(to_learn)
    canvas.itemconfig(card_title, text="French")
    canvas.itemconfig(card_word, text=current_card["French"])


def flip_card():
    canvas.itemconfig(flash_card, image=card_back_img)
    canvas.itemconfig(card_title, text="English")
    canvas.itemconfig(card_word, text=current_card["English"])
window.after(3000, flip_card)

 

이렇게 하니 첫 카드만 flip하고 그 다음 카드부터는 flipping이 적용되지 않는다.

 

data = pandas.read_csv("data/french_words.csv")
to_learn = data.to_dict(orient="records")
current_card = {}


def next_card():
    global current_card
    current_card = random.choice(to_learn)
    canvas.itemconfig(card_title, text="French", fill="black")
    canvas.itemconfig(card_word, text=current_card["French"], fill="black")
    canvas.itemconfig(flash_card, image=card_front_img)
    window.after(3000, func=flip_card)


def flip_card():
    canvas.itemconfig(flash_card, image=card_back_img)
    canvas.itemconfig(card_title, text="English", fill="white")
    canvas.itemconfig(card_word, text=current_card["English"], fill="white")

window.after(3000, flip_card)를 next_card() 안에 포함시키니 동작한다. 

하지만 이럴경우 빠르게 버튼을 클릭했을 경우 버튼을 처음 클릭했을 경우로부터 3초가 지나면 카드가 flipped된 채로 표기된다.

그렇기 때문에 next_card()가 실행됐을때 3초를 그때마다 리셋해줘야한다.

 

window = Tk()
window.title("Flashy")
window.config(padx=50, pady=50, bg=BACKGROUND_COLOR)

flip_timer = window.after(3000, func=flip_card)

canvas = Canvas(width=800, height=526, bg=BACKGROUND_COLOR, highlightthickness=0)
canvas.grid(column=0, row=0, columnspan=2)
card_front_img = PhotoImage(file='images/card_front.png')
card_back_img = PhotoImage(file='images/card_back.png')
flash_card = canvas.create_image(400, 263, image=card_front_img)
card_title = canvas.create_text(400, 150, text="title", font=FR_FONT)
card_word = canvas.create_text(400, 263, text="word", font=EN_FONT)

check_image = PhotoImage(file="images/right.png")
cross_image = PhotoImage(file="images/wrong.png")

right_button = Button(image=check_image, highlightthickness=0, command=next_card)
right_button.grid(column=1, row=1)
wrong_button = Button(image=cross_image, highlightthickness=0, command=next_card)
wrong_button.grid(column=0, row=1)

next_card()

window.mainloop()

다시 아래로 window.after()을 내리고 put it in a variable called flip_timer.

 

def next_card():
    global current_card, flip_timer
    window.after_cancel(flip_timer)
    current_card = random.choice(to_learn)
    canvas.itemconfig(card_title, text="French", fill="black")
    canvas.itemconfig(card_word, text=current_card["French"], fill="black")
    canvas.itemconfig(flash_card, image=card_front_img)
    flip_timer = window.after(3000, func=flip_card)

next_card()에서 기존 플립 타이머를 먼저 캔슬하고 다른 부분을 진행한뒤 플립 타이머를 3초로 새롭게 다시 설정한다.

 

 

 

4. Saving user's progress

 

새로운 csv 파일을 만들고 유저가 체크버튼을 누른 단어는 제외시킨다. (다음번에는 아직 학습하지 않은 또는 공부가 필요한 단어만 학습할 수 있도록)

 

Instruction

1. When the user presses on the ✅ button, it means that they know the current word on the flashcard and that word should be removed from the list of words that might come up. 

e.g. If french_words.csv had only 3 records:
chaque,eachparlé,speakarrivé,come
After the user has seen parlé,speak  it should be removedfrom the list of words that can come up again.

2. The updated data should be saved to a new file called words_to_learn.csv
e.g. words_to_learn.csv
chaque,eacharrivé,come

3. The next time the program is run, it should check if there is a words_to_learn.csv file. If it exists, the program should use those words to put on the flashcards. If the words_to_learn.csv does not exist (i.e., the first time the program is run), then it should use the words in the french_words.csv
We want our flashcard program to only test us on things we don't know. So if the user presses the ✅ button, that means the current card should not come up again.

 

체크 박스 누를 때 실행될 함수를 새로 만들어준다.

known_button = Button(image=check_image, highlightthickness=0, command=is_known)
def is_known():
    to_learn.remove(current_card)
    data = pandas.DataFrame(to_learn)
    data.to_csv("data/words_to_learn.csv", index=False)
    next_card()
def is_known():
    to_learn.remove(current_card)
    data = pandas.DataFrame(to_learn)
    data.to_csv("data/words_to_learn.csv", index=False)
    next_card()
to_learn = {}

try:
    data = pandas.read_csv("data/words_to_learn.csv")
except FileNotFoundError:
    original_data = pandas.read_csv("data/words_to.csv")
    to_learn = original_data.to_dict(orient="records")
else:
    to_learn = data.to_dict(orient="records")

이때 처음 csv를 pandas로 dictionary로 가져오고 다시 cvs로 저장하는 과정에서 pandas가 records number를 자동으로 입력하는데 이게 여러번 반복되면 의미없고 불필요한 데이터가 너무 많이 쌓이게 된다.

data.to_csv("data/words_to_learn.csv", index=False)

If you set index to False when you are saving data to cvs file, you can avoid this problem. ◡̈ 

 

 

 

 

 

Final 

 

from tkinter import *
import pandas
import random

BACKGROUND_COLOR = "#B1DDC6"
FR_FONT = ("Ariel", 40, 'italic')
EN_FONT = ("Ariel", 60, "bold")
current_card = {}
to_learn = {}

try:
    data = pandas.read_csv("data/words_to_learn.csv")
except FileNotFoundError:
    original_data = pandas.read_csv("data/words_to.csv")
    to_learn = original_data.to_dict(orient="records")
else:
    to_learn = data.to_dict(orient="records")


def next_card():
    global current_card, flip_timer
    window.after_cancel(flip_timer)
    current_card = random.choice(to_learn)
    canvas.itemconfig(card_title, text="French", fill="black")
    canvas.itemconfig(card_word, text=current_card["French"], fill="black")
    canvas.itemconfig(flash_card, image=card_front_img)
    flip_timer = window.after(3000, func=flip_card)


def flip_card():
    canvas.itemconfig(flash_card, image=card_back_img)
    canvas.itemconfig(card_title, text="English", fill="white")
    canvas.itemconfig(card_word, text=current_card["English"], fill="white")


def is_known():
    to_learn.remove(current_card)
    data = pandas.DataFrame(to_learn)
    data.to_csv("data/words_to_learn.csv", index=False)
    next_card()
    

# -------------------


window = Tk()
window.title("Flashy")
window.config(padx=50, pady=50, bg=BACKGROUND_COLOR)

flip_timer = window.after(3000, func=flip_card)

canvas = Canvas(width=800, height=526, bg=BACKGROUND_COLOR, highlightthickness=0)
canvas.grid(column=0, row=0, columnspan=2)
card_front_img = PhotoImage(file='images/card_front.png')
card_back_img = PhotoImage(file='images/card_back.png')
flash_card = canvas.create_image(400, 263, image=card_front_img)
card_title = canvas.create_text(400, 150, text="title", font=FR_FONT)
card_word = canvas.create_text(400, 263, text="word", font=EN_FONT)

check_image = PhotoImage(file="images/right.png")
cross_image = PhotoImage(file="images/wrong.png")

known_button = Button(image=check_image, highlightthickness=0, command=is_known)
known_button.grid(column=1, row=1)
unknown_button = Button(image=cross_image, highlightthickness=0, command=next_card)
unknown_button.grid(column=0, row=1)

next_card()

window.mainloop()

 

 

 

데이터 불러오기 및 저장 프로세스

 

csv파일(original data) > to_learn으로 가져옴 > 아는 단어 to_learn에서 제거 > 여태까지의 프로세스(아는 단어 뺀 to_learn dict)를 새로운 csv파일로 저장

 

만약 전에 저장된 words_to_learn.csv 있다면 가져옴 / 없다면 original data csv file 가져옴  > to_learn dict안에 데이터 넣기 > 아는 단어 to_learn에서 제거 > 여태까지의 프로세스(아는 단어 뺀 to_learn dict)를 다시 words_to_learn.csv로 저장 (덮어쓰기)