def prepare_state_m_for_insert_as(state_m_to_insert, previous_state_size): """Prepares and scales the meta data to fit into actual size of the state.""" # TODO check how much code is duplicated or could be reused for library fit functionality meta data helper # TODO DO REFACTORING !!! and move maybe the hole method to meta data and rename it if isinstance(state_m_to_insert, AbstractStateModel) and \ not gui_helper_meta_data.model_has_empty_meta(state_m_to_insert): if isinstance(state_m_to_insert, ContainerStateModel): # print("TARGET1", state_m_to_insert.state.state_element_attrs) models_dict = {'state': state_m_to_insert} for state_element_key in state_m_to_insert.state.state_element_attrs: if state_element_key == "income": continue state_element_list = getattr(state_m_to_insert, state_element_key) # Some models are hold in a gtkmvc3.support.wrappers.ObsListWrapper, not a list if hasattr(state_element_list, 'keys'): state_element_list = state_element_list.values() models_dict[state_element_key] = { elem.core_element.core_element_id: elem for elem in state_element_list } resize_factor = gui_helper_meta_data.scale_meta_data_according_state( models_dict, as_template=True) gui_helper_meta_data.resize_income_of_state_m( state_m_to_insert, (resize_factor, resize_factor)) elif isinstance(state_m_to_insert, StateModel): # print("TARGET2", state_m_to_insert.state.state_element_attrs) if previous_state_size: current_size = state_m_to_insert.get_meta_data_editor()['size'] factor = gui_helper_meta_data.divide_two_vectors( current_size, previous_state_size) state_m_to_insert.set_meta_data_editor('size', previous_state_size) factor = (min(*factor), min(*factor)) gui_helper_meta_data.resize_state_meta(state_m_to_insert, factor) else: logger.debug( "For insert as template of {0} no resize of state meta data is performed because " "the meta data has empty fields.".format( state_m_to_insert)) # library state is not resize because its ports became resized indirectly -> see was resized flag elif not isinstance(state_m_to_insert, LibraryStateModel): raise TypeError( "For insert as template of {0} no resize of state meta data is performed because " "state model type is not ContainerStateModel or StateModel". format(state_m_to_insert)) else: logger.info( "For insert as template of {0} no resize of state meta data is performed because the meta data has " "empty fields.".format(state_m_to_insert))
def paste(self, target_state_m, cursor_position=None, limited=None, convert=False): """Paste objects to target state The method checks whether the target state is a execution state or a container state and inserts respective elements and notifies the user if the parts can not be insert to the target state. - for ExecutionStates outcomes, input- and output-data ports can be inserted - for ContainerState additional states, scoped variables and data flows and/or transitions (if related) can be inserted Related data flows and transitions are determined by origin and target keys and respective objects which has to be in the state machine selection, too. Thus, transitions or data flows without the related objects are not copied. :param target_state_m: state in which the copied/cut elements should be insert :param cursor_position: cursor position used to adapt meta data positioning of elements e.g states and via points :return: """ if all([not elems for elems in self.model_copies.values()]): logger.warning( "Paste is not performed because the clipboard is empty. " "Select one or multiple elements and Copy or Cut those before performing Paste." ) return if not isinstance(target_state_m, StateModel): logger.warning( "Paste is not performed because target state indication has to be a StateModel not {0}" "".format(target_state_m.__class__.__name__)) return if target_state_m.state.get_next_upper_library_root_state( ) is not None: logger.warning( "Paste is not performed because selected target state is inside of a library state." ) return self.reset_clipboard_mapping_dicts() element_m_copy_lists = self.model_copies self.prepare_new_copy( ) # threaded in future -> important that the copy is prepared here!!! # use non empty list dict to create arguments for action signal msg and logger messages dict_of_non_empty_lists_of_model_copies, action_parent_m = self.get_action_arguments( target_state_m) action_parent_m.action_signal.emit( ActionSignalMsg(action='paste', origin='clipboard', action_parent_m=action_parent_m, affected_models=[], after=False, kwargs={ 'insert': dict_of_non_empty_lists_of_model_copies, 'convert': convert, 'limited': limited })) self.state_id_mapping_dict[ self.copy_parent_state_id] = target_state_m.state.state_id # prepare list of lists to copy for limited or converted paste of objects target_state_element_attrs = target_state_m.state.state_element_attrs if "income" in target_state_element_attrs: target_state_element_attrs.remove("income") if limited and all([ state_element_attr in target_state_element_attrs for state_element_attr in limited ]): if len(limited) == 1 and limited[0] in [ 'input_data_ports', 'output_data_ports', 'scoped_variables' ] and convert: combined_list = element_m_copy_lists['input_data_ports'] + element_m_copy_lists['output_data_ports'] + \ element_m_copy_lists['scoped_variables'] for state_element_attr in [ 'input_data_ports', 'output_data_ports', 'scoped_variables' ]: element_m_copy_lists[state_element_attr] = combined_list state_element_attrs_to_insert = limited else: state_element_attrs_to_insert = target_state_element_attrs # check list order and put transitions and data flows to the end for state_element_attr in ['transitions', 'data_flows']: if state_element_attr in state_element_attrs_to_insert: state_element_attrs_to_insert.remove(state_element_attr) state_element_attrs_to_insert.append(state_element_attr) def insert_elements_from_model_copies_list(model_list, state_element_name): """ Insert/add all core elements of model_list into the target_state_m The function returns a list of pairs of models (new and original models) because the target_state_m for some insert operations still generates a new model. :param list model_list: list of models :param str state_element_name: appendix string to "_insert_*" to get the attribute of respective methods in clipboard-class. :return: list of pairs of models (new and original models) :rtype: list[tuple] """ new_and_copied_models = [] for orig_element_m_copy in model_list: try: # hold orig_element_m_copy related to newly generated model for debugging reasons # (its doubt that ids are fully correct, meta data is considered to be alright now) insert_function = getattr(self, '_insert_{0}'.format( state_element_name)) # e.g. self._insert_state new_element_m = insert_function(target_state_m, orig_element_m_copy) new_and_copied_models.append( (new_element_m, orig_element_m_copy)) except (ValueError, AttributeError, TypeError) as e: logger.warning( "While inserting a {0} a failure was detected, exception: {1}." "".format(state_element_name, e)) return new_and_copied_models # insert all lists and their elements into target state # insert_dict hold lists of pairs of models -> new (maybe generated by parent model) and original copy insert_dict = dict() for state_element_attr in state_element_attrs_to_insert: state_element_name = singular_form( state_element_attr ) # e.g. "states" => "state", "outcomes" => "outcome" insert_dict[state_element_attr] = \ insert_elements_from_model_copies_list(element_m_copy_lists[state_element_attr], state_element_name) # move meta data from original copied model to newly insert models and resize them to fit into target_state_m models_dict = {'state': target_state_m} for state_element_attr, state_elements in insert_dict.items(): models_dict[state_element_attr] = {} for new_state_element_m, copied_state_element_m in state_elements: new_core_element_id = new_state_element_m.core_element.core_element_id models_dict[state_element_attr][ new_core_element_id] = new_state_element_m affected_models = [] for key, state_elements in insert_dict.items(): if key == 'state': continue for new_state_element_m, copied_state_element_m in state_elements: affected_models.append(new_state_element_m) # commented parts are here for later use to detect empty meta data fields and debug those if all([all([not gui_helpers_meta_data.model_has_empty_meta(state_element_m) for state_element_m, _ in elems_list]) if isinstance(elems_list, list) else gui_helpers_meta_data.model_has_empty_meta(elems_list) for elems_list in insert_dict.values()]) or \ len(dict_of_non_empty_lists_of_model_copies) == 1 and 'states' in dict_of_non_empty_lists_of_model_copies: try: gui_helpers_meta_data.scale_meta_data_according_state( models_dict) except: logger.exception( "Scale of pasted content {0} cause a problems.".format( models_dict)) else: # TODO this should become a warning in the future or the meta module has to handle the empty data fields logger.info( "Paste miss meta to scale. {0}".format(affected_models)) if not affected_models: logger.warning( "Paste with no effect. No elements pasted from {0}".format( dict_of_non_empty_lists_of_model_copies)) action_parent_m.action_signal.emit( ActionSignalMsg(action='paste', origin='clipboard', action_parent_m=action_parent_m, affected_models=affected_models, after=True)) return insert_dict