From 857aff7e1c1694a819c0dbc9b9103ef2206699e6 Mon Sep 17 00:00:00 2001 From: Simon Poirier Date: Sun, 15 Nov 2009 22:18:25 +0000 Subject: creator adt migration --- (limited to 'tutorius') diff --git a/tutorius/creator.py b/tutorius/creator.py index c477056..f59f320 100644 --- a/tutorius/creator.py +++ b/tutorius/creator.py @@ -26,14 +26,15 @@ import gtk.glade import gobject from gettext import gettext as T +import uuid import os -from sugar.graphics import icon -import copy +from sugar.graphics import icon, style from . import overlayer, gtkutils, actions, vault, properties, addon from . import filters from .services import ObjectStore -from .core import Tutorial, FiniteStateMachine, State +from .core import State +from .tutorial import Tutorial from . import viewer class Creator(object): @@ -50,10 +51,11 @@ class Creator(object): """ self._activity = activity if not tutorial: - self._tutorial = FiniteStateMachine('Untitled') - self._state = State(name='INIT') - self._tutorial.add_state(self._state) - self._state_counter = 1 + self._tutorial = Tutorial('Untitled') + self._state = self._tutorial.add_state() + self._tutorial.update_transition( + transition_name=self._tutorial.INITIAL_TRANSITION_NAME, + new_state=self._state) else: self._tutorial = tutorial # TODO load existing tutorial; unused yet @@ -110,30 +112,17 @@ class Creator(object): """ Removes the first instance of specified action from the tutorial. - @param action: the action object to remove from the tutorial + @param action: the action name @returns: True if successful, otherwise False. """ - state = self._tutorial.get_state_by_name("INIT") - - while True: - state_actions = state.get_action_list() - for fsm_action in state_actions: - if fsm_action is action: - state.clear_actions() - if state is self._state: - fsm_action.exit_editmode() - state_actions.remove(fsm_action) - self.set_insertion_point(state.name) - for keep_action in state_actions: - state.add_action(keep_action) - return True - - ev_list = state.get_event_filter_list() - if ev_list: - state = self._tutorial.get_state_by_name(ev_list[0][1]) - continue - + action_obj = self._tutorial.get_action_dict(self._state)\ + .get(action, None) + if not action_obj: return False + action_obj.exit_editmode() + self._tutorial.delete_action(action) + self._overview.win.queue_draw() + return True def delete_state(self): """ @@ -143,49 +132,25 @@ class Creator(object): @returns: True if successful, otherwise False. """ - if not self._state.get_event_filter_list(): + if self._state in (self._tutorial.INIT, self._tutorial.END): # last state cannot be removed return False - state = self._tutorial.get_state_by_name("INIT") - ev_list = state.get_event_filter_list() - if state is self._state: - next_state = self._tutorial.get_state_by_name(ev_list[0][1]) - self.set_insertion_point(next_state.name) - self._tutorial.remove_state(state.name) - self._tutorial.remove_state(next_state.name) - next_state.name = "INIT" - self._tutorial.add_state(next_state) - return True - - # loop to repair links from deleted state - while ev_list: - next_state = self._tutorial.get_state_by_name(ev_list[0][1]) - if next_state is self._state: - # the tutorial will flush the event filters. We'll need to - # clear and re-add them. - self._tutorial.remove_state(self._state.name) - state.clear_event_filters() - self._update_next_state(state, ev_list[0][0], next_state.get_event_filter_list()[0][1]) - for ev, next_state in ev_list: - state.add_event_filter(ev, next_state) - - self.set_insertion_point(ev_list[0][1]) - return True - - state = next_state - ev_list = state.get_event_filter_list() - return False + remove_state = self._state + next_state = self._tutorial\ + .get_following_states_dict(remove_state).keys()[0] + self.set_insertion_point(next_state) + return bool(self._tutorial.delete_state(remove_state)) def get_insertion_point(self): - return self._state.name + return self._state def set_insertion_point(self, state_name): - for action in self._state.get_action_list(): + for action in self._tutorial.get_action_dict(self._state).values(): action.exit_editmode() - self._state = self._tutorial.get_state_by_name(state_name) - self._overview.win.queue_draw() - state_actions = self._state.get_action_list() + + self._state = state_name + state_actions = self._tutorial.get_action_dict(self._state).values() for action in state_actions: action.enter_editmode() action._drag._eventbox.connect_after( @@ -196,6 +161,8 @@ class Creator(object): else: self._propedit.action = None + self._overview.win.queue_draw() + def _evfilt_cb(self, menuitem, event): """ @@ -249,7 +216,7 @@ class Creator(object): action_type = self._propedit.actions_list[path][ToolBox.ICON_NAME] action = addon.create(action_type) action.enter_editmode() - self._state.add_action(action) + self._tutorial.add_action(self._state, action) # FIXME: replace following with event catching action._drag._eventbox.connect_after( "button-release-event", self._action_refresh_cb, action) @@ -283,31 +250,24 @@ class Creator(object): else: raise NotImplementedError() - event_filters = self._state.get_event_filter_list() + event_filters = self._tutorial.get_transition_dict(self._state) + + # if not at the end of tutorial if event_filters: - # linearize tutorial by inserting state - new_state = State(name=str(self._state_counter)) - self._state_counter += 1 - self._state.clear_event_filters() - for evt_filt, next_state in event_filters: - new_state.add_event_filter(evt_filt, next_state) - self._update_next_state(self._state, event, new_state.name) - next_state = new_state.name - # blocks are shifted, full redraw is necessary - self._overview.win.queue_draw() + old_transition = event_filters.keys()[0] + new_state = self._tutorial.add_state(event_filters[old_transition]) + self._tutorial.update_transition(transition_name=old_transition, + new_state=new_state) + else: # append empty state only if edit inserting at end of linearized # tutorial. - self._update_next_state(self._state, event, str(self._state_counter)) - next_state = str(self._state_counter) - new_state = State(name=str(self._state_counter)) - self._state_counter += 1 + new_state = self._tutorial.add_state() + self._tutorial.add_transition(self._state, (event, new_state)) - self._state.add_event_filter(event, next_state) - self._tutorial.add_state(new_state) self._overview.win.queue_draw() - self.set_insertion_point(new_state.name) + self.set_insertion_point(new_state) def _action_refresh_cb(self, widget, evt, action): """ @@ -324,24 +284,27 @@ class Creator(object): self._overview.win.queue_draw() - def _cleanup_cb(self, *args): + def _cleanup_cb(self, *args, **kwargs): """ Quit editing and cleanup interface artifacts. + + @param force: force quitting without saving. """ # undo actions so they don't persist through step editing - for action in self._state.get_action_list(): + for action in self._tutorial.get_action_dict(self._state).values(): action.exit_editmode() - dialog = gtk.MessageDialog( - parent=self._activity, - flags=gtk.DIALOG_MODAL, - type=gtk.MESSAGE_QUESTION, - buttons=gtk.BUTTONS_YES_NO, - message_format=T('Do you want to save before stopping edition?')) - do_save = dialog.run() - dialog.destroy() - if do_save == gtk.RESPONSE_YES: - self.save() + if kwargs.get(force, False): + dialog = gtk.MessageDialog( + parent=self._activity, + flags=gtk.DIALOG_MODAL, + type=gtk.MESSAGE_QUESTION, + buttons=gtk.BUTTONS_YES_NO, + message_format=T('Do you want to save before stopping edition?')) + do_save = dialog.run() + dialog.destroy() + if do_save == gtk.RESPONSE_YES: + self.save() # remove UI remains self._hlmask.covered = None @@ -354,20 +317,24 @@ class Creator(object): del self._activity._creator def save(self, widget=None): - if not self.tuto: - dlg = TextInputDialog(self._activity, + if not self._guid: + self._guid = str(uuid.uuid1()) + dlg = TextInputDialog(parent=self._overview.win, text=T("Enter a tutorial title."), field=T("Title")) tutorialName = "" while not tutorialName: tutorialName = dlg.pop() dlg.destroy() - - # prepare tutorial for serialization - self.tuto = Tutorial(tutorialName, self._tutorial) - bundle = vault.TutorialBundler(self._guid) - self._guid = bundle.Guid - bundle.write_metadata_file(self.tuto) - bundle.write_fsm(self._tutorial) + self._metadata = { + vault.INI_GUID_PROPERTY: self._guid, + vault.INI_NAME_PROPERTY: tutorialName, + vault.INI_VERSION_PROPERTY: '1', + 'activities':{os.environ['SUGAR_BUNDLE_NAME']: + os.environ['SUGAR_BUNDLE_VERSION'] + }, + } + + vault.Vault.saveTutorial(self._tutorial, self._metadata) def launch(*args, **kwargs): diff --git a/tutorius/viewer.py b/tutorius/viewer.py index 272558e..56428e1 100644 --- a/tutorius/viewer.py +++ b/tutorius/viewer.py @@ -18,9 +18,7 @@ This module renders a widget containing a graphical representation of a tutorial and acts as a creator proxy as it has some editing functionality. """ -import sys - -import gtk, gtk.gdk +import gtk import cairo from math import pi as PI PI2 = PI/2 @@ -30,7 +28,6 @@ import rsvg from sugar.bundle import activitybundle from sugar.tutorius import addon from sugar.graphics import icon -from sugar.tutorius.filters import EventFilter from sugar.tutorius.actions import Action import os @@ -66,7 +63,7 @@ class Viewer(object): self.alloc = None self.click_pos = None self.drag_pos = None - self.selection = [] + self.selection = set() self.win = gtk.Window(gtk.WINDOW_TOPLEVEL) self.win.set_size_request(400, 200) @@ -81,7 +78,7 @@ class Viewer(object): canvas = gtk.DrawingArea() vbox.add_with_viewport(canvas) canvas.set_app_paintable(True) - canvas.connect_after("expose-event", self.on_viewer_expose, tutorial._states) + canvas.connect_after("expose-event", self.on_viewer_expose, tutorial) canvas.add_events(gtk.gdk.BUTTON_PRESS_MASK \ |gtk.gdk.BUTTON_MOTION_MASK \ |gtk.gdk.BUTTON_RELEASE_MASK \ @@ -99,10 +96,13 @@ class Viewer(object): canvas.set_size_request(2048, 180) # FIXME def destroy(self): + """ + Destroy ui resources associated with this object. + """ self.win.destroy() - def _paint_state(self, ctx, states): + def _paint_state(self, ctx, tutorial): """ Paints a tutorius fsm state in a cairo context. Final context state will be shifted by the size of the graphics. @@ -111,17 +111,13 @@ class Viewer(object): block_max_height = self.alloc.height new_insert_point = None - cur_state = 'INIT' + state_name = tutorial.INIT # FIXME: get app when we have a model that supports it cur_app = 'Calculate' app_start = ctx.get_matrix() - try: - state = states[cur_state] - except KeyError: - state = None - while state: + while state_name: new_app = 'Calculate' if new_app != cur_app: ctx.save() @@ -132,16 +128,17 @@ class Viewer(object): ctx.translate(BLOCK_PADDING, 0) cur_app = new_app - action_list = state.get_action_list() + action_list = tutorial.get_action_dict(state_name).items() if action_list: - local_height = (block_max_height - BLOCK_PADDING)/len(action_list) - BLOCK_PADDING + local_height = (block_max_height - BLOCK_PADDING)\ + / len(action_list) - BLOCK_PADDING ctx.save() - for action in action_list: + for action_name, action in action_list: origin = tuple(ctx.get_matrix())[-2:] if self.click_pos and \ self.click_pos[0]-BLOCK_WIDTHorigin[0]: - self.selection.append(action) + self.selection.add((action_name, action)) self.render_action(ctx, block_width, local_height, action) ctx.translate(0, local_height+BLOCK_PADDING) @@ -150,7 +147,7 @@ class Viewer(object): # insertion cursor painting made from two opposed triangles # joined by a line. - if state.name == self._creator.get_insertion_point(): + if state_name == self._creator.get_insertion_point(): ctx.save() bp2 = BLOCK_PADDING/2 ctx.move_to(-bp2, 0) @@ -170,36 +167,34 @@ class Viewer(object): ctx.restore() - event_list = state.get_event_filter_list() + event_list = tutorial.get_transition_dict(state_name).items() if event_list: - local_height = (block_max_height - BLOCK_PADDING)/len(event_list) - BLOCK_PADDING + local_height = (block_max_height - BLOCK_PADDING)\ + /len(event_list) - BLOCK_PADDING ctx.save() - for event, next_state in event_list: + for transition_name, transition in event_list: origin = tuple(ctx.get_matrix())[-2:] if self.click_pos and \ self.click_pos[0]-BLOCK_WIDTHorigin[0]: - self.selection.append(event) - self.render_event(ctx, block_width, local_height, event) + self.selection.add((transition_name, transition)) + self.render_event(ctx, block_width, local_height, + event=transition[0]) ctx.translate(0, local_height+BLOCK_PADDING) ctx.restore() ctx.translate(BLOCK_WIDTH, 0) - # FIXME point to next state in state, as it would highlight - # the "happy path". - cur_state = event_list[0][1] - if (not new_insert_point) and self.click_pos: origin = tuple(ctx.get_matrix())[-2:] if self.click_pos[0]