######################################################################################################################
# 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/>.
######################################################################################################################
"""
An editor dialog for editing database (relationship) parameter values.
:author: A. Soininen (VTT)
:date: 28.6.2019
"""
from enum import IntEnum, unique
from numbers import Number
from PySide2.QtCore import Qt, Slot
from PySide2.QtWidgets import QDialog, QMessageBox
from spinedb_api import (
Array,
DateTime,
Duration,
duration_to_relativedelta,
Map,
ParameterValueFormatError,
TimePattern,
TimeSeriesFixedResolution,
TimeSeriesVariableResolution,
to_database,
)
from .array_editor import ArrayEditor
from .duration_editor import DurationEditor
from .datetime_editor import DatetimeEditor
from .map_editor import MapEditor
from .plain_parameter_value_editor import PlainParameterValueEditor
from .time_pattern_editor import TimePatternEditor
from .time_series_fixed_resolution_editor import TimeSeriesFixedResolutionEditor
from .time_series_variable_resolution_editor import TimeSeriesVariableResolutionEditor
from ..mvcmodels.shared import PARSED_ROLE
[docs]@unique
class _Editor(IntEnum):
"""Indexes for the specialized editors corresponding to the selector combo box and editor stack."""
[docs] TIME_SERIES_FIXED_RESOLUTION = 2
[docs] TIME_SERIES_VARIABLE_RESOLUTION = 3
[docs]class ParameterValueEditor(QDialog):
"""
Dialog for editing (relationship) 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, parent=None):
"""
Args:
index (QModelIndex): an index to a parameter_value in parent_model
parent (QWidget): a parent widget
"""
from ..ui.parameter_value_editor import Ui_ParameterValueEditor # pylint: disable=import-outside-toplevel
super().__init__(parent)
model = index.model()
self._index = index
self._ui = Ui_ParameterValueEditor()
self._ui.setupUi(self)
self.set_data_delayed = model.get_set_data_delayed(index)
self.setWindowTitle(f"Edit value -- {model.index_name(index)} --")
self.setWindowFlag(Qt.WindowMinMaxButtonsHint)
self._ui.button_box.accepted.connect(self.accept)
self._ui.button_box.rejected.connect(self.reject)
self._time_pattern_editor = TimePatternEditor()
self._plain_value_editor = PlainParameterValueEditor()
self._map_editor = MapEditor()
self._time_series_fixed_resolution_editor = TimeSeriesFixedResolutionEditor()
self._time_series_variable_resolution_editor = TimeSeriesVariableResolutionEditor()
self._array_editor = ArrayEditor()
self._datetime_editor = DatetimeEditor()
self._duration_editor = DurationEditor()
self._ui.editor_stack.addWidget(self._plain_value_editor)
self._ui.editor_stack.addWidget(self._map_editor)
self._ui.editor_stack.addWidget(self._time_series_fixed_resolution_editor)
self._ui.editor_stack.addWidget(self._time_series_variable_resolution_editor)
self._ui.editor_stack.addWidget(self._time_pattern_editor)
self._ui.editor_stack.addWidget(self._array_editor)
self._ui.editor_stack.addWidget(self._datetime_editor)
self._ui.editor_stack.addWidget(self._duration_editor)
self._ui.parameter_type_selector.currentIndexChanged.connect(self._change_parameter_type)
self._select_editor(index.data(PARSED_ROLE))
@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 = to_database(editor.value())
except ParameterValueFormatError as error:
message = "Cannot set value: {}".format(error)
QMessageBox.warning(self, "Parameter Value error", message)
return
self.set_data_delayed(value)
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 == _Editor.TIME_SERIES_VARIABLE_RESOLUTION
and old_index == _Editor.TIME_SERIES_FIXED_RESOLUTION
):
fixed_resolution_value = self._time_series_fixed_resolution_editor.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._time_series_variable_resolution_editor.set_value(variable_resolution_value)
elif (
selector_index == _Editor.TIME_SERIES_FIXED_RESOLUTION
and old_index == _Editor.TIME_SERIES_VARIABLE_RESOLUTION
):
variable_resolution_value = self._time_series_variable_resolution_editor.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._time_series_fixed_resolution_editor.set_value(fixed_resolution_value)
self._ui.editor_stack.setCurrentIndex(selector_index)
if selector_index == _Editor.PLAIN_VALUE:
self._plain_value_editor.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, _Editor.PLAIN_VALUE)
elif isinstance(value, Map):
self._use_editor(value, _Editor.MAP)
elif isinstance(value, TimeSeriesFixedResolution):
self._use_editor(value, _Editor.TIME_SERIES_FIXED_RESOLUTION)
elif isinstance(value, TimeSeriesVariableResolution):
self._use_editor(value, _Editor.TIME_SERIES_VARIABLE_RESOLUTION)
elif isinstance(value, TimePattern):
self._use_editor(value, _Editor.TIME_PATTERN)
elif isinstance(value, Array):
self._use_editor(value, _Editor.ARRAY)
elif isinstance(value, DateTime):
self._use_editor(value, _Editor.DATETIME)
elif isinstance(value, Duration):
self._use_editor(value, _Editor.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(_Editor.PLAIN_VALUE)
self._ui.editor_stack.setCurrentIndex(_Editor.PLAIN_VALUE)
[docs] def _use_editor(self, value, editor_index):
self._ui.parameter_type_selector.setCurrentIndex(editor_index)
self._ui.editor_stack.setCurrentIndex(editor_index)
self._editor_for_index(editor_index).set_value(value)
[docs] def _editor_for_index(self, editor_index):
return {
_Editor.PLAIN_VALUE: self._plain_value_editor,
_Editor.MAP: self._map_editor,
_Editor.TIME_SERIES_FIXED_RESOLUTION: self._time_series_fixed_resolution_editor,
_Editor.TIME_SERIES_VARIABLE_RESOLUTION: self._time_series_variable_resolution_editor,
_Editor.TIME_PATTERN: self._time_pattern_editor,
_Editor.ARRAY: self._array_editor,
_Editor.DATETIME: self._datetime_editor,
_Editor.DURATION: self._duration_editor,
}[editor_index]