Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added FloatLogSlider #1928

Merged
merged 2 commits into from
Mar 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ipywidgets/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from .widget_bool import Checkbox, ToggleButton, Valid
from .widget_button import Button, ButtonStyle
from .widget_box import Box, HBox, VBox
from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider
from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider, FloatLogSlider
from .widget_image import Image
from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider, Play, SliderStyle
from .widget_color import ColorPicker
Expand Down
76 changes: 76 additions & 0 deletions ipywidgets/widgets/widget_float.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,40 @@ def _validate_max(self, proposal):
self.value = max
return max

class _BoundedLogFloat(_Float):
max = CFloat(4.0, help="Max value for the exponent").tag(sync=True)
min = CFloat(0.0, help="Min value for the exponent").tag(sync=True)
base = CFloat(10.0, help="Base of value").tag(sync=True)
value = CFloat(1.0, help="Float value").tag(sync=True)

@validate('value')
def _validate_value(self, proposal):
"""Cap and floor value"""
value = proposal['value']
if self.base ** self.min > value or self.base ** self.max < value:
value = min(max(value, self.base ** self.min), self.base ** self.max)
return value

@validate('min')
def _validate_min(self, proposal):
"""Enforce base ** min <= value <= base ** max"""
min = proposal['value']
if min > self.max:
raise TraitError('Setting min > max')
if self.base ** min > self.value:
self.value = self.base ** min
return min

@validate('max')
def _validate_max(self, proposal):
"""Enforce base ** min <= value <= base ** max"""
max = proposal['value']
if max < self.min:
raise TraitError('setting max < min')
if self.base ** max < self.value:
self.value = self.base ** max
return max


@register
class FloatText(_Float):
Expand Down Expand Up @@ -141,6 +175,48 @@ class FloatSlider(_BoundedFloat):
continuous_update = Bool(True, help="Update the value of the widget as the user is holding the slider.").tag(sync=True)
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)

style = InstanceDict(SliderStyle).tag(sync=True, **widget_serialization)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that we didn't have the right style!



@register
class FloatLogSlider(_BoundedLogFloat):
""" Slider/trackbar of logarithmic floating values with the specified range.
Parameters
----------
value : float
position of the slider
base : float
base of the logarithmic scale. Default is 10
min : float
minimal position of the slider in log scale, i.e., actual minimum is base ** min
max : float
maximal position of the slider in log scale, i.e., actual maximum is base ** max
step : float
step of the trackbar, denotes steps for the exponent, not the actual value
description : str
name of the slider
orientation : {'horizontal', 'vertical'}
default is 'horizontal', orientation of the slider
readout : {True, False}
default is True, display the current value of the slider next to it
readout_format : str
default is '.2f', specifier for the format function used to represent
slider value for human consumption, modeled after Python 3's format
specification mini-language (PEP 3101).
"""
_view_name = Unicode('FloatLogSliderView').tag(sync=True)
_model_name = Unicode('FloatLogSliderModel').tag(sync=True)
step = CFloat(0.1, help="Minimum step in the exponent to increment the value").tag(sync=True)
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
readout_format = NumberFormat(
'.2f', help="Format for the readout").tag(sync=True)
continuous_update = Bool(True, help="Update the value of the widget as the user is holding the slider.").tag(sync=True)
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
base = CFloat(10., help="Base for the logarithm").tag(sync=True)


style = InstanceDict(SliderStyle).tag(sync=True, **widget_serialization)

Expand Down
142 changes: 141 additions & 1 deletion packages/controls/src/widget_float.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import {
import * as _ from 'underscore';

import {
IntSliderView, IntRangeSliderView, IntTextView
IntSliderView, IntRangeSliderView, IntTextView, BaseIntSliderView
} from './widget_int';

import {
format
} from 'd3-format';


export
class FloatModel extends CoreDescriptionModel {
defaults() {
Expand Down Expand Up @@ -69,6 +70,39 @@ class FloatSliderModel extends BoundedFloatModel {
readout_formatter: any;
}

export
class FloatLogSliderModel extends BoundedFloatModel {
defaults() {
return _.extend(super.defaults(), {
_model_name: 'FloatLogSliderModel',
_view_name: 'FloatLogSliderView',
step: 0.1,
orientation: 'horizontal',
_range: false,
readout: true,
readout_format: '.2f',
slider_color: null,
continuous_update: true,
disabled: false,
base: 10.,
value: 1.0,
min: 0,
max: 4
});
}
initialize(attributes, options) {
super.initialize(attributes, options);
this.on('change:readout_format', this.update_readout_format, this);
this.update_readout_format();
}

update_readout_format() {
this.readout_formatter = format(this.get('readout_format'));
}

readout_formatter: any;
}

export
class FloatRangeSliderModel extends FloatSliderModel {}

Expand All @@ -85,6 +119,112 @@ class FloatSliderView extends IntSliderView {
_parse_value = parseFloat;
}


export
class FloatLogSliderView extends BaseIntSliderView {

update(options?) {
super.update(options);
let min = this.model.get('min');
let max = this.model.get('max');
let value = this.model.get('value');
let base = this.model.get('base');

let log_value = Math.log( value ) / Math.log( base );

if(log_value > max) {
log_value = max;
} else if(log_value < min) {
log_value = min;
}
this.$slider.slider('option', 'value', log_value);
this.readout.textContent = this.valueToString(value);
if(this.model.get('value') !== value) {
this.model.set('value', value, {updated_view: this});
this.touch();
}
}

/**
* Write value to a string
*/
valueToString(value: number): string {
let format = this.model.readout_formatter;
return format(value);
}

/**
* Parse value from a string
*/
stringToValue(text: string): number {
return this._parse_value(text);
}

/**
* this handles the entry of text into the contentEditable label first, the
* value is checked if it contains a parseable value then it is clamped
* within the min-max range of the slider finally, the model is updated if
* the value is to be changed
*
* if any of these conditions are not met, the text is reset
*/
handleTextChange() {
let value = this.stringToValue(this.readout.textContent);
let vmin = this.model.get('min');
let vmax = this.model.get('max');
let base = this.model.get('base');

if (isNaN(value)) {
this.readout.textContent = this.valueToString(this.model.get('value'));
} else {
value = Math.max(Math.min(value, Math.pow(base,vmax)), Math.pow(base,vmin));

if (value !== this.model.get('value')) {
this.readout.textContent = this.valueToString(value);
this.model.set('value', value, {updated_view: this});
this.touch();
} else {
this.readout.textContent = this.valueToString(this.model.get('value'));
}
}
}
/**
* Called when the slider value is changing.
*/
handleSliderChange(e, ui) {
let base = this.model.get('base');
let actual_value = Math.pow(base,this._validate_slide_value(ui.value));
this.readout.textContent = this.valueToString(actual_value);

// Only persist the value while sliding if the continuous_update
// trait is set to true.
if (this.model.get('continuous_update')) {
this.handleSliderChanged(e, ui);
}
}

/**
* Called when the slider value has changed.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
handleSliderChanged(e, ui) {
let base = this.model.get('base');
let actual_value = Math.pow(base,this._validate_slide_value(ui.value));
this.model.set('value', actual_value, {updated_view: this});
this.touch();
}

_validate_slide_value(x) {
return x;
}

_parse_value = parseFloat;

}


export
class FloatRangeSliderView extends IntRangeSliderView {
/**
Expand Down
25 changes: 25 additions & 0 deletions packages/schema/jupyterwidgetmodels.latest.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,31 @@ Attribute | Type | Default | Help
`layout` | reference to Layout widget | reference to new instance |
`style` | reference to DescriptionStyle widget | reference to new instance | Styling customizations

### FloatLogSliderModel (@jupyter-widgets/controls, 1.1.0); FloatLogSliderView (@jupyter-widgets/controls, 1.1.0)

Attribute | Type | Default | Help
-----------------|------------------|------------------|----
`_dom_classes` | array | `[]` | CSS classes applied to widget DOM element
`_model_module` | string | `'@jupyter-widgets/controls'` |
`_model_module_version` | string | `'1.1.0'` |
`_model_name` | string | `'FloatLogSliderModel'` |
`_view_module` | string | `'@jupyter-widgets/controls'` |
`_view_module_version` | string | `'1.1.0'` |
`_view_name` | string | `'FloatLogSliderView'` |
`base` | number (float) | `10.0` | Base for the logarithm
`continuous_update` | boolean | `true` | Update the value of the widget as the user is holding the slider.
`description` | string | `''` | Description of the control.
`disabled` | boolean | `false` | Enable or disable user changes
`layout` | reference to Layout widget | reference to new instance |
`max` | number (float) | `4.0` | Max value for the exponent
`min` | number (float) | `0.0` | Min value for the exponent
`orientation` | string (one of `'horizontal'`, `'vertical'`) | `'horizontal'` | Vertical or horizontal.
`readout` | boolean | `true` | Display the current value of the slider next to it.
`readout_format` | string | `'.2f'` | Format for the readout
`step` | number (float) | `0.1` | Minimum step in the exponent to increment the value
`style` | reference to SliderStyle widget | reference to new instance |
`value` | number (float) | `1.0` | Float value

### FloatProgressModel (@jupyter-widgets/controls, 1.1.0); ProgressView (@jupyter-widgets/controls, 1.1.0)

Attribute | Type | Default | Help
Expand Down