def color_selector(default=(0, 0, 1), label=None, widget=None, hide_box=False): """ A widget for choosing a color. INPUT: - ``default`` -- initial value - ``label`` -- optional label - ``hide_box`` -- (boolean) if True, do not show the textbox EXAMPLES:: sage: from sage.repl.ipython_kernel.all_jupyter import color_selector sage: w = color_selector("orange", label="color me"); w SageColorPicker(value='#ffa500', description=u'color me') sage: w.get_interact_value() RGB color (1.0, 0.6470588235294118, 0.0) sage: color_selector(Color(0.1, 0.2, 0.3)) SageColorPicker(value='#19334c') """ # widget argument is silently ignored kwds = {"value": Color(default).html_color(), "concise": hide_box} if label is not None: kwds["description"] = u(label) return SageColorPicker(**kwds)
def input_grid(nrows, ncols, default=None, label=None, to_value=None, width=4): """ A widget consisting of a grid (matrix) of textboxes. The values entered in the textboxes are evaluated (using :func:`sage_eval`). These are stored as a list of lists on the ``value`` attribute. The value of this widget for an interactive function is the result of calling ``to_value`` on this list of lists. INPUT: - ``nrows``, ``ncols`` -- number of rows and columns in the grid - ``default`` -- initial value (given as a list of lists, a single constant value or a flat list) - ``label`` -- optional label - ``to_value`` -- function to be called to get the value for interactive functions - ``width`` -- width of each textbox EXAMPLES:: sage: from sage.repl.ipython_kernel.all_jupyter import input_grid sage: input_grid(2, 2, default=42, label="answers") Grid(value=[[42, 42], [42, 42]], children=(Label(value=u'answers'), VBox(children=(EvalText(value=u'42', layout=Layout(max_width=u'5em')), EvalText(value=u'42', layout=Layout(max_width=u'5em')))), VBox(children=(EvalText(value=u'42', layout=Layout(max_width=u'5em')), EvalText(value=u'42', layout=Layout(max_width=u'5em')))))) sage: w = input_grid(2, 2, default=[[cos(x), sin(x)], [-sin(x), cos(x)]], to_value=matrix); w Grid(value=[[cos(x), sin(x)], [-sin(x), cos(x)]], children=(Label(value=u''), VBox(children=(EvalText(value=u'cos(x)', layout=Layout(max_width=u'5em')), EvalText(value=u'-sin(x)', layout=Layout(max_width=u'5em')))), VBox(children=(EvalText(value=u'sin(x)', layout=Layout(max_width=u'5em')), EvalText(value=u'cos(x)', layout=Layout(max_width=u'5em')))))) sage: w.get_interact_value() [ cos(x) sin(x)] [-sin(x) cos(x)] sage: w = input_grid(2, 2, default=[1, x, x^2, x^3], to_value=lambda x: x[1][1]); w Grid(value=[[1, x], [x^2, x^3]], children=(Label(value=u''), VBox(children=(EvalText(value=u'1', layout=Layout(max_width=u'5em')), EvalText(value=u'x^2', layout=Layout(max_width=u'5em')))), VBox(children=(EvalText(value=u'x', layout=Layout(max_width=u'5em')), EvalText(value=u'x^3', layout=Layout(max_width=u'5em')))))) sage: w.get_interact_value() x^3 """ kwds = {"transform": to_value} if label is not None: kwds["description"] = u(label) # Parse default if not isinstance(default, list): # Single value default = [[default] * ncols] * nrows if all(isinstance(elt, list) for elt in default): # List of lists pass else: # Flat list default = [[default[i * ncols + j] for j in range(ncols)] for i in range(nrows)] def make_widget(i, j): return input_box(str(default[i][j]), width=width) grid = Grid(nrows, ncols, make_widget, **kwds) return grid
def remove_unicode_u(string): """ Given a string, try to remove all unicode u prefixes inside. This will help to keep the same doctest results in Python2 and Python3. The input string is typically the documentation of a method or function. This string may contain some letters u that are unicode python2 prefixes. The aim is to remove all of these u and only them. INPUT: - ``string`` -- either ``unicode`` or ``bytes`` (if ``bytes``, it will be converted to ``unicode`` assuming UTF-8) OUTPUT: ``unicode`` string EXAMPLES:: sage: from sage.doctest.parsing import remove_unicode_u as remu sage: remu("u'you'") u"'you'" sage: remu('u') u'u' sage: remu("[u'am', 'stram', u'gram']") u"['am', 'stram', 'gram']" sage: remu('[u"am", "stram", u"gram"]') u'["am", "stram", "gram"]' This deals correctly with nested quotes:: sage: str = '''[u"Singular's stuff", u'good']''' sage: print(remu(str)) ["Singular's stuff", 'good'] TESTS: This supports python2 str type as input:: sage: euro = "'€'" sage: print(remu(euro)) '€' """ stripped, replacements = cython_strip_string_literals( u(string), "__remove_unicode_u") string = stripped.replace('u"', '"').replace("u'", "'") for magic, literal in replacements.items(): string = string.replace(magic, literal) return string
def remove_unicode_u(string): """ Given a string, try to remove all unicode u prefixes inside. This will help to keep the same doctest results in Python2 and Python3. The input string is typically the documentation of a method or function. This string may contain some letters u that are unicode python2 prefixes. The aim is to remove all of these u and only them. INPUT: - ``string`` -- either ``unicode`` or ``bytes`` (if ``bytes``, it will be converted to ``unicode`` assuming UTF-8) OUTPUT: ``unicode`` string EXAMPLES:: sage: from sage.doctest.parsing import remove_unicode_u as remu sage: remu("u'you'") u"'you'" sage: remu('u') u'u' sage: remu("[u'am', 'stram', u'gram']") u"['am', 'stram', 'gram']" sage: remu('[u"am", "stram", u"gram"]') u'["am", "stram", "gram"]' This deals correctly with nested quotes:: sage: str = '''[u"Singular's stuff", u'good']''' sage: print(remu(str)) ["Singular's stuff", 'good'] TESTS: This supports python2 str type as input:: sage: euro = "'€'" sage: print(remu(euro)) '€' """ stripped, replacements = cython_strip_string_literals(u(string), "__remove_unicode_u") string = stripped.replace('u"', '"').replace("u'", "'") for magic, literal in replacements.items(): string = string.replace(magic, literal) return string
def checkbox(default=True, label=None): """ A checkbox widget. INPUT: - ``default`` -- (boolean) initial value - ``label`` -- optional label EXAMPLES:: sage: from sage.repl.ipython_kernel.all_jupyter import checkbox sage: checkbox(label="toggle me") Checkbox(value=True, description=u'toggle me') sage: checkbox(default=0) Checkbox(value=False) """ kwds = {"value": bool(default)} if label is not None: kwds["description"] = u(label) return Checkbox(**kwds)
def input_box(default=None, label=None, type=None, width=80, height=1): """ A textbox widget. INPUT: - ``default`` -- initial value - ``label`` -- optional label - ``type`` -- function of one variable or ``None``. if ``type`` is ``str``, the value of this widget for interactive functions is just the text as ``str``. Otherwise, the text is evaluated using :func:`sage_eval`, ``type`` is called on it and the result is used as value. Except if ``type`` is ``None``, then the evaluated text is used as value. - ``width`` -- width of the box - ``height`` -- if ``height > 1``, create a textarea instead of a single-line textbox. EXAMPLES:: sage: from sage.repl.ipython_kernel.all_jupyter import input_box The most basic usage is when ``type=str``:: sage: w = input_box("4+5", type=str, label="enter a string") sage: w TransformText(value=u'4+5', description=u'enter a string', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() '4+5' Without ``type``, the text is evaluated:: sage: w = input_box("4+5") sage: w EvalText(value=u'4+5', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() 9 With a different ``type``, the text is evaluated and ``type`` is called on it: sage: w = input_box("4+5", type=float) sage: w EvalText(value=u'4+5', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() 9.0 Despite the keyword name, ``type`` does not need to be a type:: sage: w = input_box("4+5", type=sqrt) sage: w EvalText(value=u'4+5', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() 3 When ``height > 1``, a textarea is returned:: sage: w = input_box("4+5", width=100, height=1) sage: w EvalText(value=u'4+5', layout=Layout(max_width=u'101em')) sage: w = input_box("4+5", width=100, height=5) sage: w EvalTextarea(value=u'4+5', layout=Layout(max_width=u'101em')) TESTS:: sage: w = input_box(type=Color) Traceback (most recent call last): ... NotImplementedError: type=Color is not supported """ kwds = {} if type is str: kwds["transform"] = str # Convert unicode to str if height > 1: cls = TransformTextarea else: cls = TransformText elif type is Color: # This is special-cased by SageNB (with a non-trivial # implementation!), but it doesn't seem to be used in practice # because we have a SageColorPicker widget. raise NotImplementedError("type=Color is not supported") else: kwds["transform"] = type if height > 1: cls = EvalTextarea else: cls = EvalText if default is not None: kwds["value"] = str(default) if label is not None: kwds["description"] = u(label) w = cls(**kwds) w.layout.max_width = str(width+1) + "em" return w
def selector(values, label=None, default=None, nrows=None, ncols=None, width=None, buttons=False): """ A widget to select a value from a given list of values. This is rendered as a dropdown box (if ``buttons`` is False) or as a list of buttons (if ``buttons`` is True). INPUT: - ``values`` -- a list of values to choose from (see examples below for the accepted formats for this) - ``label`` -- optional label - ``default`` -- initial value - ``buttons`` -- (boolean) if True, display buttons instead of a dropdown box EXAMPLES:: sage: from sage.repl.ipython_kernel.all_jupyter import selector sage: selector(range(5), label="choose one") Dropdown(description=u'choose one', options=(0, 1, 2, 3, 4), value=0) sage: selector(range(5), buttons=True, default=4) ToggleButtons(index=4, options=(0, 1, 2, 3, 4), value=4) Apart from a simple list, ``values`` can be given as a list of 2-tuples ``(value, label)``:: sage: selector([(1,"one"), (2,"two"), (3,"three")]) Dropdown(options=(('one', 1), ('two', 2), ('three', 3)), value=1) sage: selector([(1,"one"), (2,"two"), (3,"three")], buttons=True) ToggleButtons(options=(('one', 1), ('two', 2), ('three', 3)), value=1) A dict of ``label:value`` pairs is also allowed. Since a ``dict`` is not ordered, it is better to use an :class:`OrderedDict`:: sage: from collections import OrderedDict sage: selector(OrderedDict(one=1, two=2, three=3)) Dropdown(options=OrderedDict([('one', 1), ('three', 3), ('two', 2)]), value=1) sage: selector(OrderedDict(one=1, two=2, three=3), buttons=True) ToggleButtons(options=OrderedDict([('one', 1), ('three', 3), ('two', 2)]), value=1) The values can be any kind of object: sage: selector([sin(x^2), GF(29), EllipticCurve('37a1')]) Dropdown(options=(sin(x^2), Finite Field of size 29, Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field), value=sin(x^2)) """ if isinstance(values, Sequence): values = list(values) if values: v0 = values[0] if isinstance(v0, tuple) and len(v0) == 2: # Change [(val0, lbl0), ...] to [(lbl0, val0), ...] values = [(str(lbl), val) for (val, lbl) in values] kwds = {"options": values} if buttons: cls = ToggleButtons elif nrows is not None or ncols is not None: # For compatibility with SageNB, these keywords are recognized # but their value is ignored cls = ToggleButtons else: cls = Dropdown if default is not None: kwds["value"] = default if label is not None: kwds["description"] = u(label) return cls(**kwds)
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 input_box(default=None, label=None, type=None, width=80, height=1): """ A textbox widget. INPUT: - ``default`` -- initial value - ``label`` -- optional label - ``type`` -- function of one variable or ``None``. if ``type`` is ``str``, the value of this widget for interactive functions is just the text as ``str``. Otherwise, the text is evaluated using :func:`sage_eval`, ``type`` is called on it and the result is used as value. Except if ``type`` is ``None``, then the evaluated text is used as value. - ``width`` -- width of the box - ``height`` -- if ``height > 1``, create a textarea instead of a single-line textbox. EXAMPLES:: sage: from sage.repl.ipython_kernel.all_jupyter import input_box The most basic usage is when ``type=str``:: sage: w = input_box("4+5", type=str, label="enter a string") sage: w TransformText(value=u'4+5', description=u'enter a string', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() '4+5' Without ``type``, the text is evaluated:: sage: w = input_box("4+5") sage: w EvalText(value=u'4+5', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() 9 With a different ``type``, the text is evaluated and ``type`` is called on it: sage: w = input_box("4+5", type=float) sage: w EvalText(value=u'4+5', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() 9.0 Despite the keyword name, ``type`` does not need to be a type:: sage: w = input_box("4+5", type=sqrt) sage: w EvalText(value=u'4+5', layout=Layout(max_width=u'81em')) sage: w.get_interact_value() 3 When ``height > 1``, a textarea is returned:: sage: w = input_box("4+5", width=100, height=1) sage: w EvalText(value=u'4+5', layout=Layout(max_width=u'101em')) sage: w = input_box("4+5", width=100, height=5) sage: w EvalTextarea(value=u'4+5', layout=Layout(max_width=u'101em')) TESTS:: sage: w = input_box(type=Color) Traceback (most recent call last): ... NotImplementedError: type=Color is not supported """ kwds = {} if type is str: kwds["transform"] = str # Convert unicode to str if height > 1: cls = TransformTextarea else: cls = TransformText elif type is Color: # This is special-cased by SageNB (with a non-trivial # implementation!), but it doesn't seem to be used in practice # because we have a SageColorPicker widget. raise NotImplementedError("type=Color is not supported") else: kwds["transform"] = type if height > 1: cls = EvalTextarea else: cls = EvalText if default is not None: kwds["value"] = str(default) if label is not None: kwds["description"] = u(label) w = cls(**kwds) w.layout.max_width = str(width + 1) + "em" return w
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)