Skip to content

Commit

Permalink
Fix for multiple recalculation callbacks
Browse files Browse the repository at this point in the history
This fixes an issue where callbacks to reconstruct points when
`reinit_live == True` would perform multiple updates due to
different sliders/buttons/etc. handling `on_change` separately.

This fix creates a new attr in `PrimitiveVisualizer`, `reinit_live`,
that replaces the previously independently assigned value to
several functions and constructors.

Now, when the points are reset using the 'Reset' button, this
live updating can be manually turned off and then restored to
a previous state once the update is complete.
  • Loading branch information
teald committed Oct 11, 2023
1 parent 73a723c commit 7862b7f
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 16 deletions.
10 changes: 8 additions & 2 deletions geminidr/interactive/fit/fit1d.py
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,7 @@ def __init__(self, data_source, fitting_parameters,
domains=None, title=None, primitive_name=None, filename_info=None,
template="fit1d.html", help_text=None, recalc_inputs_above=False,
ui_params=None, turbo_tabs=False, panel_class=Fit1DPanel, pad_buttons=False,
reinit_live=False,
**kwargs):
"""
Parameters
Expand Down Expand Up @@ -1195,9 +1196,14 @@ def __init__(self, data_source, fitting_parameters,
the work are methods of this class.
pad_buttons : bool
If True, pad the abort/accept buttons so the tabs can flow under them
reinit_live : bool
If True, the reinit inputs will be recalculated live as the user
changes them. If False, a 'recalculate' button will be shown below
the reinit inputs instead.
"""
super().__init__(title=title, primitive_name=primitive_name, filename_info=filename_info,
template=template, help_text=help_text, ui_params=ui_params)
template=template, help_text=help_text, ui_params=ui_params,
reinit_live=reinit_live)
self.layout = None
self.recalc_inputs_above = recalc_inputs_above
self.pad_buttons = pad_buttons
Expand All @@ -1212,7 +1218,7 @@ def __init__(self, data_source, fitting_parameters,
# Make the panel with widgets to control the creation of (x, y) arrays

# Create left panel
reinit_widgets = self.make_widgets_from_parameters(ui_params, reinit_live=modal_message is None)
reinit_widgets = self.make_widgets_from_parameters(ui_params)
if reinit_widgets:
# This should really go in the parent class, like submit_button
if modal_message:
Expand Down
2 changes: 1 addition & 1 deletion geminidr/interactive/fit/wavecal.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ def image(self):
image.append(model.y[goodpix])
return image

def make_widgets_from_parameters(self, params, reinit_live: bool = False,
def make_widgets_from_parameters(self, params,
slider_width: int = 256, add_spacer=False,
hide_textbox=None):
linelist_reinit_params = None
Expand Down
36 changes: 23 additions & 13 deletions geminidr/interactive/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ class FitQuality(Enum):

class PrimitiveVisualizer(ABC):
def __init__(self, title='', primitive_name='',
filename_info='', template=None, help_text=None, ui_params=None):
filename_info='', template=None, help_text=None, ui_params=None,
reinit_live=False):
"""
Initialize a visualizer.
Expand All @@ -81,10 +82,17 @@ def __init__(self, title='', primitive_name='',
Information about the file being operated on
template : str
Optional path to an html template to render against, if customization is desired
reinit_live : bool
If True, recalculate points on any change (default False). This is set in
__init__ to PrimitiveVisualizer.reinit_live, but can be overridden by
subclasses.
"""
global _visualizer
_visualizer = self

# Reinitialization attr
self.reinit_live = reinit_live

# Make the widgets accessible from external code so we can update
# their properties if the default setup isn't great
self.widgets = {}
Expand Down Expand Up @@ -210,11 +218,15 @@ def build_reset_button(self, extra_handler_fn=None):
width=202)

def reset_dialog_handler(result):
reinit_live = self.reinit_live
self.reinit_live = False
if result:
self.reset_reinit_panel()
if extra_handler_fn:
extra_handler_fn()

self.reinit_live = reinit_live

self.make_ok_cancel_dialog(
btn=reset_reinit_button,
message='Do you want to reset the input parameters?',
Expand Down Expand Up @@ -582,6 +594,8 @@ def make_widgets_from_parameters(self, params, reinit_live: bool = True,
-------
list : Returns a list of widgets to display in the UI.
"""
self.reinit_live = reinit_live

widgets = []
if hide_textbox is None:
hide_textbox = []
Expand All @@ -598,7 +612,7 @@ def make_widgets_from_parameters(self, params, reinit_live: bool = True,
params.titles[key], params.values[key], step, field.min, field.max, obj=params.values,
attr=key, slider_width=slider_width, allow_none=field.optional, throttled=True,
is_float=is_float,
handler=self.slider_handler_factory(key, reinit_live=reinit_live),
handler=self.slider_handler_factory(key),
add_spacer=add_spacer, hide_textbox=key in hide_textbox)

self.widgets[key] = widget.children[0]
Expand All @@ -613,7 +627,7 @@ def make_widgets_from_parameters(self, params, reinit_live: bool = True,
value=params.values[key], options=list(field.allowed.keys()))
def _select_handler(attr, old, new):
self.extras[key] = new
if reinit_live:
if self.reinit_live:
self.reconstruct_points()
widget.on_change('value', _select_handler)
self.widgets[key] = widget
Expand All @@ -625,7 +639,7 @@ def _select_handler(attr, old, new):
cb_key = key
def _cb_handler(cbkey, val):
self.extras[cbkey] = True if len(val) else False
if reinit_live:
if self.reinit_live:
self.reconstruct_points()
widget.on_click(lambda v: _cb_handler(cb_key, v))
self.widgets[key] = widget
Expand Down Expand Up @@ -657,7 +671,7 @@ def __init__(self, key, extras, fn):

def handler(self, name, old, new):
self.extras[self.key] = new
if reinit_live and self.fn is not None:
if self.reinit_live and self.fn is not None:
self.fn()

widget.on_change("value", TextHandler(key, self.extras, lambda: self.reconstruct_points()).handler)
Expand All @@ -666,16 +680,14 @@ def handler(self, name, old, new):
widgets.append(widget)
return widgets

def slider_handler_factory(self, key, reinit_live=False):
def slider_handler_factory(self, key):
"""
Returns a function that updates the `extras` attribute.
Parameters
----------
key : str
The parameter name to be updated.
reinit_live : bool, optional
Update the reconstructed points on "real time".
Returns
-------
Expand All @@ -684,21 +696,19 @@ def slider_handler_factory(self, key, reinit_live=False):

def handler(val):
self.extras[key] = val
if reinit_live:
if self.reinit_live:
self.reconstruct_points()

return handler

def select_handler_factory(self, key, reinit_live=False):
def select_handler_factory(self, key):
"""
Returns a function that updates the `extras` attribute.
Parameters
----------
key : str
The parameter name to be updated.
reinit_live : bool, optional
Update the reconstructed points on "real time".
Returns
-------
Expand All @@ -707,7 +717,7 @@ def select_handler_factory(self, key, reinit_live=False):

def handler(val):
self.extras[key] = val
if reinit_live:
if self.reinit_live:
self.reconstruct_points()

return handler
Expand Down

0 comments on commit 7862b7f

Please sign in to comment.