Пример #1
0
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))
Пример #2
0
    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