Commit 8b95ff9b authored by Samuel GAIST's avatar Samuel GAIST
Browse files

[widgets][experimenteditor] Implement error hinting mechanism

parent 9efd122d
......@@ -44,6 +44,7 @@ from ..backend.assetmodel import AssetModel
from ..backend.resourcemodels import AlgorithmResourceModel
from ..backend.resourcemodels import ExperimentResources
from ..backend.resourcemodels import QueueResourceModel
from ..backend.resourcemodels import experiment_resources
from ..widgets.experimenteditor import AlgorithmParametersEditor
from ..widgets.experimenteditor import AnalyzerBlockEditor
from ..widgets.experimenteditor import BlockEditor
......@@ -1122,6 +1123,7 @@ class TestExperimentEditor:
@pytest.fixture()
def experiment_editor(self, qtbot, beat_context, experiment_declaration):
experiment_resources.setContext(beat_context)
editor = ExperimentEditor()
editor.set_context(beat_context)
editor.load_json(experiment_declaration)
......@@ -1129,14 +1131,11 @@ class TestExperimentEditor:
return editor
@pytest.mark.parametrize("experiment", get_valid_experiments(prefix))
def test_load_and_dump(self, qtbot, beat_context, test_prefix, experiment):
def test_load_and_dump(self, experiment_editor, test_prefix, experiment):
experiment_declaration = get_experiment_declaration(test_prefix, experiment)
editor = ExperimentEditor()
editor.set_context(beat_context)
editor.load_json(experiment_declaration)
qtbot.addWidget(editor)
experiment_editor.load_json(experiment_declaration)
assert editor.dump_json() == experiment_declaration
assert experiment_editor.dump_json() == experiment_declaration
def test_change_dataset(self, qtbot, experiment_editor, experiment_declaration):
dataset_editor = experiment_editor.datasets_widget.widget_list[0]
......@@ -1299,3 +1298,17 @@ class TestExperimentEditor:
dump = experiment_editor.dump_json()
assert dump != experiment_declaration
assert "environment" not in dump["blocks"][block_editor.block_name]
def test_showing_error(self, qtbot, experiment_editor, experiment_declaration):
experiment_editor.load_json(experiment_declaration)
ERROR_BLOCK = "echo"
errors = {ERROR_BLOCK: ["show this error"]}
experiment_editor.setBlockErrors(errors)
editor = experiment_editor.findEditor(ERROR_BLOCK)
assert editor.error_label.toolTip() == "show this error"
experiment_editor.clearBlockErrors()
assert editor.error_label.toolTip() == ""
......@@ -184,7 +184,22 @@ class AbstractBaseEditor(QWidget):
def __init__(self, prefix_path, parent=None):
super().__init__(parent)
self.prefix_path = prefix_path
self.error_label = QLabel(self)
icon = self.style().standardIcon(QStyle.SP_MessageBoxWarning)
self.error_label.setPixmap(icon.pixmap(32, 32, QIcon.Normal, QIcon.On))
layout = QVBoxLayout(self)
layout.addWidget(self.error_label)
self.setErrors([])
def setErrors(self, errors):
if errors:
self.error_label.setVisible(True)
self.error_label.setToolTip("\n".join(errors))
else:
self.error_label.setVisible(False)
self.error_label.setToolTip("")
@pyqtSlot(str)
def setPrefixPath(self, prefix_path):
......@@ -280,7 +295,7 @@ class IOMapperDialog(QDialog):
outputs = block_data.get("outputs")
if outputs:
for alg_output, output in block_data["outputs"].items():
for alg_output, output in outputs.items():
combobox = QComboBox()
combobox.addItems(alg_outputs)
combobox.setCurrentText(alg_output)
......@@ -351,8 +366,9 @@ class DatasetEditor(AbstractBaseEditor):
row_layout.addWidget(self.dataset_combobox, 1)
row_layout.addWidget(self.reset_button)
layout = QFormLayout(self)
layout = QFormLayout()
layout.addRow(self.block_name, row_layout)
self.layout().addLayout(layout)
self.dataset_combobox.currentTextChanged.connect(self.dataChanged)
self.dataset_combobox.currentTextChanged.connect(self.datasetChanged)
......@@ -421,7 +437,8 @@ class AlgorithmParametersEditor(AbstractBaseEditor):
self.json_data = {}
self.edited = {}
self.dump_edited_only = True
self.form_layout = QFormLayout(self)
self.form_layout = QFormLayout()
self.layout().addLayout(self.form_layout)
def setDumpEditOnlyEnabled(self, enabled):
"""Sets whether the dump will contain all or only the modified fields"""
......@@ -617,7 +634,7 @@ class ExecutionPropertiesEditor(AbstractBaseEditor):
self.parameters_layout.addRow(self.tr("Queue"), self.queue_combobox)
self.parameters_layout.addRow(self.tr("Parameters"), self.parameters_editor)
layout = QVBoxLayout(self)
layout = self.layout()
layout.addWidget(self.algorithm_combobox)
layout.addLayout(button_layout)
layout.addWidget(groupbox)
......@@ -705,9 +722,10 @@ class ExecutionPropertiesEditor(AbstractBaseEditor):
for key, value in zip(inputs, self.io_mapping["inputs"].values()):
io_mapping["inputs"][key] = value
if outputs and sorted(outputs) != sorted(self.io_mapping["outputs"].keys()):
outputs_mapping = self.io_mapping.get("outputs", {})
if outputs and sorted(outputs) != sorted(outputs_mapping.keys()):
io_mapping["outputs"] = {}
for key, value in zip(outputs, self.io_mapping["outputs"].values()):
for key, value in zip(outputs, outputs_mapping.values()):
io_mapping["outputs"][key] = value
if io_mapping:
......@@ -832,7 +850,7 @@ class BlockEditor(AbstractBlockEditor):
self.properties_editor = ExecutionPropertiesEditor(prefix_path)
self.properties_editor.setAlgorithmResourceModel(self.algorithm_model)
layout = QVBoxLayout(self)
layout = self.layout()
layout.addWidget(QLabel(self.block_name))
layout.addWidget(self.properties_editor)
......@@ -895,7 +913,7 @@ class LoopBlockEditor(AbstractBlockEditor):
evaluator_layout = QVBoxLayout(evaluator_groupbox)
evaluator_layout.addWidget(self.evaluator_properties_editor)
layout = QVBoxLayout(self)
layout = self.layout()
layout.addWidget(QLabel(self.block_name))
layout.addWidget(processor_groupbox)
layout.addWidget(evaluator_groupbox)
......@@ -1000,7 +1018,7 @@ class GlobalParametersEditor(AbstractBaseEditor):
form_layout.addRow(self.tr("Environment"), self.environment_combobox)
form_layout.addRow(self.tr("Queue"), self.queue_combobox)
layout = QVBoxLayout(self)
layout = self.layout()
layout.addLayout(form_layout)
layout.addWidget(self.parameters_editor_listwidget)
layout.addStretch(1)
......@@ -1187,6 +1205,10 @@ class ExperimentEditor(AbstractAssetEditor):
algorithms.add(editor.evaluator_properties_editor.algorithm)
self.globalparameters_widget.setup(algorithms)
def __onBlockChanged(self, asset_name):
sender = self.sender()
self.blockChanged.emit(sender.block_name, sender.dump())
def _asset_models(self):
"""Reimpl"""
......@@ -1328,6 +1350,7 @@ class ExperimentEditor(AbstractAssetEditor):
for name, dataset in datasets.items():
editor = DatasetEditor(name, self.prefix_path)
editor.load(dataset)
editor.datasetChanged.connect(self.__onBlockChanged)
self.datasets_widget.addWidget(editor)
used_algorithms = set()
......@@ -1344,6 +1367,7 @@ class ExperimentEditor(AbstractAssetEditor):
editor.setEnvironmentModel(self.processing_env_model)
editor.load(data)
editor.algorithmChanged.connect(self.__onAlgorithmChanged)
editor.algorithmChanged.connect(self.__onBlockChanged)
container.addWidget(editor)
if type_ == "loops":
......@@ -1423,6 +1447,52 @@ class ExperimentEditor(AbstractAssetEditor):
return data
def findEditor(self, block_name):
"""Find the editor corresponding to the block_name"""
editor = None
widgets = (
self.datasets_widget.widget_list
+ self.blocks_widget.widget_list
+ self.loops_widget.widget_list
+ self.analyzers_widget.widget_list
)
for widget in widgets:
if widget.block_name == block_name:
editor = widget
break
return editor
def loadToolchainData(self, toolchain):
"""Load the data from the toolchain where needed"""
for widget in self.datasets_widget.widget_list:
widget.loadToolchainData(toolchain)
def clearBlockErrors(self):
"""Clear error hinting"""
for widget in [
self.datasets_widget,
self.blocks_widget,
self.loops_widget,
self.analyzers_widget,
]:
for editor in widget.widget_list:
editor.setErrors([])
def setBlockErrors(self, errors_map):
"""Set error hinting on editors
errors_maps provides a dictionary of block name / error string list
"""
for widget in [
self.datasets_widget,
self.blocks_widget,
self.loops_widget,
self.analyzers_widget,
]:
for editor in widget.widget_list:
errors = errors_map.get(editor.block_name, [])
editor.setErrors(errors)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment