diff options
author | Wade Brainerd <wadetb@gmail.com> | 2008-11-10 18:12:58 (GMT) |
---|---|---|
committer | Wade Brainerd <wadetb@gmail.com> | 2008-11-10 18:12:58 (GMT) |
commit | 65b790fd4984efc6ee2f98d95b1082639b8eda3f (patch) | |
tree | 212f5f0091e57a62301bb679591a46418a1a1cd1 /typingturtle.py | |
parent | 11526621d91be9b660c08e8dbc31ed2e8e7eb82f (diff) |
Lessons can now be completed, track WPM and accuracy.
Diffstat (limited to 'typingturtle.py')
-rwxr-xr-x | typingturtle.py | 182 |
1 files changed, 99 insertions, 83 deletions
diff --git a/typingturtle.py b/typingturtle.py index 1b6087f..0530b9d 100755 --- a/typingturtle.py +++ b/typingturtle.py @@ -51,15 +51,21 @@ class LessonScreen(gtk.VBox): stopbtn.add(stoplabel) stopbtn.connect('clicked', self.stop_cb) + self.wpmlabel = gtk.Label() + self.accuracylabel = gtk.Label() + hbox = gtk.HBox() hbox.pack_start(stopbtn, False, False, 10) - hbox.pack_end(title, True, True, 10) + hbox.pack_start(self.wpmlabel, True, False, 10) + hbox.pack_start(self.accuracylabel, True, False, 10) + hbox.pack_end(title, False, False, 10) self.lessontext = gtk.Label() self.lessontext.set_alignment(0, 0) + self.lessontext.set_line_wrap(True) self.lessonscroll = gtk.ScrolledWindow() - self.lessonscroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) + self.lessonscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) self.lessonscroll.add_with_viewport(self.lessontext) frame = gtk.Frame() @@ -68,94 +74,104 @@ class LessonScreen(gtk.VBox): self.keyboard = keyboard.Keyboard() self.keyboard.set_layout(keyboard.default_layout) + activity.add_events(gtk.gdk.KEY_PRESS_MASK) + activity.connect('key-press-event', self.key_press_cb) + self.pack_start(hbox, False, False, 10) self.pack_start(frame, True) self.pack_start(self.keyboard, True) self.show_all() - # Initialize the lesson playback. - self.content = lesson['content'] - self.content_pos = 0 + # Initialize the lesson. + self.lesson = lesson + self.step = None self.markup = '' - self.delay = 0 - self.span_count = 0 - - gobject.timeout_add(50, self.timer_cb) - - def timer_cb(self): - if self.delay > 0: - self.delay -= 1 - return True - - # This loop keeps processing until at least one character has been emitted. - # We don't want to slow down the typing when there are control codes. - while True: - # Read the next piece of content. - if self.content_pos >= len(self.content): - return False - - c = self.content[self.content_pos] - self.content_pos += 1 - - # Handle any control codes. - if c == '<': - # Extract and skip the contents of the < > brackets. - code_begin = self.content_pos - while self.content_pos < len(self.content) and \ - self.content[self.content_pos] != '>': - self.content_pos += 1 - - code = self.content[code_begin:self.content_pos] - self.content_pos += 1 - - # Process the control code. - args = code.split(' ') - if args[0] == 'delay': - self.delay = int(args[1]) - return True - - elif args[0] == 'br': - # Line break. - self.markup += '\n' - - elif args[0] == 'p': - # Start a new paragraph. End the current line if needed. - if len(self.markup) and self.markup[-1] != '\n': - self.markup += '\n\n' - else: - self.markup += '\n' - - elif args[0] == 'span': - # Pango span. - self.markup += '<' + code + '>' - self.span_count += 1 - - elif args[0] == '/span': - # End of pango span. - if self.span_count > 0: - self.markup += '<' + code + '>' - self.span_count -= 1 - - elif args[0] == 'type': - # Typing section. - self.markup += '<' + code + '>' - self.span_count += 1 - - elif args[0] == '/type': - # End of typing section. - if self.span_count > 0: - self.markup += '<' + code + '>' - self.span_count -= 1 - - - else: - # A plain character, insert it and return. - # Extra </span>'s are added for Pango spans we are still in the middle of. - self.markup += c - self.lessontext.set_markup(self.markup + ('</span>' * self.span_count)) - - return True + + self.total_keys = 0 + self.correct_keys = 0 + self.incorrect_keys = 0 + + self.count_words() + + self.next_step_idx = 0 + self.advance_step() + + self.start_time = time.time() + + def count_words(self): + self.total_words = 0 + for s in self.lesson['steps']: + in_word = False + for c in s['text']: + if not in_word and not c.isspace(): + self.total_words += 1 + in_word = True + elif c.isspace(): + in_word = False + + def update_stats(self): + self.total_time = time.time() - self.start_time + self.wpm = 60.0 * self.total_words / self.total_time + self.accuracy = 100.0 * self.correct_keys / self.total_keys + + self.accuracylabel.set_markup(_('<b>Accuracy:</b> %(accuracy)d%%') % { 'accuracy' : int(self.accuracy) } ) + self.wpmlabel.set_markup(_('<b>WPM:</b> %(wpm)d') % { 'wpm': int(self.wpm) } ) + + def add_text(self, text): + self.markup += text + self.lessontext.set_markup(self.markup + '_') + + def advance_step(self): + if self.next_step_idx < len(self.lesson['steps']): + # TODO - Play 'step finished' sound here. + + self.step = self.lesson['steps'][self.next_step_idx] + self.next_step_idx = self.next_step_idx + 1 + + self.add_text(self.step['instructions'] + '\n\n') + self.add_text('<span font_family="monospace">' + self.step['text'] + '</span>\n') + + self.char_idx = 0 + + else: + self.finish_lesson() + + def finish_lesson(self): + self.step = None + + self.update_stats() + + self.add_text(_('Congratulations! You finished the lesson in %(total_time)d seconds.\n\n') % + { 'total_time': int(self.total_time) } ) + + self.add_text(_('Your accuracy was %(accuracy)d%% and your speed was %(wpm)d words per minute.') % + { 'accuracy': int(self.accuracy), 'wpm': int(self.wpm) } ) + + def key_press_cb(self, widget, event): + if not self.step: + return False + + if event.keyval == ord(self.step['text'][self.char_idx]): + self.correct_keys += 1 + self.total_keys += 1 + + self.add_text('<span font_family="monospace">' + chr(event.keyval) + '</span>') + + self.char_idx += 1 + if self.char_idx >= len(self.step['text']): + self.add_text('\n\n') + self.advance_step() + + else: + # TODO - Play 'incorrect key' sound here. + + self.incorrect_keys += 1 + self.total_keys += 1 + + self.update_stats() + + return False def stop_cb(self, widget): self.activity.pop_screen() |