Source code for spinetoolbox.spine_db_editor.widgets.stacked_view_mixin

######################################################################################################################
# Copyright (C) 2017-2022 Spine project consortium
# Copyright Spine Toolbox contributors
# This file is part of Spine Toolbox.
# Spine Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
# Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option)
# any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
# Public License for more details. You should have received a copy of the GNU Lesser General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
######################################################################################################################

"""Contains the StackedViewMixin class."""
from PySide6.QtCore import Qt, Slot, QModelIndex, QSignalBlocker
from PySide6.QtWidgets import QHeaderView
from PySide6.QtGui import QGuiApplication
from .element_name_list_editor import ElementNameListEditor
from ..mvcmodels.compound_models import (
    CompoundParameterValueModel,
    CompoundParameterDefinitionModel,
    CompoundEntityAlternativeModel,
)
from ..mvcmodels.entity_tree_models import group_items_by_db_map
from ...helpers import preferred_row_height, DB_ITEM_SEPARATOR


[docs]class StackedViewMixin: """ Provides stacked parameter tables for the Spine db editor. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._filter_class_ids = {} self._filter_entity_ids = {} self._filter_alternative_ids = {} self.parameter_value_model = CompoundParameterValueModel(self, self.db_mngr) self.parameter_definition_model = CompoundParameterDefinitionModel(self, self.db_mngr) self.entity_alternative_model = CompoundEntityAlternativeModel(self, self.db_mngr) self._all_stacked_models = { self.parameter_value_model: self.ui.tableView_parameter_value, self.parameter_definition_model: self.ui.tableView_parameter_definition, self.entity_alternative_model: self.ui.tableView_entity_alternative, } for model, view in self._all_stacked_models.items(): view.setModel(model) view.verticalHeader().setDefaultSectionSize(preferred_row_height(self)) horizontal_header = view.horizontalHeader() horizontal_header.setSectionsMovable(True) view.connect_spine_db_editor(self)
[docs] def connect_signals(self): """Connects signals to slots.""" super().connect_signals() self.ui.alternative_tree_view.alternative_selection_changed.connect(self._handle_alternative_selection_changed) self.ui.scenario_tree_view.scenario_selection_changed.connect( self._handle_scenario_alternative_selection_changed ) self.ui.treeView_entity.tree_selection_changed.connect( self._handle_entity_tree_selection_changed_in_parameter_tables ) self.ui.graphicsView.graph_selection_changed.connect(self._handle_graph_selection_changed)
[docs] def init_models(self): """Initializes models.""" super().init_models() for model in self._all_stacked_models: model.reset_db_maps(self.db_maps) model.init_model() self._set_default_parameter_data()
@Slot(QModelIndex, object, object)
[docs] def show_element_name_list_editor(self, index, entity_class_id, db_map): """Shows the element name list editor. Args: index (QModelIndex) entity_class_id (int) db_map (DatabaseMapping) """ entity_class = self.db_mngr.get_item(db_map, "entity_class", entity_class_id) dimension_id_list = entity_class.get("dimension_id_list", ()) dimension_names = [] entity_byname_lists = [] for id_ in dimension_id_list: dimension_name = self.db_mngr.get_item(db_map, "entity_class", id_).get("name") entity_name_list = [ x["entity_byname"] for k in ("class_id", "superclass_id") for x in self.db_mngr.get_items_by_field(db_map, "entity", k, id_) ] dimension_names.append(dimension_name) entity_byname_lists.append(entity_name_list) entity_byname = index.data(Qt.ItemDataRole.EditRole) if entity_byname is not None: entity = db_map.get_item( "entity", entity_class_name=entity_class["name"], entity_byname=tuple(entity_byname.split(DB_ITEM_SEPARATOR)), ) current_element_byname_list = entity["element_byname_list"] if entity else [] else: current_element_byname_list = [] editor = ElementNameListEditor(self, index, dimension_names, entity_byname_lists, current_element_byname_list) editor.show()
[docs] def _set_default_parameter_data(self, index=None): """Sets default rows for parameter models according to given index. Args: index (QModelIndex): an index of the entity tree """ if index is None or not index.isValid(): default_db_map = next(iter(self.db_maps)) default_data = dict(database=default_db_map.codename) else: item = index.model().item_from_index(index) default_db_map = item.first_db_map default_data = item.default_parameter_data() self.set_default_parameter_data(default_data, default_db_map)
[docs] def set_default_parameter_data(self, default_data, default_db_map): for model in self._all_stacked_models: model.empty_model.db_map = default_db_map model.empty_model.set_default_row(**default_data) model.empty_model.set_rows_to_default(model.empty_model.rowCount() - 1)
[docs] def clear_all_filters(self): for model in self._all_stacked_models: model.clear_auto_filter() self._filter_class_ids = {} self._filter_entity_ids = {} self._filter_alternative_ids = {} self._reset_filters() trees = [self.ui.treeView_entity, self.ui.scenario_tree_view, self.ui.alternative_tree_view] for tree in trees: tree.selectionModel().clearSelection()
[docs] def _reset_filters(self): """Resets filters.""" for model in self._all_stacked_models: model.set_filter_class_ids(self._filter_class_ids) for model in (self.parameter_value_model, self.entity_alternative_model): model.set_filter_entity_ids(self._filter_entity_ids) model.set_filter_alternative_ids(self._filter_alternative_ids)
@Slot(list)
[docs] def _handle_graph_selection_changed(self, selected_items): """Resets filter according to graph selection.""" active_items = {} for x in selected_items: for db_map in x.db_maps: active_items.setdefault(db_map, []).extend(x.db_items(db_map)) self._filter_class_ids = {} for db_map, items in active_items.items(): self._filter_class_ids.setdefault(db_map, set()).update({x["class_id"] for x in items}) self._filter_entity_ids = self.db_mngr.db_map_class_ids(active_items) self._reset_filters()
@Slot(dict)
[docs] def _handle_entity_tree_selection_changed_in_parameter_tables(self, selected_indexes): """Resets filter according to entity tree selection.""" entity_indexes = set(selected_indexes.get("entity", {}).keys()) entity_indexes |= { parent for parent in (i.parent() for i in entity_indexes) if self.entity_tree_model.item_from_index(parent).item_type == "entity" } entity_class_indexes = set(selected_indexes.get("entity_class", {}).keys()) | { parent_ind for parent_ind in (ind.parent() for ind in entity_indexes) if self.entity_tree_model.item_from_index(parent_ind).item_type == "entity_class" } self._filter_class_ids = self._db_map_ids(entity_class_indexes) self._filter_entity_ids = self.db_mngr.db_map_class_ids(group_items_by_db_map(entity_indexes)) if Qt.KeyboardModifier.ControlModifier not in QGuiApplication.keyboardModifiers(): self._filter_alternative_ids.clear() self._clear_all_other_selections(self.ui.treeView_entity) self._reset_filters() self._set_default_parameter_data(self.ui.treeView_entity.selectionModel().currentIndex())
@Slot(dict)
[docs] def _handle_alternative_selection_changed(self, selected_db_map_alt_ids): """Resets filter according to selection in alternative tree view.""" self._update_alternative_selection( selected_db_map_alt_ids, self.ui.scenario_tree_view, self.ui.alternative_tree_view )
@Slot(dict)
[docs] def _handle_scenario_alternative_selection_changed(self, selected_db_map_alt_ids): """Resets filter according to selection in scenario tree view.""" self._update_alternative_selection( selected_db_map_alt_ids, self.ui.alternative_tree_view, self.ui.scenario_tree_view )
[docs] def _update_alternative_selection(self, selected_db_map_alt_ids, other_tree_view, this_tree_view): """Combines alternative selections from alternative and scenario tree views. Args: selected_db_map_alt_ids (dict): mapping from database map to set of alternative ids other_tree_view (AlternativeTreeView or ScenarioTreeView): tree view whose selection didn't change this_tree_view (AlternativeTreeView or ScenarioTreeView): tree view whose selection changed """ if Qt.KeyboardModifier.ControlModifier in QGuiApplication.keyboardModifiers(): alternative_ids = { db_map: alt_ids.copy() for db_map, alt_ids in other_tree_view.selected_alternative_ids.items() } else: alternative_ids = {} self._clear_all_other_selections(this_tree_view) self._filter_class_ids.clear() self._filter_entity_ids.clear() for db_map, alt_ids in selected_db_map_alt_ids.items(): alternative_ids.setdefault(db_map, set()).update(alt_ids) self._filter_alternative_ids = alternative_ids self._reset_filters()
[docs] def _clear_all_other_selections(self, current, other=None): """Clears all the other selections besides the one that was just made. Args: current: the tree where the selection that was just made other (optional): other optional tree """ trees = [self.ui.treeView_entity, self.ui.scenario_tree_view, self.ui.alternative_tree_view] for tree in trees: if tree not in (current, other): with QSignalBlocker(tree) as _: tree.selectionModel().clearSelection()
[docs] def tear_down(self): if not super().tear_down(): return False for model in self._all_stacked_models: model.stop_invalidating_filter() return True