diff options
Diffstat (limited to 'plugins/_extract.py')
-rwxr-xr-x | plugins/_extract.py | 896 |
1 files changed, 896 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 |