# Modified to play flashcards from .xml decks in flashcard folder # Currently # * Displaying Categories in set language, # * Pick English Questions import pygame from pygame import * import random from layout import * from frontend import hex2rgb from datetime import datetime import time import threading from xmlio import Xmlio import xml.etree.ElementTree as ET from path import path import os __PLUGIN_NAME__ = 'flashcard' QUIZSOURCE = '/home/olpc/Activities/ImageQuizPlus.activity/flashcards/flashcards' ICONSOURCE = '/home/olpc/Activities/ImageQuizPlus.activity/images' class CurrentQuestion: id = 0 imgfn = u'' map = u'' cat = 0 subcat = 0 text = u'' answer = u'' answer_link = '' hint = '' more = '' sound = '' image = '' class Answer: display = True display_line = False sound_enabled = True link = u'' text = u'' img_found_count = 0 img_notfound_count = 0 # imgfn_found = ["Emotes/face-grin.png", "Emotes/face-devil-grin.png", "Emotes/face-glasses.png"] # imgfn_notfound = ["Emotes/face-monkey.png", "Emotes/face-crying.png"] imgfn_found = ["Emotes/face-grin.png"] imgfn_notfound = ["Emotes/face-devil-grin.png"] random.shuffle(imgfn_found) random.shuffle(imgfn_notfound) found_straight = 0 straight_count = 0 icon_left = 0 bg_icons = pygame.Surface((500, 32)) bg_icons_straight = pygame.Surface((500, 32)) def __init__(self): self.sound_found = sf.sound_load("accessed.wav") self.sound_notfound = sf.sound_load("sorry.wav") def reset_points(self): self.found_straight = 0 self.straight_count = 0 self.icon_left = 0 self.bg_icons.fill(hex2rgb(Layout().Toolbar.background)) self.bg_icons_straight.fill(hex2rgb(Layout().Toolbar.background)) sf.display_surface(self.bg_icons, (540, 10), "toolbar") sf.display_surface(self.bg_icons_straight, (538, 50), "toolbar") sf.refresh() def add_straight(self): # 5 in a row -> special reward self.straight_count += 1 im, im_rect = sf.image_load("images/%s" % "Icons/bulb.png") self.bg_icons_straight.blit(im, ((self.straight_count-1) * (im_rect[2] + 4), 0)) sf.display_surface(self.bg_icons_straight, (538, 50), "toolbar") sf.refresh() def display_icon(self, icon_name): if icon_name == "found": self.found_straight += 1 if self.found_straight == 5: # Found Straight 5! self.bg_icons.fill(hex2rgb(Layout().Toolbar.background)) sf.display_surface(self.bg_icons, (540, 10), "toolbar") self.add_straight() self.icon_left = 0 self.found_straight = 1 fn = self.imgfn_found[self.img_found_count % len(self.imgfn_found)] self.img_found_count += 1 elif icon_name == "not-found": self.found_straight = 0 fn = self.imgfn_notfound[self.img_notfound_count % len(self.imgfn_notfound)] self.img_notfound_count += 1 img, img_rect = sf.image_load("images/%s" % fn) self.bg_icons.blit(img, (self.icon_left, 0)) sf.display_surface(self.bg_icons, (540, 10), "toolbar") sf.refresh() self.icon_left += img_rect[2] + 4 class PlaySoundThread (threading.Thread): def set(self, sound, interval): self.sound = sound self.i = interval def run(self): time.sleep(self.i) self.sound.play() def play_sound(self, i): if self.sound_enabled: t = self.PlaySoundThread() if i == 1: t.set(self.sound_found, 0) else: t.set(self.sound_notfound, 0.5) t.start() def display_answer(self): if self.display_line: sf.draw_lines() if self.display == False: return False # Get Top Right Spot of list print "showing answer" map_arr = Q.question.map.split(", ") # Widths & Heights bubble_width = 400 bubble_height = 300 textfield_width = 270 textfield_height = 200 # Extremas of the Polygone will be saved in here: x_max = 0 y_max = 0 x_min = 1000 y_min = 1000 # Extract Extremas from the polygon o = [] a = "x" for s in map_arr: if a == "x": a = s else: # Take care of border pixels if int(a) > x_max: x_max = int(a) if int(s) > y_max: y_max = int(s) if int(a) < x_min: x_min = int(a) if int(s) < y_min: y_min = int(s) a = "x" # Set x and y for the Answer Bubble y_med = (y_min + y_max) / 2 x_max -= 5 y_med = 150 x_max = 400 x_max += Layout().Question.x + Layout().Question.Image.x + 2 y_med += Layout().Question.y + Layout().Question.Image.y + 2 # sf.draw_polygon() # Draw Answer Bubble Image & Text im, im_rect = sf.image_load('images/bubble.gif') text_arr = self.text.split(' ') cur_line = '' cur_x = 0 cur_y = 0 # 'textfield' contains the answer text textfield = pygame.Surface((textfield_width, textfield_height)) textfield.fill((255, 255, 255)) # Make line breaks after reaching width of 'textfield' for t in text_arr: cur_line = "%s %s" % (cur_line, t) font = pygame.font.Font(None, 38) n_text = font.render(cur_line, 1, (0,0,0)) textpos = n_text.get_rect() # #print cur_line, x,y,w,h = list(textpos) if w > (textfield_width): textfield.blit(text, (cur_x, cur_y)) cur_line = t cur_y += 30 written = 1 else: written = 0 text = n_text # #print textpos # Draw leftovers on textfield if written == 0: textfield.blit(n_text, (cur_x, cur_y)) else: font = pygame.font.Font(None, 38) n_text = font.render(cur_line, 1, (0,0,0)) textfield.blit(n_text, (cur_x, cur_y)) # #print "draw" # Draw on Screen sf.display_surface(im, (x_max, y_med)) sf.display_surface(textfield, (x_max+25, y_med+20)) pygame.display.update() class Questions: global path _cat_id = -1 lang_id = 0 lang_name = 0 in_question = False played = 0 count_won = 0 count_lost = 0 questions = [] def load(self, lang_id, lang_name): self.lang_id = lang_id self.lang_name = lang_name self.question = CurrentQuestion() self.load_categories() def load_categories(self): #here we need to get the names of folders and files in the flashcard folder #make a list 'self.categories' with each entry a list containing: #cat_id, text (name of category), count, parent_id, base_parent_id #sorted alphabetically by text (name of category) decks = path(QUIZSOURCE) self.categories = [] for f in decks.files('*.xml'): tree = Xmlio(f) root = tree.getroot() count = len(root) catid = 1 parent = len(self.categories) self.categories.append([catid, f, count, parent, 0]) self.categories.sort() def question_pick(self): # Check if Questions are left to play if self.played >= len(self.questions): # Game / Cat Finished! return False # Okay, Next One! #print 'next', self.questions[self.played] self.question.id = self.questions[self.played][0] # self.question.imgfn = self.questions[self.played][1] self.question.imgfn = 'bubble.gif' self.question.map = self.questions[self.played][2] self.question.cat = self.questions[self.played][3] self.question.subcat = self.questions[self.played][4] self.question.text = self.questions[self.played][5] self.question.answer = self.questions[self.played][6] self.question.answer_link = self.questions[self.played][7] self.question.hint = self.questions[self.played][8] self.question.more = self.questions[self.played][9] self.question.sound = self.questions[self.played][10] self.question.image = self.questions[self.played][11] A.text = self.question.answer A.link = self.question.answer_link self.played += 1 return True def load_questions(self, cat_id): #print 'cat_id = ', cat_id # 2. Load Questions self._cat_id = cat_id[0] self._subcat_id = cat_id[1] self.played = 0 self.count_won = 0 self.count_lost = 0 #ImageQuiz allows user to select one deck, all decks in category, or all decks #Assume self.categories is built correctly files = [] for cat in self.categories: if self._cat_id < 0: #all selected files.append(cat) elif self._cat_id == cat[3] and self._subcat_id == 0: #all subcategories selected files.append(cat) #print 'cat', cat[3], cat[0] elif self._cat_id == cat[3] and self._subcat_id == cat[0]: #specific subcategory within category selected files.append(cat) #print 'subcat', cat[3], cat[0], cat flashcards = [] for file in files: #print 'file', file pathname = file[1] #print 'loading', pathname + '\n' tree = Xmlio(pathname) deck = tree.getroot() self.deck = deck for card in deck: cardid = int(card.get('id')) question = card.findtext('question') question_node = card.find('question') if question_node: sound = question_node.findtext('sound') hint = question_node.findtext('hint') else: sound = '' hint = '' image = '' answer_node = card.find('answer') answer = card.findtext('answer') if answer_node: temp = answer_node.findtext('image') if temp: image = temp[:-4] + '.png' else: image = '' more = answer_node.findtext('more') else: more = '' image = '' cat = file[3] subcat = file[0] flashcard = [cardid,'','',cat, subcat, question, answer, '', hint, more, sound, image] flashcards.append(flashcard) random.shuffle(flashcards) self.questions = flashcards #print 'number of questions=', len(self.questions) def show_answer(): A.display_answer() def finished(): sf.refresh() sf.add_text_item("You have finished this category!", (180, 80)) sf.add_text_item("Won: %i" % Q.count_won, (220, 120)) sf.add_text_item("Lost: %i" % Q.count_lost, (220, 160)) pygame.display.update() # ask_category(130) def startgame(db_cat_id): #print "flashcard" #print "* Starting Game, Category:", db_cat_id #print "** Loading Questions" Q.count_won = 0 Q.count_won = 0 Q.shown = datetime.now() A.reset_points() Q.load_questions(db_cat_id) display_points() sf.clear_text_items() next_question() def ask_subcat(c): y = 110 #print 'subcat=', c sf.add_text_item("Subcategory:", (580,y)) y += 50 sf.add_text_item('All', (600,y), startgame, (c, 0), True) i = 0 for q in Q.categories: if q[3] == c: y += 50 sf.add_text_item("%s (%s)" % (q[1][1], q[2]), (600,y), startgame, (q[3], q[0]), True) i += 1 #print c def ask_category(offset_y = 0): Q.load(__SERVICES__.locale.lang_id, __SERVICES__.locale.lang_name) # sf.clear_text_items() i = 1 y = 110 + offset_y sf.add_text_item("Next Category:", (280,y)) y += 50 sf.add_text_item('All', (300,y), startgame, (0, 0), True) for q in Q.categories: #print 'q', q if q[0] == 1: fname = q[1].name sf.add_text_item("%s (%s)" % (fname, q[2]), (300,y + 50 * q[3]), startgame, (q[3], q[0]), True) i += 1 display_points() def display_points(): format = TextFormat(None, 30) max_q = len(Q.questions) # sf.display_tooltip("%i" % (Q.count_won), (480, 14), format) # sf.display_tooltip("%i" % Q.count_lost, (480, 42), format) if max_q == 0: sf.display_line(0, True) sf.display_line(0, False) else: sf.display_line(100 * Q.count_won / max_q, True) sf.display_line(100 * Q.count_lost / max_q, False) pygame.display.update() def picture_tl(): global images if Q.in_question: #print images[0], Q.question.image #print 'tl' if images[0] == Q.question.image: response = True else: response = False delete_reacts(response) def picture_tr(): global images if Q.in_question: #print 'tr' #print images[1], Q.question.image if images[1] == Q.question.image: response = True else: response = False delete_reacts(response) def picture_ll(): global images if Q.in_question: #print 'll' #print images[2], Q.question.image if images[2] == Q.question.image: response = True else: response = False delete_reacts(response) def picture_lr(): global images if Q.in_question: #print 'lr' #print images[3], Q.question.image if images[3] == Q.question.image: response = True else: response = False delete_reacts(response) def delete_reacts(response): if response: correct_answer() else: wrong_answer() return True def getpossibles(teststr): #there may be multiple correct answers separated by a '/' lst = [] test = '' for i in range(len(teststr)): if teststr[i] != '/': test = test + teststr[i] else: lst.append(test) test = '' if len(test) > 0: lst.append(test) return lst def checkanswer(response): possibles = getpossibles(Q.question.answer) return response.strip() in possibles def self_test(): show_answer() #show two clickable items: smiley face, sad face image_right, xy = sf.image_load("images/Emotes/face-grin.png") sf.display_surface(image_right, (750,100)) image_wrong, xy = sf.image_load("images/Emotes/face-devil-grin.png") sf.display_surface(image_wrong, (750,150)) pygame.display.update() def play_query_again(): global query if Q.in_question: #print 'play_query' query.play() while pygame.mixer.get_busy(): clock.tick(30) def next_question(): #print "* Next Question" global images global query global rtr, rtl, rll, rlr # Select Category if Q._cat_id == -1: sf.clear_text_items() sf.clear_question_frame(True) ask_category() #print 'ask_category' return 1 # Pick only locale language's categories if Q.question_pick(): sf.clear_text_items() Renyi = False #print 'image = ', Q.question.image Q.in_question = True Q.starttime = time.time() if Q.question.image and len(Q.question.image) > 0: Renyi = True candidate_images = [] for question in Q.questions: candidate_images.append(question[11]) random.shuffle(candidate_images) #print 'candidate_images', candidate_images if Q.question.image in candidate_images[:4]: images = candidate_images[:4] else: images = candidate_images[:3] images.append(Q.question.image) random.shuffle(images) #print 'images', images #load image imagepath = path(QUIZSOURCE).joinpath('image') #print 'imagepath', imagepath image_tl, xy = sf.image_load(images[0],path = imagepath) if image_tl == None: image_tl, xy = sf.image_load(error.png, path = imagepath) image_tr, xy = sf.image_load(images[1],path = imagepath) if image_tr == None: image_tr, xy = sf.image_load(error.png, path = imagepath) image_ll, xy = sf.image_load(images[2],path = imagepath) if image_ll == None: image_ll, xy = sf.image_load(error.png, imagepath) image_lr, xy = sf.image_load(images[3],path = imagepath) if image_lr == None: image_lr, xy = sf.image_load(error.png, path = imagepath) #display image sf.display_surface(image_tl, (250, 150)) sf.display_surface(image_tr, (520,150)) sf.display_surface(image_ll, (250,420)) sf.display_surface(image_lr, (520, 420)) pygame.display.update() #print 'sound = ', Q.question.sound soundpath = path(QUIZSOURCE).joinpath('sound') soundlst = getpossibles(soundpath) random.shuffle(soundlst) iconpath = path(ICONSOURCE) if Q.question.sound and len(Q.question.sound) > 0: temp = getpossibles(Q.question.sound) random.shuffle(temp) selected = temp[0] print 'sounds', Q.question.sound, temp, selected query = sf.sound_load(selected[:-4] + '.ogg', path=soundpath) query.play() while pygame.mixer.get_busy(): clock.tick(30) #image_play, xy = sf.image_load("images/play.svg") image_play, xy = sf.image_load("play_button.png", path = iconpath) sf.display_surface(image_play, (300,30)) pygame.display.update() if not Renyi: response = __SERVICES__.frontend.ask(Q.question.text) #print 'response=', response if len(response) == 0 or len(Q.question.answer) == 0: self_test() else: correct = checkanswer(response) y = 100 sf.add_text_item("ok", (750,y), next_question) if correct: correct_answer(False) else: wrong_answer(False) pass else: # game / cat finished! Q.in_question = False #print "finished" Q._cat_id = -1 finished() pass def find_card(cardid): for card in Q.deck: if int(card.get('id')) == cardid: return card def correct_answer(next=True): correct_sounds = ['i-like-it.wav', 'ooh-yeah.wav', 'impressive.wav', 'dramatic_chord.wav', 'sweet.wav', 'brain-yes.wav', 'mk-excellent.wav', 'that-was-cool.wav', 'bt-excellent.wav', 'groovy.wav', 'yes-i-like-it.wav', 'burns-excellent.wav', 'oh-yeah.wav'] #print 'correct answer next =', next, Q.in_question if Q.in_question: A.display_icon("found") #A.play_sound(1) Q.in_question = False Q.count_won += 1 display_points() good = sf.sound_load(random.choice(correct_sounds)) good.play() while pygame.mixer.get_busy(): clock.tick(30) #update Leitner attributes think = '%0.2f' % float( time.time() - Q.starttime) print 'think',think card = find_card(Q.question.id) card.attrib['think'] = think card.attrib['shown'] = Q.shown count = int(card.attrib['count']) count +=1 card.attrib['count'] = str(count) bin = int(card.attrib['bin']) bin += 1 card.attrib['bin'] = str(bin) if next: next_question() else: show_answer() def wrong_answer(next=True): wrong_sounds = ['db_forgetaboutit.wav', 'alf_wrong.wav', 'doh.wav', 'sorry.wav', 'awh_man.wav', 'metal_clang_2.wav', 'excuse_me.wav', 'negative.wav', 'bunny_awful.wav', 'gwarsh.wav', 'not.wav', 'haha.wav', 'oh_no.wav', 'compute.wav', 'hoo-ah.wav'] #print 'wrong answer next =', next, Q.in_question if Q.in_question: #update Leitner attributes card = find_card(Q.question.id) card.attrib['think'] = \ '%0.2f' % float(time.time() - Q.starttime) card.attrib['shown'] = Q.shown card.attrib['bin'] = 1 count = int(card.attrib['count']) count += 1 card.attrib['count'] = str(count) A.display_icon("not-found") #A.play_sound(0) Q.in_question = False Q.count_lost += 1 display_points() bad = sf.sound_load(random.choice(wrong_sounds)) bad.play() while pygame.mixer.get_busy(): clock.tick(30) if next: next_question() else: show_answer() def click_on_flashcards(): global selectedlanguage #print 'flashcards selected' selectedlanguage = 'flashcards' Q.cat_id = -1 next_question() def load(): global sf sf = __SERVICES__.frontend; global Q Q = Questions() global A A = Answer() global clock clock = pygame.time.Clock() global rtl, rtr, rll, rlr, knew_it, forgot_it, play_again rtl = sf.add_react(50, 100, 320, 240, picture_tl) #print 'rtl=', rtl rtr = sf.add_react(400, 100, 320, 240, picture_tr) #print 'rtr=', rtr rll = sf.add_react(50, 360, 320, 240, picture_ll) #print 'rll=', rll rlr = sf.add_react(400, 360, 320, 240, picture_lr) #print 'rlr=', rlr knew_it = sf.add_react(750,100,50,50, correct_answer) #print 'knew_it=', knew_it forgot_it = sf.add_react(750,150,50,50, wrong_answer) #print 'forgot_it=', forgot_it play_again = sf.add_react(300,0,100,100, play_query_again) #print 'play_again=', play_again sf.add_menu_item('/', 'Flashcards', click_on_flashcards) def close(): pass