Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormike <michael.jmontcalm@gmail.com>2009-03-01 04:40:35 (GMT)
committer mike <michael.jmontcalm@gmail.com>2009-03-01 04:40:35 (GMT)
commita658a240eb0652332300db1001fc375a39692213 (patch)
tree7538175a5a0fe10c33dfaa9bce6a790eb4ab3abb
parent9936040ffc0fdf65d29757efc56135d172741eb6 (diff)
TutoriusV1 : Adding omitted core.py (git clumsiness, sorry!)
-rw-r--r--src/sugar/tutorius/core.py242
1 files changed, 171 insertions, 71 deletions
diff --git a/src/sugar/tutorius/core.py b/src/sugar/tutorius/core.py
index 9d0cba2..e41682e 100644
--- a/src/sugar/tutorius/core.py
+++ b/src/sugar/tutorius/core.py
@@ -35,11 +35,15 @@ class Tutorial (object):
"""
def __init__(self, name, fsm):
- """Create an unattached tutorial
+ """
+ Creates an unattached tutorial.
"""
object.__init__(self)
self.name = name
+
self.state_machine = fsm
+ self.state_machine.set_tutorial(self)
+
self.state = None
self.handlers = []
@@ -47,59 +51,71 @@ class Tutorial (object):
#Rest of initialisation happens when attached
def attach(self, activity):
- """Attach to a running activity
+ """
+ Attach to a running activity
+
@param activity the activity to attach to
"""
#For now, absolutely detach if a previous one!
if self.activity:
self.detach()
self.activity = activity
- self.set_state("INIT")
+ self.state_machine.set_state("INIT")
def detach(self):
- """Detach from the current activity"""
- #Remove handlers
- for eventfilter in self.state_machine.get(self.state,{}).get("EventFilters",()):
- eventfilter.remove_handlers()
-
- #Undo actions
- for act in self.state_machine.get(self.state,{}).get("Actions",()):
- act.undo()
+ """
+ Detach from the current activity
+ """
+## #Remove handlers
+## for eventfilter in self.state_machine.get(self.state,{}).get("EventFilters",()):
+## eventfilter.remove_handlers()
+##
+## #Undo actions
+## for act in self.state_machine.get(self.state,{}).get("Actions",()):
+## act.undo()
+ # Uninstall the whole FSM
+ self.state_machine.teardown()
#FIXME There should be some amount of resetting done here...
self.activity = None
def set_state(self, name):
- """Switch to a new state"""
- if not self.state_machine.has_key(name):
- return
+ """
+ Switch to a new state
+ """
+## if not self.state_machine.has_key(name):
+## return
logger.debug("====NEW STATE: %s====" % name)
- #Remove handlers
- for eventfilter in self.state_machine.get(self.state,{}).get("EventFilters",()):
- eventfilter.remove_handlers()
-
- #Undo actions
- for act in self.state_machine.get(self.state,{}).get("Actions",()):
- act.undo()
-
- #Switch to new state
- self.state = name
- newstate = self.state_machine.get(name)
+## #Remove handlers
+## for eventfilter in self.state_machine.get(self.state,{}).get("EventFilters",()):
+## eventfilter.remove_handlers()
+
+## #Undo actions
+## for act in self.state_machine.get(self.state,{}).get("Actions",()):
+## act.undo()
+ self.state_machine.set_state(name)
+
+## #Switch to new state
+## self.state = name
+## newstate = self.state_machine.get(name)
- #Register handlers for eventfilters
- for eventfilter in newstate["EventFilters"]:
- eventfilter.install_handlers(self._eventfilter_state_done,
- activity=self.activity)
+## #Register handlers for eventfilters
+## for eventfilter in newstate["EventFilters"]:
+## eventfilter.install_handlers(self._eventfilter_state_done,
+## activity=self.activity)
- #Do actions
- for act in newstate.get("Actions",()):
- act.do()
+## #Do actions
+## for act in newstate.get("Actions",()):
+## act.do()
+ # Currently unused -- equivalent function is in each state
def _eventfilter_state_done(self, eventfilter):
- """Callback handler for eventfilter to notify
- when we must go to the next state."""
+ """
+ Callback handler for eventfilter to notify
+ when we must go to the next state.
+ """
#XXX Tests should be run here normally
#Swith to the next state pointed by the eventfilter
@@ -121,19 +137,24 @@ class Tutorial (object):
# )
class State:
- """This is a step in a tutorial. The state represents a collection of
- actions to undertake when entering the state, and a series of event filters
- with associated actions that point to a possible next state."""
+ """
+ This is a step in a tutorial. The state represents a collection of actions
+ to undertake when entering the state, and a series of event filters
+ with associated actions that point to a possible next state.
+ """
- def __init__(self, action_list=[], event_filter_list=[]):
- """Initializes the content of the state, as in loading the actions
+ def __init__(self, name, action_list=[], event_filter_list=[], tutorial=None):
+ """
+ Initializes the content of the state, like loading the actions
that are required and building the correct tests.
@param action_list The list of actions to execute when entering this
state
@param event_filter_list A list of tuples of the form
(event_filter, next_state_name), that explains the outgoing links for
- this state"""
+ this state
+ @param tutorial The higher level container of the state
+ """
self._actions = action_list
# Unused for now
@@ -141,18 +162,60 @@ class State:
self._event_filters = event_filter_list
+ self.tutorial = tutorial
+
+ def set_tutorial(self, tutorial):
+ """
+ Associates this state with a tutorial. A tutorial must be set prior
+ to executing anything in the state. The reason for this is that the
+ states need to have access to the activity (via the tutorial) in order
+ to properly register their callbacks on the activities' widgets.
+
+ @param tutorial The tutorial that this state runs under.
+ """
+ if self.tutorial == None :
+ self.tutorial = tutorial
+ else:
+ raise RuntimeWarning(\
+ "The state %s was already associated with a tutorial." % self.name)
def setup(self):
- """Install the state itself. This is the best time to pop-up a dialog
- that has to remain for the duration of the state."""
+ """
+ Install the state itself, by first registering the event filters
+ and then triggering the actions.
+ """
+ for eventfilter in self._event_filters:
+ eventfilter.install_handlers(self._event_filter_state_done_cb,
+ activity=self.tutorial.activity)
+
for action in self._actions:
- act.do()
+ action.do()
def teardown(self):
- """Undo every action that was installed for this state. This means
- removing dialogs that were displayed, removing highlights, etc..."""
- for act in self._actions:
- act.undo()
+ """
+ Uninstall all the event filters that were active in this state.
+ Also undo every action that was installed for this state. This means
+ removing dialogs that were displayed, removing highlights, etc...
+ """
+ # Remove the handlers for the all of the state's event filters
+ for event_filter in self._event_filters:
+ event_filter.remove_handlers()
+
+ # Undo all the actions related to this state
+ for action in self._actions:
+ action.undo()
+
+ def _event_filter_state_done_cb(self, event_filter):
+ """
+ Callback for event filters. This function needs to inform the
+ tutorial that the state is over and tell it what is the next state.
+
+ @param event_filter The event filter that was called
+ """
+ # Run the tests here, if need be
+
+ # Warn the higher level that we wish to change state
+ self.tutorial.set_state(event_filter.get_next_state())
# Unused for now
## def verify(self):
@@ -168,38 +231,50 @@ class State:
## # a timer?
class FiniteStateMachine(State):
- """This is a collection of states, with a start state and an end callback.
+ """
+ This is a collection of states, with a start state and an end callback.
It is used to simplify the development of the various tutorials by
encapsulating a collection of states that represent a given learning
- process."""
+ process.
+
+ For now, we will consider that there can only be states
+ inserted in the FSM, and that there are no nested FSM inside.
+ """
- def __init__(self, state_dict={}, start_state_name="INIT", setup_actions=[]):
- """The constructor for a FSM. Pass in the start state and the setup
+ def __init__(self, name, tutorial=None, state_dict={}, start_state_name="INIT", action_list=[]):
+ """
+ The constructor for a FSM. Pass in the start state and the setup
actions that need to be taken when the FSM itself start (which may be
different from what is done in the first state of the machine).
+ @param name A short descriptive name for this FSM
+ @param tutorial The tutorial that will execute this FSM. If None is
+ attached on creation, then one must absolutely be attached before
+ executing the FSM with set_tutorial().
@param state_dict A dictionary containing the state names as keys and
the state themselves as entries.
@param start_state_name The name of the starting state, if different
from "INIT"
- @param setup_actions The actions to undertake when initializing the FSM
+ @param action_list The actions to undertake when initializing the FSM
"""
- State.__init__(self)
-
+ State.__init__(self, name)
+
+ self.name = name
+ self.tutorial = tutorial
+
# Dictionnary of states contained in the FSM
self._states = state_dict
# Remember the initial state - we might want to reset
# or rewind the FSM at a later moment
- self.start_state = state_dict[start_state]
+ self.start_state = state_dict[start_state_name]
+ self.current_state = self.start_state
# Register the actions for the FSM - They will be processed at the
# FSM level, meaning that when the FSM will start, it will first
# execute those actions. When the FSM closes, it will tear down the
# inner actions of the state, then close its own actions
- self.actions = setup_actions
-
- self.current_state = self.start_state
+ self.actions = action_list
# Flag to mention that the FSM was initialized
self._fsm_setup_done = False
@@ -207,20 +282,43 @@ class FiniteStateMachine(State):
self._fsm_teardown_done = False
# Flag used to declare that the FSM has reached an end state
self._fsm_has_finished = False
+
+ def set_tutorial(self, tutorial):
+ """
+ This associates the FSM to the given tutorial. It MUST be associated
+ either in the constructor or with this function prior to executing the
+ FSM.
-
+ @param tutorial The tutorial that will execute this FSM.
+ """
+ # If there was no tutorial associated
+ if self.tutorial == None:
+ # Associate it with this FSM and all the underlying states
+ self.tutorial = tutorial
+ for state in self._states.itervalues():
+ state.set_tutorial(tutorial)
+ else:
+ raise RuntimeWarning(\
+ "The FSM %s is already associated with a tutorial."%self.name\
+ )
+
def setup(self):
"""
- Set up the FSM the first time, then setup the inner state
+ This function initializes the FSM the first time it is called.
+ Then, every time it is called, it initializes the current state.
"""
+ # Are we associated with a tutorial?
+ if self.tutorial == None:
+ raise UnboundLocalError("No tutorial was associated with FSM %s" % self.name)
+
# If we never initialized the FSM itself, then we need to run all the
# actions associated with the FSM.
if self._fsm_setup_done == False:
# Flag the FSM level setup as done
self._fsm_setup_done = True
# Execute all the FSM level actions
- for act in self.actions:
- act.do()
+ for action in self.actions:
+ action.do()
# Then, we need to run the setup of the current state
self.current_state.setup()
@@ -231,12 +329,14 @@ class FiniteStateMachine(State):
@param new_state The identifier of the state we need to go to
"""
- # Pass in the name to the internal state - it might be a FSM and
- # this name will apply to it
- self.current_state.set_state(new_state_name)
+ # TODO : Since we assume no nested FSMs, we don't set state on the
+ # inner States / FSMs
+## # Pass in the name to the internal state - it might be a FSM and
+## # this name will apply to it
+## self.current_state.set_state(new_state_name)
# Make sure the given state is owned by the FSM
- if not self._states.haskey(new_state_name):
+ if not self._states.has_key(new_state_name):
# If we did not recognize the name, then we do not possess any
# state by that name - we must ignore this state change request as
# it will be done elsewhere in the hierarchy (or it's just bogus).
@@ -245,13 +345,13 @@ class FiniteStateMachine(State):
new_state = self._states[new_state_name]
# Undo the actions of the old state
- self.current_state.teardown()
+ self.teardown()
# Insert the new state
self.current_state = new_state
# Call the initial actions in the new state
- self.current_state.setup()
+ self.setup()
def teardown(self):
@@ -265,10 +365,10 @@ class FiniteStateMachine(State):
# on the FSM level actions
if self._fsm_has_finished == True:
# Flag the FSM teardown as not needed anymore
- self._fsm_teardown_done = False
+ self._fsm_teardown_done = True
# Undo all the FSM level actions here
- for act in self.actions:
- act.undo()
+ for action in self.actions:
+ action.undo()
#Unused for now
## def verify(self):