Skip to content

Commit

Permalink
add code demo
Browse files Browse the repository at this point in the history
* allows CodeDemo to be None
* add CueOutputs and update_process as input
* allows to input ParameterPanel as input for parameters change logic a
  bit
  • Loading branch information
agoscinski committed Dec 14, 2023
1 parent 69cef6a commit f027027
Showing 1 changed file with 74 additions and 43 deletions.
117 changes: 74 additions & 43 deletions src/scwidgets/code/_widget_code_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from __future__ import annotations

import types
from copy import deepcopy
from platform import python_version
from typing import Any, Callable, Dict, List, Optional, Union

Expand Down Expand Up @@ -49,40 +48,76 @@ class CodeDemo(VBox, CheckableWidget):

def __init__(
self,
code: Union[CodeInput, types.FunctionType] = None,
code: Union[None, CodeInput, types.FunctionType] = None,
check_registry: Optional[CheckRegistry] = None,
parameters: Optional[Union[Dict[str, Union[Check.FunInParamT, Widget]],
ParameterPanel]] = None,
parameters: Optional[
Union[Dict[str, Union[Check.FunInParamT, Widget]], ParameterPanel]
] = None,
update_mode: str = "release",
cue_outputs: Union[None, CueOutput, List[CueOutput]] = None,
update_process: Optional[
Callable[CodeDemo, Union[Any, Check.FunOutParamsT]]
Callable[[CodeDemo], Union[Any, Check.FunOutParamsT]]
] = None,
*args,
**kwargs,
):
allowed_update_modes = ["manual", "continuous", "release"]
if update_mode not in allowed_update_modes:
raise ValueError(
raise TypeError(
f"Got update mode {update_mode!r} but only "
f"{allowed_update_modes} are allowed."
)
self._update_mode = update_mode

self._update_process = update_process

# verify if input argument `parameter` is valid
if parameters is not None:
allowed_parameter_types = [dict, ParameterPanel]
parameter_type_allowed = False
for allowed_parameter_type in allowed_parameter_types:
if isinstance(parameters, allowed_parameter_type):
parameter_type_allowed = True
if not (parameter_type_allowed):
raise TypeError(
f"Got parameter {type(parameters)!r} but only "
f"{allowed_parameter_types} are allowed."
)

# verify if input argument `parameter` is valid
if isinstance(code, types.FunctionType):
code = CodeInput(function=code)

# check compability between code and parameters
if code is not None and parameters is not None:
if isinstance(parameters, dict):
compatibility_result = code.compatible_with_signature(
list(parameters.keys())
)
elif isinstance(parameters, ParameterPanel):
compatibility_result = code.compatible_with_signature(
list(parameters.parameters.keys())
)
if compatibility_result != "":
raise ValueError(
"Code and parameters do no match: " + compatibility_result
)

if cue_outputs is None:
cue_outputs = []
elif isinstance(cue_outputs, CueOutput):
elif not(isinstance(cue_outputs, list)):
cue_outputs = [cue_outputs]

CheckableWidget.__init__(self, check_registry)

self._code = code
# TODO this needs to be settable
self._output = CueOutput()
if isinstance(parameters, dict):
self._parameter_panel = ParameterPanel(**parameters)
elif isinstance(parameters, ParameterPanel):
self._parameter_panel = parameters
parameters = self._parameter_panel.parameters
self._parameters = parameters
self._cue_code = self._code
self._cue_outputs = cue_outputs
Expand Down Expand Up @@ -110,24 +145,6 @@ def __init__(
self._update_button = None
self._parameter_panel = VBox([])
else:
allowed_parameter_types = [dict, ParameterPanel]
parameter_type_allowed = False
for allowed_parameter_type in allowed_parameter_types:
if isinstance(ParameterPanel, allowed_parameter_type):
parameter_type_allowed = True
if parameter_type_allowed:
raise ValueError(
f"Got parameter {type(ParameterPanel)!r} but only "
f"{allowed_parameter_types} are allowed."
)
if isinstance(self._parameters, dict):
if self._code is not None:
compatibility_result = self._code.compatible_with_signature(
list(self._parameters.keys())
)
if compatibility_result != "":
raise ValueError(compatibility_result)

# set up update button and cueing
# -------------------------------

Expand All @@ -143,10 +160,6 @@ def __init__(
# set up parameter panel
# ----------------------

if isinstance(self._parameters, ParameterPanel):
self._parameter_panel = self._parameters
else:
self._parameter_panel = ParameterPanel(**self._parameters)
if self._update_mode == "continuous":
self._parameter_panel.set_parameters_widget_attr(
"continuous_update", True
Expand Down Expand Up @@ -178,7 +191,7 @@ def __init__(
cue_output._traits_to_observe = ["function_body"]
cue_output.observe_widgets()

# TODO set this
# TODO set this
self._output._widgets_to_observe = [self._code]
self._output._traits_to_observe = ["function_body"]
self._output.observe_widgets()
Expand Down Expand Up @@ -211,8 +224,12 @@ def __init__(
cue_output.observe_widgets()
else:
# TODO this has to be made public
cue_output._widgets_to_observe = self._parameter_panel.parameters_widget
cue_output._traits_to_observe = self._parameter_panel.parameters_trait
cue_output._widgets_to_observe = (
self._parameter_panel.parameters_widget
)
cue_output._traits_to_observe = (
self._parameter_panel.parameters_trait
)
cue_output.observe_widgets()

reset_update_cue_widgets = []
Expand All @@ -222,10 +239,12 @@ def __init__(
reset_update_cue_widgets.extend(self._cue_outputs)

if self._code is not None:
description="Run Code"
button_tooltip = "Runs the code and updates outputs with the specified parameters"
description = "Run Code"
button_tooltip = (
"Runs the code and updates outputs with the specified parameters"
)
else:
description="Update"
description = "Update"
button_tooltip = "Updates outputs with the specified parameters"

self._update_button = UpdateResetCueButton(
Expand Down Expand Up @@ -255,11 +274,13 @@ def __init__(
demo_children = []
if self._code is not None:
demo_children.append(self._cue_code)
demo_children.extend([
self._cue_parameter_panel,
self._buttons_panel,
self._output,
])
demo_children.extend(
[
self._cue_parameter_panel,
self._buttons_panel,
self._output,
]
)
demo_children.extend(self._cue_outputs)

VBox.__init__(
Expand Down Expand Up @@ -335,15 +356,17 @@ def _on_click_update_action(self) -> bool:
with self._output:
try:
for cue_output in self.cue_outputs:
cue_output.clear_display(wait=True)
if hasattr(cue_output, "clear_display"):
cue_output.clear_display(wait=True)

if self._update_process is not None:
self._update_process(self)
elif self._code is not None:
self.run_code(**self.panel_parameters)

for cue_output in self.cue_outputs:
cue_output.draw_display()
if hasattr(cue_output, "draw_display"):
cue_output.draw_display()

except CodeValidationError as e:
raised_error = True
Expand All @@ -355,13 +378,21 @@ def _on_click_update_action(self) -> bool:
return not (raised_error)

def run_code(self, *args, **kwargs) -> Check.FunOutParamsT:
"""
Runs the `code` with the given (keyword) arguments and returns the output of the
`code`. If no `code` was given on intialization, then a `ValueError` is raised.
"""
try:
if self._code is None:
raise ValueError(
"run_code was invoked, but no code was given on initializaion"
)
return self._code.run(*args, **kwargs)
except CodeValidationError as e:
raise e
except Exception as e:
# we give the student the additional information that this is most likely
# not because of his code
if python_version() >= "3.11":
e.add_note("This might might be not related to your code input.")
e.add_note("This might be not related to your code input.")
raise e

0 comments on commit f027027

Please sign in to comment.