Source code for spinetoolbox.widgets.parameter_value_editor_base

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

"""
A base for editor windows for editing parameter values.

:author: A. Soininen (VTT)
:date:   2.11.2020
"""

from enum import auto, Enum, unique
from numbers import Number
from PySide2.QtCore import Qt, Slot
from PySide2.QtWidgets import QMessageBox, QWidget
from spinedb_api import (
    Array,
    DateTime,
    Duration,
    duration_to_relativedelta,
    Map,
    ParameterValueFormatError,
    TimePattern,
    TimeSeriesFixedResolution,
    TimeSeriesVariableResolution,
)


[docs]@unique class ValueType(Enum): """Enum to identify value types that use different editors."""
[docs] PLAIN_VALUE = auto()
[docs] MAP = auto()
[docs] TIME_SERIES_FIXED_RESOLUTION = auto()
[docs] TIME_SERIES_VARIABLE_RESOLUTION = auto()
[docs] TIME_PATTERN = auto()
[docs] ARRAY = auto()
[docs] DATETIME = auto()
[docs] DURATION = auto()
[docs]_SELECTORS = { ValueType.PLAIN_VALUE: "Plain value", ValueType.MAP: "Map", ValueType.TIME_SERIES_FIXED_RESOLUTION: "Time series fixed resolution", ValueType.TIME_SERIES_VARIABLE_RESOLUTION: "Time series variable resolution", ValueType.TIME_PATTERN: "Time pattern", ValueType.ARRAY: "Array", ValueType.DATETIME: "Date time", ValueType.DURATION: "Duration",
}
[docs]class ParameterValueEditorBase(QWidget): """ Dialog for editing parameter values. The dialog takes an index and shows a specialized editor corresponding to the value type in a stack widget. The user can change the value type by changing the specialized editor using a combo box. When the dialog is closed the value from the currently shown specialized editor is written back to the given index. """ def __init__(self, index, editor_widgets, parent=None): """ Args: index (QModelIndex): an index to a parameter_value in parent_model editor_widgets (dict): a mapping from :class:`ValueType` to :class:`QWidget` parent (QWidget, optional): a parent widget """ from ..ui.parameter_value_editor import Ui_ParameterValueEditor # pylint: disable=import-outside-toplevel super().__init__(parent, f=Qt.Window) self._index = index self._editors = editor_widgets self._editor_indexes = {value_type: i for i, value_type in enumerate(editor_widgets)} self._ui = Ui_ParameterValueEditor() self._ui.setupUi(self) self._ui.parameter_type_selector.addItems([_SELECTORS[value_type] for value_type in editor_widgets]) self._ui.parameter_type_selector.currentIndexChanged.connect(self._change_parameter_type) self._ui.button_box.accepted.connect(self.accept) self._ui.button_box.rejected.connect(self.close) for widget in self._editors.values(): self._ui.editor_stack.addWidget(widget) @Slot()
[docs] def accept(self): """Saves the parameter_value shown in the currently selected editor widget to the database manager.""" editor = self._ui.editor_stack.currentWidget() try: value = editor.value() except ParameterValueFormatError as error: QMessageBox.warning(self.parent(), "Error", str(error)) return success = self._set_data(value) if success: self.close()
@Slot(int)
[docs] def _change_parameter_type(self, selector_index): """ Handles switching between value types. Does a rude conversion between fixed and variable resolution time series. In other cases, a default 'empty' value is used. Args: selector_index (int): an index to the selector combo box """ old_index = self._ui.editor_stack.currentIndex() if ( selector_index == self._editor_indexes[ValueType.TIME_SERIES_VARIABLE_RESOLUTION] and old_index == self._editor_indexes[ValueType.TIME_SERIES_FIXED_RESOLUTION] ): fixed_resolution_value = self._editors[ValueType.TIME_SERIES_FIXED_RESOLUTION].value() stamps = fixed_resolution_value.indexes values = fixed_resolution_value.values variable_resolution_value = TimeSeriesVariableResolution( stamps, values, fixed_resolution_value.ignore_year, fixed_resolution_value.repeat ) self._editors[ValueType.TIME_SERIES_VARIABLE_RESOLUTION].set_value(variable_resolution_value) elif ( selector_index == self._editor_indexes[ValueType.TIME_SERIES_FIXED_RESOLUTION] and old_index == self._editor_indexes[ValueType.TIME_SERIES_VARIABLE_RESOLUTION] ): variable_resolution_value = self._editors[ValueType.TIME_SERIES_VARIABLE_RESOLUTION].value() stamps = variable_resolution_value.indexes start = stamps[0] difference = stamps[1] - start resolution = [duration_to_relativedelta(str(difference))] fixed_resolution_value = TimeSeriesFixedResolution( start, resolution, variable_resolution_value.values, variable_resolution_value.ignore_year, variable_resolution_value.repeat, ) self._editors[ValueType.TIME_SERIES_FIXED_RESOLUTION].set_value(fixed_resolution_value) self._ui.editor_stack.setCurrentIndex(selector_index) if selector_index == self._editor_indexes[ValueType.PLAIN_VALUE]: self._editors[ValueType.PLAIN_VALUE].set_value("")
[docs] def _select_editor(self, value): """Shows the editor widget corresponding to the given value type on the editor stack.""" if isinstance(value, ParameterValueFormatError): self._use_default_editor(message=str(value)) elif value is None or isinstance(value, (Number, bool, str)): self._use_editor(value, ValueType.PLAIN_VALUE) elif isinstance(value, Map): self._use_editor(value, ValueType.MAP) elif isinstance(value, TimeSeriesFixedResolution): self._use_editor(value, ValueType.TIME_SERIES_FIXED_RESOLUTION) elif isinstance(value, TimeSeriesVariableResolution): self._use_editor(value, ValueType.TIME_SERIES_VARIABLE_RESOLUTION) elif isinstance(value, TimePattern): self._use_editor(value, ValueType.TIME_PATTERN) elif isinstance(value, Array): self._use_editor(value, ValueType.ARRAY) elif isinstance(value, DateTime): self._use_editor(value, ValueType.DATETIME) elif isinstance(value, Duration): self._use_editor(value, ValueType.DURATION) else: self._use_default_editor()
[docs] def _use_default_editor(self, message=None): """Opens the default editor widget. Optionally, displays a warning dialog indicating the problem. Args: message (str, optional) """ if message is not None: QMessageBox.warning(self.parent(), "Warning", message) self._ui.parameter_type_selector.setCurrentIndex(self._editor_indexes[ValueType.PLAIN_VALUE]) self._ui.editor_stack.setCurrentIndex(self._editor_indexes[ValueType.PLAIN_VALUE])
[docs] def _use_editor(self, value, value_type): """ Sets a value to edit on an editor widget. Args: value (object): value to edit value_type (ValueType): type of value """ self._ui.parameter_type_selector.setCurrentIndex(self._editor_indexes[value_type]) self._ui.editor_stack.setCurrentIndex(self._editor_indexes[value_type]) self._editors[value_type].set_value(value)
[docs] def _set_data(self, value): """ Writes parameter value back to the model. Args: value (object): value to write Returns: bool: True if the operation was successful, False otherwise """ raise NotImplementedError()