Source code for spinetoolbox.project_items.exporter.widgets.parameter_index_settings_window

######################################################################################################################
# Copyright (C) 2017-2019 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/>.
######################################################################################################################

"""
Parameter indexing settings window for .gdx export.

:author: A. Soininen (VTT)
:date:   25.11.2019
"""
from contextlib import contextmanager
from PySide2.QtCore import QItemSelectionModel, QModelIndex, Qt, Signal, Slot
from PySide2.QtWidgets import QMessageBox, QWidget
from .parameter_index_settings import IndexSettingsState, ParameterIndexSettings
from ..mvcmodels.indexing_domain_list_model import IndexingDomainListModel
from ..db_utils import scenario_filtered_database_map


[docs]class ParameterIndexSettingsWindow(QWidget): """A window which shows a list of ParameterIndexSettings widgets, one for each parameter with indexed values."""
[docs] settings_approved = Signal()
"""Emitted when the settings have been approved."""
[docs] settings_rejected = Signal()
"""Emitted when the settings have been rejected.""" def __init__(self, indexing_settings, set_settings, database_path, scenario, parent): """ Args: indexing_settings (dict): a map from parameter name to :class:`IndexingSetting` set_settings (SetSettings): export settings database_path (str): a database url scenario (str): scenario name parent (QWidget): a parent widget """ from ..ui.parameter_index_settings_window import Ui_Form # pylint: disable=import-outside-toplevel super().__init__(parent, f=Qt.Window) self._indexing_settings = indexing_settings self._set_settings = set_settings self._database_mapping = scenario_filtered_database_map(database_path, scenario) self._enable_domain_updates = True self._ui = Ui_Form() self._ui.setupUi(self) self.setWindowTitle(f"Gdx Parameter Indexing Settings -- {database_path} --") self.setAttribute(Qt.WA_DeleteOnClose, True) self._ui.splitter.setSizes([400, 50]) self._ui.button_box.accepted.connect(self._collect_and_hide) self._ui.button_box.rejected.connect(self._reject_and_close) self._additional_domains_model = IndexingDomainListModel(set_settings) self._additional_domains_model.domain_renamed.connect(self._update_after_domain_rename) self._additional_domains_model.rowsInserted.connect(self._send_domains_to_indexing_widgets) self._additional_domains_model.rowsRemoved.connect(self._send_domains_to_indexing_widgets) self._ui.additional_domains_list_view.setModel(self._additional_domains_model) self._ui.additional_domains_list_view.selectionModel().currentChanged.connect(self._load_additional_domain) self._ui.add_domain_button.clicked.connect(self._add_domain) self._ui.remove_domain_button.clicked.connect(self._remove_selected_domains) self._ui.use_expression_radio_button.clicked.connect(self._use_expression) self._ui.expression_edit.textChanged.connect(self._update_expression) self._ui.length_spin_box.valueChanged.connect(self._update_length) self._ui.extract_from_radio_button.clicked.connect(self._use_extraction) self._set_additional_domain_widgets_enabled(False) self._ui.extract_from_combo_box.addItems(sorted(indexing_settings.keys())) self._ui.extract_from_combo_box.currentTextChanged.connect(self._set_extraction_domain) self._settings_widgets = dict() self._available_domains = {name: set_settings.records(name) for name in set_settings.domain_names} for parameter_name, indexing_setting in indexing_settings.items(): settings_widget = ParameterIndexSettings( parameter_name, indexing_setting, self._available_domains, self._ui.settings_area_contents ) self._ui.settings_area_layout.insertWidget(0, settings_widget) self._settings_widgets[parameter_name] = settings_widget if not indexing_settings: self._ui.widget_stack.setCurrentIndex(1) return self._ui.widget_stack.setCurrentIndex(0) @property
[docs] def indexing_settings(self): """indexing settings dictionary""" return self._indexing_settings
[docs] def additional_indexing_domains(self): return self._additional_domains_model.gather_domains(self._database_mapping)
[docs] def set_domain_updated_enabled(self, enabled): """ Enables or disables updating the indexing settings widgets. Args: enabled (bool): if True, allow the widgets to update """ self._enable_domain_updates = enabled
[docs] def _switch_additional_domain_widgets_enabled_state(self, using_expression): """ Enabled and disables additional domain widgets. Args: using_expression (bool): True if expression is used, False if record keys are extracted from existing parameter """ self._ui.expression_edit.setEnabled(using_expression) self._ui.length_spin_box.setEnabled(using_expression) self._ui.extract_from_combo_box.setEnabled(not using_expression)
[docs] def _set_additional_domain_widgets_enabled(self, enabled): self._ui.description_edit.setEnabled(enabled) self._ui.use_expression_radio_button.setEnabled(enabled) self._ui.expression_edit.setEnabled(enabled) self._ui.extract_from_radio_button.setEnabled(enabled) self._ui.extract_from_combo_box.setEnabled(enabled)
@Slot(bool)
[docs] def _add_domain(self, _): """Creates a new additional domain.""" self._additional_domains_model.create_new_domain() new_current = self._additional_domains_model.index(self._additional_domains_model.rowCount() - 1, 0) self._ui.additional_domains_list_view.selectionModel().setCurrentIndex( new_current, QItemSelectionModel.ClearAndSelect
) @Slot()
[docs] def _collect_and_hide(self): """Collects settings from individual ParameterIndexSettings widgets and hides the window.""" for parameter_name, settings_widget in self._settings_widgets.items(): if settings_widget.state != IndexSettingsState.OK: self._ui.settings_area.ensureWidgetVisible(settings_widget) message = f"Parameter '{parameter_name}' indexing not well-defined." QMessageBox.warning(self, "Bad Parameter Indexing", message) return for parameter_name, settings_widget in self._settings_widgets.items(): setting = self._indexing_settings[parameter_name] setting.indexing_domain_name = settings_widget.indexing_domain_name() setting.picking = settings_widget.picking() self.settings_approved.emit() self.hide()
@Slot(QModelIndex, QModelIndex)
[docs] def _load_additional_domain(self, current, previous): if not previous.isValid(): self._set_additional_domain_widgets_enabled(True) if not current.isValid(): self._set_additional_domain_widgets_enabled(False) return with _disable_domain_updates(self): domain_proto = self._additional_domains_model.item_at(current.row()) self._ui.description_edit.setText(domain_proto.description) if domain_proto.expression is not None: self._ui.use_expression_radio_button.setChecked(True) self._ui.expression_edit.setText(domain_proto.expression) self._ui.length_spin_box.setValue(domain_proto.length) self._ui.extract_from_combo_box.setCurrentIndex(-1) self._switch_additional_domain_widgets_enabled_state(True) else: self._ui.extract_from_radio_button.setChecked(True) if domain_proto.extract_from: self._ui.extract_from_combo_box.setCurrentText(domain_proto.extract_from) else: self._ui.extract_from_combo_box.setCurrentIndex(-1) self._ui.expression_edit.clear() self._switch_additional_domain_widgets_enabled_state(False)
@Slot()
[docs] def _reject_and_close(self): self.close()
@Slot(bool)
[docs] def _remove_selected_domains(self, _): selection_model = self._ui.additional_domains_list_view.selectionModel() if not selection_model.hasSelection(): return rows = [index.row() for index in selection_model.selectedRows()] self._additional_domains_model.remove_rows(rows) current = selection_model.currentIndex() if current.isValid(): selection_model.select(current, QItemSelectionModel.ClearAndSelect)
@Slot(QModelIndex, int, int)
[docs] def _send_domains_to_indexing_widgets(self, parent, first, last): """Updates the available domains combo boxes in indexing widgets.""" domains = { name: self._set_settings.records(name) for name in self._set_settings.domain_names if not self._set_settings.metadata(name).is_additional() } domains.update(self._additional_domains_model.gather_domains(self._database_mapping)) for widget in self._settings_widgets.values(): widget.set_domains(domains)
@Slot(str, str)
[docs] def _update_after_domain_rename(self, old_name, new_name): """ Propagates changes in domain names to widgets. Args: old_name (str): domain's previous name new_name (str): domain's current name """ self._available_domains[new_name] = self._available_domains.pop(old_name) for widget in self._settings_widgets.values(): widget.update_domain_name(old_name, new_name)
@Slot(str)
[docs] def _update_expression(self, expression): """ Updates the domain's record key expression. Args: expression (str): new expression """ list_index = self._ui.additional_domains_list_view.currentIndex() if not list_index.isValid() or not self._enable_domain_updates: return item = self._additional_domains_model.item_at(list_index.row()) item.expression = expression records = item.records(self._database_mapping) self._available_domains[item.name] = records for widget in self._settings_widgets.values(): widget.update_records(item.name)
@Slot(int)
[docs] def _update_length(self, length): """ Updates the number of additional domain's records. Args: length (int): new record count """ list_index = self._ui.additional_domains_list_view.currentIndex() if not list_index.isValid() or not self._enable_domain_updates: return item = self._additional_domains_model.item_at(list_index.row()) item.length = length records = item.records(self._database_mapping) self._available_domains[item.name] = records for widget in self._settings_widgets.values(): widget.update_records(item.name)
@Slot(bool)
[docs] def _use_expression(self, _): self._switch_additional_domain_widgets_enabled_state(True) list_index = self._ui.additional_domains_list_view.currentIndex() if not list_index.isValid(): return item = self._additional_domains_model.item_at(list_index.row()) item.expression = self._ui.expression_edit.text() item.length = self._ui.length_spin_box.value() item.extract_from = None records = item.records(self._database_mapping) self._available_domains[item.name] = records for widget in self._settings_widgets.values(): widget.update_records(item.name)
@Slot(bool)
[docs] def _use_extraction(self, _): self._switch_additional_domain_widgets_enabled_state(False) domain_name = self._ui.extract_from_combo_box.currentText() self._set_extraction_domain(domain_name)
@Slot(str)
[docs] def _set_extraction_domain(self, domain_name): """ Sets the domain from which domain's records are extracted. Args: domain_name (str): domain name """ list_index = self._ui.additional_domains_list_view.currentIndex() if not domain_name or not list_index.isValid(): return item = self._additional_domains_model.item_at(list_index.row()) item.expression = None item.extract_from = domain_name records = item.records(self._database_mapping) self._available_domains[item.name] = records for widget in self._settings_widgets.values(): widget.update_records(item.name)
[docs] def closeEvent(self, event): """Handles the close event.""" super().closeEvent(event) self._database_mapping.connection.close() self.settings_rejected.emit()
@contextmanager
[docs]def _disable_domain_updates(window): """ A context manager which disables updates on the indexing settings widgets. Args: window (ParameterIndexSettingsWindow): settings window """ window.set_domain_updated_enabled(False) try: yield None finally: window.set_domain_updated_enabled(True)