예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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
예제 #4
0
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
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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)
예제 #8
0
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)
예제 #9
0
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
예제 #10
0
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)
예제 #11
0
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)
예제 #12
0
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
예제 #13
0
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)
예제 #14
0
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)