[Commits] typing-turtle branch master updated.

Wade Brainerd wadetb at gmail.com
Fri Nov 21 13:02:11 EST 2008


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "/home/olpc-code/git/activities/typing-turtle".

The branch, master has been updated
       via  fc297655b7b84c058985200d4f712e49dba9ac29 (commit)
       via  de9ee0339ee37a8ada6ff7dfbbebe103bb493026 (commit)
      from  afb05ac5b16412962103f9eaac9cd2ca44b80d1b (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

 TODO                               |    2 +-
 keyboard.py                        |   15 +-
 typingturtle.py => lessonscreen.py |  313 ++----------------
 mainscreen.py                      |  141 +++++++++
 medalscreen.py                     |   78 +++++
 typingturtle.py                    |  611 +-----------------------------------
 6 files changed, 269 insertions(+), 891 deletions(-)
 copy typingturtle.py => lessonscreen.py (61%)
 mode change 100755 => 100644
 create mode 100644 mainscreen.py
 create mode 100644 medalscreen.py

- Log -----------------------------------------------------------------
commit fc297655b7b84c058985200d4f712e49dba9ac29
Author: Wade Brainerd <wadetb at gmail.com>
Date:   Fri Nov 21 18:01:52 2008 +0000

    Fix bug with old lessons still hearing keypresses.
    Change timer to 1 second.
    Use Unicode in word wrap regex.

diff --git a/TODO b/TODO
index 06d79be..42bd20f 100644
--- a/TODO
+++ b/TODO
@@ -13,7 +13,7 @@ First Release
 - Better flow at the end of a level.  Report the result on the Lesson screen: Need more work, Medal received, etc.
 - Ability of lessons to list medals in other lessons as prerequisites.  Disable unavailable lessons.
 - Some sort of lesson sorting criteria.
-+ Split into file-per-screen.
+- Split into file-per-screen.
 + Scroll lessons list to the first non-medaled lesson at startup.  Or just remember scroll position.
 + Status message on the main screen.  "You unlocked a new lesson!" for example.  Eventually have the turtle 'say' it.
 - Implement a long text copying lesson and fix bugs in the scrolling and typing.
diff --git a/keyboard.py b/keyboard.py
index 5dc92c3..90b959b 100644
--- a/keyboard.py
+++ b/keyboard.py
@@ -245,10 +245,19 @@ class Keyboard(gtk.EventBox):
 
         self.shift_down = False
 
+        # Connect keyboard grabbing and releasing callbacks.        
+        self.connect('realize', self._realize_cb)
+        self.connect('unrealize', self._unrealize_cb)
+
+    def _realize_cb(self, widget):
         # Setup keyboard event snooping in the root window.
-        root_window.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.KEY_RELEASE_MASK)
-        root_window.connect('key-press-event', self._key_press_cb)
-        root_window.connect('key-release-event', self._key_release_cb)
+        self.root_window.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.KEY_RELEASE_MASK)
+        self.key_press_cb_id = self.root_window.connect('key-press-event', self._key_press_cb)
+        self.key_release_cb_id = self.root_window.connect('key-release-event', self._key_release_cb)
+        
+    def _unrealize_cb(self, widget):
+        self.root_window.disconnect(self.key_press_cb_id)
+        self.root_window.disconnect(self.key_release_cb_id)
 
     def _build_key_list(self, layout):
         """Builds a list of Keys objects from a layout description.  
diff --git a/lessonscreen.py b/lessonscreen.py
index e1871ce..b09f925 100644
--- a/lessonscreen.py
+++ b/lessonscreen.py
@@ -113,21 +113,26 @@ class LessonScreen(gtk.VBox):
         self.keyboard = keyboard.Keyboard(self.activity)
         self.keyboard.set_layout(keyboard.DEFAULT_LAYOUT)
         
-        self.activity.add_events(gtk.gdk.KEY_PRESS_MASK)
-        self.key_press_cb_id = self.activity.connect('key-press-event', self.key_press_cb)
-        
         self.pack_start(hbox, False, False, 10)
         self.pack_start(frame, True, True)
         self.pack_start(self.keyboard, True)
         
+        # Connect keyboard grabbing and releasing callbacks.        
+        self.connect('realize', self.realize_cb)
+        self.connect('unrealize', self.unrealize_cb)
+        
         self.show_all()
         
         self.begin_lesson()
-        
-        gobject.timeout_add(250, self.timer_cb)
 
-    def __del__(self):
-        print "Disconnecting keypress callback."
+        # Initialize stats update timer.        
+        gobject.timeout_add(1000, self.timer_cb)
+
+    def realize_cb(self, widget):
+        self.activity.add_events(gtk.gdk.KEY_PRESS_MASK)
+        self.key_press_cb_id = self.activity.connect('key-press-event', self.key_press_cb)
+        
+    def unrealize_cb(self, widget):
         self.activity.disconnect(self.key_press_cb_id)
         
     def update_stats(self):
@@ -168,7 +173,8 @@ class LessonScreen(gtk.VBox):
         self.advance_step()
 
     def wrap_line(self, line):
-        words = re.split('(\W+)', line)
+        r = re.compile('(\W+)', re.UNICODE)
+        words = r.split(line)
         
         new_lines = []
         cur_line = ''
diff --git a/typingturtle.py b/typingturtle.py
index 8a3fd37..5e5342d 100755
--- a/typingturtle.py
+++ b/typingturtle.py
@@ -13,7 +13,6 @@
 # 
 # You should have received a copy of the GNU General Public License
 # along with Typing Turtle.  If not, see <http://www.gnu.org/licenses/>.
-#!/usr/bin/env python
 """Typing Turtle - Interactive typing tutor for the OLPC XO."""
 
 # Import standard Python modules.
@@ -37,612 +36,8 @@ log = logging.getLogger('Typing Turtle')
 log.setLevel(logging.DEBUG)
 logging.basicConfig()
 
-# Import onscreen keyboard.
-import keyboard
-
-# Paragraph symbol unicode character.
-PARAGRAPH_CODE = u'\xb6'
-
-# Maximium width of a text line in text lesson mode.
-LINE_WIDTH = 80
-
-# Requirements for earning medals.
-# Words per minute goals came from http://en.wikipedia.org/wiki/Words_per_minute.
-MEDALS = [
-    { 'name': 'bronze', 'wpm': 25, 'accuracy': 75 },
-    { 'name': 'silver', 'wpm': 35, 'accuracy': 85 },
-    { 'name': 'gold',   'wpm': 45, 'accuracy': 95 }
-]
-
-class MedalScreen(gtk.EventBox):
-    def __init__(self, medal, activity):
-        gtk.EventBox.__init__(self)
-        
-        self.modify_bg(gtk.STATE_NORMAL, self.get_colormap().alloc_color('#ffffff'))
-        
-        self.medal = medal
-        self.activity = activity
-        
-        cert0 = gtk.Label()
-        cert0.set_markup("<span size='35000'><b><i>" + _('Certificate of\nAchievement') + "</i></b></span>")
-        
-        cert1 = gtk.Label()
-        cert1.set_markup("<span size='18000'>" + 
-            (_('This certifies that on <i><b><u>%(date)s</u></b></i>,\n<i><b><u>%(nick)s</u></b></i> earned a %(type)s medal\nin Typing Turtle lesson <i><b><u>%(lesson)s</u></b></i>.') % medal) +
-            "</span>")
-        
-        wpmlabel = gtk.Label()
-        wpmlabel.set_markup("<span size='18000'>" + (_('<b>Words Per Minute:</b> %(wpm)d') % medal) + "</span>" )
-        
-        accuracylabel = gtk.Label()
-        accuracylabel.set_markup("<span size='15000'>" + (_('<b>Accuracy:</b> %(accuracy)d%%') % medal) + "</span>" )
-        
-        statbox = gtk.HBox()
-        statbox.pack_start(wpmlabel, True)
-        statbox.pack_start(accuracylabel, True)
-        
-        oklabel = gtk.Label()
-        oklabel.set_markup("<span size='10000'>" + _('Go Back') + '</span>')
-        okbtn =  gtk.Button()
-        okbtn.add(oklabel)
-        okbtn.connect('clicked', self.ok_cb)
-        
-        btnbox = gtk.HBox()
-        btnbox.pack_start(okbtn, True, False)
-        
-        vbox = gtk.VBox()
-        
-        vbox.pack_start(cert0, True, False, 0)
-        vbox.pack_start(cert1, False, False, 0)
-        vbox.pack_start(gtk.HSeparator(), False, False, 20)
-        vbox.pack_start(statbox, False, False, 0)
-        vbox.pack_start(gtk.HSeparator(), False, False, 20)
-        vbox.pack_start(btnbox, False, False, 40)
-        
-        self.add(vbox)
-        
-        self.show_all()
-
-    def ok_cb(self, widget):
-        self.activity.pop_screen()
-
-class LessonScreen(gtk.VBox):
-    def __init__(self, lesson, activity):
-        gtk.VBox.__init__(self)
-        
-        self.lesson = lesson
-        self.activity = activity
-        
-        # Build the user interface.
-        title = gtk.Label()
-        title.set_markup("<span size='20000'><b>" + lesson['name'] + "</b></span>")
-        title.set_alignment(1.0, 0.0)
-        
-        stoplabel = gtk.Label(_('Go Back'))
-        stopbtn =  gtk.Button()
-        stopbtn.add(stoplabel)
-        stopbtn.connect('clicked', self.stop_cb)
-        
-        # TODO- These will be replaced by graphical displays using gtk.DrawingArea.
-        self.wpmlabel = gtk.Label()
-        self.accuracylabel = gtk.Label()
-        
-        #self.wpmarea = gtk.DrawingArea()
-        #self.wpmarea.connect('expose-event', self.wpm_expose_cb)
-        #self.accuracyarea = gtk.DrawingArea()
-        #self.accuracyarea.connect('expose-event', self.accuracy_expose_cb)
-        
-        gobject.timeout_add(250, self.timer_cb)
-        
-        hbox = gtk.HBox()
-        hbox.pack_start(stopbtn, False, False, 10)
-        hbox.pack_start(self.wpmlabel, True, False, 10)
-        hbox.pack_start(self.accuracylabel, True, False, 10)
-        hbox.pack_end(title, False, False, 10)
-        
-        # Set up font styles.
-        self.tagtable = gtk.TextTagTable()
-        instructions_tag = gtk.TextTag('instructions')
-        #instructions_tag.props.size = 10000
-        instructions_tag.props.justification = gtk.JUSTIFY_CENTER
-        
-        self.tagtable.add(instructions_tag)
-        text_tag = gtk.TextTag('text')
-        text_tag.props.family = 'Monospace'
-        self.tagtable.add(text_tag)
-        
-        correct_copy_tag = gtk.TextTag('correct-copy')
-        correct_copy_tag.props.family = 'Monospace'
-        correct_copy_tag.props.foreground = '#0000ff'
-        self.tagtable.add(correct_copy_tag)
-        
-        incorrect_copy_tag = gtk.TextTag('incorrect-copy')
-        incorrect_copy_tag.props.family = 'Monospace'
-        incorrect_copy_tag.props.foreground = '#ff0000'
-        self.tagtable.add(incorrect_copy_tag)
-        
-        # Set up the scrolling lesson text view.
-        self.lessonbuffer = gtk.TextBuffer(self.tagtable)
-        self.lessontext = gtk.TextView(self.lessonbuffer)
-        self.lessontext.set_editable(False)
-        self.lessontext.set_left_margin(20)
-        self.lessontext.set_right_margin(20)
-        self.lessontext.set_wrap_mode(gtk.WRAP_WORD)
-        
-        self.lessonscroll = gtk.ScrolledWindow()
-        self.lessonscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
-        self.lessonscroll.add(self.lessontext)
-        
-        frame = gtk.Frame()
-        frame.add(self.lessonscroll)
-        
-        self.keyboard = keyboard.Keyboard(self.activity)
-        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, True)
-        self.pack_start(self.keyboard, True)
-        
-        self.show_all()
-        
-        self.begin_lesson()
-
-    def update_stats(self):
-        if not self.start_time or self.lesson_finished:
-            return
-        
-        self.total_time = time.time() - self.start_time
-        if self.total_time >= 1.0:
-            self.wpm = 60 * (self.correct_keys / 5) / self.total_time
-        else:
-            self.wpm = 1.0
-        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 timer_cb(self):
-        self.update_stats()
-        return True
-    
-    def begin_lesson(self):
-        self.lesson_finished = False
-        
-        self.medal = None
-        
-        self.total_keys = 0
-        self.correct_keys = 0
-        self.incorrect_keys = 0
-        
-        self.start_time = None
-        
-        self.next_step_idx = 0
-        self.advance_step()
-
-    def wrap_line(self, line):
-        words = re.split('(\W+)', line)
-        
-        new_lines = []
-        cur_line = ''
-        for w in words:
-            # TODO: Handle single word longer than a line.
-            if not w.isspace() and len(cur_line) + len(w) > LINE_WIDTH:
-                if len(cur_line):
-                    new_lines.append(cur_line)
-                    cur_line = ''
-            cur_line += w
-        
-        if len(cur_line):
-            new_lines.append(cur_line)
-        
-        return new_lines
-
-    def advance_step(self):
-        # Clear step related variables.
-        self.step = None
-        
-        self.text = None
-        self.line = None
-        self.line_marks = None
-        
-        # End lesson if this is the last step.
-        if self.next_step_idx >= len(self.lesson['steps']):
-            print "Lesson finished."
-            self.lesson_finished = True
-            self.show_lesson_report()
-            return
-        
-        # TODO - Play 'step finished' sound here.
-        
-        self.step = self.lesson['steps'][self.next_step_idx]
-        self.next_step_idx = self.next_step_idx + 1
-        
-        # Clear the text buffer and output the instructions.
-        self.lessonbuffer.insert_with_tags_by_name(
-            self.lessonbuffer.get_end_iter(), '\n\n' + self.step['instructions'] + '\n', 'instructions')
-        
-        self.text = unicode(self.step['text'])
-        
-        # Split text into lines.
-        self.lines = self.text.splitlines(True)
-        
-        # Substitute paragraph codes.
-        self.lines = [l.replace('\n', PARAGRAPH_CODE) for l in self.lines]
-        
-        # Split by line length in addition to by paragraphs.
-        for i in range(0, len(self.lines)):
-            line = self.lines[i]
-            if len(line) > LINE_WIDTH:
-                self.lines[i:i+1] = self.wrap_line(line)
-        
-        # Center single line steps.
-        indent = ''
-        #if len(self.lines) == 1:
-        #    indent = ' ' * ((LINE_LENGTH - len(self.lines[0]))/2)
-            
-        # Fill text buffer with text lines, each followed by room for the user to type.
-        self.line_marks = {} 
-        line_idx = 0
-        for l in self.lines:
-                
-            # Add the text to copy.
-            self.lessonbuffer.insert_with_tags_by_name(
-                self.lessonbuffer.get_end_iter(), '\n' + indent + l.encode('utf-8') + '\n' + indent, 'text')
-            
-            # Leave a marker where we will later insert text.
-            self.line_marks[line_idx] = self.lessonbuffer.create_mark(None, self.lessonbuffer.get_end_iter(), True)
-            
-            line_idx += 1
-        
-        self.line_idx = 0
-        self.begin_line()
-            
-    def begin_line(self):
-        self.line = self.lines[self.line_idx]
-        self.line_mark = self.line_marks[self.line_idx]
-        
-        self.char_idx = 0
-        
-        self.hilite_next_key()
-
-    def key_press_cb(self, widget, event):
-        # Ignore hotkeys.
-        if event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.MOD1_MASK):
-            return
-        
-        # Extract information about the key pressed.
-        key = gtk.gdk.keyval_to_unicode(event.keyval)
-        if key != 0: key = chr(key)
-        key_name = gtk.gdk.keyval_name(event.keyval)
-        
-        # Simply wait for a return keypress on the lesson finished screen.
-        if self.lesson_finished:
-            # TODO: Wait a second first.
-            if key_name == 'Return':
-                self.end_lesson()
-            return
-        
-        # Convert Return keys to paragraph symbols.
-        if key_name == 'Return':
-            key = PARAGRAPH_CODE
-        
-        print "key_press_cb: key=%s key_name=%s event.keyval=%d" % (key, key_name, event.keyval)
-        
-        # Timer starts with first keypress.
-        if not self.start_time:
-            self.start_time = time.time()
-        
-        # Handle backspace by deleting text and optionally moving up lines.
-        if key_name == 'BackSpace':
-            # Move to previous line if at the end of the current one.
-            if self.char_idx == 0 and self.line_idx > 0:
-                self.line_idx -= 1 
-                self.begin_line()
-                
-                self.char_idx = len(self.line)
-            
-            # Then delete the current character.
-            if self.char_idx > 0:
-                self.char_idx -= 1
-                
-                iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
-                iter.forward_chars(self.char_idx)
-                
-                iter_end = iter.copy()
-                iter_end.forward_char()
-                
-                self.lessonbuffer.delete(iter, iter_end)
-
-            self.hilite_next_key()
-
-        # Process normal key presses.
-        elif key != 0:
-            
-            # Check to see if they pressed the correct key.
-            if key == self.line[self.char_idx]:
-                tag_name = 'correct-copy'
-                self.correct_keys += 1
-                self.total_keys += 1
-                
-            else:
-                # TODO - Play 'incorrect key' sound here.
-                
-                tag_name = 'incorrect-copy'
-                self.incorrect_keys += 1
-                self.total_keys += 1
-            
-            # Insert the key into the bufffer.
-            iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
-            iter.forward_chars(self.char_idx)
-            
-            self.lessonbuffer.insert_with_tags_by_name(iter, key, tag_name)
-            
-            # Advance to the next character (or else).
-            self.char_idx += 1
-            if self.char_idx >= len(self.line):
-                self.line_idx += 1
-                if self.line_idx >= len(self.lines):
-                    self.advance_step()
-                else:
-                    self.begin_line()
-                return
-            
-            self.update_stats()
-        
-            self.hilite_next_key()
-        
-        return False
-
-    def hilite_next_key(self):
-        # Hilite the next key on the virtual keyboard.
-        self.keyboard.clear_hilite()
-        if len(self.line) > 0:
-            key = self.keyboard.find_key_by_letter(self.line[self.char_idx])
-            if key:
-                key.set_hilite(True)
-
-        # Move the cursor to the insert location.
-        iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
-        iter.forward_chars(self.char_idx)
-        self.lessonbuffer.place_cursor(iter)
-
-        # Gain focus (this causes the cursor line to draw).
-        self.lessontext.grab_focus()
-
-        # Scroll the TextView so the cursor is on screen.
-        self.lessontext.scroll_to_mark(self.lessonbuffer.get_insert(), 0)
-
-    def show_lesson_report(self):
-        self.update_stats()
-
-        lesson_name = self.lesson['name']
-        
-        # Add to the lesson history.
-        report = { 
-            'lesson': lesson_name,
-            'time': self.total_time,
-            'wpm': self.wpm, 
-            'accuracy': self.accuracy
-        }
-        self.activity.add_history(report)
-        
-        # Show the medal screen, if one should be given.
-        got_medal = None
-        for medal in MEDALS:
-            if self.wpm >= medal['wpm'] and self.accuracy >= medal['accuracy']:
-                got_medal = medal['name']
-
-        if got_medal:
-            # Award the medal.
-            medal = {
-                'lesson': lesson_name,
-                'type': got_medal,
-                'date': datetime.date.today().strftime('%B %d, %Y'),
-                'nick': self.activity.owner.props.nick,
-                'time': self.total_time,
-                'wpm': report['wpm'],
-                'accuracy': report['accuracy']
-            }
-            self.medal = medal
-
-            # Compare this medal with any existing medals for this lesson.
-            # Only record the best one.
-            add_medal = True
-            if self.activity.data['medals'].has_key(lesson_name):
-                old_medal = self.activity.data['medals'][lesson_name]
-
-                order = ' '.join([m['name'] for m in MEDALS])
-                add_idx = order.index(medal['type'])
-                old_idx = order.index(old_medal['type']) 
-
-                if add_idx < old_idx:
-                    add_medal = False
-                elif add_idx == old_idx:
-                    if medal['accuracy'] < old_medal['accuracy']:
-                        add_medal = False
-                    elif medal['accuracy'] == old_medal['accuracy']:
-                        if medal['wpm'] < old_medal['wpm']:
-                            add_medal = False
-            
-            if add_medal:       
-                # Upgrade the player's level if needed.
-                if self.lesson['level'] > self.activity.data['level']:
-                    self.activity.data['level'] = self.lesson['level']
-                    self.activity.data['motd'] = 'newlevel'
-                
-                self.activity.data['medals'][lesson_name] = medal
-                self.activity.mainscreen.update_medals()
-        
-        # Display results to the user.
-        text = '\n'
-        
-        congrats = [
-            _('Good job!'),
-            _('Well done!'),
-            _('Nice work!'),
-            _('Way to go!')
-        ]
-        text += random.choice(congrats) + '\n\n'
-        
-        text += _('You finished the lesson in %(time)d seconds, with %(errors)d errors.\n\n') % \
-            { 'time': int(self.total_time), 'errors': self.incorrect_keys }
-        text += _('Your words per minute (WPM) was %(wpm)d, and your accuracy was %(accuracy)d%%.\n\n') % \
-            report
-        
-        if self.medal:
-            # TODO: Play medal sound here.
-            
-            text += _('Congratulations!  You earned a %(type)s medal!\n\nPress Enter to see your certificate.') % \
-            medal
-            
-        else:
-            # Comment on what the user needs to do better.
-            need_wpm = report['wpm'] < MEDALS[0]['wpm']
-            need_accuracy = report['accuracy'] < MEDALS[0]['accuracy']
-            
-            if need_accuracy and need_wpm:
-                text += _('You need to practice this lesson more before moving on.  If you are having a hard time, '
-                          'repeat the earlier lessons until you have mastered them completely before trying this one '
-                          'again.\n\n')
-                
-            elif need_accuracy:
-                text += _('You almost got a medal!  Next time, try not to make as many errors!\n\n')
-                
-            elif need_wpm:
-                text += _('You almost got a medal!  Next time, try to type a little faster!\n\n')
-                
-            text += _('Press Enter to return to the main screen.')
-            
-        self.lessonbuffer.set_text('')
-        self.lessonbuffer.insert_with_tags_by_name(
-            self.lessonbuffer.get_end_iter(),
-            text,
-            'instructions')
-        
-    def end_lesson(self):
-        self.activity.pop_screen()
-        
-        # Show the new medal if there was one.
-        if self.medal:
-            self.activity.push_screen(MedalScreen(self.medal, self.activity))
-
-    def stop_cb(self, widget):
-        self.activity.pop_screen()
-
-class MainScreen(gtk.VBox):
-    def __init__(self, activity):
-        gtk.VBox.__init__(self)
-        
-        self.activity = activity
-        
-        # Build background.
-        title = gtk.Label()
-        title.set_markup("<span size='40000'><b>" + _('Typing Turtle') + "</b></span>")
-        
-        subtitle = gtk.Label()
-        subtitle.set_markup(_('Welcome to Typing Turtle! To begin, select a lesson from the list below.'))
-        
-        spacer = gtk.HBox()
-        
-        # Lessons header.
-        headerbox = gtk.VBox()
-        label = gtk.Label()
-        label.set_alignment(0.0, 0.5)
-        label.set_markup("<span size='large'><b>"+_('Available Lessons')+"</b></span>")
-        headerbox.pack_start(label, False)
-        headerbox.pack_start(gtk.HSeparator(), False)
-        
-        # Build lessons list.
-        self.lessonbox = gtk.VBox()
-        self.lessonbox.set_spacing(10)
-        
-        bundle_path = sugar.activity.activity.get_bundle_path() 
-        code = locale.getlocale(locale.LC_ALL)[0]
-        path = bundle_path + '/lessons/' + code + '/'
-        
-        # Find all .lesson files in ./lessons/en_US/ for example.
-        lessons = []
-        for f in os.listdir(path):
-            fd = open(path + f, 'r')
-            try:
-                lesson = json.read(fd.read())
-                lessons.append(lesson)
-            finally:
-                fd.close()
-        
-        lessons.sort(lambda x, y: x['level'] - y['level'])
-        
-        for l in lessons:
-            label = gtk.Label()
-            label.set_alignment(0.0, 0.5)
-            label.set_markup("<span size='large'>" + l['name'] + "</span>\n" + l['description'])
-            
-            btn = gtk.Button()
-            btn.lesson = l
-            btn.add(label)
-            btn.connect('clicked', self.lesson_clicked_cb)
-            
-            medalimage = gtk.Image()
-            
-            medalbtn = gtk.Button()
-            medalbtn.lesson = l
-            medalbtn.add(medalimage)
-            medalbtn.connect('clicked', self.medal_clicked_cb)
-            
-            hbox = gtk.HBox()
-            hbox.pack_start(btn, True, True, 10)
-            hbox.pack_end(medalbtn, False, False)            
-            
-            hbox.button = btn
-            hbox.medalbutton = medalbtn
-            hbox.lesson = l
-            hbox.medalimage = medalimage
-            
-            self.lessonbox.pack_start(hbox, False)
-        
-        self.lessonscroll = gtk.ScrolledWindow()
-        self.lessonscroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
-        self.lessonscroll.add_with_viewport(self.lessonbox)
-        
-        self.pack_start(title, False, True, 10)
-        self.pack_start(subtitle, False)
-        self.pack_start(spacer, False, False, 50)
-        self.pack_start(headerbox, False)
-        self.pack_start(self.lessonscroll, True)
-        
-        self.update_medals()
-
-    def update_medals(self):
-        for l in self.lessonbox:
-            # Disable the lesson button unless available.
-            lesson_available = self.activity.data['level'] >= l.lesson['requiredlevel']
-            l.button.set_sensitive(lesson_available)
-            l.medalbutton.set_sensitive(lesson_available)
-            
-            # Update the medal image.
-            medal_type = 'none'
-            if self.activity.data['medals'].has_key(l.lesson['name']):
-                medal_type = self.activity.data['medals'][l.lesson['name']]['type']
-
-            bundle = sugar.activity.activity.get_bundle_path()
-            images = {
-                'none':   bundle+'/images/no-medal.jpg',
-                'bronze': bundle+'/images/bronze-medal.jpg',
-                'silver': bundle+'/images/silver-medal.jpg',
-                'gold':   bundle+'/images/gold-medal.jpg'
-            }
-            l.medalimage.set_from_file(images[medal_type])
-    
-    def lesson_clicked_cb(self, widget):
-        self.activity.push_screen(LessonScreen(widget.lesson, self.activity))
-    
-    def medal_clicked_cb(self, widget):
-        if self.activity.data['medals'].has_key(widget.lesson['name']):
-            medal = self.activity.data['medals'][widget.lesson['name']]
-            self.activity.push_screen(MedalScreen(medal, self.activity))
+# Import activity modules.
+import mainscreen, lessonscreen, medalscreen
 
 # This is the main Typing Turtle activity class.
 # 
@@ -672,7 +67,7 @@ class TypingTurtle(sugar.activity.activity.Activity):
         self.set_canvas(self.screenbox)
         
         # Start with the main screen.
-        self.mainscreen = MainScreen(self)
+        self.mainscreen = mainscreen.MainScreen(self)
         self.push_screen(self.mainscreen)
         
         self.show_all()

commit de9ee0339ee37a8ada6ff7dfbbebe103bb493026
Author: Wade Brainerd <wadetb at gmail.com>
Date:   Fri Nov 21 17:51:32 2008 +0000

    Split into file per screen.

diff --git a/lessonscreen.py b/lessonscreen.py
new file mode 100644
index 0000000..e1871ce
--- /dev/null
+++ b/lessonscreen.py
@@ -0,0 +1,478 @@
+# Copyright 2008 by Kate Scheppke and Wade Brainerd.  
+# This file is part of Typing Turtle.
+#
+# Typing Turtle 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 3 of the License, or
+# (at your option) any later version.
+# 
+# Typing Turtle 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 Typing Turtle.  If not, see <http://www.gnu.org/licenses/>.
+
+# Import standard Python modules.
+import logging, os, math, time, copy, json, locale, datetime, random, re
+from gettext import gettext as _
+
+# Import PyGTK.
+import gobject, pygtk, gtk, pango
+
+# Import Sugar UI modules.
+import sugar.activity.activity
+from sugar.graphics import *
+
+# Import activity modules.
+import keyboard, medalscreen
+
+# Paragraph symbol unicode character.
+PARAGRAPH_CODE = u'\xb6'
+
+# Maximium width of a text line in text lesson mode.
+LINE_WIDTH = 80
+
+# Requirements for earning medals.
+# Words per minute goals came from http://en.wikipedia.org/wiki/Words_per_minute.
+MEDALS = [
+    { 'name': 'bronze', 'wpm': 25, 'accuracy': 75 },
+    { 'name': 'silver', 'wpm': 35, 'accuracy': 85 },
+    { 'name': 'gold',   'wpm': 45, 'accuracy': 95 }
+]
+
+class LessonScreen(gtk.VBox):
+    def __init__(self, lesson, activity):
+        gtk.VBox.__init__(self)
+        
+        self.lesson = lesson
+        self.activity = activity
+        
+        # Build the user interface.
+        title = gtk.Label()
+        title.set_markup("<span size='20000'><b>" + lesson['name'] + "</b></span>")
+        title.set_alignment(1.0, 0.0)
+        
+        stoplabel = gtk.Label(_('Go Back'))
+        stopbtn =  gtk.Button()
+        stopbtn.add(stoplabel)
+        stopbtn.connect('clicked', self.stop_cb)
+        
+        # TODO- These will be replaced by graphical displays using gtk.DrawingArea.
+        self.wpmlabel = gtk.Label()
+        self.accuracylabel = gtk.Label()
+        
+        #self.wpmarea = gtk.DrawingArea()
+        #self.wpmarea.connect('expose-event', self.wpm_expose_cb)
+        #self.accuracyarea = gtk.DrawingArea()
+        #self.accuracyarea.connect('expose-event', self.accuracy_expose_cb)
+        
+        hbox = gtk.HBox()
+        hbox.pack_start(stopbtn, False, False, 10)
+        hbox.pack_start(self.wpmlabel, True, False, 10)
+        hbox.pack_start(self.accuracylabel, True, False, 10)
+        hbox.pack_end(title, False, False, 10)
+        
+        # Set up font styles.
+        self.tagtable = gtk.TextTagTable()
+        instructions_tag = gtk.TextTag('instructions')
+        #instructions_tag.props.size = 10000
+        instructions_tag.props.justification = gtk.JUSTIFY_CENTER
+        
+        self.tagtable.add(instructions_tag)
+        text_tag = gtk.TextTag('text')
+        text_tag.props.family = 'Monospace'
+        self.tagtable.add(text_tag)
+        
+        correct_copy_tag = gtk.TextTag('correct-copy')
+        correct_copy_tag.props.family = 'Monospace'
+        correct_copy_tag.props.foreground = '#0000ff'
+        self.tagtable.add(correct_copy_tag)
+        
+        incorrect_copy_tag = gtk.TextTag('incorrect-copy')
+        incorrect_copy_tag.props.family = 'Monospace'
+        incorrect_copy_tag.props.foreground = '#ff0000'
+        self.tagtable.add(incorrect_copy_tag)
+        
+        # Set up the scrolling lesson text view.
+        self.lessonbuffer = gtk.TextBuffer(self.tagtable)
+        self.lessontext = gtk.TextView(self.lessonbuffer)
+        self.lessontext.set_editable(False)
+        self.lessontext.set_left_margin(20)
+        self.lessontext.set_right_margin(20)
+        self.lessontext.set_wrap_mode(gtk.WRAP_WORD)
+        
+        self.lessonscroll = gtk.ScrolledWindow()
+        self.lessonscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
+        self.lessonscroll.add(self.lessontext)
+        
+        frame = gtk.Frame()
+        frame.add(self.lessonscroll)
+        
+        self.keyboard = keyboard.Keyboard(self.activity)
+        self.keyboard.set_layout(keyboard.DEFAULT_LAYOUT)
+        
+        self.activity.add_events(gtk.gdk.KEY_PRESS_MASK)
+        self.key_press_cb_id = self.activity.connect('key-press-event', self.key_press_cb)
+        
+        self.pack_start(hbox, False, False, 10)
+        self.pack_start(frame, True, True)
+        self.pack_start(self.keyboard, True)
+        
+        self.show_all()
+        
+        self.begin_lesson()
+        
+        gobject.timeout_add(250, self.timer_cb)
+
+    def __del__(self):
+        print "Disconnecting keypress callback."
+        self.activity.disconnect(self.key_press_cb_id)
+        
+    def update_stats(self):
+        if self.lesson_finished:
+            return
+        
+        if self.start_time:
+            self.total_time = time.time() - self.start_time
+            
+            if self.total_time >= 1.0:
+                self.wpm = 60 * (self.correct_keys / 5) / self.total_time
+            else:
+                self.wpm = 1.0
+                
+            self.wpmlabel.set_markup(_('<b>WPM:</b> %(wpm)d') % { 'wpm': int(self.wpm) } )
+
+        if self.total_keys:                
+            self.accuracy = 100.0 * self.correct_keys / self.total_keys
+            
+            self.accuracylabel.set_markup(_('<b>Accuracy:</b> %(accuracy)d%%') % { 'accuracy' : int(self.accuracy) } )
+    
+    def timer_cb(self):
+        self.update_stats()
+        return True
+    
+    def begin_lesson(self):
+        self.lesson_finished = False
+        
+        self.medal = None
+        
+        self.total_keys = 0
+        self.correct_keys = 0
+        self.incorrect_keys = 0
+        
+        self.start_time = None
+        
+        self.next_step_idx = 0
+        self.advance_step()
+
+    def wrap_line(self, line):
+        words = re.split('(\W+)', line)
+        
+        new_lines = []
+        cur_line = ''
+        for w in words:
+            # TODO: Handle single word longer than a line.
+            if not w.isspace() and len(cur_line) + len(w) > LINE_WIDTH:
+                if len(cur_line):
+                    new_lines.append(cur_line)
+                    cur_line = ''
+            cur_line += w
+        
+        if len(cur_line):
+            new_lines.append(cur_line)
+        
+        return new_lines
+
+    def advance_step(self):
+        # Clear step related variables.
+        self.step = None
+        
+        self.text = None
+        self.line = None
+        self.line_marks = None
+        
+        # End lesson if this is the last step.
+        if self.next_step_idx >= len(self.lesson['steps']):
+            print "Lesson finished."
+            self.lesson_finished = True
+            self.show_lesson_report()
+            return
+        
+        # TODO - Play 'step finished' sound here.
+        
+        self.step = self.lesson['steps'][self.next_step_idx]
+        self.next_step_idx = self.next_step_idx + 1
+        
+        # Clear the text buffer and output the instructions.
+        self.lessonbuffer.insert_with_tags_by_name(
+            self.lessonbuffer.get_end_iter(), '\n\n' + self.step['instructions'] + '\n', 'instructions')
+        
+        self.text = unicode(self.step['text'])
+        
+        # Split text into lines.
+        self.lines = self.text.splitlines(True)
+        
+        # Substitute paragraph codes.
+        self.lines = [l.replace('\n', PARAGRAPH_CODE) for l in self.lines]
+        
+        # Split by line length in addition to by paragraphs.
+        for i in range(0, len(self.lines)):
+            line = self.lines[i]
+            if len(line) > LINE_WIDTH:
+                self.lines[i:i+1] = self.wrap_line(line)
+        
+        # Center single line steps.
+        indent = ''
+        #if len(self.lines) == 1:
+        #    indent = ' ' * ((LINE_LENGTH - len(self.lines[0]))/2)
+            
+        # Fill text buffer with text lines, each followed by room for the user to type.
+        self.line_marks = {} 
+        line_idx = 0
+        for l in self.lines:
+                
+            # Add the text to copy.
+            self.lessonbuffer.insert_with_tags_by_name(
+                self.lessonbuffer.get_end_iter(), '\n' + indent + l.encode('utf-8') + '\n' + indent, 'text')
+            
+            # Leave a marker where we will later insert text.
+            self.line_marks[line_idx] = self.lessonbuffer.create_mark(None, self.lessonbuffer.get_end_iter(), True)
+            
+            line_idx += 1
+        
+        self.line_idx = 0
+        self.begin_line()
+            
+    def begin_line(self):
+        self.line = self.lines[self.line_idx]
+        self.line_mark = self.line_marks[self.line_idx]
+        
+        self.char_idx = 0
+        
+        self.hilite_next_key()
+
+    def key_press_cb(self, widget, event):
+        # Ignore hotkeys.
+        if event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.MOD1_MASK):
+            return
+        
+        # Extract information about the key pressed.
+        key = gtk.gdk.keyval_to_unicode(event.keyval)
+        if key != 0: key = chr(key)
+        key_name = gtk.gdk.keyval_name(event.keyval)
+        
+        # Simply wait for a return keypress on the lesson finished screen.
+        if self.lesson_finished:
+            # TODO: Wait a second first.
+            if key_name == 'Return':
+                self.end_lesson()
+            return
+        
+        # Convert Return keys to paragraph symbols.
+        if key_name == 'Return':
+            key = PARAGRAPH_CODE
+        
+        print "key_press_cb: key=%s key_name=%s event.keyval=%d" % (key, key_name, event.keyval)
+        
+        # Timer starts with first keypress.
+        if not self.start_time:
+            self.start_time = time.time()
+        
+        # Handle backspace by deleting text and optionally moving up lines.
+        if key_name == 'BackSpace':
+            # Move to previous line if at the end of the current one.
+            if self.char_idx == 0 and self.line_idx > 0:
+                self.line_idx -= 1 
+                self.begin_line()
+                
+                self.char_idx = len(self.line)
+            
+            # Then delete the current character.
+            if self.char_idx > 0:
+                self.char_idx -= 1
+                
+                iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
+                iter.forward_chars(self.char_idx)
+                
+                iter_end = iter.copy()
+                iter_end.forward_char()
+                
+                self.lessonbuffer.delete(iter, iter_end)
+
+            self.hilite_next_key()
+
+        # Process normal key presses.
+        elif key != 0:
+            
+            # Check to see if they pressed the correct key.
+            if key == self.line[self.char_idx]:
+                tag_name = 'correct-copy'
+                self.correct_keys += 1
+                self.total_keys += 1
+                
+            else:
+                # TODO - Play 'incorrect key' sound here.
+                
+                tag_name = 'incorrect-copy'
+                self.incorrect_keys += 1
+                self.total_keys += 1
+            
+            # Insert the key into the bufffer.
+            iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
+            iter.forward_chars(self.char_idx)
+            
+            self.lessonbuffer.insert_with_tags_by_name(iter, key, tag_name)
+            
+            # Advance to the next character (or else).
+            self.char_idx += 1
+            if self.char_idx >= len(self.line):
+                self.line_idx += 1
+                if self.line_idx >= len(self.lines):
+                    self.advance_step()
+                else:
+                    self.begin_line()
+                return
+            
+            self.update_stats()
+        
+            self.hilite_next_key()
+        
+        return False
+
+    def hilite_next_key(self):
+        # Hilite the next key on the virtual keyboard.
+        self.keyboard.clear_hilite()
+        if len(self.line) > 0:
+            key = self.keyboard.find_key_by_letter(self.line[self.char_idx])
+            if key:
+                key.set_hilite(True)
+
+        # Move the cursor to the insert location.
+        iter = self.lessonbuffer.get_iter_at_mark(self.line_mark)
+        iter.forward_chars(self.char_idx)
+        self.lessonbuffer.place_cursor(iter)
+
+        # Gain focus (this causes the cursor line to draw).
+        self.lessontext.grab_focus()
+
+        # Scroll the TextView so the cursor is on screen.
+        self.lessontext.scroll_to_mark(self.lessonbuffer.get_insert(), 0)
+
+    def show_lesson_report(self):
+        self.update_stats()
+
+        lesson_name = self.lesson['name']
+        
+        # Add to the lesson history.
+        report = { 
+            'lesson': lesson_name,
+            'time': self.total_time,
+            'wpm': self.wpm, 
+            'accuracy': self.accuracy
+        }
+        self.activity.add_history(report)
+        
+        # Show the medal screen, if one should be given.
+        got_medal = None
+        for medal in MEDALS:
+            if self.wpm >= medal['wpm'] and self.accuracy >= medal['accuracy']:
+                got_medal = medal['name']
+
+        if got_medal:
+            # Award the medal.
+            medal = {
+                'lesson': lesson_name,
+                'type': got_medal,
+                'date': datetime.date.today().strftime('%B %d, %Y'),
+                'nick': self.activity.owner.props.nick,
+                'time': self.total_time,
+                'wpm': report['wpm'],
+                'accuracy': report['accuracy']
+            }
+            self.medal = medal
+
+            # Compare this medal with any existing medals for this lesson.
+            # Only record the best one.
+            add_medal = True
+            if self.activity.data['medals'].has_key(lesson_name):
+                old_medal = self.activity.data['medals'][lesson_name]
+
+                order = ' '.join([m['name'] for m in MEDALS])
+                add_idx = order.index(medal['type'])
+                old_idx = order.index(old_medal['type']) 
+
+                if add_idx < old_idx:
+                    add_medal = False
+                elif add_idx == old_idx:
+                    if medal['accuracy'] < old_medal['accuracy']:
+                        add_medal = False
+                    elif medal['accuracy'] == old_medal['accuracy']:
+                        if medal['wpm'] < old_medal['wpm']:
+                            add_medal = False
+            
+            if add_medal:       
+                # Upgrade the player's level if needed.
+                if self.lesson['level'] > self.activity.data['level']:
+                    self.activity.data['level'] = self.lesson['level']
+                    self.activity.data['motd'] = 'newlevel'
+                
+                self.activity.data['medals'][lesson_name] = medal
+                self.activity.mainscreen.update_medals()
+        
+        # Display results to the user.
+        text = '\n'
+        
+        congrats = [
+            _('Good job!'),
+            _('Well done!'),
+            _('Nice work!'),
+            _('Way to go!')
+        ]
+        text += random.choice(congrats) + '\n\n'
+        
+        text += _('You finished the lesson in %(time)d seconds, with %(errors)d errors.\n\n') % \
+            { 'time': int(self.total_time), 'errors': self.incorrect_keys }
+        text += _('Your words per minute (WPM) was %(wpm)d, and your accuracy was %(accuracy)d%%.\n\n') % \
+            report
+        
+        if self.medal:
+            # TODO: Play medal sound here.
+            
+            text += _('Congratulations!  You earned a %(type)s medal!\n\nPress Enter to see your certificate.') % \
+            medal
+            
+        else:
+            # Comment on what the user needs to do better.
+            need_wpm = report['wpm'] < MEDALS[0]['wpm']
+            need_accuracy = report['accuracy'] < MEDALS[0]['accuracy']
+            
+            if need_accuracy and need_wpm:
+                text += _('You need to practice this lesson more before moving on.  If you are having a hard time, '
+                          'repeat the earlier lessons until you have mastered them completely before trying this one '
+                          'again.\n\n')
+                
+            elif need_accuracy:
+                text += _('You almost got a medal!  Next time, try not to make as many errors!\n\n')
+                
+            elif need_wpm:
+                text += _('You almost got a medal!  Next time, try to type a little faster!\n\n')
+                
+            text += _('Press Enter to return to the main screen.')
+            
+        self.lessonbuffer.set_text('')
+        self.lessonbuffer.insert_with_tags_by_name(
+            self.lessonbuffer.get_end_iter(),
+            text,
+            'instructions')
+        
+    def end_lesson(self):
+        self.activity.pop_screen()
+        
+        # Show the new medal if there was one.
+        if self.medal:
+            self.activity.push_screen(medalscreen.MedalScreen(self.medal, self.activity))
+
+    def stop_cb(self, widget):
+        self.activity.pop_screen()
diff --git a/mainscreen.py b/mainscreen.py
new file mode 100644
index 0000000..8aea101
--- /dev/null
+++ b/mainscreen.py
@@ -0,0 +1,141 @@
+# Copyright 2008 by Kate Scheppke and Wade Brainerd.  
+# This file is part of Typing Turtle.
+#
+# Typing Turtle 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 3 of the License, or
+# (at your option) any later version.
+# 
+# Typing Turtle 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 Typing Turtle.  If not, see <http://www.gnu.org/licenses/>.
+
+# Import standard Python modules.
+import logging, os, math, time, copy, json, locale, datetime, random, re
+from gettext import gettext as _
+
+# Import PyGTK.
+import gobject, pygtk, gtk, pango
+
+# Import Sugar UI modules.
+import sugar.activity.activity
+from sugar.graphics import *
+
+# Import activity modules.
+import lessonscreen, medalscreen
+
+class MainScreen(gtk.VBox):
+    def __init__(self, activity):
+        gtk.VBox.__init__(self)
+        
+        self.activity = activity
+        
+        # Build background.
+        title = gtk.Label()
+        title.set_markup("<span size='40000'><b>" + _('Typing Turtle') + "</b></span>")
+        
+        subtitle = gtk.Label()
+        subtitle.set_markup(_('Welcome to Typing Turtle! To begin, select a lesson from the list below.'))
+        
+        spacer = gtk.HBox()
+        
+        # Lessons header.
+        headerbox = gtk.VBox()
+        label = gtk.Label()
+        label.set_alignment(0.0, 0.5)
+        label.set_markup("<span size='large'><b>"+_('Available Lessons')+"</b></span>")
+        headerbox.pack_start(label, False)
+        headerbox.pack_start(gtk.HSeparator(), False)
+        
+        # Build lessons list.
+        self.lessonbox = gtk.VBox()
+        self.lessonbox.set_spacing(10)
+        
+        bundle_path = sugar.activity.activity.get_bundle_path() 
+        code = locale.getlocale(locale.LC_ALL)[0]
+        path = bundle_path + '/lessons/' + code + '/'
+        
+        # Find all .lesson files in ./lessons/en_US/ for example.
+        lessons = []
+        for f in os.listdir(path):
+            fd = open(path + f, 'r')
+            try:
+                lesson = json.read(fd.read())
+                lessons.append(lesson)
+            finally:
+                fd.close()
+        
+        lessons.sort(lambda x, y: x['level'] - y['level'])
+        
+        for l in lessons:
+            label = gtk.Label()
+            label.set_alignment(0.0, 0.5)
+            label.set_markup("<span size='large'>" + l['name'] + "</span>\n" + l['description'])
+            
+            btn = gtk.Button()
+            btn.lesson = l
+            btn.add(label)
+            btn.connect('clicked', self.lesson_clicked_cb)
+            
+            medalimage = gtk.Image()
+            
+            medalbtn = gtk.Button()
+            medalbtn.lesson = l
+            medalbtn.add(medalimage)
+            medalbtn.connect('clicked', self.medal_clicked_cb)
+            
+            hbox = gtk.HBox()
+            hbox.pack_start(btn, True, True, 10)
+            hbox.pack_end(medalbtn, False, False)            
+            
+            hbox.button = btn
+            hbox.medalbutton = medalbtn
+            hbox.lesson = l
+            hbox.medalimage = medalimage
+            
+            self.lessonbox.pack_start(hbox, False)
+        
+        self.lessonscroll = gtk.ScrolledWindow()
+        self.lessonscroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+        self.lessonscroll.add_with_viewport(self.lessonbox)
+        
+        self.pack_start(title, False, True, 10)
+        self.pack_start(subtitle, False)
+        self.pack_start(spacer, False, False, 50)
+        self.pack_start(headerbox, False)
+        self.pack_start(self.lessonscroll, True)
+        
+        self.update_medals()
+
+    def update_medals(self):
+        for l in self.lessonbox:
+            # Disable the lesson button unless available.
+            lesson_available = self.activity.data['level'] >= l.lesson['requiredlevel']
+            l.button.set_sensitive(lesson_available)
+            l.medalbutton.set_sensitive(lesson_available)
+            
+            # Update the medal image.
+            medal_type = 'none'
+            if self.activity.data['medals'].has_key(l.lesson['name']):
+                medal_type = self.activity.data['medals'][l.lesson['name']]['type']
+
+            bundle = sugar.activity.activity.get_bundle_path()
+            images = {
+                'none':   bundle+'/images/no-medal.jpg',
+                'bronze': bundle+'/images/bronze-medal.jpg',
+                'silver': bundle+'/images/silver-medal.jpg',
+                'gold':   bundle+'/images/gold-medal.jpg'
+            }
+            l.medalimage.set_from_file(images[medal_type])
+    
+    def lesson_clicked_cb(self, widget):
+        self.activity.push_screen(lessonscreen.LessonScreen(widget.lesson, self.activity))
+    
+    def medal_clicked_cb(self, widget):
+        if self.activity.data['medals'].has_key(widget.lesson['name']):
+            medal = self.activity.data['medals'][widget.lesson['name']]
+            self.activity.push_screen(medalscreen.MedalScreen(medal, self.activity))
diff --git a/medalscreen.py b/medalscreen.py
new file mode 100644
index 0000000..a361c9e
--- /dev/null
+++ b/medalscreen.py
@@ -0,0 +1,78 @@
+# Copyright 2008 by Kate Scheppke and Wade Brainerd.  
+# This file is part of Typing Turtle.
+#
+# Typing Turtle 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 3 of the License, or
+# (at your option) any later version.
+# 
+# Typing Turtle 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 Typing Turtle.  If not, see <http://www.gnu.org/licenses/>.
+
+# Import standard Python modules.
+import logging, os, math, time, copy, json, locale, datetime, random, re
+from gettext import gettext as _
+
+# Import PyGTK.
+import gobject, pygtk, gtk, pango
+
+# Import Sugar UI modules.
+import sugar.activity.activity
+from sugar.graphics import *
+
+class MedalScreen(gtk.EventBox):
+    def __init__(self, medal, activity):
+        gtk.EventBox.__init__(self)
+        
+        self.modify_bg(gtk.STATE_NORMAL, self.get_colormap().alloc_color('#ffffff'))
+        
+        self.medal = medal
+        self.activity = activity
+        
+        cert0 = gtk.Label()
+        cert0.set_markup("<span size='35000'><b><i>" + _('Certificate of\nAchievement') + "</i></b></span>")
+        
+        cert1 = gtk.Label()
+        cert1.set_markup("<span size='18000'>" + 
+            (_('This certifies that on <i><b><u>%(date)s</u></b></i>,\n<i><b><u>%(nick)s</u></b></i> earned a %(type)s medal\nin Typing Turtle lesson <i><b><u>%(lesson)s</u></b></i>.') % medal) +
+            "</span>")
+        
+        wpmlabel = gtk.Label()
+        wpmlabel.set_markup("<span size='18000'>" + (_('<b>Words Per Minute:</b> %(wpm)d') % medal) + "</span>" )
+        
+        accuracylabel = gtk.Label()
+        accuracylabel.set_markup("<span size='15000'>" + (_('<b>Accuracy:</b> %(accuracy)d%%') % medal) + "</span>" )
+        
+        statbox = gtk.HBox()
+        statbox.pack_start(wpmlabel, True)
+        statbox.pack_start(accuracylabel, True)
+        
+        oklabel = gtk.Label()
+        oklabel.set_markup("<span size='10000'>" + _('Go Back') + '</span>')
+        okbtn =  gtk.Button()
+        okbtn.add(oklabel)
+        okbtn.connect('clicked', self.ok_cb)
+        
+        btnbox = gtk.HBox()
+        btnbox.pack_start(okbtn, True, False)
+        
+        vbox = gtk.VBox()
+        
+        vbox.pack_start(cert0, True, False, 0)
+        vbox.pack_start(cert1, False, False, 0)
+        vbox.pack_start(gtk.HSeparator(), False, False, 20)
+        vbox.pack_start(statbox, False, False, 0)
+        vbox.pack_start(gtk.HSeparator(), False, False, 20)
+        vbox.pack_start(btnbox, False, False, 40)
+        
+        self.add(vbox)
+        
+        self.show_all()
+
+    def ok_cb(self, widget):
+        self.activity.pop_screen()
-----------------------------------------------------------------------


--
/home/olpc-code/git/activities/typing-turtle


More information about the Commits mailing list