def slider(vmin, vmax=None, step_size=None, default=None, label=None, display_value=True, _range=False): """ A slider widget. INPUT: For a numeric slider (select a value from a range): - ``vmin``, ``vmax`` -- minimum and maximum value - ``step_size`` -- the step size For a selection slider (select a value from a list of values): - ``vmin`` -- a list of possible values for the slider For all sliders: - ``default`` -- initial value - ``label`` -- optional label - ``display_value`` -- (boolean) if ``True``, display the current value. EXAMPLES:: sage: from sage.repl.ipython_kernel.all_jupyter import slider sage: slider(5, label="slide me") TransformIntSlider(value=5, description=u'slide me', min=5) sage: slider(5, 20) TransformIntSlider(value=5, max=20, min=5) sage: slider(5, 20, 0.5) TransformFloatSlider(value=5.0, max=20.0, min=5.0, step=0.5) sage: slider(5, 20, default=12) TransformIntSlider(value=12, max=20, min=5) The parent of the inputs determines the parent of the value:: sage: w = slider(5); w TransformIntSlider(value=5, min=5) sage: parent(w.get_interact_value()) Integer Ring sage: w = slider(int(5)); w IntSlider(value=5, min=5) sage: parent(w.get_interact_value()) <... 'int'> sage: w = slider(5, 20, step_size=RDF("0.1")); w TransformFloatSlider(value=5.0, max=20.0, min=5.0) sage: parent(w.get_interact_value()) Real Double Field sage: w = slider(5, 20, step_size=10/3); w SelectionSlider(index=2, options=(5, 25/3, 35/3, 15, 55/3), value=35/3) sage: parent(w.get_interact_value()) Rational Field Symbolic input is evaluated numerically:: sage: w = slider(e, pi); w TransformFloatSlider(value=2.718281828459045, max=3.141592653589793, min=2.718281828459045) sage: parent(w.get_interact_value()) Real Field with 53 bits of precision For a selection slider, the default is adjusted to one of the possible values:: sage: slider(range(10), default=17/10) SelectionSlider(index=2, options=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), value=2) TESTS:: sage: slider(range(5), range(5)) Traceback (most recent call last): ... TypeError: unexpected argument 'vmax' for a selection slider sage: slider(range(5), step_size=2) Traceback (most recent call last): ... TypeError: unexpected argument 'step_size' for a selection slider sage: slider(5).readout True sage: slider(5, display_value=False).readout False """ kwds = {"readout": display_value} if label: kwds["description"] = u(label) # If vmin is iterable, return a SelectionSlider if isinstance(vmin, Iterable): if vmax is not None: raise TypeError("unexpected argument 'vmax' for a selection slider") if step_size is not None: raise TypeError("unexpected argument 'step_size' for a selection slider") if _range: # https://github.com/ipython/ipywidgets/issues/760 raise NotImplementedError("range_slider does not support a list of values") options = list(vmin) # Find default in options def err(v): if v is default: return (-1, 0) try: if v == default: return (0, 0) return (0, abs(v - default)) except Exception: return (1, 0) kwds["options"] = options if default is not None: kwds["value"] = min(options, key=err) return SelectionSlider(**kwds) if default is not None: kwds["value"] = default # Sum all input numbers to figure out type/parent p = parent(sum(x for x in (vmin, vmax, step_size) if x is not None)) # Change SR to RR if p is SR: p = RR # Convert all inputs to the common parent if vmin is not None: vmin = p(vmin) if vmax is not None: vmax = p(vmax) if step_size is not None: step_size = p(step_size) def tuple_elements_p(t): "Convert all entries of the tuple `t` to `p`" return tuple(p(x) for x in t) zero = p() if isinstance(zero, Integral): if p is int: if _range: cls = IntRangeSlider else: cls = IntSlider else: if _range: kwds["transform"] = tuple_elements_p cls = TransformIntRangeSlider else: kwds["transform"] = p cls = TransformIntSlider elif isinstance(zero, Rational): # Rational => implement as SelectionSlider if _range: # https://github.com/ipython/ipywidgets/issues/760 raise NotImplementedError("range_slider does not support rational numbers") vmin, vmax, value = _get_min_max_value(vmin, vmax, default, step_size) kwds["value"] = value kwds["options"] = srange(vmin, vmax, step_size, include_endpoint=True) return SelectionSlider(**kwds) elif isinstance(zero, Real): if p is float: if _range: cls = FloatRangeSlider else: cls = FloatSlider else: if _range: kwds["transform"] = tuple_elements_p cls = TransformFloatRangeSlider else: kwds["transform"] = p cls = TransformFloatSlider else: raise TypeError("unknown parent {!r} for slider".format(p)) kwds["min"] = vmin if vmax is not None: kwds["max"] = vmax if step_size is not None: kwds["step"] = step_size return cls(**kwds)
def interact(meshcat, callback, **kwargs): """ A poor-person's implementation of a functionality like ipywidgets interact https://ipywidgets.readthedocs.io/en/latest/examples/Using%20Interact.html#Basic-interact """ values = {} # The following code was adapted from https://github.com/jupyter-widgets/ipywidgets/blob/9326d28c474cb6d2b58876d469489a73e98f903a/python/ipywidgets/ipywidgets/widgets/interaction.py # noqa def find_abbreviations(callback, kwargs): """ Find the abbreviations for the given function and kwargs. Return (name, abbrev, default) tuples. """ new_kwargs = [] try: sig = signature(callback) except (ValueError, TypeError): # can't inspect, no info from function; only use kwargs return [(key, value, value) for key, value in kwargs.items()] for param in sig.parameters.values(): for name, value, default in _yield_abbreviations_for_parameter( param, kwargs): if value is Parameter.empty: raise ValueError( 'cannot find widget or abbreviation for argument: {!r}'. format(name)) new_kwargs.append((name, value, default)) return new_kwargs new_kwargs = find_abbreviations(callback, kwargs) for name, abbrev, default in new_kwargs: kw = {} kw['value'] = None if default is Parameter.empty else default if isinstance(abbrev, tuple): kw['step'] = abbrev[2] if len(abbrev) != 3 else None kw["min"], kw["max"], kw["value"] = _get_min_max_value( abbrev[0], abbrev[1], **kw) if kw['step'] is None: kw['step'] = 0.1 meshcat.AddSlider(name, **kw) else: raise ValueError("This case is not implemented yet") # It might be simple. I just haven't tried! values[name] = kw['value'] def update_values(): changed = False for name in values: v = meshcat.GetSliderValue(name) changed |= v != values[name] values[name] = v return changed # Always call it once. callback(**values) if not running_as_notebook: return print("Press the 'Stop Interacting' button in Meshcat to continue.") meshcat.AddButton("Stop Interacting") while meshcat.GetButtonClicks("Stop Interacting") < 1: if update_values(): callback(**values) time.sleep(.1) meshcat.DeleteButton("Stop Interacting") for name in values: meshcat.DeleteSlider(name)
def slider(vmin, vmax=None, step_size=None, default=None, label=None, display_value=True, _range=False): """ A slider widget. INPUT: For a numeric slider (select a value from a range): - ``vmin``, ``vmax`` -- minimum and maximum value - ``step_size`` -- the step size For a selection slider (select a value from a list of values): - ``vmin`` -- a list of possible values for the slider For all sliders: - ``default`` -- initial value - ``label`` -- optional label - ``display_value`` -- (boolean) if ``True``, display the current value. EXAMPLES:: sage: from sage.repl.ipython_kernel.all_jupyter import slider sage: slider(5, label="slide me") TransformIntSlider(value=5, description=u'slide me', min=5) sage: slider(5, 20) TransformIntSlider(value=5, max=20, min=5) sage: slider(5, 20, 0.5) TransformFloatSlider(value=5.0, max=20.0, min=5.0, step=0.5) sage: slider(5, 20, default=12) TransformIntSlider(value=12, max=20, min=5) The parent of the inputs determines the parent of the value:: sage: w = slider(5); w TransformIntSlider(value=5, min=5) sage: parent(w.get_interact_value()) Integer Ring sage: w = slider(int(5)); w IntSlider(value=5, min=5) sage: parent(w.get_interact_value()) <... 'int'> sage: w = slider(5, 20, step_size=RDF("0.1")); w TransformFloatSlider(value=5.0, max=20.0, min=5.0) sage: parent(w.get_interact_value()) Real Double Field sage: w = slider(5, 20, step_size=10/3); w SelectionSlider(index=2, options=(5, 25/3, 35/3, 15, 55/3), value=35/3) sage: parent(w.get_interact_value()) Rational Field Symbolic input is evaluated numerically:: sage: w = slider(e, pi); w TransformFloatSlider(value=2.718281828459045, max=3.141592653589793, min=2.718281828459045) sage: parent(w.get_interact_value()) Real Field with 53 bits of precision For a selection slider, the default is adjusted to one of the possible values:: sage: slider(range(10), default=17/10) SelectionSlider(index=2, options=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), value=2) TESTS:: sage: slider(range(5), range(5)) Traceback (most recent call last): ... TypeError: unexpected argument 'vmax' for a selection slider sage: slider(range(5), step_size=2) Traceback (most recent call last): ... TypeError: unexpected argument 'step_size' for a selection slider sage: slider(5).readout True sage: slider(5, display_value=False).readout False """ kwds = {"readout": display_value} if label: kwds["description"] = u(label) # If vmin is iterable, return a SelectionSlider if isinstance(vmin, Iterable): if vmax is not None: raise TypeError( "unexpected argument 'vmax' for a selection slider") if step_size is not None: raise TypeError( "unexpected argument 'step_size' for a selection slider") if _range: # https://github.com/ipython/ipywidgets/issues/760 raise NotImplementedError( "range_slider does not support a list of values") options = list(vmin) # Find default in options def err(v): if v is default: return (-1, 0) try: if v == default: return (0, 0) return (0, abs(v - default)) except Exception: return (1, 0) kwds["options"] = options if default is not None: kwds["value"] = min(options, key=err) return SelectionSlider(**kwds) if default is not None: kwds["value"] = default # Sum all input numbers to figure out type/parent p = parent(sum(x for x in (vmin, vmax, step_size) if x is not None)) # Change SR to RR if p is SR: p = RR # Convert all inputs to the common parent if vmin is not None: vmin = p(vmin) if vmax is not None: vmax = p(vmax) if step_size is not None: step_size = p(step_size) def tuple_elements_p(t): "Convert all entries of the tuple `t` to `p`" return tuple(p(x) for x in t) zero = p() if isinstance(zero, Integral): if p is int: if _range: cls = IntRangeSlider else: cls = IntSlider else: if _range: kwds["transform"] = tuple_elements_p cls = TransformIntRangeSlider else: kwds["transform"] = p cls = TransformIntSlider elif isinstance(zero, Rational): # Rational => implement as SelectionSlider if _range: # https://github.com/ipython/ipywidgets/issues/760 raise NotImplementedError( "range_slider does not support rational numbers") vmin, vmax, value = _get_min_max_value(vmin, vmax, default, step_size) kwds["value"] = value kwds["options"] = srange(vmin, vmax, step_size, include_endpoint=True) return SelectionSlider(**kwds) elif isinstance(zero, Real): if p is float: if _range: cls = FloatRangeSlider else: cls = FloatSlider else: if _range: kwds["transform"] = tuple_elements_p cls = TransformFloatRangeSlider else: kwds["transform"] = p cls = TransformFloatSlider else: raise TypeError("unknown parent {!r} for slider".format(p)) kwds["min"] = vmin if vmax is not None: kwds["max"] = vmax if step_size is not None: kwds["step"] = step_size return cls(**kwds)