def after_notification_of_parent_or_state_from_lists(self, model, prop_name, info): # avoid updates because of execution status updates or while multi-actions if self.check_no_update_flags_and_return_combined_flag(prop_name, info): return overview = NotificationOverview(info) # avoid updates because of unimportant methods if overview.get_cause() not in ['name', 'append', '__setitem__', # '__delitem__', 'remove', 'group_states', 'ungroup_state', 'change_data_type', 'from_key', 'to_key', 'from_state', 'to_state', 'modify_origin', 'modify_target']: if self.model.parent: # check for a sibling port change if prop_name == 'states' and overview.get_affected_core_element() is self.model.parent.state and \ (overview.get_affected_core_element() in self.model.parent.state.states and overview.get_cause() in ['add_input_data_port', 'add_output_data_port'] or overview.get_affected_property() in ['data_port', 'scoped_variable'] and overview.get_cause() in ['name', 'change_data_type']): pass else: return else: return try: self.update(initiator=str(overview)) except Exception as e: if self.debug_log: import traceback self.store_debug_log_file(str(overview)) self.store_debug_log_file(str(traceback.format_exc())) logger.error("update of data_flow widget fails while detecting change in state %s %s" % (self.model.state.name, self.model.state.state_id))
def check_info_on_no_update_flags(self, info): """Stop updates while multi-actions""" # TODO that could need a second clean up # avoid updates because of state destruction if 'before' in info and info['method_name'] == "remove_state": if info.instance is self.model.state: self.no_update_state_destruction = True else: # if the state it self is removed lock the widget to never run updates and relieve all models removed_state_id = info.args[1] if len( info.args) > 1 else info.kwargs['state_id'] if removed_state_id == self.model.state.state_id or \ not self.model.state.is_root_state and removed_state_id == self.model.parent.state.state_id: self.no_update_self_or_parent_state_destruction = True self.relieve_all_models() elif 'after' in info and info['method_name'] == "remove_state": if info.instance.state_id == self.model.state.state_id: self.no_update_state_destruction = False # reduce NotificationOverview generations by the fact that after could cause False and before could cause True if not self.no_update_state_destruction and not self.no_update_self_or_parent_state_destruction and \ (not self.no_update and 'before' in info or 'after' in info and self.no_update): return overview = NotificationOverview(info) # The method causing the change raised an exception, thus nothing was changed and updates are allowed if 'after' in info and isinstance(overview.get_result(), Exception): self.no_update = False self.no_update_state_destruction = False # self.no_update_self_or_parent_state_destruction = False return if overview.get_cause() in [ 'group_states', 'ungroup_state', "change_state_type", "change_root_state_type" ]: instance_is_self = self.model.state is overview.get_affected_core_element( ) instance_is_parent = self.model.parent and self.model.parent.state is overview.get_affected_core_element( ) instance_is_parent_parent = self.model.parent and self.model.parent.parent and self.model.parent.parent.state is overview.get_affected_core_element( ) # print("no update flag: ", True if 'before' in info and (instance_is_self or instance_is_parent or instance_is_parent_parent) else False) if instance_is_self or instance_is_parent or instance_is_parent_parent: self.no_update = True if 'before' in info else False if overview.get_affected_property() == 'state' and overview.get_cause() in ["change_state_type"] and \ self.model.get_state_machine_m() is not None: changed_model = self.model.get_state_machine_m( ).get_state_model_by_path( overview.get_method_args()[1].get_path()) if changed_model not in self._model_observed: self.observe_model(changed_model)
def states_update(self, model, prop_name, info): overview = NotificationOverview(info) if not overview.caused_modification(): return if overview.get_affected_property() == 'state' and \ overview.get_cause() in ["name"]: # , "add_state", "remove_state"]: self.update_tree_store_row(overview.get_affected_model()) # TODO check the work around for get_library_root_state -> maybe the notifications can be avoided if upper lib elif overview.get_affected_property() == 'state' and not overview.get_affected_model().state.get_next_upper_library_root_state() and \ overview.get_cause() in ["add_state", "remove_state"]: self.update(overview.get_affected_model())
def after_notification_of_parent_or_state(self, model, prop_name, info): # avoid updates because of execution status updates or while multi-actions if self.check_no_update_flags_and_return_combined_flag(prop_name, info): return overview = NotificationOverview(info) if overview.get_cause() == 'parent' and overview.get_affected_core_element() is self.model.state or \ overview.get_affected_core_element() in [self.model.state, self.model.state.parent] and \ overview.get_cause() in ['name', 'group_states', 'ungroup_state', 'change_data_type', "remove_input_data_port", "remove_output_data_port", "remove_scoped_variable", "remove_data_flow"]: self.update(initiator=str(overview))
def states_update(self, model, prop_name, info): if is_execution_status_update_notification_from_state_machine_model(prop_name, info) or \ self._ongoing_complex_actions: return overview = NotificationOverview(info) if overview.get_affected_property() == 'state' and \ overview.get_cause() in ["name"]: # , "add_state", "remove_state"]: self.update_tree_store_row(overview.get_affected_model()) # TODO check the work around for get_library_root_state -> maybe the notifications can be avoided if upper lib elif overview.get_affected_property() == 'state' and not overview.get_affected_model().state.get_next_upper_library_root_state() and \ overview.get_cause() in ["add_state", "remove_state"]: self.update(overview.get_affected_model())
def assign_notification_states_before(self, model, prop_name, info): if self.busy: # if proceeding undo or redo return else: # avoid to vast computation time if 'kwargs' in info and 'method_name' in info['kwargs'] and \ info['kwargs']['method_name'] in BY_EXECUTION_TRIGGERED_OBSERVABLE_STATE_METHODS: return overview = NotificationOverview(info) # skipped state modifications if not overview.get_change( ) == 'state_change' or overview.get_cause() == 'parent': return # increase counter and generate new action if not locked by action that is performed if self.locked: self.before_count() else: if self.start_new_action(overview): self.before_count() else: logger.error( "FAILED to start NEW HISTORY ELEMENT [states]")
def assign_notification_root_state_after(self, model, prop_name, info): """ This method is called, when any state, transition, data flow, etc. within the state machine modifications. This then typically requires a redraw of the graphical editor, to display these modifications immediately. :param model: The state machine model :param prop_name: The property that was changed :param info: Information about the change """ # execution_status-changes are not observed if self.busy or info.method_name in BY_EXECUTION_TRIGGERED_OBSERVABLE_STATE_METHODS: return else: overview = NotificationOverview(info) # handle interrupts of action caused by exceptions if overview.get_result() == "CRASH in FUNCTION" or isinstance( overview.get_result(), Exception): if self.count_before == 1: return self._interrupt_active_action(info) pass # modifications of parent are not observed if overview.get_cause() == 'parent': return # decrease counter and finish action when reaching count=0 if self.locked: self.after_count() if self.count_before == 0: self.finish_new_action(overview) else: logger.error( "HISTORY after not count [root_state] -> For every before there should be a after." )
def after_notification_of_parent_or_state_from_lists( self, model, prop_name, info): """ Activates the update after update if outcomes, transitions or states list has been changed. """ # avoid updates because of execution status updates or while multi-actions if self.check_no_update_flags_and_return_combined_flag( prop_name, info): return overview = NotificationOverview(info) if overview.get_cause() not in [ 'name', 'append', '__setitem__', # '__delitem__', 'remove' 'from_outcome', 'to_outcome', 'from_state', 'to_state', 'modify_origin', 'modify_target' ]: if self.model.parent: # check for sibling port change if prop_name == 'states' and overview.get_affected_core_element() is self.model.parent.state and \ (overview.get_affected_core_element() in self.model.parent.state.states and overview.get_cause() in ['add_outcome'] or overview.get_affected_property() in ['outcome'] and overview.get_cause() in ['name']): pass else: return else: return try: self.update(initiator=str(overview)) except KeyError as e: if self.debug_log: import traceback self.store_debug_log_file(str(overview)) self.store_debug_log_file(str(traceback.format_exc())) logger.error( "update of transition widget fails while detecting list change of state %s %s %s\n%s" % (self.model.state.name, self.model.state.state_id, e, self))
def states_update_before(self, model, prop_name, info): overview = NotificationOverview(info) if not overview.caused_modification(): return if overview.get_affected_property() == 'state' and \ overview.get_cause() in ["change_state_type"]: changed_model = self._selected_sm_model.get_state_model_by_path( overview.get_method_args()[1].get_path()) self.observe_model(changed_model)
def states_update_before(self, model, prop_name, info): if is_execution_status_update_notification_from_state_machine_model(prop_name, info): return overview = NotificationOverview(info, False, self.__class__.__name__) if overview['prop_name'][-1] == 'state' and \ overview.get_cause() in ["change_state_type"]: changed_model = self._selected_sm_model.get_state_model_by_path(overview['args'][-1][1].get_path()) self.observe_model(changed_model)
def notify_state_name_change(self, model, prop_name, info): """Checks whether the name of a state was changed and change the tab label accordingly """ overview = NotificationOverview(info) # avoid updates or checks because of execution status updates if not overview.caused_modification(): return changed_model = overview.get_affected_model() method_name = overview.get_cause() if isinstance(changed_model, AbstractStateModel) and method_name in ['name', 'script_text']: self.update_tab_label(changed_model)
def notify_state_name_change(self, model, prop_name, info): """Checks whether the name of a state was changed and change the tab label accordingly """ # avoid updates or checks because of execution status updates if is_execution_status_update_notification_from_state_machine_model(prop_name, info): return overview = NotificationOverview(info, False, self.__class__.__name__) changed_model = overview['model'][-1] method_name = overview.get_cause() if isinstance(changed_model, AbstractStateModel) and method_name in ['name', 'script_text']: self.update_tab_label(changed_model)
def on_state_execution_status_changed_after(self, model, prop_name, info): """ Show current execution status in the widget This function specifies what happens if the state machine execution status of a state changes :param model: the model of the state that has changed (most likely its execution status) :param prop_name: property name that has been changed :param info: notification info dictionary :return: """ from rafcon.gui.utils.notification_overview import NotificationOverview from rafcon.core.states.state import State def name_and_next_state(state): assert isinstance(state, State) if state.is_root_state_of_library: return state.parent.parent, state.parent.name else: return state.parent, state.name def create_path(state, n=3, separator='/'): next_parent, name = name_and_next_state(state) path = separator + name n -= 1 while n > 0 and isinstance(next_parent, State): next_parent, name = name_and_next_state(next_parent) path = separator + name + path n -= 1 if isinstance(next_parent, State): path = separator + '..' + path return path if 'kwargs' in info and 'method_name' in info['kwargs']: overview = NotificationOverview(info) if overview.get_cause() == 'state_execution_status': active_state = overview.get_affected_model().state assert isinstance(active_state, State) path_depth = rafcon.gui.singleton.global_gui_config.get_config_value( "EXECUTION_TICKER_PATH_DEPTH", 3) message = self._fix_text_of_label + create_path( active_state, path_depth) if rafcon.gui.singleton.main_window_controller.view is not None: self.ticker_text_label.set_text(message) else: logger.warning("Not initialized yet")
def assign_notification_root_state_before(self, model, prop_name, info): # execution_status-changes are not observed if self.busy or info.method_name in BY_EXECUTION_TRIGGERED_OBSERVABLE_STATE_METHODS: return # first element should be prop_name="state_machine", instance=StateMachine and model=StateMachineModel # second element should be Prop_name="states" if root_state child elements are changed # --- for root_state elements it has to be prop_name in ["data_flows", "transitions", "input_data_ports", # "output_data_ports", "scoped_variables"] # third (and last element) should be prop_name in ["data_flow", "transition", ... else: overview = NotificationOverview(info) # modifications of parent are not observed if overview.get_cause() == 'parent': return # increase counter and generate new action if not locked by action that is performed if self.locked: self.before_count() else: if self.start_new_action(overview): self.before_count() else: logger.error( "FAILED to start NEW HISTORY ELEMENT [root_state]")
def execution_change(self, model, prop_name, info): from rafcon.gui.utils.notification_overview import NotificationOverview overview = NotificationOverview(info) if overview.get_cause() == 'state_execution_status': self.last_execution_change_at_state = overview.get_affected_model( ).state.get_path()