Skip to content

Commit

Permalink
nicer
Browse files Browse the repository at this point in the history
  • Loading branch information
HDembinski committed Sep 14, 2024
1 parent 8329bc2 commit b02dcd2
Showing 1 changed file with 15 additions and 12 deletions.
27 changes: 15 additions & 12 deletions src/iminuit/ipywidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ def make_widget(
raise_on_exception: bool,
):
"""Make interactive fitting widget."""
# Implementations makes heavy use of closures,
# we frequently use variables which are defined
# near the end of the function.
original_values = minuit.values[:]

def plot_with_frame(from_fit, report_success):
Expand Down Expand Up @@ -83,11 +86,11 @@ def fit():

class OnParameterChange:
# Ugly implementation notes:
# Updating the slider asynchronously calls on_parameter_change. I could not find
# a way to prevent that (and I tried many), so as a workaround we skip
# two calls for each slider update, because updating the slider generates two
# calls due to rounding (which is stupid).

# We want the plot when the user moves the slider widget, but not when
# we update the slider value manually from our code. Unfortunately,
# the latter also calls OnParameterChange, which leads to superfluous plotting.
# I could not find a nice way to prevent that (and I tried many), so as a workaround
# we optionally skip a number of calls, when the slider is updated.
def __init__(self, skip: int = 0):
self.skip = skip

Expand All @@ -103,14 +106,11 @@ def __call__(self, change: Dict[str, Any] = {}):
minuit.values[i] = x.slider.value

if any(x.fit.value for x in parameters):
from_fit = True
saved = minuit.fixed[:]
for i, x in enumerate(parameters):
minuit.fixed[i] = not x.fit.value
report_success = fit()
for i, x in enumerate(parameters):
if x.fit.value:
x.reset(minuit.values[i])
from_fit = True
report_success = do_fit(None)
minuit.fixed = saved

# Implementation like in ipywidegts.interaction.interactive_output
Expand All @@ -125,6 +125,8 @@ def do_fit(change):
report_success = fit()
for i, x in enumerate(parameters):
x.reset(minuit.values[i])
if change is None:
return report_success
OnParameterChange()({"from_fit": True, "report_success": report_success})

def on_update_button_clicked(change):
Expand All @@ -135,7 +137,7 @@ def on_reset_button_clicked(change):
minuit.reset()
minuit.values = original_values
for i, x in enumerate(parameters):
x.reset("value", minuit.values[i])
x.reset(minuit.values[i])
OnParameterChange()()

class Parameter(widgets.HBox):
Expand Down Expand Up @@ -190,6 +192,8 @@ def on_fit_toggled(change):
def reset(self, value):
self.slider.unobserve_all("value")
self.slider.value = value
# Installing the observer actually triggers a notification,
# we skip it. See notes in OnParameterChange.
self.slider.observe(OnParameterChange(1), "value")

parameters = [Parameter(minuit, par) for par in minuit.parameters]
Expand All @@ -213,7 +217,6 @@ def reset(self, value):
widgets.VBox(parameters),
]
)

out = widgets.Output()
OnParameterChange()()
return widgets.HBox([out, ui])

0 comments on commit b02dcd2

Please sign in to comment.