Aim
This program will help you train your memory on facts and trivia by the use of flashcards.
How to use
Type start
to start, then type the answer to the question of the flashcard shown above.
If the answer is correct, you will automatically progress to the next card.
If you do not know the answer, type ???
and the answer will be shown, type it in to continue.
Each time a card is:
- Guessed correctly: The knowledge of the card will INcrease by 1
- Resigned (By typing '???'): The knowledge of the card will DEcrease by 1
When the knowledge goes over the REPEATS_TO_LEARN
(currently equal to 3)
the card is removed from the deck of cards.
File Format
A deck of flashcards is required in the format of
Question_1,Answer_1,Knowledge_Level_1
Question_2,Answer_2,Knowledge_Level_2
...
For example:
Italy?,rome,0
USA?,new york,0
France,paris,0
...
The knowledge level always starts at 0 because it is assumed that the reader knows nothing about the cards when they start to use this program.
Before starting
Click the load button to load a deck of cards you want to practice (a file in the above format).
After finishing
Click the save button to save the new knowledge levels and deleted cards. You can save in a new file, so you can have multiple files representing your progress over multiple days.
Example file
If for example you want to learn about the capital cities of the world, you can use this file:
nations_and_capitals_cards
Afghanistan,Kabul,0
Albania,Tirana,0
Algeria,Algiers,0
Andorra,Andorra la Vella,0
Angola,Luanda,0
Antigua and Barbuda,Saint John's,0
Argentina,Buenos Aires,0
Armenia,Yerevan,0
Australia,Canberra,0
Austria,Vienna,0
Azerbaijan,Baku,0
Bahamas,Nassau,0
Bahrain,Manama,0
Bangladesh,Dhaka,0
Barbados,Bridgetown,0
Belarus,Minsk,0
Belgium,Brussels,0
Belize,Belmopan,0
Benin,Porto-Novo,0
Bhutan,Thimphu,0
Bolivia,Sucre,0
Bosnia and Herzegovina,Sarajevo,0
Botswana,Gaborone,0
Brazil,Brasilia,0
Brunei Bandar Seri,Begawan,0
Bulgaria,Sofia,0
Burkina Faso,Ouagadougou,0
Burundi,Bujumbura,0
Cabo Verde,Praia,0
Cambodia,Phnom Penh,0
Cameroon,Yaounde,0
Canada,Ottawa,0
Central African Republic,Bangui,0
Chad,N'Djamena,0
Chile,Santiago,0
China,Beijing,0
Colombia,Bogotá,0
Comoros,Moroni,0
Democratic Republic of the Congo,Kinshasa,0
Republic of the Congo,Brazzaville,0
Costa Rica,San Jose,0
Cote d'Ivoire,Yamoussoukro,0
Croatia,Zagreb,0
Cuba,Havana,0
Cyprus,Nicosia,0
Czech Republic,Prague,0
Denmark,Copenhagen,0
Djibouti,Djibouti(city),0
Dominica,Roseau,0
Dominican,Republic Santo Domingo,0
Ecuador,Quito,0
Egypt,Cairo,0
El Salvador,San Salvador,0
Equatorial Guinea,Malabo,0
Eritrea,Asmara,0
Estonia,Tallinn,0
Ethiopia,Addis Ababa,0
Fiji,Suva,0
Finland,Helsinki,0
France,Paris,0
Gabon,Libreville,0
Gambia,Banjul,0
Georgia,Tbilisi,0
Germany,Berlin,0
Ghana,Accra,0
Greece,Athens,0
Grenada,Saint George's,0
Guatemala,Guatemala City,0
Guinea,Conakry,0
Guinea-Bissau,Bissau,0
Guyana,Georgetown,0
Haiti,Port-au-Prince,0
Honduras,Tegucigalpa,0
Hungary,Budapest,0
Iceland,Reykjavik,0
India,New Delhi,0
Indonesia,Jakarta,0
Iran,Tehran,0
Iraq,Baghdad,0
Ireland,Dublin,0
Israel,Jerusalem,0
Italy,Rome,0
Jamaica,Kingston,0
Japan,Tokyo,0
Jordan,Amman,0
Kazakhstan,Astana,0
Kenya,Nairobi,0
Kiribati,Tarawa,0
Kosovo,Pristina,0
Kuwait,Kuwait City,0
Kyrgyzstan,Bishkek,0
Laos,Vientiane,0
Latvia,Riga,0
Lebanon,Beirut,0
Lesotho,Maseru,0
Liberia,Monrovia,0
Libya,Tripoli,0
Liechtenstein,Vaduz,0
Lithuania,Vilnius,0
Luxembourg,Luxembourg,0
Macedonia(FYROM),Skopje,0
Madagascar,Antananarivo,0
Malawi,Lilongwe,0
Malaysia,Kuala Lumpur,0
Maldives,Male,0
Mali,Bamako,0
Malta,Valletta,0
Marshall Islands,Majuro,0
Mauritania,Nouakchott,0
Mauritius,Port Louis,0
Mexico,Mexico City,0
Micronesia,Palikir,0
Moldova,Chisinau,0
Monaco,Monaco,0
Mongolia,Ulaanbaatar,0
Montenegro,Podgorica,0
Morocco,Rabat,0
Mozambique,Maputo,0
Myanmar(Burma),Naypyidaw,0
Namibia,Windhoek,0
Nauru,Yaren District,0
Nepal,Kathmandu,0
Netherlands,Amsterdam,0
New Zealand,Wellington,0
Nicaragua,Managua,0
Niger,Niamey,0
Nigeria,Abuja,0
North Korea,Pyongyang,0
Norway,Oslo,0
Oman,Muscat,0
Pakistan,Islamabad,0
Palau,Ngerulmud,0
Palestine,Jerusalem(East),0
Panama,Panama City,0
Papua,New Guinea Port Moresby,0
Paraguay, Asunción,0
Peru,Lima,0
Philippines,Manila,0
Poland,Warsaw,0
Portugal,Lisbon,0
Qatar,Doha,0
Romania,Bucharest,0
Russia,Moscow,0
Rwanda,Kigali,0
Saint,Kitts and Nevis Basseterre,0
Saint Lucia,Castries,0
Saint Vincent and the Grenadines,Kingstown,0
Samoa,Apia,0
San Marino,San Marino,0
Sao,Tome and Principe São Tomé,0
Saudi Arabia,Riyadh,0
Senegal,Dakar,0
Serbia,Belgrade,0
Seychelles,Victoria,0
Sierra Leone,Freetown,0
Singapore,Singapore,0
Slovakia,Bratislava,0
Slovenia,Ljubljana,0
Solomon Islands,Honiara,0
Somalia,Mogadishu,0
South Africa,Pretoria,0
South Korea,Seoul,0
South Sudan,Juba,0
Spain,Madrid,0
Sri Lanka,Sri Jayawardenepura Kotte,0
Sudan,Khartoum,0
Suriname,Paramaribo,0
Swaziland,Mbabane,0
Sweden,Stockholm,0
Switzerland,Bern,0
Syria,Damascus,0
Taiwan,Taipei,0
Tajikistan,Dushanbe,0
Tanzania,Dodoma,0
Thailand,Bangkok,0
Timor-Leste,Dili,0
Togo,Lomé,0
Tonga,Nukuʻalofa,0
Trinidad and Tobago,Port of Spain,0
Tunisia,Tunis,0
Turkey,Ankara,0
Turkmenistan,Ashgabat,0
Tuvalu,Funafuti,0
Uganda,Kampala,0
Ukraine,Kiev,0
United Arab Emirates,Abu Dhabi,0
United Kingdom,London,0
United States of America,Washington D.C.,0
Uruguay,Montevideo,0
Uzbekistan,Tashkent,0
Vanuatu,Port Vila,0
Vatican City,Vatican City,0
Venezuela,Caracas,0
Vietnam,Hanoi,0
Yemen,Sana'a,0
Zambia,Lusaka,0
Zimbabwe,Harare,0
Concerns
- Not possible to have ',' inside question or answer
- Weird workaround at line
CARDS.remove([c for c in CARDS if card[0]==c[0] and card[1]==c[1]][0]) # Workaround to avoid comparing knowledge
- Cards are plain tuples, maybe better as
Namedtuples
?
And obviously any and all comments are welcome. Also, please note that the question text is the same as the code documentation (only the example file is added) just nicely formatted.
"""
**AIM**
This program will help you train your memory on facts and trivia by the use
of flashcards.
**HOW TO USE**
Type `start` (no quotes) to start, then type the answer to the question of the flashcard shown above.
If the answer is correct, you will automatically progress to the next card.
If you do not know the answer, type `???` (no quotes) and the answer will be shown, type it in to continue.
Each time a card is:
* Guessed correctly : The knowledge of the card will INcrease by 1
* Resigned (By typing '???'): The knowledge of the card will DEcrease by 1
When the knowledge goes over the `REPEATS_TO_LEARN` (currently equal to {})
the card is removed from the deck of cards.
**FILE FORMAT**
A deck of flashcards is required in the format of
Question_1,Answer_1,Knowledge_Level_1
Question_2,Answer_2,Knowledge_Level_2
...
For example:
Italy?,rome,0
USA?,new york,0
France,paris,0
...
The knowledge level always starts at 0 because it is assumed that the reader knows nothing about the
cards when they start to use this program.
**BEFORE STARTING**
Click the load button to load a deck of cards you want to practice (a file in the above format).
**AFTER FINISHING**
Click the save button to save the new knowledge levels and deleted cards.
You can save in a new file, so you can have multiple files
representing your progress over multiple days.
"""
# Issues / To Do:
# # Not possible to have ',' inside question or answer
# # REPEATS_TO_LEARN and FONT not configurable from inside GUI
# # Weird workaround at line CARDS.remove( [ c for c in CARDS if card[0]==c[0] and card[1]==c[1] ][0] ) # WOìorkaround to avoid comparing knowledge
# # Cards are plain tuples, maybe better as Namedtuples?
try:
import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
except ImportError:
import Tkinter as tk
from tkFileDialog import askopenfilename, asksaveasfilename
import doctest
import random
FONT = (None, 50)
REPEATS_TO_LEARN = 3
def flatten(xss):
"""
>>> flatten([ [1,2], [3,4] ])
[1, 2, 3, 4]
"""
return [x for xs in xss for x in xs]
def pop_up(title, content):
"""
# http://stackoverflow.com/questions/17261028/how-do-i-make-a-pop-up-in-tkinter-when-a-button-is-clicked
Shows a pop-up.
"""
toplevel = tk.Toplevel()
label1 = tk.Label(toplevel, text=title, height=0, font=FONT)
label1.pack()
label2 = tk.Label(toplevel, text=content, height=0)
label2.pack()
def read_cards(text):
"""
Converts from a multi-line string, where each line represents a card,
to a list of tuples, each representing a card.
The tuple is (Question:str, Answer:str, Knowledge:int)
>>> read_cards('A,B,0\\nC,D,0')
[('A', 'B', 0), ('C', 'D', 0)]
"""
return [ (line.split(',')[0], line.split(',')[1], int(line.split(',')[2])) for line in text.split('\n') if line]
def format_cards_to_write(cards):
"""
Converts from a a list of tuples,
to a multi-line string, where each line represents a card,
The opposite of `read_cards`
>>> format_cards_to_write([('A', 'B', 0), ('C', 'D', 0)])
'A,B,0\\nC,D,0'
>>> x = 'A,B,0\\nC,D,0'
>>> format_cards_to_write(read_cards(x)) == x
True
"""
return '\n'.join(','.join(card[:2])+','+str(card[2]) for card in cards)
def load_cards():
global CARDS
filename = askopenfilename()
with open(filename) as f:
CARDS = read_cards(f.read())
def save_progress():
filename = asksaveasfilename()
with open(filename, 'w+') as f:
f.write(format_cards_to_write(CARDS))
root = tk.Tk()
root.title("Typing trainer")
help_button = tk.Button(root, text="Help", command=lambda: pop_up("Flashcard training", __doc__.format(REPEATS_TO_LEARN)))
help_button.pack()
load_button = tk.Button(root, text="Load Cards", command=load_cards)
load_button.pack()
save_button = tk.Button(root, text="Save Progress", command=save_progress)
save_button.pack()
text_to_copy = tk.Label(root, text = "Load, then type \"start\"", font=FONT)
text_to_copy.pack()
typing_ground = tk.Entry(root, font=FONT)
typing_ground.pack()
typing_ground.focus_set()
points = tk.Label(root, text = "Score: 0", font=FONT)
points.pack()
card = ('Type `start` to start (after loading a deck of cards)', "start", None)
def check_answer_correct(ev):
try:
CARDS
except: # Not defined
pop_up("Load Deck Before Usage", "Please load a deck of cards before starting, press the `Help` button for more information.")
if not CARDS:
text_to_copy['text'] = "Congratulations! All cards learned!"
return
global card
if typing_ground.get() == card[1]:
#flatten([c * (4-c[2]) for c in CARDS])
card = random.choice( flatten([[c] * (4-c[2]) for c in CARDS]) ) # Inversely weight on knowledge
typing_ground.delete(0, 'end')
text_to_copy['text'] = card[0]
points['text'] = "Score: " + str(int(points['text'].split(': ')[-1]) + 1)
CARDS.remove( card )
if card[2] <= REPEATS_TO_LEARN:
CARDS.append( (card[0], card[1], card[2] + 1) )
elif typing_ground.get() == "???":
text_to_copy['text'] = card[0] + ' -> ' + card[1]
print(CARDS, card)
CARDS.remove( [ c for c in CARDS if card[0]==c[0] and card[1]==c[1] ][0] ) # WOìorkaround to avoid comparing knowledge
CARDS.append( (card[0], card[1], card[2] - 1) )
print(card, typing_ground.get())
root.bind('<Key>', check_answer_correct)
doctest.testmod()
root.mainloop()