Add initial implementation of 2-handle range sliders for integers.

Gordon Ball 12 years ago
parent c9961b44b6
commit 3178a184c0

@ -25,35 +25,35 @@ define(["widgets/js/widget"], function(WidgetManager){
.appendTo(this.$el)
.addClass('widget-hlabel')
.hide();
this.$slider = $('<div />')
.slider({})
.addClass('slider');
// Put the slider in a container
// Put the slider in a container
this.$slider_container = $('<div />')
.addClass('widget-hslider')
.append(this.$slider);
this.$el_to_style = this.$slider_container; // Set default element to style
this.$el.append(this.$slider_container);
this.$readout = $('<div/>')
.appendTo(this.$el)
.addClass('widget-hreadout')
.hide();
// Set defaults.
this.update();
},
update : function(options){
// Update the contents of this view
//
// Called when the model is changed. The model may have been
// Called when the model is changed. The model may have been
// changed by another view or by a state update from the back-end.
if (options === undefined || options.updated_view != this) {
// JQuery slider option keys. These keys happen to have a
// one-to-one mapping with the corrosponding keys of the model.
var jquery_slider_keys = ['step', 'max', 'min', 'disabled'];
var jquery_slider_keys = ['step', 'max', 'min', 'disabled', 'range'];
var that = this;
_.each(jquery_slider_keys, function(key, i) {
var model_value = that.model.get(key);
@ -68,15 +68,25 @@ define(["widgets/js/widget"], function(WidgetManager){
// of orientation change. Before applying the new
// workaround, we set the value to the minimum to
// make sure that the horizontal placement of the
// handle in the vertical slider is always
// handle in the vertical slider is always
// consistent.
var orientation = this.model.get('orientation');
var value = this.model.get('min');
this.$slider.slider('option', 'value', value);
if (this.model.get('range')) {
this.$slider.slider('option', 'values', [value, value]);
} else {
this.$slider.slider('option', 'value', value);
}
this.$slider.slider('option', 'orientation', orientation);
value = this.model.get('value');
this.$slider.slider('option', 'value', value);
this.$readout.text(value);
if (this.model.get('range')) {
this.$slider.slider('option', 'values', value);
this.$readout.text(value.join("-"));
} else {
this.$slider.slider('option', 'value', value);
this.$readout.text(value);
}
// Use the right CSS classes for vertical & horizontal sliders
if (orientation=='vertical') {
@ -116,7 +126,7 @@ define(["widgets/js/widget"], function(WidgetManager){
MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
this.$label.show();
}
var readout = this.model.get('readout');
if (readout) {
this.$readout.show();
@ -126,21 +136,27 @@ define(["widgets/js/widget"], function(WidgetManager){
}
return IntSliderView.__super__.update.apply(this);
},
events: {
// Dictionary of events and their handlers.
"slide" : "handleSliderChange"
},
},
handleSliderChange: function(e, ui) {
handleSliderChange: function(e, ui) {
// Called when the slider value is changed.
// Calling model.set will trigger all of the other views of the
// Calling model.set will trigger all of the other views of the
// model to update.
var actual_value = this._validate_slide_value(ui.value);
if (this.model.get("range")) {
var actual_value = ui.values.map(this._validate_slide_value);
this.$readout.text(actual_value.join("-"));
} else {
var actual_value = this._validate_slide_value(ui.value);
this.$readout.text(actual_value);
}
this.model.set('value', actual_value, {updated_view: this});
this.$readout.text(actual_value);
this.touch();
},
_validate_slide_value: function(x) {
@ -154,7 +170,7 @@ define(["widgets/js/widget"], function(WidgetManager){
WidgetManager.register_widget_view('IntSliderView', IntSliderView);
var IntTextView = IPython.DOMWidgetView.extend({
var IntTextView = IPython.DOMWidgetView.extend({
render : function(){
// Called when view is rendered.
this.$el
@ -170,18 +186,18 @@ define(["widgets/js/widget"], function(WidgetManager){
this.$el_to_style = this.$textbox; // Set default element to style
this.update(); // Set defaults.
},
update : function(options){
// Update the contents of this view
//
// Called when the model is changed. The model may have been
// Called when the model is changed. The model may have been
// changed by another view or by a state update from the back-end.
if (options === undefined || options.updated_view != this) {
var value = this.model.get('value');
if (this._parse_value(this.$textbox.val()) != value) {
this.$textbox.val(value);
}
if (this.model.get('disabled')) {
this.$textbox.attr('disabled','disabled');
} else {
@ -208,20 +224,20 @@ define(["widgets/js/widget"], function(WidgetManager){
// Fires only when control is validated or looses focus.
"change input" : "handleChanged"
},
handleChanging: function(e) {
},
handleChanging: function(e) {
// Handles and validates user input.
// Try to parse value as a int.
var numericalValue = 0;
if (e.target.value !== '') {
var trimmed = e.target.value.trim();
if (!(['-', '-.', '.', '+.', '+'].indexOf(trimmed) >= 0)) {
numericalValue = this._parse_value(e.target.value);
}
numericalValue = this._parse_value(e.target.value);
}
}
// If parse failed, reset value to value stored in model.
if (isNaN(numericalValue)) {
e.target.value = this.model.get('value');
@ -232,18 +248,18 @@ define(["widgets/js/widget"], function(WidgetManager){
if (this.model.get('min') !== undefined) {
numericalValue = Math.max(this.model.get('min'), numericalValue);
}
// Apply the value if it has changed.
if (numericalValue != this.model.get('value')) {
// Calling model.set will trigger all of the other views of the
// Calling model.set will trigger all of the other views of the
// model to update.
this.model.set('value', numericalValue, {updated_view: this});
this.touch();
}
}
},
handleChanged: function(e) {
// Applies validated input.
if (this.model.get('value') != e.target.value) {
@ -279,18 +295,18 @@ define(["widgets/js/widget"], function(WidgetManager){
.appendTo(this.$progress);
this.update(); // Set defaults.
},
update : function(){
// Update the contents of this view
//
// Called when the model is changed. The model may have been
// Called when the model is changed. The model may have been
// changed by another view or by a state update from the back-end.
var value = this.model.get('value');
var max = this.model.get('max');
var min = this.model.get('min');
var percent = 100.0 * (value - min) / (max - min);
this.$bar.css('width', percent + '%');
var description = this.model.get('description');
if (description.length === 0) {
this.$label.hide();
@ -300,7 +316,7 @@ define(["widgets/js/widget"], function(WidgetManager){
this.$label.show();
}
return ProgressView.__super__.update.apply(this);
},
},
});
WidgetManager.register_widget_view('ProgressView', ProgressView);

@ -5,7 +5,7 @@ from .widget_button import ButtonWidget
from .widget_container import ContainerWidget, PopupWidget
from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
from .widget_image import ImageWidget
from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget, IntRangeSliderWidget
from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
from .widget_selectioncontainer import TabWidget, AccordionWidget
from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget

@ -23,7 +23,7 @@ from inspect import getcallargs
from IPython.core.getipython import get_ipython
from IPython.html.widgets import (Widget, TextWidget,
FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
ContainerWidget, DOMWidget)
ContainerWidget, DOMWidget, IntRangeSliderWidget)
from IPython.display import display, clear_output
from IPython.utils.py3compat import string_types, unicode_type
from IPython.utils.traitlets import HasTraits, Any, Unicode
@ -107,6 +107,15 @@ def _widget_abbrev(o):
else:
cls = FloatSliderWidget
return cls(value=value, min=min, max=max, step=step)
elif _matches(o, [float_or_int]*4):
min, low, high, max = o
if not min <= low <= high <= max:
raise ValueError("Range input expects min <= low <= high <= max, got %r" % o)
if all(isinstance(_, int) for _ in o):
cls = IntRangeSliderWidget
else:
cls = FloatRangeSliderWidget
return cls(value=(low, high), min=min, max=max)
else:
return _widget_abbrev_single_value(o)

@ -1,4 +1,4 @@
"""IntWidget class.
"""IntWidget class.
Represents an unbounded int using a widget.
"""
@ -14,13 +14,13 @@ Represents an unbounded int using a widget.
# Imports
#-----------------------------------------------------------------------------
from .widget import DOMWidget
from IPython.utils.traitlets import Unicode, CInt, Bool, Enum
from IPython.utils.traitlets import Unicode, CInt, Bool, Enum, Tuple
#-----------------------------------------------------------------------------
# Classes
#-----------------------------------------------------------------------------
class _IntWidget(DOMWidget):
value = CInt(0, help="Int value", sync=True)
value = CInt(0, help="Int value", sync=True)
disabled = Bool(False, help="Enable or disable user changes", sync=True)
description = Unicode(help="Description of the value this widget represents", sync=True)
@ -51,10 +51,40 @@ class BoundedIntTextWidget(_BoundedIntWidget):
class IntSliderWidget(_BoundedIntWidget):
_view_name = Unicode('IntSliderView', sync=True)
orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
help="Vertical or horizontal.", sync=True)
range = Bool(False, help="Display a range selector", sync=True)
readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
class IntProgressWidget(_BoundedIntWidget):
_view_name = Unicode('ProgressView', sync=True)
class _IntRangeWidget(_IntWidget):
value = Tuple(CInt, CInt, default_value=(0, 1), help="Low and high int values", sync=True)
class _BoundedIntRangeWidget(_IntRangeWidget):
step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
max = CInt(100, help="Max value", sync=True)
min = CInt(0, help="Min value", sync=True)
def __init__(self, *pargs, **kwargs):
"""Constructor"""
DOMWidget.__init__(self, *pargs, **kwargs)
self.on_trait_change(self._validate, ['value', 'min', 'max'])
def _validate(self, name, old, new):
"""Validate min <= low <= high <= max"""
if name == "value":
low, high = new
low = max(low, self.min)
high = min(high, self.max)
self.value = (min(low, high), max(low, high))
class IntRangeSliderWidget(_BoundedIntRangeWidget):
_view_name = Unicode('IntSliderView', sync=True)
orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
help="Vertical or horizontal.", sync=True)
range = Bool(True, help="Display a range selector", sync=True)
readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)

Loading…
Cancel
Save