# 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 import time import threading import shutil import xml.etree.ElementTree as ET import os, sys __PLUGIN_NAME__ = 'flashcard' # reads and writes xml files # output files indented # import xml.etree.ElementTree as ET #tree = Xmlio(path) #elem = tree.getroot() #tree.save(path) --- uses root of tree #tree.save(path,root=elem) -- saves tree from supplied root element class Xmlio(): def __init__(self, path = '', root = ''): if root: if path: self.root = root self.tree = ET.ElementTree(self.root) else: self.root = ET.Element(root) self.tree = ET.ElementTree(self.root) else: self.tree = ET.parse(path) self.root = self.tree.getroot() def getroot(self, root = ''): if not root: return self.root else: return ET.Element(root) def indent(self, elem, level=0): i = "\n" + level * " " if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + " " for elem in elem: self.indent(elem, level+1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i def save(self, path, root = ''): if not root: root = self.root self.indent(root) #prettyprint ET.ElementTree(root).write(path) 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: _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 walk( self, root, recurse=0, pattern='*', return_folders=0 ): import fnmatch, os, string # initialize result = [] # must have at least root folder try: names = os.listdir(root) except os.error: return result # expand pattern pattern = pattern or '*' pat_list = string.splitfields( pattern , ';' ) # check each file for name in names: fullname = os.path.normpath(os.path.join(root, name)) # grab if it matches our pattern and entry type for pat in pat_list: if fnmatch.fnmatch(name, pat): if os.path.isfile(fullname) or (return_folders and os.path.isdir(fullname)): result.append(fullname) continue # recursively scan other folders, appending results if recurse: if os.path.isdir(fullname) and not os.path.islink(fullname): result = result + self.walk( fullname, recurse, pattern, return_folders ) return result def path_strip(self, pathname): """pathname = path[0] + path[1] + path[2] - splits pathname into components""" # get part after last /, if any filename = os.path.basename(pathname) #decktype determines whether it is a deck (.xml) or superdeck (.sbj) decktype = filename[-4:] #deckname strips off path deckname = filename[:-4] path = (pathname[:-len(filename)],deckname,decktype) return path def load_categories(self): global selectedlanguage #here we need to get the names of folders and files in the flashcard folder folder = 'flashcards/' + selectedlanguage decks = self.walk(folder, 1, '*.xml') print decks #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) self.categories = [] folders = [] for file in decks: print file tree = Xmlio(file) root = tree.getroot() count = len(root) path = self.path_strip(file) if path[0] in folders: catid += 1 parent = folders.index(path[0]) + 1 else: folders.append(path[0]) parent = len(folders) catid = 1 self.categories.append([catid, path, count, parent, 0]) self.categories.sort() print selectedlanguage, len(self.categories) 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: pathname = file[1][0] + file[1][1] + file[1][2] print 'loading', pathname + '\n' tree = Xmlio(pathname) deck = tree.getroot() 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) print flashcard random.shuffle(flashcards) self.questions = flashcards print 'number of questions=', len(self.questions) def create_flashcard(card): 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 = '' temp = sound.split('/') print 'temp', temp sound = [] for item in temp: print 'before', item item = item[:-4] + '.ogg' print 'after', item sound.append(item) print 'sound list is', sound 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 = '' temp = answer_node.findtext('more') if temp: more = temp else: more = '' else: more = '' image = '' cat = ' ' subcat = ' ' flashcard = [cardid,'','',cat, subcat, question, answer, '', hint, more, sound, image] print flashcard return (sound, image) def extract(): global selectedlanguage #here we need to get the names of folders and files in the flashcard folder folder = 'flashcards/' + selectedlanguage print 'folder=', folder decks = Q.walk(folder, 1, '*.xml') print 'decks=', decks for deck in decks: pathname = deck src = os.path.dirname(os.path.dirname(pathname)) print 'loading', pathname + '\n' tree = Xmlio(pathname) deck = tree.getroot() for card in deck: sound, image = create_flashcard(card) #copy image.jpg from image to img print os.path.dirname(pathname) print os.path.dirname(os.path.dirname(pathname)) print 'copy image', src, image try: shutil.copyfile(src + '/image/' + image, src + '/img/' + image) except: print 'not found', pathname, image #copy sound.mp3 from sound to snd for item in sound: print 'copy sound', src, item try: shutil.copyfile(src + '/sound/' + item, src + '/snd/' + item) except: print 'not found', pathname, item 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 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): global selectedlanguage 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: if q[0] == 1: if q[1][0] != 'flashcards/' + selectedlanguage + '/': name = q[1][0][len('flashcards/' + selectedlanguage + '/') - len(q[1][0]):-1] sf.add_text_item("%s (%s)" % (name, q[2]), (300,y + 50 * q[3]), ask_subcat, q[3], True) else: name = q[1][1] sf.add_text_item("%s (%s)" % (name, 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() 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 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) images = candidate_images[:3] if Q.question.image in images: images.append(candidate_images[3]) else: images.append(Q.question.image) random.shuffle(images) #load image image_tl, xy = sf.image_load(images[0],path = 'flashcards/' + selectedlanguage + '/image') if image_tl == None: image_tl, xy = sf.image_load(error.png, path = 'flashcards/') image_tr, xy = sf.image_load(images[1],path = 'flashcards/' + selectedlanguage + '/image') if image_tr == None: image_tr, xy = sf.image_load(error.png, path = 'flashcards/') image_ll, xy = sf.image_load(images[2],path = 'flashcards/' + selectedlanguage + '/image') if image_ll == None: image_ll, xy = sf.image_load(error.png, path = 'flashcards/') image_lr, xy = sf.image_load(images[3],path = 'flashcards/' + selectedlanguage + '/image') if image_lr == None: image_lr, xy = sf.image_load(error.png, path = 'flashcards/') #display image sf.display_surface(image_tl, (50, 100)) sf.display_surface(image_tr, (400,100)) sf.display_surface(image_ll, (50,360)) sf.display_surface(image_lr, (400, 360)) pygame.display.update() print 'sound = ', Q.question.sound if Q.question.sound and len(Q.question.sound) > 0: query = sf.sound_load(Q.question.sound[:-4] + '.ogg', path='flashcards/' + selectedlanguage + '/sound') query.play() while pygame.mixer.get_busy(): clock.tick(30) image_play, xy = sf.image_load("images/xclock.png") sf.display_surface(image_play, (300,0)) 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 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) 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: 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_russian_renyi_1(): global selectedlanguage print 'russian renyi_1 selected' selectedlanguage = 'russian/renyi_1' Q.cat_id = -1 extract() def click_russian_renyi_2(): global selectedlanguage print 'russian renyi_2 selected' selectedlanguage = 'russian/renyi_2' Q.cat_id = -1 extract() def click_russian_renyi_3(): global selectedlanguage print 'russian renyi_3 selected' selectedlanguage = 'russian/renyi_3' Q.cat_id = -1 extract() def click_russian_renyi_4(): global selectedlanguage print 'russian renyi_4 selected' selectedlanguage = 'russian/renyi_4' Q.cat_id = -1 extract() def load(): global sf sf = __SERVICES__.frontend; global Q Q = Questions() global A A = Answer() global clock clock = pygame.time.Clock() global button1 global button2 global button3 global button4 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_dir('/extract', "Extract") button1 = sf.add_menu_item('/extract', 'Russian Renyi_1', click_russian_renyi_1) print 'button', button1 button2 = sf.add_menu_item('/extract', 'Russian Renyi_2', click_russian_renyi_2) print 'button', button2 button3 = sf.add_menu_item('/extract', 'Russian Renyi_3', click_russian_renyi_3) print 'button', button3 button4 = sf.add_menu_item('/extract', 'Russian Renyi_4', click_russian_renyi_4) print 'button', button4 pass def close(): pass