Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rwxr-xr-xplugins/_extract.py896
-rwxr-xr-xplugins/_flashcard.py728
-rwxr-xr-xplugins/_library.py198
-rwxr-xr-xplugins/_quizsocket.py247
-rwxr-xr-xplugins/dbtool.py99
-rwxr-xr-xplugins/demoplugin.py25
-rwxr-xr-xplugins/ink.py61
-rwxr-xr-xplugins/make.py422
-rwxr-xr-xplugins/multi_player.py552
-rwxr-xr-xplugins/path.py971
-rwxr-xr-xplugins/readme11
-rwxr-xr-xplugins/single_player.py744
-rwxr-xr-xplugins/tools.py100
13 files changed, 5054 insertions, 0 deletions
diff --git a/plugins/_extract.py b/plugins/_extract.py
new file mode 100755
index 0000000..071a869
--- /dev/null
+++ b/plugins/_extract.py
@@ -0,0 +1,896 @@
+# 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
diff --git a/plugins/_flashcard.py b/plugins/_flashcard.py
new file mode 100755
index 0000000..e1869ac
--- /dev/null
+++ b/plugins/_flashcard.py
@@ -0,0 +1,728 @@
+# 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
diff --git a/plugins/_library.py b/plugins/_library.py
new file mode 100755
index 0000000..60e6922
--- /dev/null
+++ b/plugins/_library.py
@@ -0,0 +1,198 @@
+__PLUGIN_NAME__ = 'library'
+LIB = '/home/olpc/Activities/ImageQuizPlus.activity/flashcards/library'
+OUTPATH = '/home/olpc/Activities/ImageQuizPlus.activity/flashcards/flashcards'
+
+from path import path
+from xmlio import Xmlio
+import xml.etree.ElementTree as ET
+
+'''
+ List of Services:
+ - ImageQuiz.py, Line 37: "Hook-In Services"
+ - http://wiki.laptop.org/index.php?title=XO_ImageQuiz/Plugins#Overview
+'''
+#
+# /media/SDCARD02 should be global for easy change
+# top level menu items are folders in /media/SDCARD02/library
+# change is to make this happen dynamically based on directory (using path.py)
+# top level categories are folders in selected top level item
+# sub categories are decks in selected category
+# selected deck is copied to /home/olpc/ImageQuizPlus.activity/flashcards (adding Leitner attributes)
+# if deck references image or sound files, they are copied to /home/olpc/ImageQuizPlus.activity/flashcards/image or sound.
+# finish by returning to main menu
+#
+# strategy
+# first get menu working without doing anything (print xxx.xml selected)
+# next add print for image copied or sound copied
+# debug source is /home/tonya/Desktop/ImageQuizPlus.activity/flashcards/library
+# debug destination is /home/tonya/Desktop/ImageQuizPlus.activity/flashcards
+#
+# next actually copy deck adding Leitner attributes
+#
+# modify flashcard.py to use Leitner attributes (first ignore as in quiz mode)
+
+def clickOnItem2():
+ #here we need to display list of decks already checked out
+ #print "Return to Library"
+ d = path(OUTPATH)
+ count = 0
+ y = 110
+ for item in d.files():
+ brdr = False
+ sf.add_text_item(item.name, (300, y+50*count), remove_deck, (d, item.name), brdr)
+ count += 1
+
+def remove_deck(s):
+ #print s[0], s[1], 'selected'
+ d = path(OUTPATH)
+ deck = d.joinpath(s[1])
+ #print 'remove deck', deck
+ path.remove(deck)
+ #we need to redisplay menu with deck removed
+ sf.clear_text_items()
+ clickOnItem2()
+
+def clickOnItem1():
+ #here we need to display categories (folders in selected folder)
+ #print "Demoplugin Menu Item 1"
+ #make path to folder
+ #here we need to use path to make a list of directories
+ brdr = False
+ d = path(LIB)
+ dsel = d.joinpath(sf.current_caption())
+ count = 0
+ y = 110
+ for folder in dsel.dirs():
+ #print folder, folder.name, len(folder.files()), len(folder.dirs())
+ sf.add_text_item("%s (%s)" % (unicode(folder.name), str(len(folder.dirs()))), (300,y + 50 * count),
+ ask_subcat, (dsel, folder.name), brdr)
+ count += 1
+
+def ask_subcat(s):
+ global sourcepath
+ #print s[0], s[1], 'selected'
+ brdr = True
+ sf.clear_text_items()
+ d = path(s[0])
+ dsel = d.joinpath(s[1])
+ #this is source for image and sound files
+ sourcepath = d
+ count = 0
+ y = 70
+ for f in dsel.listdir():
+ #may be deck or folder
+ if f.isdir():
+ cnt = len(f.listdir())
+ brdr = False
+ else:
+ tree = Xmlio(f)
+ root = tree.getroot()
+ cnt = len(root)
+ brdr = True
+ if count < 12:
+ sf.add_text_item("%s (%s)" % (unicode(f.namebase), str(cnt)), (200,y + 50 * count),
+ make_local, (dsel,f.name), brdr)
+ elif count < 24:
+ sf.add_text_item("%s (%s)" % (unicode(f.namebase), str(cnt)), (450,y + 50 * (count - 12)),
+ make_local, (dsel,f.name), brdr)
+ else:
+ sf.add_text_item("%s (%s)" % (unicode(f.namebase), str(cnt)), (700,y + 50 * (count - 24)),
+ make_local, (dsel,f.name), brdr)
+ count += 1
+
+def make_local(s):
+ global sourcepath
+ #this could be deck or folder
+ #print s[0], s[1], 'deck selected'
+ count = 0
+ sel = path(s[0])
+ selp = sel.joinpath(s[1])
+ if selp.isdir():
+ ask_subcat(s)
+ return
+ #print 'should be file', selp
+ deck = Xmlio(selp)
+ cards = deck.getroot()
+ outpath = path(OUTPATH)
+ fullpath = outpath.joinpath(s[1])
+ #print fullpath
+ outpath = path(OUTPATH)
+ soundsource = path(sourcepath).joinpath('sound')
+ imagesource = path(sourcepath).joinpath('image')
+ outdeck = Xmlio(root = "quiz")
+ outcards = outdeck.getroot()
+ for card in cards:
+ #copy card to outcard
+ question_node = card.find('question')
+ if question_node:
+ temp = question_node.findtext('sound')
+ if temp:
+ sounds = temp.split('/')
+ for item in sounds:
+ item = item[:-4] + '.ogg'
+ #copy sounds here
+ source = soundsource.joinpath(item)
+ temp = outpath.joinpath('sound')
+ destination = temp.joinpath(item)
+ #print 'sound to copy', item, 'from', source, 'to', destination
+ path.copy(source,destination)
+ sound = '/'.join(sounds)
+ else:
+ sound = ''
+ hint = question_node.findtext('hint')
+ answer_node = card.find('answer')
+ if answer_node:
+ more = answer_node.findtext('more')
+ temp = answer_node.findtext('image')
+ if temp:
+ image = temp[:-4] + '.png'
+ else:
+ image = ''
+ count += 1
+ outcard = ET.SubElement(outcards, 'card', id = str(count), bin = str(0),
+ count = str(0), shown = '', think = '')
+ question = ET.SubElement(outcard, 'question')
+ question.text = question_node.text
+ qsound = ET.SubElement(question, 'sound')
+ qsound.text = sound
+ #copy sound
+ #print 'sound', sound
+ qhint = ET.SubElement(question, 'hint')
+ qhint.text = hint
+ answer = ET.SubElement(outcard, 'answer')
+ answer.text = answer_node.text
+ aimage = ET.SubElement(answer, 'image')
+ aimage.text = image
+ #copy image
+ source = path(imagesource).joinpath(image)
+ temp = outpath.joinpath('image')
+ destination = temp.joinpath(image)
+ #print 'image to copy', image, 'from', source, 'to', destination
+ path.copy(source,destination)
+ amore = ET.SubElement(answer, 'more')
+ amore.text = more
+
+ #write outdeck
+ outdeck.save(fullpath)
+ sf.clear_text_items()
+
+def debug():
+ pass
+
+def load():
+ global sf
+ sf = __SERVICES__.frontend;
+
+# #print __SERVICES__.db.query("SELECT * FROM xoquiz WHERE 1")
+ sf.add_menu_dir('/library', 'Library')
+ #here we need to use path to make a list of directories
+ d = path(LIB)
+ for folder in d.dirs():
+ #print folder
+ if not folder[-3:] == 'bzr':
+ sf.add_menu_item('/library', folder.name, clickOnItem1)
+ #to remove completed decks
+ sf.add_menu_item('/library','return quiz', clickOnItem2)
+
+def close():
+ pass
diff --git a/plugins/_quizsocket.py b/plugins/_quizsocket.py
new file mode 100755
index 0000000..12e9e07
--- /dev/null
+++ b/plugins/_quizsocket.py
@@ -0,0 +1,247 @@
+#! /usr/bin/env python
+
+#!/usr/bin/env python
+
+import select
+import socket
+import sys
+import threading
+import time
+
+class Interact:
+# status = Status()
+ def __init__(self, status):
+ self.status = status
+
+ def strip(self, str, what):
+ str = str.strip()
+ while len(str) > 0 and str[-1:] == what: str = str[:-1]
+ while len(str) > 0 and str[:1] == what: str = str[1:]
+ return str
+
+ def process_input(self, s):
+ """ input comes as arg:param """
+ s = self.strip(s, "'")
+ print "processing: %s" % s
+
+ if len(s) == 0: return False
+ if ":" not in s: return False
+
+ s1 = s[:s.index(":")]
+ s2 = s[s.index(":")+1:]
+
+
+ if s1 == 'get' and s2 == 'userlist':
+ return self.status.get_user_names()
+
+ elif s1 == 'get' and s2 == 'status':
+ return self.status.get_status_line()
+
+ elif s1 == 'reguser' and len(s2) > 1:
+ i = 1
+ user_name = s2
+ while self.status.add_user(user_name) == False:
+ user_name = "%s-%i" % (s2, i)
+ i += 1
+
+ print "interact: registered user '%s'" % s2
+ return user_name
+
+class Server(threading.Thread):
+
+ def __init__(self, host, port, status):
+ threading.Thread.__init__(self)
+ self.host = host
+ self.port = port
+ self.backlog = 5
+ self.size = 1024
+ self.server = None
+ self.threads = []
+ self.interact = Interact(status)
+
+
+ def open_socket(self):
+ try:
+ self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.server.setblocking(0)
+ self.server.bind((self.host,self.port))
+ self.server.listen(5)
+ except socket.error, (value,message):
+ if self.server:
+ self.server.close()
+ print "Could not open socket: " + message
+ if message == "Address already in use":
+ self.__init__(self.host, self.port + 5)
+ else:
+ sys.exit(1)
+
+ def run(self):
+ self.open_socket()
+ input = [self.server]
+ self.running = True
+ while self.running:
+ inputready,outputready,exceptready = select.select(input,[],[],3)
+
+ for s in inputready:
+
+ if self.running and s == self.server:
+ # handle the server socket
+ print "start new client"
+ c = ClientServer(self.server.accept(), self.interact)
+ c.start()
+ self.threads.append(c)
+
+ # close all threads
+ print "closing server.run()"
+
+ def quit(self):
+ print "server quitting"
+ self.running = False
+ self.server.close()
+ for c in self.threads:
+ c.running = 0
+ c.join()
+
+class ClientServer(threading.Thread):
+ def __init__(self,(client,address), interact):
+ threading.Thread.__init__(self)
+ self.client = client
+ self.client.setblocking(0)
+ self.address = address
+ self.size = 1024
+ self.interact = interact
+
+ def run(self):
+ self.running = 1
+ while self.running:
+ try:
+ data = self.client.recv(self.size)
+ if self.running and data:
+ if self.running:
+ if data[:3] == "bye" and ":" in data:
+ self.interact.status.del_user(data[4:])
+ data_back = "bye"
+ else:
+ data_back = self.interact.process_input(repr(data))
+ else:
+ data_back = "bye"
+
+ print "sending back:", data_back
+ self.client.send("%s" % data_back)
+ else:
+ self.client.close()
+ self.running = 0
+
+ except Exception, inst:
+ # socket is in non-blocking mode, so it raises a lot of exceptions
+ if inst.args[0] == 104:
+ # connection reset by peer
+ self.interact.status.del_user(data[4:])
+ self.running = False
+
+ elif inst.args[0] != 11:
+ print type(inst)
+ print inst.args
+
+# time.sleep(0.3)
+
+class Client(threading.Thread):
+ def __init__(self, host, port, username, nonet_function=None):
+ threading.Thread.__init__(self)
+ self.host = host
+ self.port = port
+ self.username = username
+ self.nonet_function = nonet_function
+
+ def close(self):
+ print "sending bye to server"
+ try:
+ self.s.send("bye:%s" % self.username)
+ time.sleep(1)
+ except: pass
+ self.running = False
+
+ def run(self):
+ host = self.host
+ port = self.port
+ size = 1024
+ self.size = size
+
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+ try: s.connect((host,port))
+ except:
+ if self.nonet_function != None: self.nonet_function()
+ return False
+
+ s.setblocking(0)
+
+ self.s = s
+
+ # Register Username
+ cmd = "reguser:%s" % self.username
+ self.username = self.talk(cmd)
+ print "received username: %s" % self.username
+
+ self.running = True
+ while self.running:
+ time.sleep(2)
+# print self.talk("get:userlist")
+
+ s.close()
+
+ def talk(self, s):
+# cmd = "get:userlist"
+ print "< %s" % s
+ loop = True
+ while loop:
+ try:
+ self.s.send(s)
+ loop = False
+ except Exception, inst:
+ print type(inst)
+ print inst.args
+
+# errno, errstr = list(inst.args)
+ if inst.args[0] == 11: time.sleep(0.5)
+ else:
+ print "Host has quit"
+ self.running = False
+ return False
+
+ loop = True
+ while loop:
+ try:
+ data = self.s.recv(self.size)
+ loop = False
+ return data
+ except Exception, inst:
+# print type(inst)
+# print inst.args
+ errno, errstr = list(inst.args)
+ if errno == 11: time.sleep(0.5)
+ else:
+ print "Host has quit"
+ self.running = False
+ return False
+
+
+
+if __name__ == "__main__":
+ host = "localhost"
+ port = 50056
+ username = "chris"
+
+ if len(sys.argv) > 1:
+ if sys.argv[1] == "0":
+ s = Server('', port)
+ s.start()
+
+ time.sleep(10)
+ s.quit()
+
+ print "ciao ciao"
+
+ else:
+ c = Client(host, port, username)
+ c.start()
diff --git a/plugins/dbtool.py b/plugins/dbtool.py
new file mode 100755
index 0000000..db706c4
--- /dev/null
+++ b/plugins/dbtool.py
@@ -0,0 +1,99 @@
+'''
+ List of Services:
+ - ImageQuiz.py, Line 37: "Hook-In Services"
+ - http://wiki.laptop.org/index.php?title=XO_ImageQuiz/Plugins#Overview
+'''
+import subprocess
+from path import path
+from sugar.activity import activity
+
+__PLUGIN_NAME__ = 'dbtool'
+
+DATADIR = path(activity.get_activity_root()) / 'data'
+ACTIVITYDIR = path(activity.get_bundle_path())
+print 'directories', DATADIR, ACTIVITYDIR
+
+def dbdelete():
+ sf.clear_text_items()
+ sf.add_text_item("Clear database", (280,110))
+ cmd = "rm -rf *"
+ #cmd = "ls -l > " + DATADIR + "/iq.log"
+ subprocess.call(cmd, shell=True, cwd=DATADIR)
+ print "Clear database"
+
+
+#TABLE 'categories' ('id','cat_id', 'lang_id', 'text', 'parent_id', 'base_parent_id')
+#TABLE 'questions' ('id', 'image_id', 'sound_id', 'map', 'cat', 'subcat', 'answer_link', 'count_found', 'count notfound', 'box', 'time', 'day')
+#TABLE 'quizlinks' ('id', 'quiz_id', 'question_id')
+#TABLE 'catlinks' ('id', 'parent_id', 'child_id')
+#TABLE 'Leitner' ('id', 'question_id', etc )
+
+def dbcategories():
+ sf.clear_text_items()
+ sf.add_text_item("Show categories", (280,110))
+ cats = __SERVICES__.db.query("SELECT id, text FROM categories;")
+ count = 0
+ for cat in cats:
+ count += 1
+ catstr = str(cat[0]) + ' category: ' + str(cat[1])
+ sf.add_text_item(catstr, (180 + 50, 110 +count * 50))
+ print "Show categories"
+
+def dbquestions(count):
+ sf.clear_text_items()
+ sf.add_text_item("Show questions", (280,110))
+ #allow user to go to next page
+ sf.add_text_item("next page", (400,110), dbquestions, count + 12, True)
+ q = "SELECT id, prompt, response, image_fn, sound_fn, map, answer_link FROM questions;"
+ questions = __SERVICES__.db.query(q)
+ if count < len(questions):
+ showpage(questions, count)
+
+def showpage(questions, count):
+ line = 0
+ for i in range(12):
+ if count < len(questions):
+ question = questions[count]
+ qstr = str(question[0]) + ' prompt:' + question[1]
+ qstr = qstr + ' response:' + question[2]
+ qstr = qstr + ' img: ' + question[3]
+ qstr = qstr + ' snd: ' + question[4] + ' map ' + question[5]
+ qstr = qstr + ' answer_link: ' + question[6]
+ sf.add_text_item(qstr, (180 + 50, 160 + line * 25))
+ else:
+ return True
+ count += 1
+ line += 1
+ return False
+
+def dbquizlinks():
+ sf.clear_text_items()
+ sf.add_text_item("Show questions in quiz", (280,110))
+ q = "SELECT id, quiz_id, question_id FROM quizlink;"
+ links = __SERVICES__.db.query(q)
+ count = 0
+ for link in links:
+ count += 1
+ qstr = str(link[0]) + ' quiz: ' + str(link[1]) + ' question ' + str(link[2]) + ');'
+ sf.add_text_item(qstr, (180 + 50, 110 + count * 25))
+
+
+def debug():
+ pass
+
+def dbquestions1():
+ dbquestions(0)
+
+def load():
+ global sf
+ sf = __SERVICES__.frontend;
+ sf.add_menu_dir('/dbtool', 'DB Tool')
+ sf.add_menu_item('/dbtool', 'show categories', dbcategories)
+ sf.add_menu_item('/dbtool', 'show questions', dbquestions1)
+ sf.add_menu_item('/dbtool', 'show quizlinks', dbquizlinks)
+ #sf.add_menu_item('/dbtool', 'clear database', dbdelete)
+
+ pass
+
+def close():
+ pass
diff --git a/plugins/demoplugin.py b/plugins/demoplugin.py
new file mode 100755
index 0000000..a6f8093
--- /dev/null
+++ b/plugins/demoplugin.py
@@ -0,0 +1,25 @@
+__PLUGIN_NAME__ = 'demo plugin 1'
+
+'''
+ List of Services:
+ - ImageQuiz.py, Line 37: "Hook-In Services"
+ - http://wiki.laptop.org/index.php?title=XO_ImageQuiz/Plugins#Overview
+'''
+
+def clickOnItem1():
+ print "Demoplugin Menu Item 1"
+
+def debug():
+ pass
+
+def load():
+ global sf
+ sf = __SERVICES__.frontend;
+
+# print __SERVICES__.db.query("SELECT * FROM xoquiz WHERE 1")
+# sf.add_menu_dir('/demodir', 'Demo Directory')
+# sf.add_menu_item('/', 'Demo Item 3', clickOnItem1)
+ pass
+
+def close():
+ pass \ No newline at end of file
diff --git a/plugins/ink.py b/plugins/ink.py
new file mode 100755
index 0000000..544bc78
--- /dev/null
+++ b/plugins/ink.py
@@ -0,0 +1,61 @@
+# ink.py
+#
+# B. Mayton <bmayton@cs.washington.edu>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# -*- mode:python; tab-width:4; indent-tabs-mode:t; -*-
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import random
+import logging
+
+class Path:
+
+ def __init__(self, inkstr=None):
+ self.__logger = logging.getLogger('Path')
+ self.points=[]
+ self.color = (0,0,1.0)
+ self.pen = 4
+ self.uid = random.randint(0, 2147483647)
+ if inkstr:
+ try:
+ i=0
+ parts = inkstr.split('#')
+ if len(parts) > 1:
+ params = parts[i].split(';')
+ self.uid = int(params[0])
+ colorparts = params[1].split(',')
+ self.color = (float(colorparts[0]),float(colorparts[1]),float(colorparts[2]))
+ self.pen = float(params[2])
+ i = i + 1
+ pathstr = parts[i]
+ pointstrs = pathstr.split(';')
+ for pointstr in pointstrs:
+ pparts = pointstr.split(',')
+ if len(pparts) == 2:
+ self.add((int(pparts[0]), int(pparts[1])))
+ except Exception, e:
+ self.__logger.debug('Could not unserialize ink string (old ink?)')
+
+ def add(self, point):
+ self.points.append(point)
+
+ def __str__(self):
+ s = str(self.uid) + ";"
+ s = s + str(self.color[0]) + "," + str(self.color[1]) + "," + str(self.color[2]) + ";"
+ s = s + str(self.pen) + "#"
+ for p in self.points:
+ s = s + str(int(p[0])) + "," + str(int(p[1])) + ";"
+ return s
diff --git a/plugins/make.py b/plugins/make.py
new file mode 100755
index 0000000..e6f6189
--- /dev/null
+++ b/plugins/make.py
@@ -0,0 +1,422 @@
+__PLUGIN_NAME__ = 'make'
+
+'''
+ List of Services:
+ - ImageQuiz.py, Line 37: "Hook-In Services"
+ - http://wiki.laptop.org/index.php?title=XO_ImageQuiz/Plugins#Overview
+'''
+
+import os, sys
+
+import pygame
+from pygame.locals import *
+
+from sugar.activity import activity
+
+# the following line is not needed if pgu is installed
+import sys; sys.path.insert(0, "..")
+
+from pgu import gui
+
+import pygst
+pygst.require("0.10")
+import gst
+
+import ink
+
+from path import path
+
+#set up paths to for adding images and sounds
+DATAPATH = os.path.join(activity.get_activity_root(), "data")
+ACTIVITYPATH = activity.get_bundle_path()
+IMAGEPATH = os.path.join(DATAPATH, 'image')
+SOUNDPATH = os.path.join(DATAPATH, 'sound')
+
+clock = pygame.time.Clock()
+
+class CurrentQuestion:
+ id = 0
+ prompt = u''
+ response = u''
+ imgfn = u''
+ sndfn = u''
+ map = u''
+ answer_link = u''
+
+class EditDialog(gui.Dialog):
+ def __init__(self, editsave, cq):
+ max = 100
+ title = gui.Label("Edit Question")
+
+ t = gui.Table()
+ self.form = gui.Form()
+
+ t.tr()
+ print 'cq.imgfn', len(cq.imgfn.split()), len(cq.imgfn), cq.imgfn
+ imgpath = path(IMAGEPATH) / 'blank.png'
+ if len(cq.imgfn) > 0:
+ temp = path(IMAGEPATH) / cq.imgfn
+ if temp.exists():
+ imgpath = temp
+ print 'load image', imgpath.exists(), imgpath
+ self.img = pygame.image.load(imgpath)
+ t.td(gui.Image(self.img), align=-1, valign=-1, colspan = 8)
+
+ if len(cq.map) > 0:
+ self.draw_map(cq.map)
+
+ print 'table', t.getRows(), t.getColumns(), t.resize()
+
+ t.tr()
+ t.td(gui.Label(""))
+
+ t.tr()
+ t.td(gui.Label("Image: "), align = -1)
+ t.td(gui.Input(name = 'image', value = cq.imgfn, size = len(cq.imgfn) + 10), align = -1, colspan = 3)
+ imgBrowseButton = gui.Button("Browse...")
+ imgBrowseButton.connect(gui.CLICK, imgbrowse, cq)
+ t.td(imgBrowseButton)
+
+ print 'table0', t.getRows(), t.getColumns(), t.resize()
+
+ t.tr()
+ t.td(gui.Label("Clip: "), align = -1)
+ print 'clip', len(cq.sndfn), cq.sndfn
+ t.td(gui.Input(name = 'sound', value = cq.sndfn, size = len(cq.sndfn) + 10), align = -1, colspan = 3)
+ clipBrowseButton = gui.Button("Browse...")
+ clipBrowseButton.connect(gui.CLICK, clipbrowse, cq)
+ t.td(clipBrowseButton)
+ clipBrowseButton = gui.Button("Record")
+ clipBrowseButton.connect(gui.CLICK, record, cq)
+ t.td(clipBrowseButton)
+ clipBrowseButton = gui.Button("Stop")
+ clipBrowseButton.connect(gui.CLICK, stop, cq)
+ t.td(clipBrowseButton)
+ clipBrowseButton = gui.Button("Play")
+ clipBrowseButton.connect(gui.CLICK, play, cq)
+ t.td(clipBrowseButton)
+
+ print 'table1', t.getRows(), t.getColumns(), t.resize()
+
+ t.tr()
+ t.td(gui.Label("Prompt: "), align = -1)
+ sz = len(cq.prompt) + 10
+ if sz > max:
+ sz = max
+ print 'prompt', sz, len(cq.prompt), cq.prompt
+ t.td(gui.Input(name = 'prompt', value = cq.prompt, size = sz), align = -1, colspan=6)
+
+ print 'table2', t.getRows(), t.getColumns(), t.resize()
+
+ t.tr()
+ t.td(gui.Label("Response: "), align = -1)
+ sz = len(cq.response) + 10
+ if sz > max:
+ sz = max
+ print 'response',sz, len(cq.response), cq.response
+ t.td(gui.Input(name = 'response', value = cq.response, size = sz), align = -1, colspan=6)
+
+ print 'table3', t.getRows(), t.getColumns(), t.resize()
+
+ t.tr()
+ t.td(gui.Label("Answer_link: "), align = -1)
+ sz = len(cq.answer_link) + 10
+ if sz > max:
+ sz = max
+ print 'answer_link', sz, len(cq.answer_link), cq.answer_link
+ t.td(gui.Input(name = 'answer_link', value = cq.answer_link, size = sz), align = -1, colspan=7)
+
+ print 'table4', t.getRows(), t.getColumns(), t.resize()
+
+ t.tr()
+ t.td(gui.Label(""))
+
+ t.tr()
+ saveButton = gui.Button("Save")
+ saveButton.connect(gui.CLICK, editsave, cq)
+ t.td(saveButton,colspan=3)
+
+ self.t = t
+ print 'tablef', self.t.getRows(), self.t.getColumns(), self.t.resize()
+ gui.Dialog.__init__(self,title,self.t)
+
+ def draw_map(self, map):
+ color = (100,0,0)
+ pen = 4
+ pts = []
+ coords = []
+ maplst = map.split(',')
+ for pt in maplst:
+ try:
+ coords.append(int(pt))
+ except:
+ pass
+ i = 0
+ while i < len(coords):
+ pts.append((coords[i],coords[i+1]))
+ i += 2
+ pygame.draw.lines(self.img, color, True, pts, pen)
+
+def clickOnMake():
+ # Select Category
+ cat_id = -1
+ sf.clear_text_items()
+ sf.clear_question_frame(True)
+ print 'ask_category', cat_id
+ ask_category(cat_id)
+
+def ask_category(cat_id, offset_y = 0):
+ global c
+ global new_cat
+
+ i = 1
+ y = 110 + offset_y
+
+ sf.clear_text_items()
+ sf.add_text_item("Next Category:", (280,y))
+
+
+ new_cat = gui.Input(value="new", size = 20)
+ new_cat.connect(gui.ENTER,add_cat, 0)
+ c.add(new_cat, 0, 0)
+ app = sf.app()
+ app.init(c)
+
+
+ #first we need to query for categories
+ #might be nice to show them in alphabetical order
+ #when y gets too big, we should change x
+ if cat_id == -1:
+ cats = __SERVICES__.db.query("SELECT id, text FROM categories;")
+ print 'cats', len(cats), cats
+ for cat in cats:
+ print 'cat', cat[0], cat[1]
+ cat_id = cat[0]
+ category = cat[1]
+ try:
+ q = 'SELECT count(*) FROM quizlink WHERE quiz_id=%i' % cat_id
+ res1 = __SERVICES__.db.query(q)
+ except:
+ print 'query error', q
+ count = res1[0][0]
+ print 'count=', count
+ if count > 0:
+ # this is a quiz, display in green
+ y += 50
+ sf.add_text_item("%s (%s)" % (category, count), (300,y), ask_question, cat_id)
+ else:
+ # this is a category - get number of children
+ q = 'SELECT count(*) FROM catlink WHERE parent_id=%i' % cat_id
+ res2 = __SERVICES__.db.query(q)
+ count = res2[0][0]
+ y += 50
+ sf.add_text_item("%s (%s)" % (category, count), (300,y), ask_category, cat_id)
+ i += 1
+ else:
+ #we need to pass a cat_id from the user's selection
+ print 'second level selection not implemented - must select quiz'
+
+def add_cat(params):
+ global new_cat
+ global CATID
+ #add cat to db with new_cat.value as text
+ CATID= __SERVICES__.db.add_cat(new_cat.value)
+ #create empty question to pass to startedit
+ cq = CurrentQuestion()
+ cq.id = -1
+ cq.prompt = ""
+ cq.response = ""
+ cq.imgfn = ""
+ cq.sndfn = ""
+ cq.map = ""
+ cq.answer_link = ""
+ startedit(cq)
+
+def ask_question(cat_id, offset_y = 0):
+ global CATID
+
+ print 'ask_question', cat_id, offset_y
+
+ i = 1
+ y = 110 + offset_y
+ CATID = cat_id
+
+
+ sf.clear_text_items()
+ sf.add_text_item("Select question:", (280,y))
+
+ #we need to query for questions
+ #when y gets too big, we should change x
+ try:
+ q = 'SELECT question_id FROM quizlink WHERE quiz_id=%i' % cat_id
+ print 'query=', q
+ res = __SERVICES__.db.query(q)
+ except:
+ print 'failure in query', q
+ print 'res', len(res), res
+
+ count = 0
+ for question in res:
+ count += 1
+ try:
+ q = 'SELECT prompt, response, image_fn, sound_fn, map, answer_link FROM questions WHERE id = %i' % question[0]
+ print 'query=', q
+ res2 = __SERVICES__.db.query(q)
+ except:
+ print 'error in query', q
+ print 'res2', len(res2), res2
+ res1 = res2[0]
+ cq = CurrentQuestion()
+ cq.id = question[0]
+ cq.prompt = res1[0]
+ cq.response = res1[1]
+ cq.imgfn = res1[2]
+ cq.sndfn = res1[3]
+ cq.map = res1[4]
+ cq.answer_link = res1[5]
+ if len(cq.imgfn) > 0:
+ ipath = path(IMAGEPATH) /cq.imgfn
+ if len(cq.imgfn)>0 and ipath.exists():
+ y += 50
+ image, xy = sf.image_load(ipath)
+ sf.display_surface(pygame.transform.scale(image,(128,128)), (300,y))
+ y += 128
+ if len(cq.prompt.strip()) > 0:
+ sf.add_text_item(cq.prompt,(300,y),startedit, cq)
+ else:
+ sf.add_text_item(cq.imgfn,(300,y),startedit, cq)
+ elif len(cq.prompt.strip()) > 0:
+ y += 50
+ sf.add_text_item(cq.prompt,(300,y),startedit, cq)
+ else:
+ y += 50
+ sf.add_text_item(cq.sndfn,(300,y), startedit, cq)
+
+def startedit(question, offset_y = 0):
+ global edit_d
+
+ cq = question
+
+ #if len(cq.imgfn) > 0 and path(cq.imgfn).exists:
+ #display cq.imgfn
+ #else:
+ #display 'no image' image
+ #display cq.imgfn entry as caption
+ #display audio controls (right of sound entry)
+ #if not (len(cq.imgfn) > 0 and path(cq.imgfn).exists()):
+ #grey 'play' control
+ #if len(cq.sndfn) > 0 and path(cq.sndfn) exists():
+ #display audio controls (to right of sound entry)
+ #if sound recorded, ungrey 'play' control
+ #if sound path changed, reset 'play' control depending on whether path exists
+
+
+ i = 1
+ print 'i=', i
+ y = 110 + offset_y
+ print 'y=', y
+
+ print 'clear text items'
+ sf.clear_text_items()
+
+ edit_d = EditDialog(editsave, cq)
+ edit_d.open()
+
+def editsave(cq):
+ global CATID
+ global edit_d
+
+ form = edit_d.form
+ cq.prompt = form['prompt'].value
+ cq.response = form['response'].value
+ cq.imgfn = form['image'].value
+ cq.sndfn = form['sound'].value
+ cq.answer_link = form['answer_link'].value
+
+ #if a new question, it needs to be inserted into database
+ #insert question
+ #add quizlink with quiz id and question id
+
+ #insert updated question into database
+ q = "UPDATE questions SET prompt='%s' WHERE id=%i" % (cq.prompt, cq.id)
+ updatedb(q)
+ q = "UPDATE questions SET response = '%s' WHERE id = %i" % (cq.response, cq.id)
+ updatedb(q)
+ q = "UPDATE questions SET image_fn = '%s' WHERE id = %i" % (cq.imgfn, cq.id)
+ updatedb(q)
+ q = "UPDATE questions SET sound_fn = '%s' WHERE id = %i" % (cq.sndfn, cq.id)
+ updatedb(q)
+ q = "UPDATE questions SET map = '%s' WHERE id = %i" % (cq.map, cq.id)
+ updatedb(q)
+ q = "UPDATE questions SET answer_link = '%s' WHERE id = %i" % (cq.answer_link, cq.id)
+ updatedb(q)
+
+ edit_d.close()
+ print 'edit_d closed', edit_d.t.getRows(), edit_d.t.getColumns()
+ edit_d.t.clear()
+ print 'edit_d closed and table cleared', edit_d.t.getRows(), edit_d.t.getColumns()
+ cat_id = CATID
+ ask_question(cat_id)
+
+def updatedb(q):
+ print 'updatedb'
+ __SERVICES__.db.commit(q)
+
+def imgbrowse(cq):
+ print 'imgbrowse not implemented'
+
+def clipbrowse(cq):
+ print 'clipbrowse not implemented'
+
+def record(cq):
+ global player
+ global fileout
+ fileout.set_property("location", os.path.join(SOUNDPATH, cq.sndfn))
+ print player.get_state()
+ player.set_state(gst.STATE_PLAYING)
+
+def stop(cq):
+ player.set_state(gst.STATE_READY)
+ pygame.mixer.stop()
+
+def play(cq):
+ sound = sf.sound_load(os.path.join(SOUNDPATH, cq.sndfn))
+ sound.play()
+ while pygame.mixer.get_busy():
+ clock.tick(30)
+
+def debug():
+ pass
+
+def load():
+ global sf
+ global player
+ global fileout
+ global c
+ sf = __SERVICES__.frontend
+ #print __SERVICES__.db.query("SELECT text FROM categories")
+ #sf.add_menu_dir('/demodir', 'Demo Directory')
+ sf.add_menu_item('/', 'Make', clickOnMake)
+ #initialize audio record pipeline
+ player = gst.Pipeline("player")
+ source = gst.element_factory_make("alsasrc", "alsa-source")
+ player.add(source)
+ convert = gst.element_factory_make("audioconvert", "converter")
+ player.add(convert)
+ enc = gst.element_factory_make("vorbisenc", "vorbis-encoder")
+ player.add(enc)
+ create = gst.element_factory_make("oggmux", "ogg-create")
+ player.add(create)
+ fileout = gst.element_factory_make("filesink", "sink")
+ fileout.set_property("location", "test.ogg")
+ player.add(fileout)
+ gst.element_link_many(source, convert, enc, create, fileout)
+
+
+ #intialize gui
+ c = gui.Container(width = 400, height = 600)
+ app = sf.get_app()
+ app.init(c)
+
+def close():
+ pass
diff --git a/plugins/multi_player.py b/plugins/multi_player.py
new file mode 100755
index 0000000..68acc63
--- /dev/null
+++ b/plugins/multi_player.py
@@ -0,0 +1,552 @@
+import pygame
+from pygame import *
+from layout import *
+from frontend import hex2rgb
+import threading
+import time
+import traceback
+
+__PLUGIN_NAME__ = 'multi player'
+#import single_player
+
+class Status:
+ """ edited by the hosts """
+
+ game_state = '0' # 0=open_before_game, 1=..?
+
+ status_line = 'statusline'
+ clients = []
+
+ class Client:
+ def __init__(self, name, status=''):
+ self.name = name
+ self.status = status
+
+ def set_status_line(self, s1, s2):
+ return True
+
+ def get_status_line(self):
+ self.status_line = "%s;;%s" % (self.game_state, self.get_user_names(";"))
+ return self.status_line
+
+ def get_user_names(self, split_str=';;'):
+ usernames = []
+ for c in self.clients:
+ usernames.append(c.name)
+ return split_str.join(usernames)
+
+ def add_user(self, user_name):
+ for c in self.clients:
+ if c.name.upper() == user_name.upper(): return False
+
+ new_client = self.Client(user_name)
+ self.clients.append(new_client)
+ return True
+
+ def del_user(self, user_name):
+ print "deleting user %s" % user_name
+ new_clients = []
+ for c in self.clients:
+ if c.name.upper() != user_name.upper():
+ new_clients.append(c)
+
+ self.clients = new_clients
+ return True
+
+
+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 "* 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(0)
+
+
+def ask_category(offset_y = 0, offset_x = 0, cat_id=0):
+ Q.load(__SERVICES__.locale.lang_id, __SERVICES__.locale.lang_name)
+
+# sf.clear_text_items()
+
+ i = 1
+ y = 110 + offset_y
+ x = 340 + offset_x
+
+ sf.add_text_item("Category:", (x,y))
+ y += 50
+ x += 50
+ sf.add_text_item('All', (x,y), game_host.set_category, 0, True)
+# global game_host
+ for q in Q.categories:
+ y += 50
+ sf.add_text_item("%s (%s)" % (q[1], q[2]), (x,y), game_host.set_category, 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)
+
+def click_on_next_question():
+ next_question(0)
+
+def next_question(params):
+ print "* Next Question"
+
+ # Select Category
+ if Q._cat_id == -1:
+ ask_category()
+ return 1
+
+
+ # Pick only locale language's categories
+ if Q.question_pick():
+ __SERVICES__.frontend.question_display(Q.question)
+ Q.in_question = True
+
+ pass
+ else:
+ # game / cat finished!
+ in_question = False
+ print "finished"
+ Q._cat_id = -1
+ finished()
+ pass
+
+def spot_found(params):
+ if Q.in_question:
+ print "found"
+ A.display_icon("found")
+ A.play_sound(1)
+ Q.in_question = False
+ Q.count_won += 1
+ display_points()
+ show_answer()
+
+def spot_not_found(params):
+ print params
+ if Q.in_question:
+ print "not found"
+ A.display_icon("not-found")
+ A.play_sound(0)
+ Q.in_question = False
+ Q.count_lost += 1
+ display_points()
+ show_answer()
+ return "ok"
+
+
+
+
+class getStatus(threading.Thread):
+ running = True
+
+ def set(self, client_talk, status_call_function, abort_function=None):
+ self.talk = client_talk
+ self.status_call_function = status_call_function
+ self.abort_function = abort_function
+ self.running = True
+
+ def run(self):
+ while self.running:
+ time.sleep(2)
+ try:
+ self.status = self.talk('get:status')
+ self.status_call_function(self.status)
+ except:
+ traceback.print_exc()
+ print "getstatus aborted"
+ self.running = False
+ if self.abort_function != None: self.abort_function()
+
+
+class waitForUsersToJoin(threading.Thread):
+ running = True
+ last_users = ''
+
+ def set(self, talk, waittext_id, textid_connectedto=None):
+ self.talk = talk
+ self.waittext_id = waittext_id
+ self.textid_connectedto = textid_connectedto
+
+ def display_server_quit(self):
+ self.running = False
+
+ def run(self):
+ format = TextFormat()
+ font = pygame.font.Font(None, format.size)
+ bg = pygame.Surface((560, 30))
+
+ while self.running:
+ time.sleep(2)
+
+ users = self.talk("get:userlist")
+ if users == False: return
+
+ self.users = users.split(";;")
+ users = users.replace(";;", ", ")
+
+ print self.users
+ print users
+
+ text = font.render("> %s" % users, 1, hex2rgb(format.color))
+ textpos = text.get_rect()
+
+ bg.fill(hex2rgb(Layout().background))
+ bg.blit(text, (0,0))
+ sf.display_surface(bg, (48, 610))
+
+ pygame.display.update((48, 610, 560, 30))
+
+class DrawTimer:
+ def __init__(self):
+ self.bg = pygame.Surface((100, 60))
+
+ def draw(self, time):
+ self.bg.fill(hex2rgb(Layout().Question.background))
+
+ format = TextFormat(None, 46, None)
+ font = pygame.font.Font(None, format.size)
+ text = font.render("%i sec" % time, 1, hex2rgb(format.color))
+
+ self.bg.blit(text, (0,0))
+ sf.display_surface(self.bg, (150, 440))
+ pygame.display.update()
+
+class MultiPlayerHost:
+ category = 0
+ game_time = 0
+ textid_timer = -1
+ status = Status()
+
+ def __init__(self, host, port):
+ self.host = host
+ self.port = port
+ self.socket = __import__("plugins/_quizsocket")
+
+ print "starting server"
+ self.server = self.socket.Server('', port, self.status)
+ self.server.start()
+
+ def close(self):
+ self.client.running = False
+ self.waituser.running = False
+ self.server.quit()
+
+ def update_status(self, params):
+ print params
+
+ def load(self):
+ print "Creating a new game"
+ sf.clear_question_frame(True)
+
+ # Display some text
+ format = TextFormat(None, 40)
+ sf.add_text_item("New Multiplayer Game", (340, 45), None, None, False, format)
+ waittext_id = sf.add_text_item("Waiting for users to join...", (40, 570))
+
+ # let's spawn our own client now
+ self.client = self.socket.Client(self.host, self.port, Layout().user_name)
+ self.client.start()
+
+ # Display "Waiting for users to join..."
+ self.waituser = waitForUsersToJoin()
+ self.waituser.set(self.client.talk, waittext_id, None)
+ self.waituser.start()
+
+ # Constantly Get Status Updates from the Host
+# self.getstatus = getStatus()
+# self.getstatus.set(self.Spawner.client_talk, self.update_status, self.abort_net)
+# self.getstatus.start()
+
+ # Display Start Button
+ format = TextFormat(None, None, Colors().green)
+ sf.add_text_item("Start", (Layout().Question.x + Layout().Question.width - 150, 570), None, None, True, format)
+
+ # Display Categories
+ ask_category()
+ self.set_category(0)
+ pygame.display.update()
+
+ self.hookid_click = sf.add_event_hook("onclick", self.click)
+
+
+ # Draw Timer Option
+ img, rect = sf.image_load("images/xclock.png")
+ sf.display_surface(img, (40, 400))
+ self.draw_timer = DrawTimer()
+ self.click_timer()
+
+
+ def click(self, params):
+ x, y = list(params)
+ if x > 40 and x < (40 + 96) and y > 400 and y < (400 + 96):
+ # Click on Clock
+ self.click_timer()
+
+ def click_timer(self):
+ if self.game_time == 30:
+ self.game_time = 10
+ elif self.game_time == 20:
+ self.game_time = 30
+ elif self.game_time == 10:
+ self.game_time = 20
+ elif self.game_time == 0:
+ self.game_time = 10
+
+# self.Spawner.master_set_status("time:%i" % self.game_time)
+ self.draw_timer.draw(self.game_time)
+
+ def set_category(self, param):
+ cat_id = int(param)
+ self.category = cat_id
+
+ cat_name = 'All'
+ if cat_id > 0:
+ q = "SELECT text FROM categories WHERE cat_id=%i AND lang_id=%s" % (cat_id, __SERVICES__.locale.lang_id)
+ res = __SERVICES__.db.query(q)
+ print res
+ cat_name = res[0][0]
+
+# self.Spawner.master_set_status(u"cat:%s" % cat_name)
+
+ bg = pygame.Surface((360, 30))
+ bg.fill(hex2rgb(Layout().background))
+
+ format = TextFormat()
+ font = pygame.font.Font(None, format.size)
+
+ text = font.render("%s" % cat_name, 1, hex2rgb(format.color))
+ bg.blit(text, (0,0))
+ sf.display_surface(bg, (480, 115))
+ pygame.display.update()
+
+class MultiPlayerClient:
+ connected = False
+ game_time = 0
+ cat_name = u''
+
+ socket = None
+ waittext_id = None
+
+ format = TextFormat()
+ font = pygame.font.Font(None, format.size)
+ bg = pygame.Surface((560, 30))
+
+ def __init__(self, host, port):
+ self.host = host
+ self.port = port
+ self.connected = False
+ if self.socket == None: self.socket = __import__("plugins/_quizsocket")
+
+ def close(self):
+ self.getstatus.running = False
+ self.client.close()
+ self.connected = False
+
+ def load(self):
+ print "Joining a multiplayer game"
+
+ sf.clear_question_frame(True)
+
+ format = TextFormat(None, 40)
+ sf.add_text_item("Joining a Multiplayer Game", (340, 45), None, None, False, format)
+ pygame.display.update()
+
+# print self.connected
+ if self.connected == False:
+ host = sf.ask("host")
+ sf.refresh()
+ print "host: ", host
+ self.host = host
+
+ if len(host) == 0:
+ return False
+
+# if host == "l": host = "localhost"
+ print "starting client"
+ textid_connectedto = sf.add_text_item("[connecting to %s]" % host, (340, 80))
+ self.textid_connectedto = textid_connectedto
+
+ self.client = self.socket.Client(self.host, self.port, Layout().user_name, self.nonet_function)
+ self.client.start()
+
+ self.getstatus = getStatus()
+ self.getstatus.set(self.client.talk, self.update_status, self.nonet_function)
+ self.getstatus.start()
+
+ def display_catname(self):
+ bg = pygame.Surface((300, 60))
+ bg.fill(hex2rgb(Layout().Question.background))
+
+ format = TextFormat(None, 40)
+ font = pygame.font.Font(None, format.size)
+ text = font.render("Category: %s" % self.cat_name, 1, hex2rgb(format.color))
+
+ bg.blit(text, (0,0))
+ sf.display_surface(bg, (350, 190))
+ pygame.display.update()
+
+ def update_status(self, params):
+ self.status = params
+ print "STATUS UPDATE:",params
+
+ if params == False or len(params) == 0 or params == None or params == "None":
+ self.close()
+ return
+
+ if self.connected == False:
+ self.connection_ok()
+
+ n = params.split(";;")
+
+ users = n[1].replace(";", ", ")
+
+ text = self.font.render("> %s" % users, 1, hex2rgb(self.format.color))
+ textpos = text.get_rect()
+
+ self.bg.fill(hex2rgb(Layout().background))
+ self.bg.blit(text, (0,0))
+ sf.display_surface(self.bg, (48, 610))
+
+ pygame.display.update((48, 610, 560, 30))
+ print params
+
+# for n in params:
+# m = n.split(":")
+# if m[0] == "time":
+# if int(m[1]) != self.game_time:
+# self.game_time = int(m[1])
+# self.draw_timer.draw(self.game_time)
+#
+# if m[0] == "cat":
+# if m[1] != self.cat_name:
+# self.cat_name = m[1]
+# self.display_catname()
+
+
+ def connection_ok(self):
+ self.connected = True
+ sf.del_text_item(self.textid_connectedto)
+
+ self.textid_connectedto = sf.add_text_item("[connected to %s]" % self.host, (340, 80))
+ self.waittext_id = sf.add_text_item("Waiting for users to join...", (40, 570))
+
+ pygame.display.update()
+
+ # Draw Timer Option
+ img, rect = sf.image_load("images/xclock.png")
+ sf.display_surface(img, (40, 400))
+ self.draw_timer = DrawTimer()
+
+ def nonet_function(self):
+ print "client net aborted, quitting MultiPlayer-Client"
+
+ self.running = False
+
+ if self.waittext_id != None: sf.del_text_item(self.waittext_id)
+ if self.textid_connectedto != None: sf.del_text_item(self.textid_connectedto)
+
+ sf.add_text_item("Server quit the game", (340, 84))
+
+ # clear player
+ bg = pygame.Surface((560, 30))
+ bg.fill(hex2rgb(Layout().background))
+ sf.display_surface(bg, (48, 610))
+ pygame.display.update()
+
+ self.close()
+
+ global game_client
+ game_client = None
+
+
+
+
+def click_create():
+ global game_client
+ global game_host
+
+ if game_client == None and game_host == None:
+ game_host = MultiPlayerHost("", Layout().port)
+ sf.clear_text_items(False)
+ game_host.load()
+
+
+def click_join():
+ global game_client
+ global game_host
+
+ if game_client == None and game_host == None:
+ game_client = ''
+ game_client = MultiPlayerClient("localhost", Layout().port)
+
+ sf.clear_text_items()
+
+ res = game_client.load()
+ if res == False:
+ game_client = None
+
+def load():
+ global sf
+ sf = __SERVICES__.frontend;
+
+ sp = __import__("plugins/single_player")
+ sp.sf = sf
+
+ global Q
+ Q = sp.Questions()
+
+ global A
+ A = sp.Answer()
+
+ global game_host
+ game_host = None
+
+ global game_client
+ game_client = None
+
+ sf.add_menu_dir('/multi-player', 'Multiplayer', 1)
+ sf.add_menu_item('/multi-player', 'Create Game', click_create, 1)
+ sf.add_menu_item('/multi-player', 'Join Game', click_join, 2)
+
+ __SERVICES__.add_service("next_question", next_question)
+ __SERVICES__.add_service("spot_found", spot_found)
+ __SERVICES__.add_service("spot_not_found", spot_not_found)
+
+def close():
+ global game_host
+ global game_client
+
+ if game_host != None: game_host.close()
+ if game_client != None: game_client.close()
+
diff --git a/plugins/path.py b/plugins/path.py
new file mode 100755
index 0000000..01c2c04
--- /dev/null
+++ b/plugins/path.py
@@ -0,0 +1,971 @@
+""" path.py - An object representing a path to a file or directory.
+
+Example:
+
+from path import path
+d = path('/home/guido/bin')
+for f in d.files('*.py'):
+ f.chmod(0755)
+
+This module requires Python 2.2 or later.
+
+
+URL: http://www.jorendorff.com/articles/python/path
+Author: Jason Orendorff <jason.orendorff\x40gmail\x2ecom> (and others - see the url!)
+Date: 9 Mar 2007
+"""
+
+
+# TODO
+# - Tree-walking functions don't avoid symlink loops. Matt Harrison
+# sent me a patch for this.
+# - Bug in write_text(). It doesn't support Universal newline mode.
+# - Better error message in listdir() when self isn't a
+# directory. (On Windows, the error message really sucks.)
+# - Make sure everything has a good docstring.
+# - Add methods for regex find and replace.
+# - guess_content_type() method?
+# - Perhaps support arguments to touch().
+
+from __future__ import generators
+
+import sys, warnings, os, fnmatch, glob, shutil, codecs, md5
+
+__version__ = '2.2'
+__all__ = ['path']
+
+# Platform-specific support for path.owner
+if os.name == 'nt':
+ try:
+ import win32security
+ except ImportError:
+ win32security = None
+else:
+ try:
+ import pwd
+ except ImportError:
+ pwd = None
+
+# Pre-2.3 support. Are unicode filenames supported?
+_base = str
+_getcwd = os.getcwd
+try:
+ if os.path.supports_unicode_filenames:
+ _base = unicode
+ _getcwd = os.getcwdu
+except AttributeError:
+ pass
+
+# Pre-2.3 workaround for booleans
+try:
+ True, False
+except NameError:
+ True, False = 1, 0
+
+# Pre-2.3 workaround for basestring.
+try:
+ basestring
+except NameError:
+ basestring = (str, unicode)
+
+# Universal newline support
+_textmode = 'r'
+if hasattr(file, 'newlines'):
+ _textmode = 'U'
+
+
+class TreeWalkWarning(Warning):
+ pass
+
+class path(_base):
+ """ Represents a filesystem path.
+
+ For documentation on individual methods, consult their
+ counterparts in os.path.
+ """
+
+ # --- Special Python methods.
+
+ def __repr__(self):
+ return 'path(%s)' % _base.__repr__(self)
+
+ # Adding a path and a string yields a path.
+ def __add__(self, more):
+ try:
+ resultStr = _base.__add__(self, more)
+ except TypeError: #Python bug
+ resultStr = NotImplemented
+ if resultStr is NotImplemented:
+ return resultStr
+ return self.__class__(resultStr)
+
+ def __radd__(self, other):
+ if isinstance(other, basestring):
+ return self.__class__(other.__add__(self))
+ else:
+ return NotImplemented
+
+ # The / operator joins paths.
+ def __div__(self, rel):
+ """ fp.__div__(rel) == fp / rel == fp.joinpath(rel)
+
+ Join two path components, adding a separator character if
+ needed.
+ """
+ return self.__class__(os.path.join(self, rel))
+
+ # Make the / operator work even when true division is enabled.
+ __truediv__ = __div__
+
+ def getcwd(cls):
+ """ Return the current working directory as a path object. """
+ return cls(_getcwd())
+ getcwd = classmethod(getcwd)
+
+
+ # --- Operations on path strings.
+
+ isabs = os.path.isabs
+ def abspath(self): return self.__class__(os.path.abspath(self))
+ def normcase(self): return self.__class__(os.path.normcase(self))
+ def normpath(self): return self.__class__(os.path.normpath(self))
+ def realpath(self): return self.__class__(os.path.realpath(self))
+ def expanduser(self): return self.__class__(os.path.expanduser(self))
+ def expandvars(self): return self.__class__(os.path.expandvars(self))
+ def dirname(self): return self.__class__(os.path.dirname(self))
+ basename = os.path.basename
+
+ def expand(self):
+ """ Clean up a filename by calling expandvars(),
+ expanduser(), and normpath() on it.
+
+ This is commonly everything needed to clean up a filename
+ read from a configuration file, for example.
+ """
+ return self.expandvars().expanduser().normpath()
+
+ def _get_namebase(self):
+ base, ext = os.path.splitext(self.name)
+ return base
+
+ def _get_ext(self):
+ f, ext = os.path.splitext(_base(self))
+ return ext
+
+ def _get_drive(self):
+ drive, r = os.path.splitdrive(self)
+ return self.__class__(drive)
+
+ parent = property(
+ dirname, None, None,
+ """ This path's parent directory, as a new path object.
+
+ For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib')
+ """)
+
+ name = property(
+ basename, None, None,
+ """ The name of this file or directory without the full path.
+
+ For example, path('/usr/local/lib/libpython.so').name == 'libpython.so'
+ """)
+
+ namebase = property(
+ _get_namebase, None, None,
+ """ The same as path.name, but with one file extension stripped off.
+
+ For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz',
+ but path('/home/guido/python.tar.gz').namebase == 'python.tar'
+ """)
+
+ ext = property(
+ _get_ext, None, None,
+ """ The file extension, for example '.py'. """)
+
+ drive = property(
+ _get_drive, None, None,
+ """ The drive specifier, for example 'C:'.
+ This is always empty on systems that don't use drive specifiers.
+ """)
+
+ def splitpath(self):
+ """ p.splitpath() -> Return (p.parent, p.name). """
+ parent, child = os.path.split(self)
+ return self.__class__(parent), child
+
+ def splitdrive(self):
+ """ p.splitdrive() -> Return (p.drive, <the rest of p>).
+
+ Split the drive specifier from this path. If there is
+ no drive specifier, p.drive is empty, so the return value
+ is simply (path(''), p). This is always the case on Unix.
+ """
+ drive, rel = os.path.splitdrive(self)
+ return self.__class__(drive), rel
+
+ def splitext(self):
+ """ p.splitext() -> Return (p.stripext(), p.ext).
+
+ Split the filename extension from this path and return
+ the two parts. Either part may be empty.
+
+ The extension is everything from '.' to the end of the
+ last path segment. This has the property that if
+ (a, b) == p.splitext(), then a + b == p.
+ """
+ filename, ext = os.path.splitext(self)
+ return self.__class__(filename), ext
+
+ def stripext(self):
+ """ p.stripext() -> Remove one file extension from the path.
+
+ For example, path('/home/guido/python.tar.gz').stripext()
+ returns path('/home/guido/python.tar').
+ """
+ return self.splitext()[0]
+
+ if hasattr(os.path, 'splitunc'):
+ def splitunc(self):
+ unc, rest = os.path.splitunc(self)
+ return self.__class__(unc), rest
+
+ def _get_uncshare(self):
+ unc, r = os.path.splitunc(self)
+ return self.__class__(unc)
+
+ uncshare = property(
+ _get_uncshare, None, None,
+ """ The UNC mount point for this path.
+ This is empty for paths on local drives. """)
+
+ def joinpath(self, *args):
+ """ Join two or more path components, adding a separator
+ character (os.sep) if needed. Returns a new path
+ object.
+ """
+ return self.__class__(os.path.join(self, *args))
+
+ def splitall(self):
+ r""" Return a list of the path components in this path.
+
+ The first item in the list will be a path. Its value will be
+ either os.curdir, os.pardir, empty, or the root directory of
+ this path (for example, '/' or 'C:\\'). The other items in
+ the list will be strings.
+
+ path.path.joinpath(*result) will yield the original path.
+ """
+ parts = []
+ loc = self
+ while loc != os.curdir and loc != os.pardir:
+ prev = loc
+ loc, child = prev.splitpath()
+ if loc == prev:
+ break
+ parts.append(child)
+ parts.append(loc)
+ parts.reverse()
+ return parts
+
+ def relpath(self):
+ """ Return this path as a relative path,
+ based from the current working directory.
+ """
+ cwd = self.__class__(os.getcwd())
+ return cwd.relpathto(self)
+
+ def relpathto(self, dest):
+ """ Return a relative path from self to dest.
+
+ If there is no relative path from self to dest, for example if
+ they reside on different drives in Windows, then this returns
+ dest.abspath().
+ """
+ origin = self.abspath()
+ dest = self.__class__(dest).abspath()
+
+ orig_list = origin.normcase().splitall()
+ # Don't normcase dest! We want to preserve the case.
+ dest_list = dest.splitall()
+
+ if orig_list[0] != os.path.normcase(dest_list[0]):
+ # Can't get here from there.
+ return dest
+
+ # Find the location where the two paths start to differ.
+ i = 0
+ for start_seg, dest_seg in zip(orig_list, dest_list):
+ if start_seg != os.path.normcase(dest_seg):
+ break
+ i += 1
+
+ # Now i is the point where the two paths diverge.
+ # Need a certain number of "os.pardir"s to work up
+ # from the origin to the point of divergence.
+ segments = [os.pardir] * (len(orig_list) - i)
+ # Need to add the diverging part of dest_list.
+ segments += dest_list[i:]
+ if len(segments) == 0:
+ # If they happen to be identical, use os.curdir.
+ relpath = os.curdir
+ else:
+ relpath = os.path.join(*segments)
+ return self.__class__(relpath)
+
+ # --- Listing, searching, walking, and matching
+
+ def listdir(self, pattern=None):
+ """ D.listdir() -> List of items in this directory.
+
+ Use D.files() or D.dirs() instead if you want a listing
+ of just files or just subdirectories.
+
+ The elements of the list are path objects.
+
+ With the optional 'pattern' argument, this only lists
+ items whose names match the given pattern.
+ """
+ names = os.listdir(self)
+ if pattern is not None:
+ names = fnmatch.filter(names, pattern)
+ return [self / child for child in names]
+
+ def dirs(self, pattern=None):
+ """ D.dirs() -> List of this directory's subdirectories.
+
+ The elements of the list are path objects.
+ This does not walk recursively into subdirectories
+ (but see path.walkdirs).
+
+ With the optional 'pattern' argument, this only lists
+ directories whose names match the given pattern. For
+ example, d.dirs('build-*').
+ """
+ return [p for p in self.listdir(pattern) if p.isdir()]
+
+ def files(self, pattern=None):
+ """ D.files() -> List of the files in this directory.
+
+ The elements of the list are path objects.
+ This does not walk into subdirectories (see path.walkfiles).
+
+ With the optional 'pattern' argument, this only lists files
+ whose names match the given pattern. For example,
+ d.files('*.pyc').
+ """
+
+ return [p for p in self.listdir(pattern) if p.isfile()]
+
+ def walk(self, pattern=None, errors='strict'):
+ """ D.walk() -> iterator over files and subdirs, recursively.
+
+ The iterator yields path objects naming each child item of
+ this directory and its descendants. This requires that
+ D.isdir().
+
+ This performs a depth-first traversal of the directory tree.
+ Each directory is returned just before all its children.
+
+ The errors= keyword argument controls behavior when an
+ error occurs. The default is 'strict', which causes an
+ exception. The other allowed values are 'warn', which
+ reports the error via warnings.warn(), and 'ignore'.
+ """
+ if errors not in ('strict', 'warn', 'ignore'):
+ raise ValueError("invalid errors parameter")
+
+ try:
+ childList = self.listdir()
+ except Exception:
+ if errors == 'ignore':
+ return
+ elif errors == 'warn':
+ warnings.warn(
+ "Unable to list directory '%s': %s"
+ % (self, sys.exc_info()[1]),
+ TreeWalkWarning)
+ return
+ else:
+ raise
+
+ for child in childList:
+ if pattern is None or child.fnmatch(pattern):
+ yield child
+ try:
+ isdir = child.isdir()
+ except Exception:
+ if errors == 'ignore':
+ isdir = False
+ elif errors == 'warn':
+ warnings.warn(
+ "Unable to access '%s': %s"
+ % (child, sys.exc_info()[1]),
+ TreeWalkWarning)
+ isdir = False
+ else:
+ raise
+
+ if isdir:
+ for item in child.walk(pattern, errors):
+ yield item
+
+ def walkdirs(self, pattern=None, errors='strict'):
+ """ D.walkdirs() -> iterator over subdirs, recursively.
+
+ With the optional 'pattern' argument, this yields only
+ directories whose names match the given pattern. For
+ example, mydir.walkdirs('*test') yields only directories
+ with names ending in 'test'.
+
+ The errors= keyword argument controls behavior when an
+ error occurs. The default is 'strict', which causes an
+ exception. The other allowed values are 'warn', which
+ reports the error via warnings.warn(), and 'ignore'.
+ """
+ if errors not in ('strict', 'warn', 'ignore'):
+ raise ValueError("invalid errors parameter")
+
+ try:
+ dirs = self.dirs()
+ except Exception:
+ if errors == 'ignore':
+ return
+ elif errors == 'warn':
+ warnings.warn(
+ "Unable to list directory '%s': %s"
+ % (self, sys.exc_info()[1]),
+ TreeWalkWarning)
+ return
+ else:
+ raise
+
+ for child in dirs:
+ if pattern is None or child.fnmatch(pattern):
+ yield child
+ for subsubdir in child.walkdirs(pattern, errors):
+ yield subsubdir
+
+ def walkfiles(self, pattern=None, errors='strict'):
+ """ D.walkfiles() -> iterator over files in D, recursively.
+
+ The optional argument, pattern, limits the results to files
+ with names that match the pattern. For example,
+ mydir.walkfiles('*.tmp') yields only files with the .tmp
+ extension.
+ """
+ if errors not in ('strict', 'warn', 'ignore'):
+ raise ValueError("invalid errors parameter")
+
+ try:
+ childList = self.listdir()
+ except Exception:
+ if errors == 'ignore':
+ return
+ elif errors == 'warn':
+ warnings.warn(
+ "Unable to list directory '%s': %s"
+ % (self, sys.exc_info()[1]),
+ TreeWalkWarning)
+ return
+ else:
+ raise
+
+ for child in childList:
+ try:
+ isfile = child.isfile()
+ isdir = not isfile and child.isdir()
+ except:
+ if errors == 'ignore':
+ continue
+ elif errors == 'warn':
+ warnings.warn(
+ "Unable to access '%s': %s"
+ % (self, sys.exc_info()[1]),
+ TreeWalkWarning)
+ continue
+ else:
+ raise
+
+ if isfile:
+ if pattern is None or child.fnmatch(pattern):
+ yield child
+ elif isdir:
+ for f in child.walkfiles(pattern, errors):
+ yield f
+
+ def fnmatch(self, pattern):
+ """ Return True if self.name matches the given pattern.
+
+ pattern - A filename pattern with wildcards,
+ for example '*.py'.
+ """
+ return fnmatch.fnmatch(self.name, pattern)
+
+ def glob(self, pattern):
+ """ Return a list of path objects that match the pattern.
+
+ pattern - a path relative to this directory, with wildcards.
+
+ For example, path('/users').glob('*/bin/*') returns a list
+ of all the files users have in their bin directories.
+ """
+ cls = self.__class__
+ return [cls(s) for s in glob.glob(_base(self / pattern))]
+
+
+ # --- Reading or writing an entire file at once.
+
+ def open(self, mode='r'):
+ """ Open this file. Return a file object. """
+ return file(self, mode)
+
+ def bytes(self):
+ """ Open this file, read all bytes, return them as a string. """
+ f = self.open('rb')
+ try:
+ return f.read()
+ finally:
+ f.close()
+
+ def write_bytes(self, bytes, append=False):
+ """ Open this file and write the given bytes to it.
+
+ Default behavior is to overwrite any existing file.
+ Call p.write_bytes(bytes, append=True) to append instead.
+ """
+ if append:
+ mode = 'ab'
+ else:
+ mode = 'wb'
+ f = self.open(mode)
+ try:
+ f.write(bytes)
+ finally:
+ f.close()
+
+ def text(self, encoding=None, errors='strict'):
+ r""" Open this file, read it in, return the content as a string.
+
+ This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r'
+ are automatically translated to '\n'.
+
+ Optional arguments:
+
+ encoding - The Unicode encoding (or character set) of
+ the file. If present, the content of the file is
+ decoded and returned as a unicode object; otherwise
+ it is returned as an 8-bit str.
+ errors - How to handle Unicode errors; see help(str.decode)
+ for the options. Default is 'strict'.
+ """
+ if encoding is None:
+ # 8-bit
+ f = self.open(_textmode)
+ try:
+ return f.read()
+ finally:
+ f.close()
+ else:
+ # Unicode
+ f = codecs.open(self, 'r', encoding, errors)
+ # (Note - Can't use 'U' mode here, since codecs.open
+ # doesn't support 'U' mode, even in Python 2.3.)
+ try:
+ t = f.read()
+ finally:
+ f.close()
+ return (t.replace(u'\r\n', u'\n')
+ .replace(u'\r\x85', u'\n')
+ .replace(u'\r', u'\n')
+ .replace(u'\x85', u'\n')
+ .replace(u'\u2028', u'\n'))
+
+ def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
+ r""" Write the given text to this file.
+
+ The default behavior is to overwrite any existing file;
+ to append instead, use the 'append=True' keyword argument.
+
+ There are two differences between path.write_text() and
+ path.write_bytes(): newline handling and Unicode handling.
+ See below.
+
+ Parameters:
+
+ - text - str/unicode - The text to be written.
+
+ - encoding - str - The Unicode encoding that will be used.
+ This is ignored if 'text' isn't a Unicode string.
+
+ - errors - str - How to handle Unicode encoding errors.
+ Default is 'strict'. See help(unicode.encode) for the
+ options. This is ignored if 'text' isn't a Unicode
+ string.
+
+ - linesep - keyword argument - str/unicode - The sequence of
+ characters to be used to mark end-of-line. The default is
+ os.linesep. You can also specify None; this means to
+ leave all newlines as they are in 'text'.
+
+ - append - keyword argument - bool - Specifies what to do if
+ the file already exists (True: append to the end of it;
+ False: overwrite it.) The default is False.
+
+
+ --- Newline handling.
+
+ write_text() converts all standard end-of-line sequences
+ ('\n', '\r', and '\r\n') to your platform's default end-of-line
+ sequence (see os.linesep; on Windows, for example, the
+ end-of-line marker is '\r\n').
+
+ If you don't like your platform's default, you can override it
+ using the 'linesep=' keyword argument. If you specifically want
+ write_text() to preserve the newlines as-is, use 'linesep=None'.
+
+ This applies to Unicode text the same as to 8-bit text, except
+ there are three additional standard Unicode end-of-line sequences:
+ u'\x85', u'\r\x85', and u'\u2028'.
+
+ (This is slightly different from when you open a file for
+ writing with fopen(filename, "w") in C or file(filename, 'w')
+ in Python.)
+
+
+ --- Unicode
+
+ If 'text' isn't Unicode, then apart from newline handling, the
+ bytes are written verbatim to the file. The 'encoding' and
+ 'errors' arguments are not used and must be omitted.
+
+ If 'text' is Unicode, it is first converted to bytes using the
+ specified 'encoding' (or the default encoding if 'encoding'
+ isn't specified). The 'errors' argument applies only to this
+ conversion.
+
+ """
+ if isinstance(text, unicode):
+ if linesep is not None:
+ # Convert all standard end-of-line sequences to
+ # ordinary newline characters.
+ text = (text.replace(u'\r\n', u'\n')
+ .replace(u'\r\x85', u'\n')
+ .replace(u'\r', u'\n')
+ .replace(u'\x85', u'\n')
+ .replace(u'\u2028', u'\n'))
+ text = text.replace(u'\n', linesep)
+ if encoding is None:
+ encoding = sys.getdefaultencoding()
+ bytes = text.encode(encoding, errors)
+ else:
+ # It is an error to specify an encoding if 'text' is
+ # an 8-bit string.
+ assert encoding is None
+
+ if linesep is not None:
+ text = (text.replace('\r\n', '\n')
+ .replace('\r', '\n'))
+ bytes = text.replace('\n', linesep)
+
+ self.write_bytes(bytes, append)
+
+ def lines(self, encoding=None, errors='strict', retain=True):
+ r""" Open this file, read all lines, return them in a list.
+
+ Optional arguments:
+ encoding - The Unicode encoding (or character set) of
+ the file. The default is None, meaning the content
+ of the file is read as 8-bit characters and returned
+ as a list of (non-Unicode) str objects.
+ errors - How to handle Unicode errors; see help(str.decode)
+ for the options. Default is 'strict'
+ retain - If true, retain newline characters; but all newline
+ character combinations ('\r', '\n', '\r\n') are
+ translated to '\n'. If false, newline characters are
+ stripped off. Default is True.
+
+ This uses 'U' mode in Python 2.3 and later.
+ """
+ if encoding is None and retain:
+ f = self.open(_textmode)
+ try:
+ return f.readlines()
+ finally:
+ f.close()
+ else:
+ return self.text(encoding, errors).splitlines(retain)
+
+ def write_lines(self, lines, encoding=None, errors='strict',
+ linesep=os.linesep, append=False):
+ r""" Write the given lines of text to this file.
+
+ By default this overwrites any existing file at this path.
+
+ This puts a platform-specific newline sequence on every line.
+ See 'linesep' below.
+
+ lines - A list of strings.
+
+ encoding - A Unicode encoding to use. This applies only if
+ 'lines' contains any Unicode strings.
+
+ errors - How to handle errors in Unicode encoding. This
+ also applies only to Unicode strings.
+
+ linesep - The desired line-ending. This line-ending is
+ applied to every line. If a line already has any
+ standard line ending ('\r', '\n', '\r\n', u'\x85',
+ u'\r\x85', u'\u2028'), that will be stripped off and
+ this will be used instead. The default is os.linesep,
+ which is platform-dependent ('\r\n' on Windows, '\n' on
+ Unix, etc.) Specify None to write the lines as-is,
+ like file.writelines().
+
+ Use the keyword argument append=True to append lines to the
+ file. The default is to overwrite the file. Warning:
+ When you use this with Unicode data, if the encoding of the
+ existing data in the file is different from the encoding
+ you specify with the encoding= parameter, the result is
+ mixed-encoding data, which can really confuse someone trying
+ to read the file later.
+ """
+ if append:
+ mode = 'ab'
+ else:
+ mode = 'wb'
+ f = self.open(mode)
+ try:
+ for line in lines:
+ isUnicode = isinstance(line, unicode)
+ if linesep is not None:
+ # Strip off any existing line-end and add the
+ # specified linesep string.
+ if isUnicode:
+ if line[-2:] in (u'\r\n', u'\x0d\x85'):
+ line = line[:-2]
+ elif line[-1:] in (u'\r', u'\n',
+ u'\x85', u'\u2028'):
+ line = line[:-1]
+ else:
+ if line[-2:] == '\r\n':
+ line = line[:-2]
+ elif line[-1:] in ('\r', '\n'):
+ line = line[:-1]
+ line += linesep
+ if isUnicode:
+ if encoding is None:
+ encoding = sys.getdefaultencoding()
+ line = line.encode(encoding, errors)
+ f.write(line)
+ finally:
+ f.close()
+
+ def read_md5(self):
+ """ Calculate the md5 hash for this file.
+
+ This reads through the entire file.
+ """
+ f = self.open('rb')
+ try:
+ m = md5.new()
+ while True:
+ d = f.read(8192)
+ if not d:
+ break
+ m.update(d)
+ finally:
+ f.close()
+ return m.digest()
+
+ # --- Methods for querying the filesystem.
+
+ exists = os.path.exists
+ isdir = os.path.isdir
+ isfile = os.path.isfile
+ islink = os.path.islink
+ ismount = os.path.ismount
+
+ if hasattr(os.path, 'samefile'):
+ samefile = os.path.samefile
+
+ getatime = os.path.getatime
+ atime = property(
+ getatime, None, None,
+ """ Last access time of the file. """)
+
+ getmtime = os.path.getmtime
+ mtime = property(
+ getmtime, None, None,
+ """ Last-modified time of the file. """)
+
+ if hasattr(os.path, 'getctime'):
+ getctime = os.path.getctime
+ ctime = property(
+ getctime, None, None,
+ """ Creation time of the file. """)
+
+ getsize = os.path.getsize
+ size = property(
+ getsize, None, None,
+ """ Size of the file, in bytes. """)
+
+ if hasattr(os, 'access'):
+ def access(self, mode):
+ """ Return true if current user has access to this path.
+
+ mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK
+ """
+ return os.access(self, mode)
+
+ def stat(self):
+ """ Perform a stat() system call on this path. """
+ return os.stat(self)
+
+ def lstat(self):
+ """ Like path.stat(), but do not follow symbolic links. """
+ return os.lstat(self)
+
+ def get_owner(self):
+ r""" Return the name of the owner of this file or directory.
+
+ This follows symbolic links.
+
+ On Windows, this returns a name of the form ur'DOMAIN\User Name'.
+ On Windows, a group can own a file or directory.
+ """
+ if os.name == 'nt':
+ if win32security is None:
+ raise Exception("path.owner requires win32all to be installed")
+ desc = win32security.GetFileSecurity(
+ self, win32security.OWNER_SECURITY_INFORMATION)
+ sid = desc.GetSecurityDescriptorOwner()
+ account, domain, typecode = win32security.LookupAccountSid(None, sid)
+ return domain + u'\\' + account
+ else:
+ if pwd is None:
+ raise NotImplementedError("path.owner is not implemented on this platform.")
+ st = self.stat()
+ return pwd.getpwuid(st.st_uid).pw_name
+
+ owner = property(
+ get_owner, None, None,
+ """ Name of the owner of this file or directory. """)
+
+ if hasattr(os, 'statvfs'):
+ def statvfs(self):
+ """ Perform a statvfs() system call on this path. """
+ return os.statvfs(self)
+
+ if hasattr(os, 'pathconf'):
+ def pathconf(self, name):
+ return os.pathconf(self, name)
+
+
+ # --- Modifying operations on files and directories
+
+ def utime(self, times):
+ """ Set the access and modified times of this file. """
+ os.utime(self, times)
+
+ def chmod(self, mode):
+ os.chmod(self, mode)
+
+ if hasattr(os, 'chown'):
+ def chown(self, uid, gid):
+ os.chown(self, uid, gid)
+
+ def rename(self, new):
+ os.rename(self, new)
+
+ def renames(self, new):
+ os.renames(self, new)
+
+
+ # --- Create/delete operations on directories
+
+ def mkdir(self, mode=0777):
+ os.mkdir(self, mode)
+
+ def makedirs(self, mode=0777):
+ os.makedirs(self, mode)
+
+ def rmdir(self):
+ os.rmdir(self)
+
+ def removedirs(self):
+ os.removedirs(self)
+
+
+ # --- Modifying operations on files
+
+ def touch(self):
+ """ Set the access/modified times of this file to the current time.
+ Create the file if it does not exist.
+ """
+ fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666)
+ os.close(fd)
+ os.utime(self, None)
+
+ def remove(self):
+ os.remove(self)
+
+ def unlink(self):
+ os.unlink(self)
+
+
+ # --- Links
+
+ if hasattr(os, 'link'):
+ def link(self, newpath):
+ """ Create a hard link at 'newpath', pointing to this file. """
+ os.link(self, newpath)
+
+ if hasattr(os, 'symlink'):
+ def symlink(self, newlink):
+ """ Create a symbolic link at 'newlink', pointing here. """
+ os.symlink(self, newlink)
+
+ if hasattr(os, 'readlink'):
+ def readlink(self):
+ """ Return the path to which this symbolic link points.
+
+ The result may be an absolute or a relative path.
+ """
+ return self.__class__(os.readlink(self))
+
+ def readlinkabs(self):
+ """ Return the path to which this symbolic link points.
+
+ The result is always an absolute path.
+ """
+ p = self.readlink()
+ if p.isabs():
+ return p
+ else:
+ return (self.parent / p).abspath()
+
+
+ # --- High-level functions from shutil
+
+ copyfile = shutil.copyfile
+ copymode = shutil.copymode
+ copystat = shutil.copystat
+ copy = shutil.copy
+ copy2 = shutil.copy2
+ copytree = shutil.copytree
+ if hasattr(shutil, 'move'):
+ move = shutil.move
+ rmtree = shutil.rmtree
+
+
+ # --- Special stuff from os
+
+ if hasattr(os, 'chroot'):
+ def chroot(self):
+ os.chroot(self)
+
+ if hasattr(os, 'startfile'):
+ def startfile(self):
+ os.startfile(self)
+
+
diff --git a/plugins/readme b/plugins/readme
new file mode 100755
index 0000000..5e6f5fe
--- /dev/null
+++ b/plugins/readme
@@ -0,0 +1,11 @@
+ How to use / write Plugins:
+ - http://wiki.laptop.org/index.php?title=XO_ImageQuiz/Plugins
+
+ List of Services:
+ - ImageQuiz.py, Line 37: "Hook-In Services"
+ - http://wiki.laptop.org/index.php?title=XO_ImageQuiz/Plugins#Overview
+
+ Services can accessed via the Class __SERVICES__
+ __SERVICES__.db.query(q)
+ __SERVICES__.db.commit(q)
+ __SERVICES__.frontend.add_menu_item(path, caption, onclick_function)
diff --git a/plugins/single_player.py b/plugins/single_player.py
new file mode 100755
index 0000000..ef0936e
--- /dev/null
+++ b/plugins/single_player.py
@@ -0,0 +1,744 @@
+# Currently
+# * Displaying Categories in set language,
+# * Pick English Questions
+
+# To Do
+# change to update Leitner info
+# display questions based on box
+# fix accessed wav
+
+# structure
+# class CurrentQuestion
+# class Answer
+# class Questions
+
+# functions
+
+# show_answer
+# finished
+# startgame
+# ask_subcat
+# ask_category
+# display_points
+# click_on_
+# next_question
+# next_question
+# spot_found
+# spot_not_found
+# load
+
+import pygame
+from pygame import *
+from sugar.activity import activity
+import os
+
+import random
+from layout import *
+from frontend import hex2rgb
+import time
+import threading
+
+__PLUGIN_NAME__ = 'single player'
+
+#set up paths to for adding images and sounds
+DATAPATH = os.path.join(activity.get_activity_root(), "data")
+ACTIVITYPATH = activity.get_bundle_path()
+IMAGEPATH = os.path.join(DATAPATH, 'image')
+SOUNDPATH = os.path.join(DATAPATH, 'sound')
+ICONPATH = os.path.join(ACTIVITYPATH, 'images')
+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']
+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']
+
+clock = pygame.time.Clock()
+
+class CurrentQuestion:
+ id = 0
+ prompt = u''
+ response = u''
+ imgfn = u''
+ sndfn = u''
+ map = u''
+ answer_link = u''
+
+class Imagequiz_question:
+ id = 0
+ map = u''
+ cat = 0
+ subcat = 0
+ text = u''
+ answer = u''
+ answer_link = u''
+
+class Answer:
+ display = True
+ display_line = True
+ sound_enabled = True
+
+ link = u''
+ text = u''
+
+ img_found_count = 0
+ img_notfound_count = 0
+ imgfn_found = ["Emotes/face-grin.png"]
+ imgfn_notfound = ["Emotes/face-devil-grin.png"]
+
+ 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
+ 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
+
+
+ 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:
+
+ def __init__(self):
+ in_question = False
+
+ played = 0
+ count_won = 0
+ count_lost = 0
+ id = u''
+ questions = []
+ question = CurrentQuestion()
+
+ 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!
+ self.question = self.questions[self.played]
+ self.id = self.question.id
+ A.text = self.question.response
+ A.link = self.question.answer_link
+
+ self.played += 1
+ return True
+
+ def load_questions(self, cat_id):
+ self._cat_id = cat_id
+
+ self.played = 0
+ self.count_won = 0
+ self.count_lost = 0
+
+ q = 'SELECT text FROM categories WHERE id = %i' % cat_id
+ res = __SERVICES__.db.query(q)
+ Q.id = res[0][0]
+
+ #get list of questions (by question_id)
+ try:
+ q = "SELECT question_id FROM quizlink WHERE quiz_id = %i" % cat_id
+ questionlist = __SERVICES__.db.query(q)
+ except:
+ questionlist = []
+
+ #get actual questions
+ self.questions = []
+ for questionid in questionlist:
+ q = 'SELECT * from questions WHERE id = %i' % questionid[0]
+ res = __SERVICES__.db.query(q)
+ question = CurrentQuestion()
+ question.id = res[0][0]
+ question.prompt = res[0][1]
+ question.response = res[0][2]
+ question.imgfn = res[0][3]
+ question.sndfn = res[0][4]
+ question.map = res[0][5]
+ question.answer_link=res[0][6]
+ self.questions.append(question)
+
+ #present questions randomly
+ random.shuffle(self.questions)
+
+def show_answer():
+ A.display_answer()
+ Q.in_question = True
+ next_question()
+
+def finished():
+ sf.clear_text_items()
+ sf.clear_question_frame(True)
+ 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 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)
+ sf.add_text_item("this is the score " + str(Q.count_won * 10), (600,0))
+ pygame.display.update()
+
+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,100))
+ pygame.display.update()
+ #this should provide for click on image_right or image_wrong
+ response = True
+ return response
+
+def display_images():
+ global images
+ candidate_images = []
+ for question in Q.questions:
+ candidate_images.append(question.imgfn)
+ random.shuffle(candidate_images)
+ if Q.question.imgfn in candidate_images[:4]:
+ images = candidate_images[:4]
+ else:
+ images = candidate_images[:3]
+ images.append(Q.question.imgfn)
+ random.shuffle(images)
+ image_tl, xy = sf.image_load(images[0], path = IMAGEPATH)
+ image_tr, xy = sf.image_load(images[1], path = IMAGEPATH)
+ image_ll, xy = sf.image_load(images[2], path = IMAGEPATH)
+ image_lr, xy = sf.image_load(images[3], path = IMAGEPATH)
+ 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
+
+def play_query_again():
+ global query
+ if Q.in_question:
+ #print 'play_query'
+ query.play()
+ while pygame.mixer.get_busy():
+ clock.tick(30)
+
+def play_sound(fn):
+ global query
+ query = sf.sound_load(Q.question.sndfn, path=SOUNDPATH)
+ image_play, xy = sf.image_load("play_button.png", path = ICONPATH)
+ sf.display_surface(image_play, (300,30))
+ pygame.display.update()
+ query.play()
+ while pygame.mixer.get_busy():
+ clock.tick(30)
+
+def picture_tl():
+ global images
+ if Q.in_question:
+ if images[0] == Q.question.imgfn:
+ response = True
+ else:
+ response = False
+ delete_reacts(response)
+
+def picture_tr():
+ global images
+ if Q.in_question:
+ if images[1] == Q.question.imgfn:
+ response = True
+ else:
+ response = False
+ delete_reacts(response)
+
+def picture_ll():
+ global images
+ if Q.in_question:
+ if images[2] == Q.question.imgfn:
+ response = True
+ else:
+ response = False
+ delete_reacts(response)
+
+def picture_lr():
+ global images
+ if Q.in_question:
+ if images[3] == Q.question.imgfn:
+ response = True
+ else:
+ response = False
+ delete_reacts(response)
+
+def delete_reacts(response):
+ if response:
+ correct_answer()
+ else:
+ wrong_answer()
+ return True
+
+def play_correct_response_sound():
+ good = sf.sound_load(random.choice(CORRECT_SOUNDS))
+ good.play()
+ while pygame.mixer.get_busy():
+ pygame.time.wait(30)
+
+def play_wrong_response_sound():
+ good = sf.sound_load(random.choice(WRONG_SOUNDS))
+ good.play()
+ while pygame.mixer.get_busy():
+ pygame.time.wait(30)
+
+
+def update_leitner_attributes():
+ #update Leitner attributes
+ #but need to get from db when loading questions
+ #need to save in db before next_question
+ #and update attributes actually in db
+ #finally display 'boxes' and 'points'
+ try:
+ print 'update Leitner attributes'
+ think = '%0.2f' % float( time.time() - Q.starttime)
+ Q.time = think
+ Q.count_found += 1
+ if Q.box < 5:
+ Q.box += 1
+ Q.date = sys.date()
+ except:
+ print 'Leitner update failed'
+ #now update database
+ try:
+ q = "UPDATE questions SET time = Q.time, count_found = Q.count_found, count_unfound = Q.count_unfound, box = Q.box, time = Q.time, date = Q.date WHERE id = Q.id"
+ res = __SERVICES__.db.query(q)
+ except:
+ print 'Leitner db update failed'
+
+def correct_answer(next=True):
+ if Q.in_question:
+ A.display_icon("found")
+ #A.play_sound(1)
+ Q.in_question = False
+ Q.count_won += 1
+ display_points()
+ try:
+ play_correct_response_sound()
+ except:
+ print 'play_correct_response_sound failed'
+ update_leitner_attributes()
+
+ if next:
+ next_question()
+ else:
+ show_answer()
+
+def wrong_answer(next=True):
+ if Q.in_question:
+ A.display_icon("not-found")
+ Q.in_question = False
+ Q.count_lost += 1
+ display_points()
+ play_wrong_response_sound()
+ update_leitner_attributes()
+ if next:
+ next_question()
+ else:
+ show_answer()
+
+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.response)
+ return response.strip() in possibles
+
+
+def startgame(db_cat_id):
+
+ Q.count_won = 0
+ A.reset_points()
+
+ Q.load_questions(db_cat_id)
+ display_points()
+
+ sf.clear_text_items()
+ next_question()
+
+def next_question():
+ #Q._cat_id
+ #Q.questions
+ global reacts
+
+ if Q.question_pick():
+ clear_reacts()
+ Q.in_question = True
+ Q.starttime = time.time()
+ if len(Q.question.map) > 0: #this is an imagequiz question
+ iq = Imagequiz_question()
+ iq.id = Q.question.id
+ iq.imgfn = Q.question.imgfn
+ iq.map = Q.question.map
+ iq.cat = 0
+ iq.subcat = 0
+ iq.text = Q.question.prompt
+ iq.answer = Q.question.response
+ iq.answer_link = Q.question.answer_link
+ __SERVICES__.frontend.question_display(iq)
+ elif len(Q.question.imgfn) > 0 and len(Q.question.sndfn) > 0: #this is Renyi
+ print 'Renyi', Q.question.imgfn, Q.question.sndfn
+ #get four images (one is correct), shuffle them, put them in 'react' squares
+ set_reacts()
+ print 'display_images'
+ display_images()
+ print 'update pygame display'
+ pygame.display.update()
+ #load and play sound
+ play_sound(Q.question.sndfn)
+ elif len(Q.question.sndfn) > 0: #this in 'phrase' question
+ #load and play sound
+ print 'phrase question'
+ play_sound(Q.question.sndfn)
+ response = __SERVICES__.frontend.ask(Q.question.text)
+ 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)
+ else: # this is flashcard
+ print 'flashcard'
+ print 'prompt', len(Q.question.prompt), Q.question.prompt
+ print 'answer', len(Q.question.response), Q.question.response
+ response = __SERVICES__.frontend.ask(Q.question.prompt)
+ print 'response', len(response), response
+ if len(response) == 0 or len(Q.question.response) == 0:
+ correct = self_test()
+ else:
+ correct = checkanswer(response)
+ delete_reacts(correct)
+ next_question()
+ Q.in_question = True
+ else:
+ # game / cat finished
+ Q.in_question = False
+ print "finished"
+ Q._cat_id = -1
+ finished()
+
+def spot_found():
+ if Q.in_question:
+ A.display_icon("found")
+ play_correct_response_sound()
+ #A.play_sound(1)
+ Q.in_question = False
+ Q.count_won += 1
+ display_points()
+ show_answer()
+
+def spot_not_found():
+ if Q.in_question:
+ A.display_icon("not-found")
+ play_wrong_response_sound()
+ #A.play_sound(0)
+ Q.in_question = False
+ Q.count_lost += 1
+ display_points()
+ show_answer()
+
+def set_reacts():
+ global reacts
+ reacts = []
+ reacts.append(sf.add_react(250, 150, 320, 240, picture_tl, stopsearch = True))
+ reacts.append(sf.add_react(520, 150, 320, 240, picture_tr, stopsearch = True))
+ reacts.append(sf.add_react(250, 420, 320, 240, picture_ll, stopsearch = True))
+ reacts.append(sf.add_react(520, 420, 320, 240, picture_lr, stopsearch = True))
+ reacts.append(sf.add_react(750,100,50,50, correct_answer, stopsearch = True))
+ reacts.append(sf.add_react(750,150,50,50, wrong_answer, stopsearch = True))
+ reacts.append(sf.add_react(300,30,100,100, play_query_again, stopsearch = True))
+
+def clear_reacts():
+ global reacts
+ for react in reacts:
+ sf.del_react(react)
+
+def click_on_pick_category():
+ # Select Category
+ cat_id = -1
+ sf.clear_text_items()
+ sf.clear_question_frame(True)
+ ask_category(cat_id)
+
+def ask_category(cat_id, offset_y = 0):
+
+ i = 1
+ y = 110 + offset_y
+
+ sf.add_text_item("Next Category:", (280,y))
+
+ #first we need to query for categories
+ #might be nice to show them in alphabetical order
+ #when y gets too big, we should change x
+ if cat_id == -1:
+ q = 'SELECT id, text FROM categories'
+ res = __SERVICES__.db.query(q)
+ for cat in res:
+ cat_id = cat[0]
+ category = cat[1]
+ q = 'SELECT count(*) FROM quizlink WHERE quiz_id=%i' % cat_id
+ res1 = __SERVICES__.db.query(q)
+ count = res1[0][0]
+ if count > 0:
+ # this is a quiz, display in green
+ y += 50
+ sf.add_text_item("%s (%s)" % (category, count), (300,y), startgame, cat_id, True)
+ else:
+ # this is a category - get number of children
+ q = 'SELECT count(*) FROM catlink WHERE parent_id=%i' % cat_id
+ res2 = __SERVICES__.db.query(q)
+ count = res2[0][0]
+ y += 50
+ sf.add_text_item("%s (%s)" % (category, count), (300,y), ask_category, cat_id, True)
+ i += 1
+ else:
+ #we need to pass a cat_id from the user's selection
+ print 'second level selection not implemented - must select quiz'
+
+
+#initialization called by quiz.py
+def load():
+ global sf
+ sf = __SERVICES__.frontend;
+
+ global Q
+ Q = Questions()
+
+ global A
+ A = Answer()
+
+ global button1
+ global button2
+ global button3
+
+ global reacts
+ reacts = []
+
+ sf.add_menu_item('/', 'Pick Category', click_on_pick_category, 0)
+
+ sf.add_menu_dir('/options', "Options")
+ button1 = sf.add_menu_item('/options', 'Display Answer [ On ]', click_options_answer)
+ button2 = sf.add_menu_item('/options', 'Play Sound [ On ]', click_options_sound)
+ button3 = sf.add_menu_item('/options', 'Draw Line [ On ]', click_options_line)
+
+ __SERVICES__.add_service("next_question", next_question)
+ __SERVICES__.add_service("spot_found", spot_found)
+ __SERVICES__.add_service("spot_not_found", spot_not_found)
+
+#clean close
+def close():
+ pass
+
+# handle options tab on main menu
+def click_options_answer():
+ if A.display:
+ A.display = False
+ sf.change_menu_item("change_caption", button1, "Display Answer [ Off ]")
+ else:
+ A.display = True
+ sf.change_menu_item("change_caption", button1, "Display Answer [ On ]")
+
+def click_options_line():
+ if A.display_line:
+ A.display_line = False
+ sf.change_menu_item("change_caption", button3, "Draw Line [ Off ]")
+ else:
+ A.display_line = True
+ sf.change_menu_item("change_caption", button3, "Draw Line [ On ]")
+
+def click_options_sound():
+ if A.sound_enabled:
+ A.sound_enabled = False
+ sf.change_menu_item("change_caption", button2, "Play Sound [ Off ]")
+ else:
+ A.sound_enabled = True
+ sf.change_menu_item("change_caption", button2, "Play Sound [ On ]")
diff --git a/plugins/tools.py b/plugins/tools.py
new file mode 100755
index 0000000..d453c2c
--- /dev/null
+++ b/plugins/tools.py
@@ -0,0 +1,100 @@
+import sys
+import pygame
+
+__PLUGIN_NAME__ = 'db tools'
+
+''' Mixed Functions '''
+def clickOnDBSave():
+ pass
+
+def clickOnDBRevert():
+ pass
+
+def clickOnDBWipe():
+ pass
+
+def clickOnQuitYes():
+ sys.exit(0)
+
+def clickOnToggleFS():
+ pygame.display.toggle_fullscreen()
+
+def clickOnOptions_Exchange():
+ print "Click on Options - Exchange"
+
+def event1(params):
+ print "Event 1 Started :-) params:", params
+
+def React1():
+ print "React 1"
+
+def clickOnQuestionItem(params):
+ print params
+
+
+# Click on Test()
+def clickOnTest():
+ print "Start Test"
+ sf.clear_question_items()
+
+ # Clear the Frame and display Stuff
+ sf.question_frame_clear()
+ sf.question_frame_show_text('images/europe.gif',0,0)
+ sf.question_frame_show_image('images/europe.gif',0,50)
+ pygame.display.update()
+
+ # Add Hook to onclick
+ # hook_id = sf.add_event_hook('onclick', event1)
+ # print "- added hook", hook_id
+
+
+''' Click on Test2() '''
+def clickOnTest2():
+ # Show last Question
+ # sf.question_display()
+
+ # Add Reacts
+ # a = sf.add_react(0, 0, 100, 100, React1, False, True)
+ # b = sf.add_react(50, 50, 100, 100, React1, False, True)
+
+ # Remove React a
+ # sf.del_react(a)
+
+ x = sf.ask("Your name:")
+ print ">", x
+
+ sf.add_question_item(x, (100, 100), clickOnQuestionItem, 0)
+ sf.add_question_item(x*2, (100, 150), clickOnQuestionItem, 1)
+
+ sf.draw_question_items()
+ pygame.display.update()
+
+''' Init Part '''
+def load():
+ global sf
+ sf = __SERVICES__.frontend;
+
+ # Use DB like that:
+ # print __SERVICES__.db.query("SELECT * FROM xoquiz WHERE 1")
+
+ # Menu /test
+# sf.add_menu_dir('/test', 'Test')
+# sf.add_menu_item('/test', 'Test()', clickOnTest,3)
+# sf.add_menu_item('/test', 'Test2()', clickOnTest2,3)
+
+ # Menu /options
+ sf.add_menu_dir('/options', 'Options')
+ sf.add_menu_item('/options', 'Toggle Fullscreen', clickOnToggleFS, 0)
+
+ # Menu /options/database
+# sf.add_menu_dir('/options/database', 'Database')
+# sf.add_menu_item('/options/database', 'Save', clickOnDBSave, 0)
+# sf.add_menu_item('/options/database', 'Revert', clickOnDBRevert, 1)
+# sf.add_menu_item('/options/database', 'Wipe All', clickOnDBWipe, 2)
+
+ # Menu /quit
+ sf.add_menu_dir('/quit', 'Quit', 5)
+ sf.add_menu_item('/quit', 'Yes, really Quit', clickOnQuitYes, 3)
+
+def close():
+ pass \ No newline at end of file