def __init__(self, parent, meta=None): MetaModel.__init__(self, meta) self.parent = parent self.destruction_signal = Signal() # this class is an observer of its own properties: self.register_observer(self)
def __init__(self, parent_signal=None): ModelMT.__init__(self) self._selected = set() self._input_data_ports = set() self._output_data_ports = set() self._outcomes = set() self._data_flows = set() self._transitions = set() self._states = set() self._scoped_variables = set() self.selection_changed_signal = Signal() self.focus_signal = Signal() self.parent_signal = parent_signal
def __init__(self, meta=None): ModelMT.__init__(self) if isinstance(meta, dict): self.meta = Vividict(meta) else: self.meta = Vividict() self.temp = Vividict() self.meta_signal = Signal()
def __init__(self, state, parent=None, meta=None): if type(self) == AbstractStateModel: raise NotImplementedError MetaModel.__init__(self, meta) assert isinstance(state, State) self.is_start = False self.state = state self.parent = parent self.meta_signal = Signal() self.action_signal = Signal() self.destruction_signal = Signal() self.register_observer(self) self.input_data_ports = [] self.output_data_ports = [] self.outcomes = [] self._load_input_data_port_models() self._load_output_data_port_models() self._load_outcome_models()
class StateElementModel(MetaModel, Hashable): """This model class serves as base class for all models within a state model (ports, connections) Each state element model has a parent, meta and temp data. If observes itself and informs the parent about changes. :param rafcon.gui.models.abstract_state.AbstractStateModel parent: The state model of the state element :param rafcon.utils.vividict.Vividict meta: The meta data of the state element model """ _parent = None meta_signal = Signal() destruction_signal = Signal() __observables__ = ("meta_signal", "destruction_signal") def __init__(self, parent, meta=None): MetaModel.__init__(self, meta) self.parent = parent self.destruction_signal = Signal() # this class is an observer of its own properties: self.register_observer(self) def __eq__(self, other): if type(self) != type(other): return False if self.core_element != other.core_element: return False if self.meta != other.meta: return False return True def __ne__(self, other): return not self.__eq__(other) def __cmp__(self, other): if isinstance(other, StateElementModel): return self.core_element.__cmp__(other.core_element) def update_hash(self, obj_hash): self.update_hash_from_dict(obj_hash, self.core_element) if self.parent and not self.parent.state.get_library_root_state(): self.update_hash_from_dict(obj_hash, self.meta) @property def parent(self): """Getter for the parent state model of the state element :return: None if parent is not defined, else the model of the parent state :rtype: rafcon.gui.models.abstract_state.AbstractState """ if not self._parent: return None return self._parent() @parent.setter def parent(self, parent_m): """Setter for the parent state model of the state element :param rafcon.gui.models.abstract_state.AbstractState parent_m: Parent state model or None """ if isinstance(parent_m, AbstractStateModel): self._parent = ref(parent_m) else: self._parent = None @property def core_element(self): """Return the core element represented by this model :return: core element of the model :rtype: rafcon.core.state_elements.state_element.StateElement """ raise NotImplementedError() def get_state_machine_m(self): if self.parent: return self.parent.get_state_machine_m() return None def prepare_destruction(self): """Prepares the model for destruction Unregisters the model from observing itself. """ if self.core_element is None: logger.verbose( "Multiple calls of prepare destruction for {0}".format(self)) self.destruction_signal.emit() try: self.unregister_observer(self) except KeyError: # Might happen if the observer was already unregistered pass def model_changed(self, model, prop_name, info): """This method notifies the parent state about changes made to the state element """ if self.parent is not None: self.parent.model_changed(model, prop_name, info) @ModelMT.observe("meta_signal", signal=True) def meta_changed(self, model, prop_name, info): """This method notifies the parent state about changes made to the meta data """ if self.parent is not None: msg = info.arg # Add information about notification to the signal message notification = Notification(model, prop_name, info) msg = msg._replace(notification=notification) info.arg = msg self.parent.meta_changed(model, prop_name, info)
class Selection(ModelMT): """ This class contains the selected models of a state_machine """ _selected = None _input_data_ports = None _output_data_ports = None _scoped_variables = None _outcomes = None _data_flows = None _transitions = None _states = None _focus = None selection_changed_signal = Signal() focus_signal = Signal() __observables__ = ("selection_changed_signal", "focus_signal") def __init__(self, parent_signal=None): ModelMT.__init__(self) self._selected = set() self._input_data_ports = set() self._output_data_ports = set() self._outcomes = set() self._data_flows = set() self._transitions = set() self._states = set() self._scoped_variables = set() self.selection_changed_signal = Signal() self.focus_signal = Signal() self.parent_signal = parent_signal def __str__(self): return_string = "Selected: " for item in self._selected: return_string = "%s, %s" % (return_string, str(item)) return return_string def _check_model_types(self, models): """ Check types of passed models for correctness and in case raise exception :rtype: set :returns: set of models that are valid for the class""" if not hasattr(models, "__iter__"): models = {models} if not all([ isinstance(model, (AbstractStateModel, StateElementModel)) for model in models ]): raise TypeError( "The selection supports only models with base class AbstractStateModel or " "StateElementModel, see handed elements {0}".format(models)) return models if isinstance(models, set) else set(models) @updates_selection def add(self, models): """ Adds the passed model(s) to the selection""" if models is None: return models = self._check_model_types(models) self._selected.update(models) self._selected = reduce_to_parent_states(self._selected) @updates_selection def remove(self, models): """ Removed the passed model(s) from the selection""" models = self._check_model_types(models) for model in models: if model in self._selected: self._selected.remove(model) @updates_selection def set(self, models): """ Sets the selection to the passed model(s) """ # Do not add None values to selection if models is None: models = set() models = self._check_model_types(models) if len(models) > 1: models = reduce_to_parent_states(models) self._selected = set(models) @updates_selection def clear(self): """ Removes all models from the selection """ self._selected.clear() @updates_selection def handle_prepared_selection_of_core_class_elements( self, core_class, models): """Handles the selection for TreeStore widgets maintaining lists of a specific `core_class` elements If widgets hold a TreeStore with elements of a specific `core_class`, the local selection of that element type is handled by that widget. This method is called to integrate the local selection with the overall selection of the state machine. If no modifier key (indicating to extend the selection) is pressed, the state machine selection is set to the passed selection. If the selection is to be extended, the state machine collection will consist of the widget selection plus all previously selected elements not having the core class `core_class`. :param State | StateElement core_class: The core class of the elements the widget handles :param models: The list of models that are currently being selected locally """ if extend_selection(): self._selected.difference_update( self.get_selected_elements_of_core_class(core_class)) else: self._selected.clear() models = self._check_model_types(models) if len(models) > 1: models = reduce_to_parent_states(models) self._selected.update(models) @updates_selection def handle_new_selection(self, models): """Handles the selection for generic widgets This is a helper method for generic widgets that want to modify the selection. These widgets can pass a list of newly selected (or clicked on) models. The method looks at the previous selection, the passed models and the lift of pressed (modifier) keys: * If no modifier key is pressed, the previous selection is cleared and the new selection is set to the passed models * If the extend-selection modifier key is pressed, elements of `models` that are _not_ in the previous selection are selected, those that are in the previous selection are deselected :param models: The list of models that are newly selected/clicked on """ models = self._check_model_types(models) if extend_selection(): already_selected_elements = models & self._selected newly_selected_elements = models - self._selected self._selected.difference_update(already_selected_elements) self._selected.update(newly_selected_elements) else: self._selected = models self._selected = reduce_to_parent_states(self._selected) @property def focus(self): """ Returns the currently focused element """ return self._focus @focus.setter def focus(self, model): """Sets the passed model as focused element :param ModelMT model: The element to be focused """ if model is None: del self.focus return self._check_model_types(model) self.add(model) focus_msg = FocusSignalMsg(model, self._focus) self._focus = model self._selected.add(model) self._selected = reduce_to_parent_states(self._selected) self.focus_signal.emit(focus_msg) @focus.deleter def focus(self): """ Unsets the focused element """ focus_msg = FocusSignalMsg(None, self._focus) self._focus = None self.focus_signal.emit(focus_msg) def __iter__(self): return self._selected.__iter__() def __len__(self): return len(self._selected) def __contains__(self, item): return item in self._selected def __getitem__(self, key): return [s for s in self._selected][key] def update_core_element_lists(self): """ Maintains inner lists of selected elements with a specific core element class """ def get_selected_elements_of_core_class(core_class): return set(element for element in self._selected if isinstance(element.core_element, core_class)) self._states = get_selected_elements_of_core_class(State) self._transitions = get_selected_elements_of_core_class(Transition) self._data_flows = get_selected_elements_of_core_class(DataFlow) self._input_data_ports = get_selected_elements_of_core_class( InputDataPort) self._output_data_ports = get_selected_elements_of_core_class( OutputDataPort) self._scoped_variables = get_selected_elements_of_core_class( ScopedVariable) self._outcomes = get_selected_elements_of_core_class(Outcome) @property def states(self): """Returns all selected states :return: Subset of the selection, only containing states :rtype: set """ return self._states @property def transitions(self): """Returns all selected transitions :return: Subset of the selection, only containing transitions :rtype: set """ return self._transitions @property def data_flows(self): """Returns all selected data flows :return: Subset of the selection, only containing data flows :rtype: set """ return self._data_flows @property def outcomes(self): """Returns all selected outcomes :return: Subset of the selection, only containing outcomes :rtype: set """ return self._outcomes @property def input_data_ports(self): """Returns all selected input data ports :return: Subset of the selection, only containing input data ports :rtype: set """ return self._input_data_ports @property def output_data_ports(self): """Returns all selected output data ports :return: Subset of the selection, only containing output data ports :rtype: set """ return self._output_data_ports @property def scoped_variables(self): """Returns all selected scoped variables :return: Subset of the selection, only containing scoped variables :rtype: set """ return self._scoped_variables def get_selected_elements_of_core_class(self, core_element_type): """Returns all selected elements having the specified `core_element_type` as state element class :return: Subset of the selection, only containing elements having `core_element_type` as state element class :rtype: set """ if core_element_type is Outcome: return self.outcomes elif core_element_type is InputDataPort: return self.input_data_ports elif core_element_type is OutputDataPort: return self.output_data_ports elif core_element_type is ScopedVariable: return self.scoped_variables elif core_element_type is Transition: return self.transitions elif core_element_type is DataFlow: return self.data_flows elif core_element_type is State: return self.states raise RuntimeError("Invalid core element type: " + core_element_type) def is_selected(self, model): """Checks whether the given model is selected :param model: :return: True if the model is within the selection, False else :rtype: bool """ if model is None: return len(self._selected) == 0 return model in self._selected def get_all(self): """Return a copy of the selection :return: Copy of the set of selected elements :rtype: set """ return set(s for s in self._selected) def get_selected_state(self): """Return the first state within the selection :return: First state within the selection or None if there is none :rtype: AbstractStateModel """ if not self.states: return None else: return next(iter(self.states)) # sets don't support indexing @ModelMT.observe("destruction_signal", signal=True) def on_model_destruct(self, destructed_model, signal, info): """ Deselect models that are being destroyed """ self.remove(destructed_model)
class AbstractStateModel(MetaModel, Hashable): """This is an abstract class serving as base class for state models The model class is part of the MVC architecture. It holds the data to be shown (in this case a state). :param state: The state to be managed which can be any derivative of rafcon.core.states.state.State. :param AbstractStateModel parent: The state to be managed :param rafcon.utils.vividict.Vividict meta: The meta data of the state """ _parent = None _is_about_to_be_destroyed_recursively = False is_start = None state = None outcomes = [] input_data_ports = [] output_data_ports = [] meta_signal = Signal() action_signal = Signal() destruction_signal = Signal() __observables__ = ("state", "input_data_ports", "output_data_ports", "outcomes", "is_start", "meta_signal", "action_signal", "destruction_signal") def __init__(self, state, parent=None, meta=None): if type(self) == AbstractStateModel: raise NotImplementedError MetaModel.__init__(self, meta) assert isinstance(state, State) self.is_start = False self.state = state self.parent = parent self.meta_signal = Signal() self.action_signal = Signal() self.destruction_signal = Signal() self.register_observer(self) self.input_data_ports = [] self.output_data_ports = [] self.outcomes = [] self._load_input_data_port_models() self._load_output_data_port_models() self._load_outcome_models() def __str__(self): return "Model of state: {0}".format(self.state) def __eq__(self, other): if type(self) != type(other): return False if self.state != other.state: return False if self.meta != other.meta: return False for attr in self.state.state_element_attrs: # E.g. compare lists of outcomes and data ports. The lists are converted to sets, as those are unordered if hasattr(getattr(self, attr), "__radd__" ): # elements are stored in a list (ObsListWrapper) elements = getattr(self, attr) other_elements = getattr(other, attr) else: # elements are stored in a dict (ObsMapWrapper) elements = getattr(self, attr).items() other_elements = getattr(other, attr).items() if len(elements) != len(other_elements): return False if not all( [element in other_elements for element in other_elements]): return False return True def __ne__(self, other): return not self.__eq__(other) def __cmp__(self, other): if isinstance(other, AbstractStateModel): return self.core_element.__cmp__(other.core_element) def __contains__(self, item): """Checks whether `item` is an element of the state model Following child items are checked: outcomes, input data ports, output data ports :param item: :class:`StateModel` or :class:`StateElementModel` :return: Whether item is a direct child of this state :rtype: bool """ from rafcon.gui.models.state_element import StateElementModel if not isinstance(item, StateElementModel): return False return item in self.outcomes or item in self.input_data_ports or item in self.output_data_ports def __copy__(self): state = copy(self.state) state_m = self.__class__(state, parent=None, meta=None, load_meta_data=False) state_m.copy_meta_data_from_state_m(self) return state_m def __deepcopy__(self, memo=None, _nil=[]): return self.__copy__() def update_is_start(self): """Updates the `is_start` property of the state A state is a start state, if it is the root state, it has no parent, the parent is a LibraryState or the state's state_id is identical with the ContainerState.start_state_id of the ContainerState it is within. """ self.is_start = self.state.is_root_state or \ self.parent is None or \ isinstance(self.parent.state, LibraryState) or \ self.state.state_id == self.state.parent.start_state_id @property def core_element(self): return self.state @property def hierarchy_level(self): # TODO rewrite it to be more efficient -> try a recursive pattern on parent return len(self.state.get_path().split('/')) @property def hierarchy_level(self): return len(self.state.get_path().split('/')) def prepare_destruction(self, recursive=True): """Prepares the model for destruction Recursively un-registers all observers and removes references to child models """ if self.state is None: logger.verbose( "Multiple calls of prepare destruction for {0}".format(self)) self.destruction_signal.emit() try: self.unregister_observer(self) except KeyError: # Might happen if the observer was already unregistered pass if recursive: for port in self.input_data_ports[:] + self.output_data_ports[:] + self.outcomes[:]: port.prepare_destruction() del self.input_data_ports[:] del self.output_data_ports[:] del self.outcomes[:] self.state = None def update_hash(self, obj_hash): self.update_hash_from_dict(obj_hash, self.core_element) for state_element in sorted(self.outcomes[:] + self.input_data_ports[:] + self.output_data_ports[:]): self.update_hash_from_dict(obj_hash, state_element) if not self.state.get_next_upper_library_root_state(): self.update_hash_from_dict(obj_hash, self.meta) def update_meta_data_hash(self, obj_hash): super(AbstractStateModel, self).update_meta_data_hash(obj_hash) for state_element in sorted(self.outcomes[:] + self.input_data_ports[:] + self.output_data_ports[:]): state_element.update_meta_data_hash(obj_hash) @property def parent(self): if not self._parent: return None return self._parent() @parent.setter def parent(self, parent_m): if isinstance(parent_m, AbstractStateModel): self._parent = ref(parent_m) else: self._parent = None self.update_is_start() @property def is_about_to_be_destroyed_recursively(self): return self._is_about_to_be_destroyed_recursively @is_about_to_be_destroyed_recursively.setter def is_about_to_be_destroyed_recursively(self, value): if not isinstance(value, bool): raise TypeError( "The is_about_to_be_destroyed_recursively property has to be boolean." ) self._is_about_to_be_destroyed_recursively = value def get_state_machine_m(self, two_factor_check=True): """ Get respective state machine model Get a reference of the state machine model the state model belongs to. As long as the root state model has no direct reference to its state machine model the state machine manager model is checked respective model. :rtype: rafcon.gui.models.state_machine.StateMachineModel :return: respective state machine model """ from rafcon.gui.singleton import state_machine_manager_model state_machine = self.state.get_state_machine() if state_machine: if state_machine.state_machine_id in state_machine_manager_model.state_machines: sm_m = state_machine_manager_model.state_machines[ state_machine.state_machine_id] if not two_factor_check or sm_m.get_state_model_by_path( self.state.get_path()) is self: return sm_m else: logger.debug( "State model requesting its state machine model parent seems to be obsolete. " "This is a hint to duplicated models and dirty coding") return None def get_input_data_port_m(self, data_port_id): """Returns the input data port model for the given data port id :param data_port_id: The data port id to search for :return: The model of the data port with the given id """ for data_port_m in self.input_data_ports: if data_port_m.data_port.data_port_id == data_port_id: return data_port_m return None def get_output_data_port_m(self, data_port_id): """Returns the output data port model for the given data port id :param data_port_id: The data port id to search for :return: The model of the data port with the given id """ for data_port_m in self.output_data_ports: if data_port_m.data_port.data_port_id == data_port_id: return data_port_m return None def get_data_port_m(self, data_port_id): """Searches and returns the model of a data port of a given state The method searches a port with the given id in the data ports of the given state model. If the state model is a container state, not only the input and output data ports are looked at, but also the scoped variables. :param data_port_id: The data port id to be searched :return: The model of the data port or None if it is not found """ from itertools import chain data_ports_m = chain(self.input_data_ports, self.output_data_ports) for data_port_m in data_ports_m: if data_port_m.data_port.data_port_id == data_port_id: return data_port_m return None def get_outcome_m(self, outcome_id): """Returns the outcome model for the given outcome id :param outcome_id: The outcome id to search for :return: The model of the outcome with the given id """ for outcome_m in self.outcomes: if outcome_m.outcome.outcome_id == outcome_id: return outcome_m return False def _load_input_data_port_models(self): raise NotImplementedError def _load_output_data_port_models(self): raise NotImplementedError def _load_outcome_models(self): raise NotImplementedError @ModelMT.observe("state", after=True, before=True) def model_changed(self, model, prop_name, info): """This method notifies parent state about changes made to the state """ # Notify the parent state about the change (this causes a recursive call up to the root state) if self.parent is not None: self.parent.model_changed(model, prop_name, info) if prop_name == 'parent': self.update_is_start() @ModelMT.observe("action_signal", signal=True) def action_signal_triggered(self, model, prop_name, info): """This method notifies the parent state and child state models about complex actions """ msg = info.arg # print "action_signal_triggered state: ", self.state.state_id, model, prop_name, info if msg.action.startswith('sm_notification_'): return # # affected child propagation from state # if hasattr(self, 'states'): # for m in info['arg'].affected_models: # print m, self.states # print [m is mm for mm in self.states.itervalues()], [m in self for m in info['arg'].affected_models], \ # [m in self.states.values() for m in info['arg'].affected_models] if any([m in self for m in info['arg'].affected_models]): if not msg.action.startswith('parent_notification_'): new_msg = msg._replace(action='parent_notification_' + msg.action) else: new_msg = msg for m in info['arg'].affected_models: # print '???propagate it to', m, m.parent if isinstance(m, AbstractStateModel) and m in self: # print '!!!propagate it from {0} to {1} {2}'.format(self.state.state_id, m.state.state_id, m) m.action_signal.emit(new_msg) if msg.action.startswith('parent_notification_'): return # recursive propagation of action signal TODO remove finally if self.parent is not None: # Notify parent about change of meta data info.arg = msg # print "DONE1", self.state.state_id, msg self.parent.action_signal_triggered(model, prop_name, info) # print "FINISH DONE1", self.state.state_id, msg # state machine propagation of action signal (indirect) TODO remove finally elif not msg.action.startswith( 'sm_notification_'): # Prevent recursive call # If we are the root state, inform the state machine model by emitting our own meta signal. # To make the signal distinguishable for a change of meta data to our state, the change property of # the message is prepended with 'sm_notification_' # print "DONE2", self.state.state_id, msg new_msg = msg._replace(action='sm_notification_' + msg.action) self.action_signal.emit(new_msg) # print "FINISH DONE2", self.state.state_id, msg else: # print "DONE3 NOTHING" pass @ModelMT.observe("meta_signal", signal=True) def meta_changed(self, model, prop_name, info): """This method notifies the parent state about changes made to the meta data """ msg = info.arg # print "meta_changed state: ", model, prop_name, info if msg.notification is None: # Meta data of this state was changed, add information about notification to the signal message notification = Notification(model, prop_name, info) msg = msg._replace(notification=notification) # print "DONE0 ", msg if self.parent is not None: # Notify parent about change of meta data info.arg = msg self.parent.meta_changed(model, prop_name, info) # print "DONE1 ", msg elif not msg.change.startswith( 'sm_notification_'): # Prevent recursive call # If we are the root state, inform the state machine model by emitting our own meta signal. # To make the signal distinguishable for a change of meta data to our state, the change property of # the message is prepended with 'sm_notification_' msg = msg._replace(change='sm_notification_' + msg.change) self.meta_signal.emit(msg) # print "DONE2 ", msg else: # print "DONE3 NOTHING" pass def _mark_state_machine_as_dirty(self): state_machine = self.state.get_state_machine() if state_machine: state_machine_id = state_machine.state_machine_id from rafcon.core.singleton import state_machine_manager if state_machine_id is not None and state_machine_id in state_machine_manager.state_machines: state_machine_manager.state_machines[ state_machine_id].marked_dirty = True if self.state.get_next_upper_library_root_state(): lib_state_path = self.state.get_next_upper_library_root_state( ).parent.get_path() if self.get_state_machine_m().get_state_model_by_path( lib_state_path).is_about_to_be_destroyed_recursively: return logger.warning( "You have modified core property of an inner state of a library state." ) # ---------------------------------------- meta data methods --------------------------------------------- def load_meta_data(self, path=None): """Load meta data of state model from the file system The meta data of the state model is loaded from the file system and stored in the meta property of the model. Existing meta data is removed. Also the meta data of all state elements (data ports, outcomes, etc) are loaded, as those stored in the same file as the meta data of the state. This is either called on the __init__ of a new state model or if a state model for a container state is created, which then calls load_meta_data for all its children. :param str path: Optional file system path to the meta data file. If not given, the path will be derived from the state's path on the filesystem :return: if meta data file was loaded True otherwise False :rtype: bool """ # TODO: for an Execution state this method is called for each hierarchy level again and again, still?? check it! # print "1AbstractState_load_meta_data: ", path, not path if not path: path = self.state.file_system_path # print "2AbstractState_load_meta_data: ", path if path is None: self.meta = Vividict({}) return False path_meta_data = os.path.join(path, storage.FILE_NAME_META_DATA) # TODO: Should be removed with next minor release if not os.path.exists(path_meta_data): logger.debug( "Because meta data was not found in {0} use backup option {1}" "".format(path_meta_data, os.path.join(path, storage.FILE_NAME_META_DATA_OLD))) path_meta_data = os.path.join(path, storage.FILE_NAME_META_DATA_OLD) # TODO use the following logger message to debug meta data load process and to avoid maybe repetitive loads # if not os.path.exists(path_meta_data): # logger.info("path not found {0}".format(path_meta_data)) try: # print "try to load meta data from {0} for state {1}".format(path_meta_data, self.state) tmp_meta = storage.load_data_file(path_meta_data) except ValueError as e: # if no element which is newly generated log a warning # if os.path.exists(os.path.dirname(path)): # logger.debug("Because '{1}' meta data of {0} was not loaded properly.".format(self, e)) if not path.startswith( constants.RAFCON_TEMP_PATH_STORAGE) and not os.path.exists( os.path.dirname(path)): logger.debug( "Because '{1}' meta data of {0} was not loaded properly.". format(self, e)) tmp_meta = {} # JSON returns a dict, which must be converted to a Vividict tmp_meta = Vividict(tmp_meta) if tmp_meta: self._parse_for_element_meta_data(tmp_meta) # assign the meta data to the state self.meta = tmp_meta self.meta_signal.emit(MetaSignalMsg("load_meta_data", "all", True)) return True else: # print "nothing to parse", tmp_meta return False def store_meta_data(self, copy_path=None): """Save meta data of state model to the file system This method generates a dictionary of the meta data of the state together with the meta data of all state elements (data ports, outcomes, etc.) and stores it on the filesystem. Secure that the store meta data method is called after storing the core data otherwise the last_stored_path is maybe wrong or None. The copy path is considered to be a state machine file system path but not the current one but e.g. of a as copy saved state machine. The meta data will be stored in respective relative state folder in the state machine hierarchy. This folder has to exist. Dues the core elements of the state machine has to be stored first. :param str copy_path: Optional copy path if meta data is not stored to the file system path of state machine """ if copy_path: meta_file_path_json = os.path.join(copy_path, self.state.get_storage_path(), storage.FILE_NAME_META_DATA) else: if self.state.file_system_path is None: logger.error( "Meta data of {0} can be stored temporary arbitrary but by default first after the " "respective state was stored and a file system path is set." .format(self)) return meta_file_path_json = os.path.join(self.state.file_system_path, storage.FILE_NAME_META_DATA) meta_data = deepcopy(self.meta) self._generate_element_meta_data(meta_data) storage_utils.write_dict_to_json(meta_data, meta_file_path_json) def copy_meta_data_from_state_m(self, source_state_m): """Dismiss current meta data and copy meta data from given state model The meta data of the given state model is used as meta data for this state. Also the meta data of all state elements (data ports, outcomes, etc.) is overwritten with the meta data of the elements of the given state. :param source_state_m: State model to load the meta data from """ self.meta = deepcopy(source_state_m.meta) for input_data_port_m in self.input_data_ports: source_data_port_m = source_state_m.get_input_data_port_m( input_data_port_m.data_port.data_port_id) input_data_port_m.meta = deepcopy(source_data_port_m.meta) for output_data_port_m in self.output_data_ports: source_data_port_m = source_state_m.get_output_data_port_m( output_data_port_m.data_port.data_port_id) output_data_port_m.meta = deepcopy(source_data_port_m.meta) for outcome_m in self.outcomes: source_outcome_m = source_state_m.get_outcome_m( outcome_m.outcome.outcome_id) outcome_m.meta = deepcopy(source_outcome_m.meta) self.meta_signal.emit(MetaSignalMsg("copy_state_m", "all", True)) def _parse_for_element_meta_data(self, meta_data): """Load meta data for state elements The meta data of the state meta data file also contains the meta data for state elements (data ports, outcomes, etc). This method parses the loaded meta data for each state element model. The meta data of the elements is removed from the passed dictionary. :param meta_data: Dictionary of loaded meta data """ # print "_parse meta data", meta_data for data_port_m in self.input_data_ports: self._copy_element_meta_data_from_meta_file_data( meta_data, data_port_m, "input_data_port", data_port_m.data_port.data_port_id) for data_port_m in self.output_data_ports: self._copy_element_meta_data_from_meta_file_data( meta_data, data_port_m, "output_data_port", data_port_m.data_port.data_port_id) for outcome_m in self.outcomes: self._copy_element_meta_data_from_meta_file_data( meta_data, outcome_m, "outcome", outcome_m.outcome.outcome_id) @staticmethod def _copy_element_meta_data_from_meta_file_data(meta_data, element_m, element_name, element_id): """Helper method to assign the meta of the given element The method assigns the meta data of the elements from the given meta data dictionary. The copied meta data is then removed from the dictionary. :param meta_data: The loaded meta data :param element_m: The element model that is supposed to retrieve the meta data :param element_name: The name string of the element type in the dictionary :param element_id: The id of the element """ meta_data_element_id = element_name + str(element_id) meta_data_element = meta_data[meta_data_element_id] # print meta_data_element_id, element_m, meta_data_element element_m.meta = meta_data_element del meta_data[meta_data_element_id] def _generate_element_meta_data(self, meta_data): """Generate meta data for state elements and add it to the given dictionary This method retrieves the meta data of the state elements (data ports, outcomes, etc) and adds it to the given meta data dictionary. :param meta_data: Dictionary of meta data """ for data_port_m in self.input_data_ports: self._copy_element_meta_data_to_meta_file_data( meta_data, data_port_m, "input_data_port", data_port_m.data_port.data_port_id) for data_port_m in self.output_data_ports: self._copy_element_meta_data_to_meta_file_data( meta_data, data_port_m, "output_data_port", data_port_m.data_port.data_port_id) for outcome_m in self.outcomes: self._copy_element_meta_data_to_meta_file_data( meta_data, outcome_m, "outcome", outcome_m.outcome.outcome_id) @staticmethod def _copy_element_meta_data_to_meta_file_data(meta_data, element_m, element_name, element_id): """Helper method to generate meta data for meta data file for the given element The methods loads teh meta data of the given element and copies it into the given meta data dictionary intended for being stored on the filesystem. :param meta_data: The meta data to be stored :param element_m: The element model to get the meta data from :param element_name: The name string of the element type in the dictionary :param element_id: The id of the element """ meta_data_element_id = element_name + str(element_id) meta_data_element = element_m.meta meta_data[meta_data_element_id] = meta_data_element def _meta_data_editor_gaphas2opengl(self, vividict): vividict = mirror_y_axis_in_vividict_element(vividict, 'rel_pos') if 'income' in vividict: del vividict['income'] if 'name' in vividict: del vividict['name'] return vividict def _meta_data_editor_opengl2gaphas(self, vividict): from rafcon.gui.helpers.meta_data import contains_geometric_info vividict = mirror_y_axis_in_vividict_element(vividict, 'rel_pos') if contains_geometric_info(vividict['size']): self.temp['conversion_from_opengl'] = True # Determine income position size = vividict['size'] vividict['income']['rel_pos'] = (0, size[1] / 2.) # Determine size and position of NameView margin = min(size) / 12. name_height = min(size) / 8. name_width = size[0] - 2 * margin vividict['name']['size'] = (name_width, name_height) vividict['name']['rel_pos'] = (margin, margin) return vividict