diff options
author | Wade Brainerd <wadetb@gmail.com> | 2009-09-10 23:34:21 (GMT) |
---|---|---|
committer | Wade Brainerd <wadetb@gmail.com> | 2009-09-10 23:34:21 (GMT) |
commit | 9ffd0d146b25b5430bdf18ac44c552d0d8a53454 (patch) | |
tree | 9297e698c5ccbb94310cea726b0f52f6f6f083df | |
parent | 97319d7dfc11382ac65fdcb3028d89738543f138 (diff) |
Work in progress on lesson editor. Screens basically functional. Lessons are editable but data is not saved.
-rw-r--r-- | TODO | 10 | ||||
-rw-r--r-- | editlessonlistscreen.py | 179 | ||||
-rw-r--r-- | editlessonscreen.py | 308 | ||||
-rw-r--r-- | mainscreen.py | 21 |
4 files changed, 449 insertions, 69 deletions
@@ -1,6 +1,16 @@ Typing Turtle +Lesson Editor ++ Fix Delete Lesson ++ Wire up step editing keys ++ Aesthetic improvements to lesson editor ++ Better help text ++ Saving and loading from Journal ++ Import and export from JSON data in Journal ++ Graphical lesson builder + First Release ++ Export PDF of lesson history and progress. + Status message on the main screen. "You unlocked a new lesson!" for example. Eventually have the turtle 'say' it. + Graphical WPM and accuracy meters. + Sound effects. diff --git a/editlessonlistscreen.py b/editlessonlistscreen.py new file mode 100644 index 0000000..d1ed405 --- /dev/null +++ b/editlessonlistscreen.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python +# vi: sw=4 et +# 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, locale, datetime, random, re +from gettext import gettext as _ + +# Import PyGTK. +import gobject, pygtk, gtk, pango + +# Import Sugar UI modules. +import sugar.activity.activity +import sugar.graphics.style + +# Import activity modules. +import editlessonscreen + +class EditLessonListScreen(gtk.VBox): + def __init__(self, activity, lessons): + gtk.VBox.__init__(self) + + self.activity = activity + self.lessons = lessons + + # Add the header. + title = gtk.Label() + title.set_markup("<span size='20000'><b>" + _("Edit Lessons") + "</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_clicked_cb) + + titlebox = gtk.HBox() + titlebox.pack_start(stopbtn, False, False, 10) + titlebox.pack_end(title, False, False, 10) + + help = gtk.Label() + help.set_padding(10, 0) + help.set_alignment(1.0, 0.5) + help.set_markup( + _("Choose the lesson that you wish to modify. If you wish to add a new lesson, ") + + _("or delete an existing lesson, use the buttons at the bottom.") + ) + + # Add the lesson list. + self.treeview = gtk.TreeView() + self.treeview.set_rules_hint(True) + self.treeview.set_enable_search(False) + + self.treeview.connect('cursor-changed', self.lesson_selected_cb) + + # Note that the only thing we store in our liststore is the lesson id. + # All the actual data is in the lessons list. + self.liststore = gtk.ListStore(gobject.TYPE_INT) + self.treeview.set_model(self.liststore) + + # Construct the columns. + renderer = gtk.CellRendererText() + col = gtk.TreeViewColumn(_('Name'), renderer) + col.set_cell_data_func(renderer, self.name_render_cb) + self.treeview.append_column(col) + + renderer = gtk.CellRendererText() + col = gtk.TreeViewColumn(_('Description'), renderer) + col.set_cell_data_func(renderer, self.description_render_cb) + col.set_expand(True) + self.treeview.append_column(col) + + scroll = gtk.ScrolledWindow() + scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scroll.add(self.treeview) + + addlabel = gtk.Label() + addlabel.set_markup(_('Add Lesson')) + addlessonbtn = gtk.Button() + addlessonbtn.add(addlabel) + addlessonbtn.connect('clicked', self.add_lesson_clicked_cb) + + editlabel = gtk.Label() + editlabel.set_markup('<span size="x-large"><b>'+_('Edit Lesson')+'</b></span>') + self.editlessonbtn = gtk.Button() + self.editlessonbtn.add(editlabel) + self.editlessonbtn.connect('clicked', self.edit_lesson_clicked_cb) + self.editlessonbtn.set_sensitive(False) + + dellabel = gtk.Label() + dellabel.set_markup(_('Delete Lesson')) + self.dellessonbtn = gtk.Button() + self.dellessonbtn.add(dellabel) + self.dellessonbtn.connect('clicked', self.del_lesson_clicked_cb) + self.dellessonbtn.set_sensitive(False) + + buttonbox = gtk.HBox() + buttonbox.pack_start(addlessonbtn, True, False, 10) + buttonbox.pack_start(self.editlessonbtn, True, False, 10) + buttonbox.pack_start(self.dellessonbtn, True, False, 10) + + self.pack_start(titlebox, False, False, 10) + self.pack_start(help, False, False, 10) + self.pack_start(gtk.HSeparator(), False, False, 0) + self.pack_start(scroll, True, True, 10) + self.pack_start(buttonbox, False, False, 10) + + self.build() + + self.show_all() + + def build(self): + # Fill the lesson list. + for t in range(0, len(self.lessons)-1): + self.liststore.append((t,)) + + def name_render_cb(self, column, cell_renderer, model, iter): + id = model.get_value(iter, 0) + t = self.lessons[id] + cell_renderer.set_property('text', t['name']) + + def description_render_cb(self, column, cell_renderer, model, iter): + id = model.get_value(iter, 0) + t = self.lessons[id] + cell_renderer.set_property('text', t['description']) + + def stop_clicked_cb(self, btn): + self.activity.pop_screen() + + def add_lesson_clicked_cb(self, btn): + lesson = {} + self.activity.push_screen(editlessonscreen.EditLessonScreen(self.activity, lesson)) + + def del_lesson_clicked_cb(self, btn): + path = self.treeview.get_cursor()[0] + if path: + model = self.liststore + iter = model.get_iter(path) + id = model.get_value(iter, 0) + self.lessons = self.lessons[id:] + self.lessons[id:] + self.liststore.remove(iter) + + def edit_lesson_clicked_cb(self, btn): + path = self.treeview.get_cursor()[0] + if path: + model = self.liststore + iter = model.get_iter(path) + id = model.get_value(iter, 0) + lesson = self.lessons[id] + self.activity.push_screen(editlessonscreen.EditLessonScreen(self.activity, lesson)) + + def lesson_selected_cb(self, treeview): + path = treeview.get_cursor()[0] + if path: + self.editlessonbtn.set_sensitive(True) + self.dellessonbtn.set_sensitive(True) + else: + self.editlessonbtn.set_sensitive(False) + self.dellessonbtn.set_sensitive(False) + + def lesson_activated_cb(self, treeview, path, column): + model = treeview.get_model() + id = model.get_value(model.get_iter(path), 0) + lesson = self.lessons[id] + self.activity.push_screen(editlessonscreen.EditLessonScreen(self.activity, lesson)) + diff --git a/editlessonscreen.py b/editlessonscreen.py index 9a2ce59..26c74e2 100644 --- a/editlessonscreen.py +++ b/editlessonscreen.py @@ -26,93 +26,285 @@ import gobject, pygtk, gtk, pango # Import Sugar UI modules. import sugar.activity.activity import sugar.graphics.style +import sugar.graphics.icon class EditLessonScreen(gtk.VBox): - def add_step_cb(w): - pass + def __init__(self, activity, lesson): + gtk.VBox.__init__(self) + self.set_border_width(10) + + self.activity = activity + self.lesson = lesson + + # Add the header. + title = gtk.Label() + title.set_markup("<span size='20000'><b>" + _("Edit a Lesson") + "</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_clicked_cb) + + titlebox = gtk.HBox() + titlebox.pack_start(stopbtn, False, False, 10) + titlebox.pack_end(title, False, False, 10) + + help = gtk.Label() + help.set_padding(10, 0) + help.set_alignment(1.0, 0.5) + help.set_markup( + _("Choose the lesson that you wish to modify. If you wish to add a new lesson, ") + + _("or delete an existing lesson, use the buttons at the bottom.") + ) + + self.vp = gtk.Viewport() + + self.scroll = gtk.ScrolledWindow() + self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.scroll.add(self.vp) - def make_step(self): + self.pack_start(titlebox, False, False, 10) + self.pack_start(help, False, False, 10) + self.pack_start(gtk.HSeparator(), False, False, 0) + self.pack_start(self.scroll, True, True, 0) + + self.build() + + self.show_all() + + def build_step(self, step, idx): stepbox = gtk.VBox() - stepbox.pack_start(gtk.HSeparator()) + steplabel = gtk.Label() + steplabel.set_markup("<span size='x-large' weight='bold'>" + (_('Step #%d') % (idx+1)) + "</span>") + steplabel.set_alignment(0.0, 0.5) + steplabel.set_padding(10, 0) + generatelabel = gtk.Label() + generatelabel.set_markup(_('Generate')) + generatebtn = gtk.Button() + generatebtn.add(generatelabel) delstepbtn = gtk.Button() - delstepbtn.add(gtk.Label('Delete')) - + delstepbtn.add(sugar.graphics.icon.Icon(icon_name='list-remove')) + delstepbtn.connect('clicked', self.del_step_clicked_cb, idx) addstepbtn = gtk.Button() - addstepbtn.add(gtk.Label('Add')) - + addstepbtn.add(sugar.graphics.icon.Icon(icon_name='list-add')) + addstepbtn.connect('clicked', self.add_step_clicked_cb, idx) moveupbtn = gtk.Button() - moveupbtn.add(gtk.Label('Move Up')) - + moveupbtn.add(sugar.graphics.icon.Icon(icon_name='go-up')) + moveupbtn.connect('clicked', self.move_step_up_clicked_cb, idx) movedownbtn = gtk.Button() - movedownbtn.add(gtk.Label('Move Down')) + movedownbtn.add(sugar.graphics.icon.Icon(icon_name='go-down')) + movedownbtn.connect('clicked', self.move_step_down_clicked_cb, idx) btnbox = gtk.HBox() - btnbox.pack_start(gtk.Label('Step')) + btnbox.pack_start(steplabel, False, False) btnbox.pack_end(addstepbtn, False, False) btnbox.pack_end(delstepbtn, False, False) btnbox.pack_end(moveupbtn, False, False) btnbox.pack_end(movedownbtn, False, False) + btnbox.pack_end(generatebtn, False, False) + + instlabel = gtk.Label() + instlabel.set_markup("<span size='large' weight='bold'>" + _('Instructions') + "</span>") + instlabel.set_alignment(0.0, 0.5) + instlabel.set_padding(20, 0) + + stepbox.insttext = gtk.TextView(gtk.TextBuffer()) + stepbox.insttext.props.wrap_mode = gtk.WRAP_WORD + instscroll = gtk.ScrolledWindow() + instscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + instscroll.add(stepbox.insttext) + instscroll.set_size_request(-1, 75) + stepbox.insttext.get_buffer().set_text(step['instructions']) + + instbox = gtk.HBox() + instbox.pack_start(instlabel, False, False) + instbox.pack_start(instscroll, True, True) + + textlabel = gtk.Label() + textlabel.set_markup("<span size='large' weight='bold'>" + _('Text') + "</span>") + textlabel.set_alignment(0.0, 0.5) + textlabel.set_padding(20, 0) + + stepbox.texttext = gtk.TextView(gtk.TextBuffer()) + stepbox.texttext.props.wrap_mode = gtk.WRAP_WORD + textscroll = gtk.ScrolledWindow() + textscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + textscroll.add(stepbox.texttext) + textscroll.set_size_request(-1, 100) + stepbox.texttext.get_buffer().set_text(step['text']) - stepbox.pack_start(btnbox) - - stepbox.pack_start(gtk.Label(_('Instructions'))) - inst_text = gtk.TextView(gtk.TextBuffer()) - inst_text.props.wrap_mode = gtk.WRAP_WORD - inst_scroll = gtk.ScrolledWindow() - inst_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - inst_scroll.add(inst_text) - inst_scroll.set_size_request(-1, 100) - stepbox.pack_start(inst_scroll) - - stepbox.pack_start(gtk.Label(_('Text'))) - text_text = gtk.TextView(gtk.TextBuffer()) - text_text.props.wrap_mode = gtk.WRAP_WORD - text_scroll = gtk.ScrolledWindow() - text_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - text_scroll.add(text_text) - text_scroll.set_size_request(-1, 200) - stepbox.pack_start(text_scroll) + textbox = gtk.HBox() + textbox.pack_start(textlabel, False, False) + textbox.pack_start(textscroll, True, True) + sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) + sizegroup.add_widget(instlabel) + sizegroup.add_widget(textlabel) + + stepbox.pack_start(btnbox, False, False, 10) + stepbox.pack_start(instbox, False, False, 10) + stepbox.pack_start(textbox, False, False, 10) + return stepbox - def make_lesson(self, lesson): - table = gtk.Table(8, 2) + def build(self): + # Add the editing controls. + detailslabel = gtk.Label() + detailslabel.set_markup("<span size='x-large'><b>" + _('Lesson Details') + "</b></span>") + detailslabel.set_alignment(0.0, 0.5) + detailslabel.set_padding(10, 0) - table.attach(gtk.Label(_('Name')), 0, 1, 0, 1) - nameent = gtk.Entry() - table.attach(nameent, 1, 2, 0, 1) + namelabel = gtk.Label() + namelabel.set_markup("<span size='large' weight='bold'>" + _('Name') + "</span>") + namelabel.set_alignment(0.0, 0.5) + namelabel.set_padding(20, 0) - table.attach(gtk.Label(_('Description')), 0, 1, 1, 2) - descent = gtk.Entry() - table.attach(descent, 1, 2, 1, 2) + self.nameent = gtk.Entry() + self.nameent.set_text(self.lesson['name']) - table.attach(gtk.Label(_('Type')), 0, 1, 2, 3) - type_drop = gtk.combo_box_new_text() - type_drop.append_text(_('Text')) - type_drop.append_text(_('Game')) - table.attach(type_drop, 1, 2, 2, 3) + namebox = gtk.HBox() + namebox.pack_start(namelabel, False, False) + namebox.pack_start(self.nameent, True, True) + + typelabel = gtk.Label() + typelabel.set_markup("<span size='large' weight='bold'>" + _('Type') + "</span>") + typelabel.set_alignment(0.0, 0.5) + typelabel.set_padding(20, 0) - vbox = gtk.VBox() - vbox.pack_start(table, False, False) + self.textradio = gtk.RadioButton(None, _('Normal Lesson')) + self.balloonradio = gtk.RadioButton(self.textradio, _('Balloon Game')) + + self.textradio.set_active(self.lesson['type'] == 'normal') + self.balloonradio.set_active(self.lesson['type'] == 'balloon') - vbox.pack_start(self.make_step_widgets()) - vbox.pack_start(self.make_step_widgets()) + typebox = gtk.HBox() + typebox.pack_start(typelabel, False, False) + typebox.pack_start(self.textradio, False, False) + typebox.pack_start(self.balloonradio, False, False) + + desclabel = gtk.Label() + desclabel.set_markup("<span size='large' weight='bold'>" + _('Description') + "</span>") + desclabel.set_alignment(0.0, 0.5) + desclabel.set_padding(20, 0) - vp = gtk.Viewport() - vp.add(vbox) + self.desctext = gtk.TextView(gtk.TextBuffer()) + self.desctext.props.wrap_mode = gtk.WRAP_WORD + descscroll = gtk.ScrolledWindow() + descscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + descscroll.add(self.desctext) + descscroll.set_size_request(-1, 75) + self.desctext.get_buffer().set_text(self.lesson['description']) - scroll = gtk.ScrolledWindow() - scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scroll.add(vp) + descbox = gtk.HBox() + descbox.pack_start(desclabel, False, False) + descbox.pack_start(descscroll, True, True) + sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) + sizegroup.add_widget(namelabel) + sizegroup.add_widget(typelabel) + sizegroup.add_widget(desclabel) + self.vbox = gtk.VBox() + self.vbox.set_border_width(20) + self.vbox.pack_start(detailslabel, False, False, 10) + self.vbox.pack_start(namebox, False, False, 10) + self.vbox.pack_start(typebox, False, False, 10) + self.vbox.pack_start(descbox, False, False, 10) + self.vbox.pack_start(gtk.HSeparator(), False, False, 0) + + if self.lesson['type'] == 'normal': + self.stepboxes = [] + + for step in self.lesson['steps']: + stepbox = self.build_step(step, len(self.stepboxes)) + self.stepboxes.append(stepbox) + + self.vbox.pack_start(stepbox, False, False, 0) + + if self.lesson['type'] == 'balloon': + textlabel = gtk.Label() + textlabel.set_markup("<span size='large' weight='bold'>" + _('Words') + "</span>") + textlabel.set_alignment(0.0, 0.5) + textlabel.set_padding(20, 0) - def __init__(self, activity): - gtk.VBox.__init__(self) + self.wordstext = gtk.TextView(gtk.TextBuffer()) + self.wordstext.props.wrap_mode = gtk.WRAP_WORD + textscroll = gtk.ScrolledWindow() + textscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + textscroll.add(self.wordstext) + textscroll.set_size_request(-1, 200) + self.wordstext.get_buffer().set_text(' '.join(self.lesson['words'])) + + textbox = gtk.HBox() + textbox.pack_start(textlabel, False, False) + textbox.pack_start(textscroll, True, True) + + self.vbox.pack_start(textbox, False, False, 10) - # Build the lesson editor. - self.pack_start(scroll) + self.vbox.show_all() + + # Remove any existing controls. + if self.vp.get_child(): + self.vp.remove(self.vp.get_child()) + + self.vp.add(self.vbox) + + def stop_clicked_cb(self, btn): + self.save() + + self.activity.pop_screen() + + def save(self): + self.lesson['name'] = self.nameent.get_text() + + buf = self.desctext.get_buffer() + self.lesson['description'] = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) + + if self.textradio.get_active(): + self.lesson['type'] = 'normal' + + steps = [] + for sb in self.stepboxes: + step = {} + + buf = sb.insttext.get_buffer() + step['instructions'] = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) + + buf = sb.texttext.get_buffer() + step['text'] = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) + + steps.append(step) + + self.lesson['steps'] = steps + + if self.balloonradio.get_active(): + self.lesson['type'] = 'balloon' + + buf = self.wordstext.get_buffer() + text = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) + self.lesson['words'] = text.split(' ') + + def add_step_clicked_cb(self, btn, index): + step = { 'instructions': '', 'text': '' } + self.lesson['steps'].insert(index, step) + self.build() + + def del_step_clicked_cb(self, btn, index): + self.lesson['steps'].pop(index) + self.build() + + def move_step_up_clicked_cb(self, btn, index): + if index > 0: + step = self.lesson['steps'].pop(index) + self.lesson['steps'].insert(index-1, step) + self.build() + + def move_step_down_clicked_cb(self, btn, index): + if index < len(self.lesson['steps']) - 1: + step = self.lesson['steps'].pop(index) + self.lesson['steps'].insert(index+1, step) + self.build() - self.show_all() diff --git a/mainscreen.py b/mainscreen.py index 988a8f9..5975ac2 100644 --- a/mainscreen.py +++ b/mainscreen.py @@ -27,7 +27,7 @@ import sugar.activity.activity from sugar.graphics import * # Import activity modules. -import lessonscreen, medalscreen, editlessonscreen +import lessonscreen, medalscreen, editlessonlistscreen import balloongame import titlescene import keyboard @@ -97,14 +97,13 @@ class MainScreen(gtk.VBox): self.keyboard_images = keyboard.KeyboardImages(width, height) self.keyboard_images.load_images() - # not yet ready - # - #editbtn = gtk.Button() - #editbtn.add(gtk.Label(_('Edit Lessons'))) - #editbtn.connect('clicked', self.edit_lessons_cb) - # - #toolbar = gtk.HBox() - #toolbar.pack_end(editbtn) + # Access lesson editor. + editbtn = gtk.Button() + editbtn.add(gtk.Label(_('Edit Lessons'))) + editbtn.connect('clicked', self.edit_lessons_cb) + + toolbar = gtk.HBox() + toolbar.pack_end(editbtn) navbox = gtk.HBox() navbox.set_spacing(10) @@ -117,7 +116,7 @@ class MainScreen(gtk.VBox): lessonbox.pack_start(navbox, False) lessonbox.pack_start(self.lessonbox) - #self.pack_start(toolbar) + self.pack_start(toolbar) self.pack_start(self.titlescene, False, True, 10) self.pack_start(lessonbox, True) @@ -256,4 +255,4 @@ class MainScreen(gtk.VBox): self.activity.push_screen(medalscreen.MedalScreen(medal, self.activity)) def edit_lessons_cb(self, widget): - self.activity.push_screen(editlessonscreen.EditLessonScreen(self.activity)) + self.activity.push_screen(editlessonlistscreen.EditLessonListScreen(self.activity, self.lessons)) |