scwidgets 0.1.0.dev0__tar.gz → 0.1.0.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/PKG-INFO +28 -12
  2. scwidgets-0.1.0.dev1/README.rst +35 -0
  3. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/__init__.py +2 -20
  4. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/check/_widget_check_registry.py +26 -2
  5. scwidgets-0.1.0.dev1/src/scwidgets/code/__init__.py +7 -0
  6. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/code/_widget_code_input.py +127 -35
  7. scwidgets-0.1.0.dev0/src/scwidgets/code/_widget_parameter_panel.py → scwidgets-0.1.0.dev1/src/scwidgets/code/_widget_parameters_panel.py +42 -23
  8. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/cue/_widget_cue.py +1 -1
  9. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/cue/_widget_cue_box.py +12 -4
  10. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/cue/_widget_cue_figure.py +5 -5
  11. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/cue/_widget_cue_object.py +13 -13
  12. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/cue/_widget_cue_output.py +1 -1
  13. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/exercise/_widget_code_exercise.py +140 -109
  14. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/exercise/_widget_exercise_registry.py +115 -45
  15. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/exercise/_widget_text_exercise.py +38 -30
  16. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets.egg-info/PKG-INFO +28 -12
  17. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets.egg-info/SOURCES.txt +2 -2
  18. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/tests/test_answer.py +32 -24
  19. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/tests/test_code.py +119 -44
  20. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/tests/test_widgets.py +3 -3
  21. scwidgets-0.1.0.dev0/README.rst +0 -19
  22. scwidgets-0.1.0.dev0/src/scwidgets/code/__init__.py +0 -7
  23. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/LICENSE +0 -0
  24. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/pyproject.toml +0 -0
  25. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/setup.cfg +0 -0
  26. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/_utils.py +0 -0
  27. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/check/__init__.py +0 -0
  28. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/check/_asserts.py +0 -0
  29. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/check/_check.py +0 -0
  30. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/css/widgets.css +0 -0
  31. /scwidgets-0.1.0.dev0/src/scwidgets/_css_style.py → /scwidgets-0.1.0.dev1/src/scwidgets/css_style.py +0 -0
  32. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/cue/__init__.py +0 -0
  33. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/cue/_widget_reset_cue_button.py +0 -0
  34. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets/exercise/__init__.py +0 -0
  35. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets.egg-info/dependency_links.txt +0 -0
  36. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets.egg-info/requires.txt +0 -0
  37. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/src/scwidgets.egg-info/top_level.txt +0 -0
  38. {scwidgets-0.1.0.dev0 → scwidgets-0.1.0.dev1}/tests/test_check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scwidgets
3
- Version: 0.1.0.dev0
3
+ Version: 0.1.0.dev1
4
4
  Summary: A collection of widgets to prepare interactive scientific visualisations, including user code input and validation
5
5
  License: BSD-3-Clause
6
6
  Classifier: Intended Audience :: Science/Research
@@ -24,22 +24,38 @@ Requires-Dist: widget_code_input>=4.0.13
24
24
  Requires-Dist: matplotlib
25
25
  Requires-Dist: termcolor
26
26
 
27
- Important
28
- =========
29
-
30
- So far scicode-widget has been created by prototyping without much concern about the code quality. This resulted in faster development time but in cost of readability and maintanability of the code. Since we finished now the prototype phase and have converged on a set of functionalities we are satisfied with, we are in the process of refactoring the resulting code in this branch. While we are refactoring we recommend the usage of the `vertical-slice branch <https://github.com/osscar-org/scicode-widgets/tree/vertical-slice>`_ till all features have been implemented in the refactor.
31
-
32
-
33
27
  scicode-widgets
34
28
  ===============
35
29
 
36
30
  .. marker-package-description
37
31
 
38
- A collection of ipywidgets for the creation of interactive code demos and educational notebooks with exercises that can be checked and exported.
32
+ *scicode-widgets* is a widget library for the purpose of creating computational
33
+ experiments for educational content. It is targeted to teach students how to
34
+ code and interpret computational experiments while hiding technical details and
35
+ boiler plate code that are not essential for the learning experience. The logic
36
+ is purely written in Python to simplify the development process for scientific
37
+ researchers as they usually are more capable in writing Python code than
38
+ JavaScript. It therefore builds on top of widgets provided by ipywidgets to
39
+ creating a framework out of it.
40
+
41
+ Features
42
+ --------
43
+
44
+ The core features of scicode-widgets are:
45
+
46
+ **Customizable coding exercises and demos**
47
+
48
+ **Checks for students to verify their solution**
49
+
50
+ **Automatic grading using nbgrader**
51
+
52
+ Please continue with our `getting started page <https://scicode-widgets.readthedocs.io/en/latest/getting_started.html>`_
53
+ for a more detailed overview of our features.
39
54
 
40
- Installation
41
- ------------
55
+ Supportde jupyter environments
56
+ ------------------------------
42
57
 
43
- .. code-block:: bash
58
+ We support the following widget environments
44
59
 
45
- pip install .
60
+ * jupyterlab
61
+ * notebook < 7
@@ -0,0 +1,35 @@
1
+ scicode-widgets
2
+ ===============
3
+
4
+ .. marker-package-description
5
+
6
+ *scicode-widgets* is a widget library for the purpose of creating computational
7
+ experiments for educational content. It is targeted to teach students how to
8
+ code and interpret computational experiments while hiding technical details and
9
+ boiler plate code that are not essential for the learning experience. The logic
10
+ is purely written in Python to simplify the development process for scientific
11
+ researchers as they usually are more capable in writing Python code than
12
+ JavaScript. It therefore builds on top of widgets provided by ipywidgets to
13
+ creating a framework out of it.
14
+
15
+ Features
16
+ --------
17
+
18
+ The core features of scicode-widgets are:
19
+
20
+ **Customizable coding exercises and demos**
21
+
22
+ **Checks for students to verify their solution**
23
+
24
+ **Automatic grading using nbgrader**
25
+
26
+ Please continue with our `getting started page <https://scicode-widgets.readthedocs.io/en/latest/getting_started.html>`_
27
+ for a more detailed overview of our features.
28
+
29
+ Supportde jupyter environments
30
+ ------------------------------
31
+
32
+ We support the following widget environments
33
+
34
+ * jupyterlab
35
+ * notebook < 7
@@ -1,38 +1,21 @@
1
- __version__ = "0.1.0-dev0"
1
+ __version__ = "0.1.0-dev1"
2
2
  __authors__ = "the scicode-widgets developer team"
3
3
 
4
- from ._css_style import CssStyle, get_css_style
5
4
  from .check import * # noqa: F403
6
5
  from .code import * # noqa: F403
7
6
  from .cue import * # noqa: F403
8
7
  from .exercise import * # noqa: F403
9
8
 
10
9
  __all__ = [ # noqa: F405
11
- # css_style
12
- "CssStyle",
13
- "get_css_style",
14
10
  # cue
15
- "CueWidget",
16
- "CheckCueBox",
17
- "CueBox",
18
- "SaveCueBox",
19
- "UpdateCueBox",
20
- "ResetCueButton",
21
- "SaveResetCueButton",
22
- "CheckResetCueButton",
23
- "UpdateResetCueButton",
24
11
  "CueOutput",
25
12
  "CueObject",
26
13
  "CueFigure",
27
14
  # code
28
15
  "CodeInput",
29
- "ParameterPanel",
16
+ "ParametersPanel",
30
17
  # check
31
- "Check",
32
- "CheckResult",
33
- "AssertResult",
34
18
  "CheckRegistry",
35
- "CheckableWidget",
36
19
  "assert_equal",
37
20
  "assert_shape",
38
21
  "assert_numpy_allclose",
@@ -42,6 +25,5 @@ __all__ = [ # noqa: F405
42
25
  # exercise
43
26
  "CodeExercise",
44
27
  "TextExercise",
45
- "ExerciseWidget",
46
28
  "ExerciseRegistry",
47
29
  ]
@@ -7,8 +7,8 @@ from typing import Callable, List, Optional, Union
7
7
 
8
8
  from ipywidgets import Button, HBox, Layout, Output, VBox, Widget
9
9
 
10
- from .._css_style import CssStyle
11
10
  from .._utils import Formatter
11
+ from ..css_style import CssStyle
12
12
  from ._check import Check, CheckResult
13
13
 
14
14
 
@@ -147,11 +147,19 @@ class CheckRegistry(VBox):
147
147
  self._check_all_widgets_button = Button(description="Check all widgets")
148
148
  self._output = Output()
149
149
  kwargs["layout"] = kwargs.pop("layout", Layout(width="100%"))
150
+
151
+ self._buttons_hbox = HBox()
152
+
153
+ # needs to be after the _buttons_hbox already was created
154
+ self.display_set_all_references_button = kwargs.pop(
155
+ "display_set_all_references_button", False
156
+ )
157
+
150
158
  VBox.__init__(
151
159
  self,
152
160
  [
153
161
  CssStyle(),
154
- HBox([self._set_all_references_button, self._check_all_widgets_button]),
162
+ self._buttons_hbox,
155
163
  self._output,
156
164
  ],
157
165
  *args,
@@ -170,6 +178,22 @@ class CheckRegistry(VBox):
170
178
  """
171
179
  return self._checks
172
180
 
181
+ @property
182
+ def display_set_all_references_button(self) -> bool:
183
+ return self._display_set_all_references_button
184
+
185
+ @display_set_all_references_button.setter
186
+ def display_set_all_references_button(self, value: bool):
187
+ if value:
188
+ self._display_set_all_references_button = True
189
+ self._buttons_hbox.children = (
190
+ self._check_all_widgets_button,
191
+ self._set_all_references_button,
192
+ )
193
+ else:
194
+ self._display_set_all_references_button = False
195
+ self._buttons_hbox.children = (self._check_all_widgets_button,)
196
+
173
197
  @property
174
198
  def registered_widgets(self):
175
199
  return self._widgets.copy()
@@ -0,0 +1,7 @@
1
+ from ._widget_code_input import CodeInput
2
+ from ._widget_parameters_panel import ParametersPanel
3
+
4
+ __all__ = [
5
+ "CodeInput",
6
+ "ParametersPanel",
7
+ ]
@@ -1,11 +1,14 @@
1
+ import ast
2
+ import copy
1
3
  import inspect
2
4
  import re
3
5
  import sys
6
+ import textwrap
4
7
  import traceback
5
8
  import types
6
9
  import warnings
7
10
  from functools import wraps
8
- from typing import List, Optional
11
+ from typing import Any, List, Optional, Tuple
9
12
 
10
13
  from widget_code_input import WidgetCodeInput
11
14
  from widget_code_input.utils import (
@@ -20,6 +23,20 @@ from ..check import Check
20
23
  class CodeInput(WidgetCodeInput):
21
24
  """
22
25
  Small wrapper around WidgetCodeInput that controls the output
26
+
27
+ :param function: We can automatically parse the function. Note that during
28
+ parsing the source code might be differently formatted and certain
29
+ python functionalities are not formatted. If you notice undesired
30
+ changes by the parsing, please directly specify the function as string
31
+ using the other parameters.
32
+ :param function_name: The name of the function
33
+ :param function_paramaters: The parameters as continuous string as specified in
34
+ the signature of the function. e.g for `foo(x, y = 5)` it should be
35
+ `"x, y = 5"`
36
+ :param docstring: The docstring of the function
37
+ :param function_body: The function definition without indentation
38
+ :param builtins: A dict of variable name and value that is added to the
39
+ globals __builtins__ and thus available on initialization
23
40
  """
24
41
 
25
42
  valid_code_themes = ["nord", "solarizedLight", "basicLight"]
@@ -31,6 +48,7 @@ class CodeInput(WidgetCodeInput):
31
48
  function_parameters: Optional[str] = None,
32
49
  docstring: Optional[str] = None,
33
50
  function_body: Optional[str] = None,
51
+ builtins: Optional[dict[str, Any]] = None,
34
52
  code_theme: str = "basicLight",
35
53
  ):
36
54
  if function is not None:
@@ -38,13 +56,15 @@ class CodeInput(WidgetCodeInput):
38
56
  function.__name__ if function_name is None else function_name
39
57
  )
40
58
  function_parameters = (
41
- ", ".join(inspect.getfullargspec(function).args)
59
+ self.get_function_parameters(function)
42
60
  if function_parameters is None
43
61
  else function_parameters
44
62
  )
45
- docstring = inspect.getdoc(function) if docstring is None else docstring
63
+ docstring = self.get_docstring(function) if docstring is None else docstring
46
64
  function_body = (
47
- self.get_code(function) if function_body is None else function_body
65
+ self.get_function_body(function)
66
+ if function_body is None
67
+ else function_body
48
68
  )
49
69
 
50
70
  # default parameters from WidgetCodeInput
@@ -53,6 +73,7 @@ class CodeInput(WidgetCodeInput):
53
73
  function_parameters = "" if function_parameters is None else function_parameters
54
74
  docstring = "\n" if docstring is None else docstring
55
75
  function_body = "" if function_body is None else function_body
76
+ self._builtins = {} if builtins is None else builtins
56
77
  super().__init__(
57
78
  function_name, function_parameters, docstring, function_body, code_theme
58
79
  )
@@ -66,7 +87,7 @@ class CodeInput(WidgetCodeInput):
66
87
  )
67
88
 
68
89
  @property
69
- def function(self) -> types.FunctionType:
90
+ def unwrapped_function(self) -> types.FunctionType:
70
91
  """
71
92
  Return the compiled function object.
72
93
 
@@ -78,11 +99,37 @@ class CodeInput(WidgetCodeInput):
78
99
  :raise SyntaxError: if the function code has syntax errors (or if
79
100
  the function name is not a valid identifier)
80
101
  """
81
- return inspect.unwrap(self.wrapped_function)
102
+ # we shallow copy the builtins to be able to overwrite it
103
+ # if self.builtins changes
104
+ globals_dict = {
105
+ "__builtins__": copy.copy(globals()["__builtins__"]),
106
+ "__name__": "__main__",
107
+ "__doc__": None,
108
+ "__package__": None,
109
+ }
110
+
111
+ globals_dict["__builtins__"].update(self._builtins)
112
+
113
+ if not is_valid_variable_name(self.function_name):
114
+ raise SyntaxError("Invalid function name '{}'".format(self.function_name))
115
+
116
+ # Optionally one could do a ast.parse here already, to check syntax
117
+ # before execution
118
+ try:
119
+ exec(
120
+ compile(self.full_function_code, __name__, "exec", dont_inherit=True),
121
+ globals_dict,
122
+ )
123
+ except SyntaxError as exc:
124
+ raise CodeValidationError(
125
+ format_syntax_error_msg(exc), orig_exc=exc
126
+ ) from exc
127
+
128
+ return globals_dict[self.function_name]
82
129
 
83
130
  def __call__(self, *args, **kwargs) -> Check.FunOutParamsT:
84
131
  """Calls the wrapped function"""
85
- return self.wrapped_function(*args, **kwargs)
132
+ return self.function(*args, **kwargs)
86
133
 
87
134
  def compatible_with_signature(self, parameters: List[str]) -> str:
88
135
  """
@@ -105,8 +152,68 @@ class CodeInput(WidgetCodeInput):
105
152
  return self.function_parameters.replace(",", "").split(" ")
106
153
 
107
154
  @staticmethod
108
- def get_code(func: types.FunctionType) -> str:
109
- source_lines, _ = inspect.getsourcelines(func)
155
+ def get_docstring(function: types.FunctionType) -> str:
156
+ docstring = function.__doc__
157
+ return "" if docstring is None else textwrap.dedent(docstring)
158
+
159
+ @staticmethod
160
+ def _get_function_source_and_def(
161
+ function: types.FunctionType,
162
+ ) -> Tuple[str, ast.FunctionDef]:
163
+ function_source = inspect.getsource(function)
164
+ function_source = textwrap.dedent(function_source)
165
+ module = ast.parse(function_source)
166
+ if len(module.body) != 1:
167
+ raise ValueError(
168
+ f"Expected code with one function definition but found {module.body}"
169
+ )
170
+ function_definition = module.body[0]
171
+ if not isinstance(function_definition, ast.FunctionDef):
172
+ raise ValueError(
173
+ f"While parsing code found {module.body[0]}"
174
+ " but only ast.FunctionDef is supported."
175
+ )
176
+ return function_source, function_definition
177
+
178
+ @staticmethod
179
+ def get_function_parameters(function: types.FunctionType) -> str:
180
+ function_parameters = []
181
+ function_source, function_definition = CodeInput._get_function_source_and_def(
182
+ function
183
+ )
184
+ idx_start_defaults = len(function_definition.args.args) - len(
185
+ function_definition.args.defaults
186
+ )
187
+ for i, arg in enumerate(function_definition.args.args):
188
+ function_parameter = ast.get_source_segment(function_source, arg)
189
+ # Following PEP 8 in formatting
190
+ if arg.annotation:
191
+ annotation = function_parameter = ast.get_source_segment(
192
+ function_source, arg.annotation
193
+ )
194
+ function_parameter = f"{arg.arg}: {annotation}"
195
+ else:
196
+ function_parameter = f"{arg.arg}"
197
+ if i >= idx_start_defaults:
198
+ default_val = ast.get_source_segment(
199
+ function_source,
200
+ function_definition.args.defaults[i - idx_start_defaults],
201
+ )
202
+ # Following PEP 8 in formatting
203
+ if arg.annotation:
204
+ function_parameter = f"{function_parameter} = {default_val}"
205
+ else:
206
+ function_parameter = f"{function_parameter}={default_val}"
207
+ function_parameters.append(function_parameter)
208
+
209
+ if function_definition.args.kwarg is not None:
210
+ function_parameters.append(f"**{function_definition.args.kwarg.arg}")
211
+
212
+ return ", ".join(function_parameters)
213
+
214
+ @staticmethod
215
+ def get_function_body(function: types.FunctionType) -> str:
216
+ source_lines, _ = inspect.getsourcelines(function)
110
217
 
111
218
  found_def = False
112
219
  def_index = 0
@@ -144,10 +251,10 @@ class CodeInput(WidgetCodeInput):
144
251
  line[leading_indent:] if line.strip() else "" for line in lines
145
252
  )
146
253
 
147
- return source
254
+ return source.strip()
148
255
 
149
256
  @property
150
- def wrapped_function(self) -> types.FunctionType:
257
+ def function(self) -> types.FunctionType:
151
258
  """
152
259
  Return the compiled function object wrapped by an try-catch block
153
260
  raising a `CodeValidationError`.
@@ -160,29 +267,6 @@ class CodeInput(WidgetCodeInput):
160
267
  :raise SyntaxError: if the function code has syntax errors (or if
161
268
  the function name is not a valid identifier)
162
269
  """
163
- globals_dict = {
164
- "__builtins__": globals()["__builtins__"],
165
- "__name__": "__main__",
166
- "__doc__": None,
167
- "__package__": None,
168
- }
169
-
170
- if not is_valid_variable_name(self.function_name):
171
- raise SyntaxError("Invalid function name '{}'".format(self.function_name))
172
-
173
- # Optionally one could do a ast.parse here already, to check syntax
174
- # before execution
175
- try:
176
- exec(
177
- compile(self.full_function_code, __name__, "exec", dont_inherit=True),
178
- globals_dict,
179
- )
180
- except SyntaxError as exc:
181
- raise CodeValidationError(
182
- format_syntax_error_msg(exc), orig_exc=exc
183
- ) from exc
184
-
185
- function_object = globals_dict[self.function_name]
186
270
 
187
271
  def catch_exceptions(func):
188
272
  @wraps(func)
@@ -198,7 +282,15 @@ class CodeInput(WidgetCodeInput):
198
282
 
199
283
  return wrapper
200
284
 
201
- return catch_exceptions(function_object)
285
+ return catch_exceptions(self.unwrapped_function)
286
+
287
+ @property
288
+ def builtins(self) -> dict[str, Any]:
289
+ return self._builtins
290
+
291
+ @builtins.setter
292
+ def builtins(self, value: dict[str, Any]):
293
+ self._builtins = value
202
294
 
203
295
 
204
296
  # Temporary fix until https://github.com/osscar-org/widget-code-input/pull/26
@@ -1,4 +1,4 @@
1
- from typing import Callable, Dict, List, Union
1
+ from typing import Any, Callable, Dict, List, Union
2
2
 
3
3
  from ipywidgets import Output, VBox, Widget, fixed, interactive
4
4
  from traitlets.utils.sentinel import Sentinel
@@ -6,7 +6,7 @@ from traitlets.utils.sentinel import Sentinel
6
6
  from ..check import Check
7
7
 
8
8
 
9
- class ParameterPanel(VBox):
9
+ class ParametersPanel(VBox):
10
10
  """
11
11
  A wrapper around ipywidgets.interactive to have more control how to connect the
12
12
  parameters and the observation of parameters by buttons and the panels
@@ -25,7 +25,7 @@ class ParameterPanel(VBox):
25
25
  if "_option" in parameters.keys():
26
26
  raise ValueError(
27
27
  "Found interactive argument `_option` in paramaters, but "
28
- "ParameterPanels should be controled by an exercise widget "
28
+ "ParametersPanels should be controled by an exercise widget "
29
29
  "to ensure correct initialization."
30
30
  )
31
31
 
@@ -39,42 +39,61 @@ class ParameterPanel(VBox):
39
39
  "Assumed that interactive returns an output as last child. "
40
40
  "Parameter will be wrongly initialized if this is not True."
41
41
  )
42
- self._parameters_widget = list(self._interactive_widget.children[:-1])
43
- super().__init__(self._parameters_widget)
42
+ # Because interact only keeps a list of the widgets we build a map
43
+ # so the params can be changed in arbitrary order.
44
+ # Last widget is an output that interact adds to the widgets.
45
+ self._param_to_widget_map = {
46
+ key: widget
47
+ for key, widget in zip(
48
+ parameters.keys(), self._interactive_widget.kwargs_widgets
49
+ )
50
+ }
51
+ super().__init__(self.panel_parameters_widget)
44
52
 
45
53
  @property
46
- def parameters_widget(self) -> List[Widget]:
47
- return self._parameters_widget
54
+ def param_to_widget_map(self) -> dict[str, Widget]:
55
+ return self._param_to_widget_map
48
56
 
49
57
  @property
50
- def parameters_trait(self) -> List[str]:
51
- return ["value"] * len(self._parameters_widget)
58
+ def panel_parameters_trait(self) -> List[str]:
59
+ return ["value"] * len(self.panel_parameters)
60
+
61
+ @property
62
+ def panel_parameters_widget(self) -> List[Widget]:
63
+ """
64
+ :return: Only parameters that are tunable in the parameter panel are returned.
65
+ Fixed parameters are ignored.
66
+ """
67
+ return [
68
+ widget
69
+ for widget in self._param_to_widget_map.values()
70
+ if not (isinstance(widget, fixed))
71
+ ]
52
72
 
53
73
  @property
54
- def params(self) -> dict:
74
+ def parameters(self) -> Dict[str, Any]:
55
75
  """
56
76
  :return: All parameters that were given on initialization are returned,
57
77
  also including also fixed parameters.
58
78
  """
59
- return self._interactive_widget.kwargs.copy()
60
-
61
- @params.setter
62
- def params(self, parameters: dict):
63
- for i, key in enumerate(self._interactive_widget.kwargs.keys()):
64
- self._interactive_widget.kwargs_widgets[i].value = parameters[key]
79
+ return {key: widget.value for key, widget in self._param_to_widget_map.items()}
65
80
 
66
81
  @property
67
- def panel_parameters(self) -> dict:
82
+ def panel_parameters(self) -> Dict[str, Any]:
68
83
  """
69
84
  :return: Only parameters that are tunable in the parameter panel are returned.
70
85
  Fixed parameters are ignored.
71
86
  """
72
87
  return {
73
- key: self._interactive_widget.kwargs_widgets[i].value
74
- for i, key in enumerate(self._interactive_widget.kwargs.keys())
75
- if not (isinstance(self._interactive_widget.kwargs_widgets[i], fixed))
88
+ key: widget.value
89
+ for key, widget in self._param_to_widget_map.items()
90
+ if not (isinstance(widget, fixed))
76
91
  }
77
92
 
93
+ def update_parameters(self, new_parameters: Dict[str, Any]):
94
+ for key, value in new_parameters.items():
95
+ self.param_to_widget_map[key].value = value
96
+
78
97
  def observe_parameters(
79
98
  self,
80
99
  handler: Callable[[dict], None],
@@ -82,7 +101,7 @@ class ParameterPanel(VBox):
82
101
  notification_type: Union[None, str, Sentinel] = "change",
83
102
  ):
84
103
  """ """
85
- for widget in self._parameters_widget:
104
+ for widget in self.panel_parameters_widget:
86
105
  widget.observe(handler, trait_name, notification_type)
87
106
 
88
107
  def unobserve_parameters(
@@ -91,10 +110,10 @@ class ParameterPanel(VBox):
91
110
  trait_name: Union[str, Sentinel, List[str]],
92
111
  notification_type: Union[None, str, Sentinel] = "change",
93
112
  ):
94
- for widget in self._parameters_widget:
113
+ for widget in self.panel_parameters_widget:
95
114
  widget.unobserve(handler, trait_name, notification_type)
96
115
 
97
116
  def set_parameters_widget_attr(self, name: str, value):
98
- for widget in self._parameters_widget:
117
+ for widget in self.panel_parameters_widget:
99
118
  if hasattr(widget, name):
100
119
  setattr(widget, name, value)
@@ -56,7 +56,7 @@ class CueWidget:
56
56
  "traits_to_observe cannot contain lists when "
57
57
  "widgets_to_observe is not a list."
58
58
  )
59
- traits_to_observe = [traits_to_observe]
59
+ traits_to_observe = [traits_to_observe] # type: ignore[list-item]
60
60
  else:
61
61
  if not (isinstance(traits_to_observe, list)):
62
62
  raise ValueError(
@@ -37,7 +37,9 @@ class CueBox(VBox, CueWidget):
37
37
  def __init__(
38
38
  self,
39
39
  widgets_to_observe: Union[List[Widget], Widget],
40
- traits_to_observe: Union[str, List[str], List[List[str]], Sentinel] = "value",
40
+ traits_to_observe: Union[
41
+ str, Sentinel, List[Union[str, Sentinel, List[str]]]
42
+ ] = "value",
41
43
  widget_to_cue: Optional[Widget] = None,
42
44
  cued: bool = True,
43
45
  css_style: Optional[dict] = None,
@@ -112,7 +114,9 @@ class SaveCueBox(CueBox):
112
114
  def __init__(
113
115
  self,
114
116
  widgets_to_observe: Widget,
115
- traits_to_observe: Union[str, List[str], Sentinel] = "value",
117
+ traits_to_observe: Union[
118
+ str, Sentinel, List[Union[str, Sentinel, List[str]]]
119
+ ] = "value",
116
120
  widget_to_cue: Optional[Widget] = None,
117
121
  cued: bool = True,
118
122
  *args,
@@ -151,7 +155,9 @@ class CheckCueBox(CueBox):
151
155
  def __init__(
152
156
  self,
153
157
  widgets_to_observe: Widget,
154
- traits_to_observe: Union[str, List[str], Sentinel] = "value",
158
+ traits_to_observe: Union[
159
+ str, Sentinel, List[Union[str, Sentinel, List[str]]]
160
+ ] = "value",
155
161
  widget_to_cue: Optional[Widget] = None,
156
162
  cued: bool = True,
157
163
  *args,
@@ -190,7 +196,9 @@ class UpdateCueBox(CueBox):
190
196
  def __init__(
191
197
  self,
192
198
  widgets_to_observe: Widget,
193
- traits_to_observe: Union[str, List[str], Sentinel] = "value",
199
+ traits_to_observe: Union[
200
+ str, Sentinel, List[Union[str, Sentinel, List[str]]]
201
+ ] = "value",
194
202
  widget_to_cue: Optional[Widget] = None,
195
203
  cued: bool = True,
196
204
  *args,
@@ -32,8 +32,8 @@ class CueFigure(CueOutput):
32
32
  Specify `traitlets.All` to observe all traits.
33
33
  :param cued:
34
34
  Specifies if it is cued on initialization
35
- :param no_toolbars:
36
- Hide toolbars and headers when using widget mode
35
+ :param show_toolbars:
36
+ Hide toolbars and headers when using in widget mode.
37
37
  :param css_syle:
38
38
  - **base**: the css style of the box during initialization
39
39
  - **cue**: the css style that is added when :param
@@ -47,10 +47,10 @@ class CueFigure(CueOutput):
47
47
  figure: Figure,
48
48
  widgets_to_observe: Union[None, List[Widget], Widget] = None,
49
49
  traits_to_observe: Union[
50
- None, str, List[str], List[List[str]], Sentinel
50
+ None, str, Sentinel, List[Union[str, Sentinel, List[str]]]
51
51
  ] = None,
52
52
  cued: bool = True,
53
- no_toolbars: bool = True,
53
+ show_toolbars: bool = False,
54
54
  css_style: Optional[dict] = None,
55
55
  **kwargs,
56
56
  ):
@@ -87,7 +87,7 @@ class CueFigure(CueOutput):
87
87
  "that should be supported on all systems."
88
88
  )
89
89
 
90
- if no_toolbars:
90
+ if show_toolbars:
91
91
  # hides unnecessary elements shown with %matplotlib widget
92
92
  self.figure.canvas.header_visible = False
93
93
  self.figure.canvas.footer_visible = False