#!/usr/bin/env python # vim: set ts=8 sw=4 sts=4 et: # # ANWB_Rijopleiding_Linux.py: een pygtk wrapper om de bestanden van de # ANWB Examentraining Rijbewijs B. # # Gemaakt op 6 maart, 2010 door Walter Doekes. Licentie: public domain. # Ja, de code is niet de mooiste die je gezien hebt. Maar over een paar # dagen plan ik de CD toch nooit meer nodig te hebben ;-) # # N.B.: om dit script te gebruiken heb je wel de data van de CD-ROM # nodig. De vragen/plaatjes zijn tenslotte auteursrechtelijk beschermd # en mag ik er niet bij leveren. # # Veel succes met leren! # # (versie: 0.1, laatste edit: 2010-03-06 23:13, initial release) # (versie: 0.2, laatste edit: 2010-03-10 20:37, decoden van cp1252) # import codecs, gobject, gtk, os, random, sys, traceback class AnwbWindow(object): IMAGE_SIZE = (495, 374) def __init__(self, data, questions): self._data = data self._questions = list(reversed(questions)) # so we can pop self._right, self._wrong = 0, 0 self._showing_dialog = False self._create_window() #self.set_head('Vraag #NNN') #self.set_image('/home/walter/Desktop/ANWB2008/Fdata/Gdata/100.jpg') #self.set_question('_\n_\n_\n_\n_') self._window.connect('key-press-event', self.update_answer_keypress) self.show() def last_question(self): return not bool(self._questions) def next_question(self): question_num = self._questions.pop() question = self._data.get_question(question_num) self.set_head('Vraag #%03d' % question_num) self.set_image(question.get('image')) self.set_question(question.get('question')) self.set_answer(question.get('answer')) self.set_explanation(question.get('explanation')) self.reset_pulse() def pulse(self): if self._showing_dialog: return self._progress_value += 0.03 if self._progress_value >= 1.0: self._progress.set_fraction(1.0) self.check_answer('') else: self._progress.set_fraction(self._progress_value) gobject.timeout_add(500, self.pulse) def reset_pulse(self): self._progress_value = 0 self._progress.set_fraction(0) gobject.timeout_add(500, self.pulse) def show(self): self._window.show_all() def show_dialog(self, title, body): self._showing_dialog = True dialog = gtk.Dialog( title=title, parent=self._window, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT) ) label = gtk.Label() label.set_justify(gtk.JUSTIFY_CENTER) label.set_line_wrap(True) label.set_text(body) dialog.vbox.add(label) dialog.show_all() dialog.run() dialog.destroy() self._showing_dialog = False def set_head(self, head): self._head.set_text(head) # Vraag #NNN def set_image(self, filename): self._image.set_from_file(filename) # Fdata/Gdata/123.jpg def set_question(self, question): self._question.set_text(question) def set_answer(self, answer): self._answer.set_text('') self._answer_value = answer def set_explanation(self, explanation): self._explanation_value = explanation def check_answer(self, answer): try: if isinstance(self._answer_value, float): answer = float(answer) except ValueError: answer = 'False' if answer == self._answer_value: self._right += 1 text = 'Antwoord %s is JUIST!\n\n%s' % (self._answer_value, self._explanation_value) else: self._wrong += 1 text = 'FOUT! Het goede antwoord had moeten zijn %s.\n\n%s' % (self._answer_value, self._explanation_value) self.show_dialog('Antwoord', text) if self.last_question(): self._quit(self) else: self.next_question() def update_answer_keypress(self, window, event): assert event.type == gtk.gdk.KEY_PRESS text = self._answer.get_text() if event.keyval in (gtk.keysyms.Return, gtk.keysyms.KP_Enter): if text != '': self.check_answer(text) elif event.keyval == gtk.keysyms.Escape: self._answer.set_text('') elif event.keyval == gtk.keysyms.BackSpace: if text != '': text = text[0:-1] self._answer.set_text(text) elif 0 <= event.keyval < 128 or 65456 <= event.keyval <= 65465: if event.keyval < 128: char = chr(event.keyval).upper() else: char = chr(event.keyval - 65456 + 48) if char in 'ABCJN0123456789,.': if char == ',': char = '.' if char == '.' and (text == '' or '.' in text): return # break if char in 'ABCJN': text = '' self._answer.set_text(text + char) def _create_window(self): self._window = gtk.Window(gtk.WINDOW_TOPLEVEL) self._window.connect('destroy', self._quit) self._window.set_border_width(10) box1v = gtk.VBox(homogeneous=False, spacing=10) self._window.add(box1v) self._head = gtk.Label() box1v.add(self._head) self._image = gtk.Image() self._image.set_size_request(*self.IMAGE_SIZE) box1v.add(self._image) self._progress = gtk.ProgressBar() box1v.add(self._progress) box11h = gtk.HBox(homogeneous=False, spacing=10) box1v.add(box11h) self._question = gtk.TextBuffer() question_view = gtk.TextView(buffer=self._question) # request a large size to take the most room in the hbox question_view.set_size_request(self.IMAGE_SIZE[0] - 120, -1) question_view.set_cursor_visible(False) question_view.set_editable(False) question_view.set_wrap_mode(gtk.WRAP_WORD) box11h.add(question_view) box111v = gtk.VBox(homogeneous=False, spacing=10) box11h.add(box111v) answer_label = gtk.Label() answer_label.set_text('Vul antwoord in\nen druk op enter.') box111v.add(answer_label) self._answer = gtk.Label() box111v.add(self._answer) def _quit(self, window): print 'Right answers: %d' % self._right print 'Wrong answers: %d' % self._wrong gtk.main_quit() class AnwbData(object): QUESTIONS_PER_FILE = 50 QUESTION_TYPES = ('', 'JN', 'ABC', '#', 'AB') QUESTION_PATH = os.path.join('Fdata', 'Data') IMAGE_PATH = os.path.join('Fdata', 'Gdata') def __init__(self, path): print 'Attempting to parse ANWB files in %s...' % path self._path = path self._questions = {} translator = codecs.getreader('cp1252') # or latin1 perhaps path = os.path.join(self._path, self.QUESTION_PATH) files = [i for i in os.listdir(path) if i.startswith('DATA') and i.endswith('.CFG')] for file in files: offset = (int(''.join([i for i in file if i in '0123456789'])) - 1) * self.QUESTIONS_PER_FILE print 'Parsing %s (offset %d)...' % (file, offset) try: self.parse_questions(translator(open(os.path.join(path, file), 'r')), offset) except: print traceback.format_exc() print 'Found and parsed %d questions...' % len(self._questions) path = os.path.join(self._path, self.IMAGE_PATH) files = [i for i in os.listdir(path) if i.endswith('.jpg')] image_count = 0 for num in self._questions: image_file = '%03d.jpg' % num if image_file in files: self._questions[num]['image'] = os.path.join(path, image_file) image_count += 1 print 'Found %d corresponding images...' % image_count def parse_questions(self, file, offset): while True: line = file.next().strip() if line.endswith('---'): num, unknown, qtype, rest = line.split(' ', 3) num, unknown, qtype = int(num), int(unknown), self.QUESTION_TYPES[int(qtype)] question = file.next().strip() if qtype in ('AB', 'ABC'): question += '\nA: ' + file.next().strip() question += '\nB: ' + file.next().strip() if qtype == 'ABC': question += '\nC: ' + file.next().strip() elif qtype == '#': question += '\n(Antwoord in %s)' % file.next().strip() answer = file.next().strip().split()[0] explanation = file.next().strip() while True: more = file.next().strip() if more == '': break explanation += more if qtype == 'AB': assert answer in ('A', 'B') elif qtype == 'ABC': assert answer in ('A', 'B', 'C') elif qtype == 'JN': answer = answer[0:1] assert answer in ('J', 'N') elif qtype == '#': answer = float(answer.replace(',', '.')) self._questions[offset + num] = { 'question': question, 'answer': answer, 'explanation': explanation, } else: # line is probably "*EOF" return def get_question(self, number): return self._questions[number] def get_numbers(self): return self._questions.keys() def main(path=None, first_question=None, last_question=None, randomize=False): try: assert os.listdir(path) first_question = int(first_question) last_question = int(last_question) assert first_question >= 0 and first_question <= last_question randomize = bool(randomize) except (AssertionError, OSError, TypeError, ValueError): print >>sys.stderr, 'Usage: %s [path] [firstnum] [lastnum] [randomize]' % sys.argv[0] print >>sys.stderr, 'E.g.: %s /media/cdrom 1 50' % sys.argv[0] print >>sys.stderr, 'or: %s /media/cdrom 1 500 true' % sys.argv[0] sys.exit(1) try: data = AnwbData(path) questions = range(first_question, last_question + 1) question_nums = data.get_numbers() for question_num in questions: if question_num not in question_nums: raise RuntimeError('Question number %d not found!' % question_num) if randomize: random.shuffle(questions) except Exception, e: print >>sys.stderr, 'Error: %s' % e sys.exit(1) # Create the UI window = AnwbWindow(data, questions) window.next_question() # Enter the main event loop gtk.main() if __name__ == '__main__': main(*sys.argv[1:])