From 9fafb49af210e956d43d6a00106558d1a00d13df Mon Sep 17 00:00:00 2001 From: Simon Poirier Date: Thu, 02 Jul 2009 05:27:27 +0000 Subject: * Modularized actions and event filters through add-on components * Working serialization * Working editor with addons * began refactoring actions and events ** fixed some tests to work with addons ** filters and actions tests won't pass until refactoring is done --- (limited to 'src/sugar/tutorius/bundler.py') diff --git a/src/sugar/tutorius/bundler.py b/src/sugar/tutorius/bundler.py index 0eb6b64..8e7fc3d 100644 --- a/src/sugar/tutorius/bundler.py +++ b/src/sugar/tutorius/bundler.py @@ -25,7 +25,7 @@ import os import uuid import xml.dom.minidom -from sugar.tutorius import gtkutils, overlayer, tutorial +from sugar.tutorius import addon from sugar.tutorius.core import Tutorial, State, FiniteStateMachine from sugar.tutorius.filters import * from sugar.tutorius.actions import * @@ -45,6 +45,7 @@ INI_NAME_PROPERTY = "NAME" INI_XML_FSM_PROPERTY = "FSM_FILENAME" INI_FILENAME = "meta.ini" TUTORIAL_FILENAME = "tutorial.xml" +NODE_COMPONENT = "Component" class TutorialStore(object): @@ -147,54 +148,35 @@ class XMLSerializer(Serializer): eventfiltersList = stateNode.appendChild(self._create_event_filters_node(state.get_event_filter_list(), doc)) return statesList - def _create_action_node(self, action, doc): + def _create_component_node(self, comp, doc): """ - Takes a single action and transforms it into a xml node. + Takes a single component (action or eventfilter) and transforms it + into a xml node. - @param action A single action + @param comp A single component @param doc The XML document root (used to create nodes only - @return A XML Node object with the Action tag name + @return A XML Node object with the component tag name """ - actionNode = doc.createElement("Action") + compNode = doc.createElement(NODE_COMPONENT) # Write down just the name of the Action class as the Class # property -- - actionNode.setAttribute("Class",type(action).__name__) - - if type(action) is DialogMessage: - actionNode.setAttribute("Message", action.message.value) - actionNode.setAttribute("PositionX", str(action.position.value[0])) - actionNode.setAttribute("PositionY", str(action.position.value[1])) - elif type(action) is BubbleMessage: - actionNode.setAttribute("Message", action.message.value) - actionNode.setAttribute("PositionX", str(action.position.value[0])) - actionNode.setAttribute("PositionY", str(action.position.value[1])) - actionNode.setAttribute("Tail_posX", str(action.tail_pos.value[0])) - actionNode.setAttribute("Tail_posY", str(action.tail_pos.value[1])) - # TO ADD : elif for each type of action - elif type(action) is WidgetIdentifyAction: - # Nothing else to save - pass - elif type(action) is ChainAction: - # Recusively write the contained actions - in the correct order - subActionsNode = self._create_action_list_node(action._actions, doc) - actionNode.appendChild(subActionsNode) - elif type(action) is DisableWidgetAction: - # Remember the target - actionNode.setAttribute("Target", action._target) - elif type(action) is TypeTextAction: - # Save the text and the widget - actionNode.setAttribute("Widget", action._widget) - actionNode.setAttribute("Text", action._text) - elif type(action) is ClickAction: - # Save the widget to click - actionNode.setAttribute("Widget", action._widget) - elif type(action) is OnceWrapper: - # Encapsulate the action in a OnceWrapper - subActionNode = self._create_action_node(action._action, doc) - actionNode.appendChild(subActionNode) - - return actionNode + compNode.setAttribute("Class",type(comp).__name__) + + # serialize all tutorius properties + for propname in comp.get_properties(): + propval = getattr(comp, propname) + if getattr(type(comp), propname).type == "addonlist": + for subval in propval: + compNode.appendChild(self._create_component_node(subval, doc)) + elif getattr(type(comp), propname).type == "addonlist": + compNode.appendChild(self._create_component_node(subval, doc)) + else: + # repr instead of str, as we want to be able to eval() it into a + # valid object. + compNode.setAttribute(propname, repr(propval)) + + return compNode def _create_action_list_node(self, action_list, doc): """ @@ -208,7 +190,7 @@ class XMLSerializer(Serializer): actionsList = doc.createElement("Actions") for action in action_list: # Create the action node - actionNode = self._create_action_node(action, doc) + actionNode = self._create_component_node(action, doc) # Append it to the list actionsList.appendChild(actionNode) @@ -220,38 +202,17 @@ class XMLSerializer(Serializer): """ eventFiltersList = doc.createElement("EventFiltersList") for event_f in event_filters: - eventFilterNode = doc.createElement("EventFilter") + eventFilterNode = self._create_component_node(event_f, doc) eventFiltersList.appendChild(eventFilterNode) - - # Write down just the name of the Action class as the Class - # property -- - eventFilterNode.setAttribute("Class", type(event_f).__name__) - - # Write the name of the next state - eventFilterNode.setAttribute("NextState", event_f.next_state) - - if type(event_f) is TimerEvent: - eventFilterNode.setAttribute("Timeout_s", str(event_f._timeout)) - - elif type(event_f) is GtkWidgetEventFilter: - eventFilterNode.setAttribute("EventName", event_f._event_name) - eventFilterNode.setAttribute("ObjectId", event_f._object_id) - - elif type(event_f) is GtkWidgetTypeFilter: - eventFilterNode.setAttribute("ObjectId", event_f._object_id) - if event_f._strokes is not None: - eventFilterNode.setAttribute("Strokes", event_f._strokes) - if event_f._text is not None: - eventFilterNode.setAttribute("Text", event_f._text) - - return eventFiltersList + + return eventFiltersList def save_fsm(self, fsm, xml_filename, path): """ Save fsm to disk, in the xml file specified by "xml_filename", in the "path" folder. If the specified file doesn't exist, it will be created. """ - doc = xml.dom.minidom.Document() + self.doc = doc = xml.dom.minidom.Document() fsm_element = doc.createElement("FSM") doc.appendChild(fsm_element) fsm_element.setAttribute("Name", fsm.name) @@ -335,84 +296,42 @@ class XMLSerializer(Serializer): @param filters_elem An XML Element representing a list of event filters """ reformed_event_filters_list = [] - event_filter_element_list = filters_elem.getElementsByTagName("EventFilter") + event_filter_element_list = filters_elem.getElementsByTagName(NODE_COMPONENT) new_event_filter = None for event_filter in event_filter_element_list: - # Load the name of the next state for this filter - next_state = event_filter.getAttribute("NextState") - - if event_filter.getAttribute("Class") == str(TimerEvent): - timeout = int(event_filter.getAttribute("Timeout_s")) - new_event_filter = TimerEvent(next_state, timeout) - - elif event_filter.getAttribute("Class") == str(GtkWidgetEventFilter): - # Get the event name and the object's ID - event_name = event_filter.getAttribute("EventName") - object_id = event_filter.getAttribute("ObjectId") - - new_event_filter = GtkWidgetEventFilter(next_state, object_id, event_name) - elif event_filter.getAttribute("Class") == str(GtkWidgetTypeFilter): - # Get the widget to write in and the text - object_id = event_filter.getAttribute("ObjectId") - if event_filter.hasAttribute("Text"): - text = event_filter.getAttribute("Text") - new_event_filter = GtkWidgetTypeFilter(next_state, object_id, text=text) - elif event_filter.hasAttribute("Strokes"): - strokes = event_filter.getAttribute("Strokes") - new_event_filter = GtkWidgetTypeFilter(next_state, object_id, strokes=strokes) + new_event_filter = self._load_xml_component(event_filter) if new_event_filter is not None: reformed_event_filters_list.append(new_event_filter) return reformed_event_filters_list - def _load_xml_action(self, action): + def _load_xml_component(self, node): """ - Loads a single action from an Xml Action node. + Loads a single addon component instance from an Xml node. - @param action The Action XML Node to transform + @param node The component XML Node to transform object - @return The Action object of the correct type according to the XML + @return The addon component object of the correct type according to the XML description """ - # TO ADD: an elif for each type of action - if action.getAttribute("Class") == 'DialogMessage': - message = action.getAttribute("Message") - positionX = int(action.getAttribute("PositionX")) - positionY = int(action.getAttribute("PositionY")) - position = [positionX, positionY] - return DialogMessage(message,position) - elif action.getAttribute("Class") == 'BubbleMessage': - message = action.getAttribute("Message") - positionX = int(action.getAttribute("PositionX")) - positionY = int(action.getAttribute("PositionY")) - position = [positionX, positionY] - tail_posX = int(action.getAttribute("Tail_posX")) - tail_posY = int(action.getAttribute("Tail_posY")) - tail_pos = [tail_posX, tail_posY] - return BubbleMessage(message,position,None,tail_pos) - elif action.getAttribute("Class") == 'WidgetIdentifyAction': - return WidgetIdentifyAction() - elif action.getAttribute("Class") == 'ChainAction': - # Load the subactions - subActionsList = self._load_xml_actions(action.getElementsByTagName("Actions")[0]) - return ChainAction(subActionsList) - elif action.getAttribute("Class") == 'DisableWidgetAction': - # Get the target - targetName = action.getAttribute("Target") - return DisableWidgetAction(targetName) - elif action.getAttribute("Class") == 'TypeTextAction': - # Get the widget and the text to type - widget = action.getAttribute("Widget") - text = action.getAttribute("Text") - - return TypeTextAction(widget, text) - elif action.getAttribute("Class") == 'ClickAction': - # Load the widget to click - widget = action.getAttribute("Widget") - - return ClickAction(widget) + new_action = addon.create(node.getAttribute("Class")) + if not new_action: + return None + + for attrib in node.attributes.keys(): + if attrib == "Class": continue + # security note: keep sandboxed + setattr(new_action, attrib, eval(node.getAttribute(attrib), {}, {})) + + # recreate complex attributes + for sub in node.childNodes: + name = getattr(new_action, sub.nodeName) + if name == "addon": + setattr(new_action, sub.getAttribute("Name"), self._load_xml_action(sub)) + + return new_action def _load_xml_actions(self, actions_elem): """ @@ -421,10 +340,10 @@ class XMLSerializer(Serializer): @param actions_elem An XML Element representing a list of Actions """ reformed_actions_list = [] - actions_element_list = actions_elem.getElementsByTagName("Action") + actions_element_list = actions_elem.getElementsByTagName(NODE_COMPONENT) for action in actions_element_list: - new_action = self._load_xml_action(action) + new_action = self._load_xml_component(action) reformed_actions_list.append(new_action) @@ -451,7 +370,7 @@ class XMLSerializer(Serializer): reformed_state_list.append(State(stateName, actions_list, event_filters_list)) return reformed_state_list - + def _load_xml_fsm(self, fsm_elem): """ Takes in an XML element representing an FSM and returns the fully -- cgit v0.9.1