steer-core 0.1.25__py3-none-any.whl → 0.1.27__py3-none-any.whl

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.
@@ -1,693 +0,0 @@
1
- import dash as ds
2
- import numpy as np
3
- from typing import Union
4
- from dash import Input, Output
5
-
6
-
7
- class SliderWithTextInput:
8
- """
9
- A custom Dash component that combines a slider and text input for synchronized value control.
10
-
11
- This component creates a user interface element consisting of a slider and a numeric input field
12
- that can be used together to set numeric values. The slider provides visual feedback and easy
13
- adjustment, while the text input allows for precise value entry. Both components are synchronized
14
- and share the same value constraints.
15
-
16
- The component is designed for use in Dash applications where users need to input numeric values
17
- within a specified range, with the flexibility of both visual (slider) and precise (text input)
18
- control methods.
19
-
20
- Attributes:
21
- id_base (dict): Base identifier dictionary used to construct unique IDs for child components
22
- min_val (float): Minimum allowed value for both slider and input
23
- max_val (float): Maximum allowed value for both slider and input
24
- default_val (Union[float, list[float], None]): Default value to display on initialization
25
- step (float): Step size for value increments/decrements
26
- mark_interval (float): Interval between tick marks on the slider
27
- property_name (str): Property identifier used in component ID construction
28
- title (str): Display title for the component
29
- with_slider_titles (bool): Whether to show the title above the slider
30
- div_width (str): CSS width specification for the container div
31
- slider_disable (bool): Whether the components should be disabled
32
- message (str): Optional message displayed between title and slider
33
- slider_id (dict): Computed ID for the slider component
34
- input_id (dict): Computed ID for the input component
35
-
36
- Example:
37
- >>> slider_component = SliderWithTextInput(
38
- ... id_base={'type': 'parameter', 'index': 0},
39
- ... property_name='temperature',
40
- ... title='Temperature (°C)',
41
- ... min_val=0.0,
42
- ... max_val=100.0,
43
- ... step=1.0,
44
- ... mark_interval=50.0,
45
- ... default_val=25.0,
46
- ... message='Optimal range is 20-30°C' # Optional message
47
- ... )
48
- >>> layout_element = slider_component() # Returns Dash HTML Div component
49
- """
50
-
51
- def __init__(
52
- self,
53
- id_base: dict,
54
- property_name: str,
55
- title: str,
56
- min_val: float = 0.0,
57
- max_val: float = 100.0,
58
- step: float = 1.0,
59
- mark_interval: float = 50.0,
60
- default_val: Union[float, list[float]] = None,
61
- with_slider_titles: bool = True,
62
- slider_disable: bool = False,
63
- div_width: str = "calc(90%)",
64
- message: str = None,
65
- marks: dict = None,
66
- ):
67
- """
68
- Initialize the SliderWithTextInput component.
69
-
70
- Args:
71
- id_base (dict): Base dictionary for generating component IDs. Should contain
72
- identifying information that will be extended with component-specific
73
- subtypes and properties.
74
- property_name (str): A string identifier for this specific property, used
75
- in ID generation and callbacks.
76
- title (str): Human-readable title displayed above the component.
77
- min_val (float, optional): Minimum value that can be selected on the slider or entered
78
- in the text input. Defaults to 0.0.
79
- max_val (float, optional): Maximum value that can be selected on the slider or entered
80
- in the text input. Defaults to 100.0.
81
- step (float, optional): The granularity of value changes. Determines the smallest
82
- increment/decrement possible. Defaults to 1.0.
83
- mark_interval (float, optional): The spacing between tick marks displayed on the slider.
84
- Should be a multiple of step for best visual alignment. Defaults to 50.0.
85
- default_val (Union[float, list[float]], optional): Initial value to display.
86
- If None, defaults to min_val. Can be a single float or list
87
- for compatibility with range sliders.
88
- with_slider_titles (bool, optional): If True, displays the title above
89
- the slider. If False, shows a non-breaking
90
- space to maintain layout. Defaults to True.
91
- slider_disable (bool, optional): If True, disables both slider and input
92
- interactions. Defaults to False.
93
- div_width (str, optional): CSS width specification for the container div.
94
- Defaults to 'calc(90%)'.
95
- message (str, optional): Optional message to display between the title
96
- and slider. If None, no message is displayed.
97
- Defaults to None.
98
- marks (dict, optional): Pre-computed marks dictionary for the slider.
99
- If provided, these marks will be used instead of
100
- auto-generating marks from mark_interval.
101
- Keys should be numeric positions, values should be labels.
102
- Defaults to None.
103
-
104
- Raises:
105
- ValueError: If min_val >= max_val, or if step <= 0, or if mark_interval <= 0.
106
- TypeError: If default_val is provided but not numeric.
107
- """
108
-
109
- self.id_base = id_base
110
- self.min_val = min_val
111
- self.max_val = max_val
112
- self.default_val = default_val
113
- self.step = step
114
- self.mark_interval = mark_interval
115
- self.property_name = property_name
116
- self.title = title
117
- self.with_slider_titles = with_slider_titles
118
- self.div_width = div_width
119
- self.slider_disable = slider_disable
120
- self.message = message
121
- self.marks = marks
122
-
123
- self.slider_id = self._make_id("slider")
124
- self.input_id = self._make_id("input")
125
-
126
- def _make_id(self, subtype: str):
127
- """
128
- Generate a unique ID dictionary for component sub-elements.
129
-
130
- Combines the base ID with component-specific subtype and property information
131
- to create unique identifiers for Dash callbacks and component references.
132
-
133
- Args:
134
- subtype (str): The specific component subtype (e.g., 'slider', 'input').
135
-
136
- Returns:
137
- dict: Complete ID dictionary containing base ID information plus subtype
138
- and property specifications.
139
-
140
- Example:
141
- >>> component._make_id('slider')
142
- {'type': 'parameter', 'index': 0, 'subtype': 'slider', 'property': 'temperature'}
143
- """
144
- return {**self.id_base, "subtype": subtype, "property": self.property_name}
145
-
146
- def _make_slider(self):
147
- """
148
- Create and configure the Dash slider component.
149
-
150
- Generates a dcc.Slider with the specified range, step size, default value,
151
- and tick marks. The slider provides visual feedback for value selection
152
- and is synchronized with the text input component.
153
-
154
- Returns:
155
- dash.dcc.Slider: Configured slider component with ID, value constraints,
156
- tick marks, and styling options.
157
-
158
- Note:
159
- - Uses pre-computed marks if provided, otherwise generates tick marks
160
- at intervals specified by mark_interval
161
- - updatemode is set to 'mouseup' to reduce callback frequency
162
- - The slider can be disabled via the slider_disable attribute
163
- """
164
- # Use provided marks or generate them from mark_interval
165
- if self.marks is not None:
166
- slider_marks = self.marks
167
- else:
168
- slider_marks = {
169
- int(i): ""
170
- for i in np.arange(
171
- self.min_val, self.max_val + self.mark_interval, self.mark_interval
172
- )
173
- }
174
-
175
- return ds.dcc.Slider(
176
- id=self.slider_id,
177
- min=self.min_val,
178
- max=self.max_val,
179
- value=self.default_val,
180
- step=self.step,
181
- disabled=self.slider_disable,
182
- marks=slider_marks,
183
- updatemode="mouseup",
184
- tooltip={"placement": "right", "always_visible": False}
185
- )
186
-
187
- def _make_input(self):
188
- """
189
- Create and configure the Dash numeric input component.
190
-
191
- Generates a dcc.Input with number type for precise value entry.
192
- The input is synchronized with the slider and provides an alternative
193
- method for users to specify exact values.
194
-
195
- Returns:
196
- dash.dcc.Input: Configured numeric input component with ID, type,
197
- value constraints, styling, and step specification.
198
-
199
- Note:
200
- - Input type is set to 'number' for numeric validation
201
- - Left margin styling provides visual alignment with slider
202
- - Step size matches the slider for consistent granularity
203
- """
204
- return ds.dcc.Input(
205
- id=self.input_id,
206
- type="number",
207
- value=self.default_val,
208
- style={"margin-left": "20px"},
209
- step=self.step,
210
- disabled=self.slider_disable,
211
- )
212
-
213
- def __call__(self):
214
- """
215
- Generate the complete component layout as a callable object.
216
-
217
- Creates and returns a Dash HTML Div containing the title, optional message,
218
- slider, and input components arranged in a cohesive layout. This method allows
219
- the class instance to be used as a callable that returns the complete
220
- component structure.
221
-
222
- Returns:
223
- dash.html.Div: Complete component layout containing:
224
- - Title paragraph (conditional based on with_slider_titles)
225
- - Optional message paragraph (if message is provided)
226
- - Slider component in a styled container
227
- - Numeric input component
228
- - Spacing elements (line breaks)
229
-
230
- Note:
231
- - Title display is controlled by with_slider_titles attribute
232
- - When title is hidden, a non-breaking space maintains layout
233
- - Message is displayed only if provided during initialization
234
- - Negative bottom margin on slider container reduces spacing
235
- - Container width is controlled by div_width attribute
236
- """
237
- slider_title = self.title if self.with_slider_titles else "\u00A0"
238
-
239
- # Build the component list
240
- components = [
241
- ds.html.P(
242
- slider_title,
243
- style={
244
- "margin-left": "20px",
245
- "margin-bottom": "0px",
246
- "font-weight": "bold",
247
- },
248
- )
249
- ]
250
-
251
- # Add optional message if provided
252
- if self.message:
253
- components.append(
254
- ds.html.P(
255
- self.message,
256
- style={
257
- "margin-left": "20px",
258
- "margin-bottom": "5px",
259
- "margin-top": "2px",
260
- "font-size": "0.9em",
261
- "color": "#666666",
262
- "font-style": "italic",
263
- },
264
- )
265
- )
266
-
267
- # Add slider, input, and breaks
268
- components.extend(
269
- [
270
- ds.html.Div([self._make_slider()], style={"margin-bottom": "-18px"}),
271
- self._make_input(),
272
- ds.html.Br(),
273
- ds.html.Br(),
274
- ]
275
- )
276
-
277
- return ds.html.Div(
278
- components, style={"width": self.div_width, "margin-left": "-20px"}
279
- )
280
-
281
- @property
282
- def components(self):
283
- """
284
- Get a dictionary mapping component types to their IDs.
285
-
286
- Provides easy access to the IDs of the slider and input components
287
- for use in Dash callbacks and component interactions.
288
-
289
- Returns:
290
- dict: Dictionary with component type keys mapping to their ID dictionaries.
291
-
292
- Example:
293
- >>> component.components
294
- {
295
- 'slider': {'type': 'parameter', 'subtype': 'slider', 'property': 'temperature'},
296
- 'input': {'type': 'parameter', 'subtype': 'input', 'property': 'temperature'}
297
- }
298
-
299
- Note:
300
- This property is particularly useful for setting up Dash callbacks
301
- that need to reference the specific component IDs.
302
- """
303
- return {"slider": self.slider_id, "input": self.input_id}
304
-
305
- def get_value_inputs(self):
306
- """
307
- Get Input objects for listening to component value changes.
308
-
309
- Returns a list of Dash Input objects that can be used in callbacks to
310
- listen for value changes from either the slider or input components.
311
-
312
- Returns:
313
- list: List containing [Input(slider_id, 'value'), Input(input_id, 'value')]
314
-
315
- Example:
316
- >>> @app.callback(
317
- ... Output('some-output', 'children'),
318
- ... component.get_value_inputs()
319
- ... )
320
- ... def update_display(slider_val, input_val):
321
- ... return f"Value: {slider_val or input_val}"
322
- """
323
- return [Input(self.slider_id, "value"), Input(self.input_id, "value")]
324
-
325
- def get_value_outputs(self):
326
- """
327
- Get Output objects for updating component values.
328
-
329
- Returns a list of Dash Output objects that can be used in callbacks to
330
- update the values of both the slider and input components.
331
-
332
- Returns:
333
- list: List containing [Output(slider_id, 'value'), Output(input_id, 'value')]
334
-
335
- Example:
336
- >>> @app.callback(
337
- ... component.get_value_outputs(),
338
- ... [Input('some-input', 'value')]
339
- ... )
340
- ... def update_components(new_value):
341
- ... return [new_value, new_value]
342
- """
343
- return [Output(self.slider_id, "value"), Output(self.input_id, "value")]
344
-
345
- def get_pattern_matching_value_inputs(self, property_name="ALL"):
346
- """
347
- Get pattern-matching Input objects for listening to multiple component instances.
348
-
349
- Returns Input objects using pattern-matching to listen to all components
350
- with the specified property name, useful for multi-component callbacks.
351
-
352
- Args:
353
- property_name: The property to match. Use 'ALL' for all properties,
354
- or specify a specific property name.
355
-
356
- Returns:
357
- list: List containing pattern-matching Input objects
358
-
359
- Example:
360
- >>> @app.callback(
361
- ... Output('summary', 'children'),
362
- ... component.get_pattern_matching_value_inputs('ALL')
363
- ... )
364
- ... def update_summary(slider_values, input_values):
365
- ... return f"Total sliders: {len(slider_values)}"
366
- """
367
- pattern = {"type": "parameter", "subtype": "slider", "property": property_name}
368
- input_pattern = {
369
- "type": "parameter",
370
- "subtype": "input",
371
- "property": property_name,
372
- }
373
-
374
- return [Input(pattern, "value"), Input(input_pattern, "value")]
375
-
376
- def get_pattern_matching_value_outputs(self, property_name="ALL"):
377
- """
378
- Get pattern-matching Output objects for updating multiple component instances.
379
-
380
- Returns Output objects using pattern-matching to update all components
381
- with the specified property name.
382
-
383
- Args:
384
- property_name: The property to match. Use 'ALL' for all properties,
385
- or specify a specific property name.
386
-
387
- Returns:
388
- list: List containing pattern-matching Output objects
389
-
390
- Example:
391
- >>> @app.callback(
392
- ... component.get_pattern_matching_value_outputs('ALL'),
393
- ... [Input('reset-button', 'n_clicks')]
394
- ... )
395
- ... def reset_all_components(n_clicks):
396
- ... if n_clicks:
397
- ... return [[0] * len(slider_values), [0] * len(input_values)]
398
- ... return [dash.no_update, dash.no_update]
399
- """
400
- pattern = {"type": "parameter", "subtype": "slider", "property": property_name}
401
- input_pattern = {
402
- "type": "parameter",
403
- "subtype": "input",
404
- "property": property_name,
405
- }
406
-
407
- return [Output(pattern, "value"), Output(input_pattern, "value")]
408
-
409
- def _validate_and_clamp_value(self, value):
410
- """
411
- Validate and clamp a value to the component's valid range.
412
-
413
- Ensures that any input value is within the min_val to max_val range
414
- and handles None values appropriately.
415
-
416
- Args:
417
- value: The value to validate and clamp.
418
-
419
- Returns:
420
- float: The clamped value within the valid range, or the current
421
- default_val if the input value is invalid.
422
- """
423
- if value is None:
424
- return self.default_val if self.default_val is not None else self.min_val
425
-
426
- try:
427
- numeric_value = float(value)
428
- return max(self.min_val, min(self.max_val, numeric_value))
429
- except (ValueError, TypeError):
430
- return self.default_val if self.default_val is not None else self.min_val
431
-
432
-
433
- class SliderWithTextInputAndCheckbox(SliderWithTextInput):
434
- """
435
- A custom Dash component that extends SliderWithTextInput by adding a checkbox with a message.
436
-
437
- This component inherits all functionality from SliderWithTextInput and adds a checkbox
438
- positioned beneath the input field. The checkbox can be used to enable/disable features,
439
- toggle options, or provide additional control over the parameter being adjusted.
440
-
441
- The checkbox includes an associated message that describes its purpose or provides
442
- additional context for the user. The checkbox is larger than standard size and has
443
- proper spacing between the checkbox and its label text.
444
-
445
- Attributes:
446
- checkbox_message (str): The message displayed next to the checkbox
447
- checkbox_default (bool): Default checked state of the checkbox
448
- checkbox_id (dict): Computed ID for the checkbox component
449
-
450
- Note:
451
- The checkbox value is [True] when checked, [] when unchecked.
452
-
453
- Example:
454
- >>> slider_component = SliderWithTextInputAndCheckbox(
455
- ... id_base={'type': 'parameter', 'index': 0},
456
- ... property_name='temperature',
457
- ... title='Temperature (°C)',
458
- ... checkbox_message='Use automatic temperature control',
459
- ... min_val=0.0,
460
- ... max_val=100.0,
461
- ... step=1.0,
462
- ... mark_interval=50.0,
463
- ... default_val=25.0,
464
- ... checkbox_default=True
465
- ... )
466
- >>> layout_element = slider_component() # Returns Dash HTML Div component
467
- """
468
-
469
- def __init__(
470
- self,
471
- id_base: dict,
472
- property_name: str,
473
- title: str,
474
- checkbox_message: str,
475
- min_val: float = 0.0,
476
- max_val: float = 100.0,
477
- step: float = 1.0,
478
- mark_interval: float = 50.0,
479
- default_val: Union[float, list[float]] = None,
480
- checkbox_default: bool = False,
481
- with_slider_titles: bool = True,
482
- slider_disable: bool = False,
483
- div_width: str = "calc(90%)",
484
- message: str = None,
485
- ):
486
- """
487
- Initialize the SliderWithTextInputAndCheckbox component.
488
-
489
- Args:
490
- id_base (dict): Base dictionary for generating component IDs.
491
- property_name (str): String identifier for this property.
492
- title (str): Title displayed above the component.
493
- checkbox_message (str): Message displayed next to the checkbox.
494
- min_val (float, optional): Minimum value for the slider and input. Defaults to 0.0.
495
- max_val (float, optional): Maximum value for the slider and input. Defaults to 100.0.
496
- step (float, optional): Step size for value increments/decrements. Defaults to 1.0.
497
- mark_interval (float, optional): Interval between tick marks on the slider. Defaults to 50.0.
498
- default_val (Union[float, list[float]], optional): Initial value for slider/input.
499
- checkbox_default (bool, optional): Initial checked state of checkbox. Defaults to False.
500
- with_slider_titles (bool, optional): Whether to show title. Defaults to True.
501
- slider_disable (bool, optional): Whether to disable interactions. Defaults to False.
502
- div_width (str, optional): CSS width for container. Defaults to 'calc(90%)'.
503
- message (str, optional): Optional message between title and slider.
504
- """
505
- # Initialize parent class
506
- super().__init__(
507
- id_base=id_base,
508
- property_name=property_name,
509
- title=title,
510
- min_val=min_val,
511
- max_val=max_val,
512
- step=step,
513
- mark_interval=mark_interval,
514
- default_val=default_val,
515
- with_slider_titles=with_slider_titles,
516
- slider_disable=slider_disable,
517
- div_width=div_width,
518
- message=message,
519
- )
520
-
521
- # Add checkbox-specific attributes
522
- self.checkbox_message = checkbox_message
523
- self.checkbox_default = checkbox_default
524
- self.checkbox_id = self._make_id("checkbox")
525
-
526
- def _make_checkbox(self):
527
- """
528
- Create and configure the Dash checkbox component.
529
-
530
- Returns:
531
- dash.html.Div: Container with a larger checkbox and separated label text.
532
- """
533
- return ds.html.Div(
534
- [
535
- ds.dcc.Checklist(
536
- id=self.checkbox_id,
537
- options=[{"label": "", "value": True}],
538
- value=[True] if self.checkbox_default else [],
539
- style={
540
- "display": "inline-block",
541
- "margin-right": "10px",
542
- "transform": "scale(1.3)", # Make checkbox larger
543
- "transform-origin": "left top", # Changed to 'top' for better alignment
544
- "vertical-align": "baseline", # Use baseline alignment
545
- },
546
- labelStyle={"margin": "0px"},
547
- ),
548
- ds.html.Span(
549
- self.checkbox_message,
550
- style={
551
- "display": "inline-block",
552
- "vertical-align": "baseline", # Match checkbox baseline
553
- "margin-left": "5px",
554
- "line-height": "1.3", # Slightly higher line height
555
- "margin-top": "-4px", # Pull text up more aggressively
556
- },
557
- ),
558
- ],
559
- style={"margin-left": "20px", "margin-top": "10px", "line-height": "1"},
560
- )
561
-
562
- def __call__(self):
563
- """
564
- Generate the complete component layout including the checkbox.
565
-
566
- Creates and returns a Dash HTML Div containing the title, optional message,
567
- slider, input, and checkbox components arranged in a cohesive layout.
568
-
569
- Returns:
570
- dash.html.Div: Complete component layout containing all elements plus checkbox.
571
- """
572
- slider_title = self.title if self.with_slider_titles else "\u00A0"
573
-
574
- # Build the component list
575
- components = [
576
- ds.html.P(
577
- slider_title, style={"margin-left": "20px", "margin-bottom": "0px"}
578
- )
579
- ]
580
-
581
- # Add optional message if provided
582
- if self.message:
583
- components.append(
584
- ds.html.P(
585
- self.message,
586
- style={
587
- "margin-left": "20px",
588
- "margin-bottom": "5px",
589
- "margin-top": "2px",
590
- "font-size": "0.9em",
591
- "color": "#666666",
592
- "font-style": "italic",
593
- },
594
- )
595
- )
596
-
597
- # Add slider and input
598
- components.extend(
599
- [
600
- ds.html.Div([self._make_slider()], style={"margin-bottom": "-18px"}),
601
- self._make_input(),
602
- ]
603
- )
604
-
605
- # Add checkbox beneath the input
606
- components.extend([self._make_checkbox(), ds.html.Br(), ds.html.Br()])
607
-
608
- return ds.html.Div(
609
- components, style={"width": self.div_width, "margin-left": "-20px"}
610
- )
611
-
612
- @property
613
- def components(self):
614
- """
615
- Get a dictionary mapping component types to their IDs, including the checkbox.
616
-
617
- Returns:
618
- dict: Dictionary with component type keys mapping to their ID dictionaries.
619
- """
620
- return {
621
- "slider": self.slider_id,
622
- "input": self.input_id,
623
- "checkbox": self.checkbox_id,
624
- }
625
-
626
- def get_checkbox_input(self):
627
- """
628
- Get Input object for listening to checkbox value changes.
629
-
630
- Returns:
631
- Input: Dash Input object for the checkbox component.
632
-
633
- Note:
634
- The checkbox value will be [True] when checked, [] when unchecked.
635
-
636
- Example:
637
- >>> @app.callback(
638
- ... Output('some-output', 'children'),
639
- ... component.get_checkbox_input()
640
- ... )
641
- ... def update_display(checkbox_value):
642
- ... is_checked = True in (checkbox_value or [])
643
- ... return f"Checkbox is {'checked' if is_checked else 'unchecked'}"
644
- """
645
- return Input(self.checkbox_id, "value")
646
-
647
- def get_checkbox_output(self):
648
- """
649
- Get Output object for updating checkbox value.
650
-
651
- Returns:
652
- Output: Dash Output object for the checkbox component.
653
-
654
- Note:
655
- To check the checkbox, return [True]. To uncheck, return [].
656
-
657
- Example:
658
- >>> @app.callback(
659
- ... component.get_checkbox_output(),
660
- ... [Input('some-button', 'n_clicks')]
661
- ... )
662
- ... def toggle_checkbox(n_clicks):
663
- ... if n_clicks and n_clicks % 2 == 1:
664
- ... return [True]
665
- ... return []
666
- """
667
- return Output(self.checkbox_id, "value")
668
-
669
- def get_all_inputs(self):
670
- """
671
- Get Input objects for all component values (slider, input, and checkbox).
672
-
673
- Returns:
674
- list: List containing Input objects for slider, input, and checkbox.
675
-
676
- Note:
677
- The checkbox value will be [True] when checked, [] when unchecked.
678
-
679
- Example:
680
- >>> @app.callback(
681
- ... Output('summary', 'children'),
682
- ... component.get_all_inputs()
683
- ... )
684
- ... def update_summary(slider_val, input_val, checkbox_val):
685
- ... is_checked = True in (checkbox_val or [])
686
- ... value = slider_val or input_val
687
- ... return f"Value: {value}, Option enabled: {is_checked}"
688
- """
689
- return [
690
- Input(self.slider_id, "value"),
691
- Input(self.input_id, "value"),
692
- Input(self.checkbox_id, "value"),
693
- ]
File without changes