Source code for spinetoolbox.import_editor.widgets.import_mappings

######################################################################################################################
# Copyright (C) 2017-2020 Spine project consortium
# 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/>.
######################################################################################################################

"""
ImportMappings widget.

:author: P. Vennström (VTT)
:date:   1.6.2019
"""

from PySide2.QtCore import QObject, QItemSelectionModel, Signal, Slot
from ...widgets.custom_delegates import ComboBoxDelegate
from ..commands import CreateMapping, DeleteMapping

[docs]MAPPING_CHOICES = ("Constant", "Column", "Row", "Column Header", "Headers", "Table Name", "None")
[docs]class ImportMappings(QObject): """ Provides methods for managing Mappings (add, remove, edit, visualize, and so on). """
[docs] mapping_selection_changed = Signal(object)
"""Emitted when a new mapping specification is selected from the Mappings list."""
[docs] mapping_data_changed = Signal(object)
"""Emits the new MappingListModel."""
[docs] about_to_undo = Signal(str)
"""Emitted before an undo/redo action.""" def __init__(self, ui, undo_stack): """ Args: ui (QWidget): importer window's UI undo_stack (QUndoStack): undo stack """ super().__init__() self._ui = ui self._source_table = None self._mappings_model = None self._undo_stack = undo_stack # initialize interface self._ui.table_view_mappings.setItemDelegateForColumn(1, ComboBoxDelegate(MAPPING_CHOICES)) # connect signals self._ui.new_button.clicked.connect(self.new_mapping) self._ui.remove_button.clicked.connect(self.delete_selected_mapping) self.mapping_selection_changed.connect(self._ui.table_view_mappings.setModel) @Slot(str, object)
[docs] def set_mappings_model(self, source_table_name, model): """ Sets new mappings. Args: source_table_name (str): source table's name model (MappingListModel): mapping list model """ self._source_table = source_table_name if self._mappings_model is not None: self._mappings_model.dataChanged.disconnect(self.data_changed) self._mappings_model = model self._ui.list_view.setModel(model) for specification in self._mappings_model.mapping_specifications: specification.about_to_undo.connect(self.focus_on_changing_specification) self._ui.list_view.selectionModel().currentChanged.connect(self.change_mapping) self._mappings_model.dataChanged.connect(self.data_changed) if self._mappings_model.rowCount() > 0: self._select_row(0) else: self._ui.list_view.clearSelection()
@Slot(str, str)
[docs] def focus_on_changing_specification(self, source_table_name, mapping_name): """ Selects the given mapping from the list and emits about_to_undo. Args: source_table_name (str): name of the source table mapping_name (str): name of the mapping specification """ self.about_to_undo.emit(source_table_name) row = self._mappings_model.row_for_mapping(mapping_name) index = self._mappings_model.index(row, 0) self._ui.list_view.selectionModel().setCurrentIndex(index, QItemSelectionModel.ClearAndSelect)
@Slot()
[docs] def data_changed(self): """Emits the mappingDataChanged signal with the currently selected data mappings.""" m = None indexes = self._ui.list_view.selectedIndexes() if self._mappings_model and indexes: m = self._mappings_model.data_mapping(indexes[0]) self.mapping_data_changed.emit(m)
@Slot()
[docs] def new_mapping(self): """ Pushes a CreateMapping command to the undo stack """ if self._mappings_model is None: return row = self._mappings_model.rowCount() command = CreateMapping(self._source_table, self, row) self._undo_stack.push(command)
[docs] def create_mapping(self): if self._mappings_model is None: return mapping_name = self._mappings_model.add_mapping() specification = self._mappings_model.mapping_specification(mapping_name) row = self._mappings_model.rowCount() - 1 def select_row_slot(): # We want to select the mapping during undo/redo to show the user where the changes happen. self._select_row(row) specification.dataChanged.connect(select_row_slot) specification.about_to_undo.connect(self.focus_on_changing_specification) self._select_row(row) return mapping_name
[docs] def insert_mapping_specification(self, source_table_name, name, row, mapping_specification): self.about_to_undo.emit(source_table_name) if self._mappings_model is None: return self._mappings_model.insert_mapping_specification(name, row, mapping_specification) self._select_row(row)
@Slot()
[docs] def delete_selected_mapping(self): """ Pushes a DeleteMapping command to the undo stack. """ selection_model = self._ui.list_view.selectionModel() if self._mappings_model is None or not selection_model.hasSelection(): return row = selection_model.currentIndex().row() mapping_name = self._mappings_model.mapping_name_at(row) self._undo_stack.push(DeleteMapping(self._source_table, self, mapping_name, row))
[docs] def delete_mapping(self, source_table_name, name): self.about_to_undo.emit(source_table_name) if self._mappings_model is None: return None row = self._mappings_model.row_for_mapping(name) if row is None: return None mapping_specification = self._mappings_model.remove_mapping(row) mapping_count = self._mappings_model.rowCount() if mapping_count: if row == mapping_count: self._select_row(mapping_count - 1) else: self._select_row(row) else: self._ui.list_view.clearSelection() return mapping_specification
[docs] def _select_row(self, row): selection_model = self._ui.list_view.selectionModel() if selection_model.hasSelection(): current_row = selection_model.currentIndex().row() if row == current_row: return selection_model.setCurrentIndex(self._mappings_model.index(row, 0), QItemSelectionModel.ClearAndSelect)
@Slot(object, object)
[docs] def change_mapping(self, selected, deselected): row = selected.row() index = self._mappings_model.index(row, 0) if index.isValid(): self.mapping_selection_changed.emit(self._mappings_model.data_mapping(index)) else: self.mapping_selection_changed.emit(None)