Source code for menpowidgets.options
import ipywidgets
from IPython.display import display, Javascript
from traitlets.traitlets import Int, Dict, List
from traitlets import link
from collections import OrderedDict
import numpy as np
from base64 import b64decode
from menpo.compatibility import unicode
from .abstract import MenpoWidget
from .tools import (IndexSliderWidget, IndexButtonsWidget, SlicingCommandWidget,
LineOptionsWidget, MarkerOptionsWidget, LogoWidget,
NumberingOptionsWidget, LegendOptionsWidget,
ZoomOneScaleWidget, ZoomTwoScalesWidget, AxesOptionsWidget,
GridOptionsWidget, ImageOptionsWidget, CameraWidget,
ColourSelectionWidget, HOGOptionsWidget, DSIFTOptionsWidget,
IGOOptionsWidget, LBPOptionsWidget, DaisyOptionsWidget)
from .style import (map_styles_to_hex_colours, format_box, format_font,
format_slider)
from .utils import sample_colours_from_colourmap
[docs]class AnimationOptionsWidget(MenpoWidget):
r"""
Creates a widget for animating through a list of objects. The widget
consists of the following objects from `ipywidgets` and
:ref:`api-tools-index`:
== ========================= ====================== ====================
No Object Property (`self.`) Description
== ========================= ====================== ====================
1 `ToggleButton` `play_stop_toggle` The play/stop button
2 `Button` `fast_forward_button` Increase speed
3 `Button` `fast_backward_button` Decrease speed
4 `ToggleButton` `loop_toggle` Repeat mode
5 `HBox` `animation_box` Contains 1, 2, 3, 4
8 :map:`IndexButtonsWidget` `index_wid` The index selector
:map:`IndexSliderWidget`
== ========================= ====================== ====================
Note that:
* The selected values are stored in the ``self.selected_values`` `trait`.
* To set the styling of this widget please refer to the :meth:`style` and
:meth:`predefined_style` methods.
* To update the state of the widget, please refer to the
:meth:`set_widget_state` method.
* To update the handler callback function of the widget, please refer to the
:meth:`replace_render_function` method.
Parameters
----------
index : `dict`
The initial options. It must be a `dict` with the following keys:
* ``min`` : (`int`) The minimum value (e.g. ``0``).
* ``max`` : (`int`) The maximum value (e.g. ``100``).
* ``step`` : (`int`) The index step (e.g. ``1``).
* ``index`` : (`int`) The index value (e.g. ``10``).
render_function : `callable` or ``None``, optional
The render function that is executed when a widgets' value changes.
It must have signature ``render_function(change)`` where ``change`` is
a `dict` with the following keys:
* ``type`` : The type of notification (normally ``'change'``).
* ``owner`` : the `HasTraits` instance
* ``old`` : the old value of the modified trait attribute
* ``new`` : the new value of the modified trait attribute
* ``name`` : the name of the modified trait attribute.
If ``None``, then nothing is assigned.
index_style : ``{'buttons', 'slider'}``, optional
If ``'buttons'``, then :map:`IndexButtonsWidget` class is called. If
``'slider'``, then :map:`IndexSliderWidget` class is called.
interval : `float`, optional
The interval between the animation progress in seconds.
interval_step : `float`, optional
The interval step (in seconds) that is applied when fast
forward/backward buttons are pressed.
description : `str`, optional
The title of the widget.
loop_enabled : `bool`, optional
If ``True``, then after reach the minimum (maximum) index values, the
counting will continue from the end (beginning). If ``False``, the
counting will stop at the minimum (maximum) value.
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
continuous_update : `bool`, optional
If ``True`` and `index_style` is set to ``'slider'``, then the render
and update functions are called while moving the slider's handle. If
``False``, then the the functions are called only when the handle
(mouse click) is released.
Example
-------
Let's create an animation widget and then update its state. Firstly, we need
to import it:
>>> from menpowidgets.options import AnimationOptionsWidget
Now let's define a render function that will get called on every widget
change and will dynamically print the selected index:
>>> from menpo.visualize import print_dynamic
>>> def render_function(change):
>>> s = "Selected index: {}".format(wid.selected_values)
>>> print_dynamic(s)
Create the widget with some initial options and display it:
>>> index = {'min': 0, 'max': 100, 'step': 1, 'index': 10}
>>> wid = AnimationOptionsWidget(index, index_style='buttons',
>>> render_function=render_function,
>>> style='info')
>>> wid
By pressing the buttons (or simply pressing the Play button), the printed
message gets updated. Finally, let's change the widget status with a new
dictionary of options:
>>> new_options = {'min': 0, 'max': 20, 'step': 2, 'index': 16}
>>> wid.set_widget_state(new_options, allow_callback=False)
"""
def __init__(self, index, render_function=None, index_style='buttons',
interval=0.2, interval_step=0.05, description='Index: ',
loop_enabled=True, style='minimal', continuous_update=False):
from time import sleep
from IPython import get_ipython
# Get the kernel to use it later in order to make sure that the widgets'
# traits changes are passed during a while-loop
kernel = get_ipython().kernel
# Create index widget
if index_style == 'slider':
self.index_wid = IndexSliderWidget(
index, description=description,
continuous_update=continuous_update)
elif index_style == 'buttons':
self.index_wid = IndexButtonsWidget(
index, description=description, minus_description='fa-minus',
plus_description='fa-plus', loop_enabled=loop_enabled,
text_editable=True)
else:
raise ValueError('index_style should be either slider or buttons')
self.index_wid.style(box_style=None, border_visible=False,
padding=0, margin='0.1cm')
# Create other widgets
self.play_stop_toggle = ipywidgets.ToggleButton(
icon='fa-play', description='', value=False, margin='0.1cm',
tooltip='Play animation')
self._toggle_play_style = '' if style == 'minimal' else 'success'
self._toggle_stop_style = '' if style == 'minimal' else 'danger'
self.fast_forward_button = ipywidgets.Button(
icon='fa-fast-forward', description='', margin='0.1cm',
tooltip='Increase animation speed')
self.fast_backward_button = ipywidgets.Button(
icon='fa-fast-backward', description='', margin='0.1cm',
tooltip='Decrease animation speed')
loop_icon = 'fa-repeat' if loop_enabled else 'fa-long-arrow-right'
self.loop_toggle = ipywidgets.ToggleButton(
icon=loop_icon, description='', value=loop_enabled,
margin='0.1cm', tooltip='Repeat animation')
self.animation_box = ipywidgets.HBox(
children=[self.play_stop_toggle, self.loop_toggle,
self.fast_backward_button, self.fast_forward_button])
# Create final widget
children = [self.index_wid, self.animation_box]
super(AnimationOptionsWidget, self).__init__(
children, Int, index['index'], render_function=render_function,
orientation='horizontal', align='start')
# Assign properties
self.min = index['min']
self.max = index['max']
self.step = index['step']
self.loop_enabled = loop_enabled
self.index_style = index_style
self.continuous_update = continuous_update
self.interval = interval
self.interval_step = interval_step
# Set style
self.predefined_style(style)
# Set functionality
def play_stop_pressed(change):
value = change['new']
if value:
# Animation was not playing, so Play was pressed.
# Change the button style
self.play_stop_toggle.button_style = self._toggle_stop_style
# Change the icon and tooltip to Stop
self.play_stop_toggle.icon = 'fa-stop'
self.play_stop_toggle.tooltip = 'Stop animation'
else:
# Animation was playing, so Stop was pressed.
# Change the button style
self.play_stop_toggle.button_style = self._toggle_play_style
# Change the icon and tooltip to Play
self.play_stop_toggle.icon = 'fa-play'
self.play_stop_toggle.tooltip = 'Play animation'
self.play_stop_toggle.observe(play_stop_pressed, names='value',
type='change')
def loop_pressed(change):
if change['new']:
self.loop_toggle.icon = 'fa-repeat'
else:
self.loop_toggle.icon = 'fa-long-arrow-right'
kernel.do_one_iteration()
self.loop_toggle.observe(loop_pressed, names='value', type='change')
def fast_forward_pressed(name):
tmp = self.interval
tmp -= self.interval_step
if tmp < 0:
tmp = 0
self.interval = tmp
kernel.do_one_iteration()
self.fast_forward_button.on_click(fast_forward_pressed)
def fast_backward_pressed(name):
self.interval += self.interval_step
kernel.do_one_iteration()
self.fast_backward_button.on_click(fast_backward_pressed)
def animate(change):
i = self.selected_values
if self.loop_toggle.value and i >= self.max:
i = self.min
else:
i += self.step
while i <= self.max and self.play_stop_toggle.value:
# update index value
if index_style == 'slider':
self.index_wid.slider.value = i
else:
self.index_wid.index_text.value = i
# Run IPython iteration.
# This is the code that makes this operation non-blocking.
# This allows widget messages and callbacks to be processed.
kernel.do_one_iteration()
# update counter
if self.loop_toggle.value and i >= self.max:
i = self.min
else:
i += self.step
# wait
sleep(self.interval)
if not self.loop_toggle.value and i > self.max:
self.stop_animation()
self.play_stop_toggle.observe(animate, names='value', type='change')
def save_value(change):
self.selected_values = self.index_wid.selected_values
self.index_wid.observe(save_value, names='selected_values',
type='change')
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='solid', border_width=1, border_radius=0, padding=0,
margin=0, font_family='', font_size=None, font_style='',
font_weight=''):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
format_font(self, font_family, font_size, font_style, font_weight)
if self.index_style == 'buttons':
self.index_wid.style(
box_style=None, border_visible=False, padding=0,
margin='0.1cm', font_family=font_family, font_size=font_size,
font_style=font_style, font_weight=font_weight)
else:
self.index_wid.style(
box_style=None, border_visible=False, padding=0,
margin='0.1cm', font_family=font_family, font_size=font_size,
font_style=font_style, font_weight=font_weight)
[docs] def predefined_style(self, style):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if style == 'minimal':
self.style(box_style='', border_visible=False)
self.play_stop_toggle.button_style = ''
self.fast_forward_button.button_style = ''
self.fast_backward_button.button_style = ''
self.loop_toggle.button_style = ''
if self.index_style == 'buttons':
self.index_wid.button_plus.button_style = ''
self.index_wid.button_plus.font_weight = 'normal'
self.index_wid.button_minus.button_style = ''
self.index_wid.button_minus.font_weight = 'normal'
self.index_wid.index_text.background_color = None
elif self.index_style == 'slider':
self.index_wid.slider.slider_color = None
self.index_wid.slider.background_color = None
self._toggle_play_style = ''
self._toggle_stop_style = ''
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
self.style(box_style=style, border_visible=False)
self.play_stop_toggle.button_style = 'success'
self.fast_forward_button.button_style = 'info'
self.fast_backward_button.button_style = 'info'
self.loop_toggle.button_style = 'info'
if self.index_style == 'buttons':
self.index_wid.button_plus.button_style = 'primary'
self.index_wid.button_plus.font_weight = 'bold'
self.index_wid.button_minus.button_style = 'primary'
self.index_wid.button_minus.font_weight = 'bold'
self.index_wid.index_text.background_color = \
map_styles_to_hex_colours(style, True)
elif self.index_style == 'slider':
self.index_wid.slider.slider_color = \
map_styles_to_hex_colours(style)
self.index_wid.slider.background_color = \
map_styles_to_hex_colours(style)
self._toggle_play_style = 'success'
self._toggle_stop_style = 'danger'
else:
raise ValueError('style must be minimal or info or success or '
'danger or warning')
[docs] def set_widget_state(self, index, allow_callback=True):
r"""
Method that updates the state of the widget, if the provided `index`
values are different than `self.selected_values`.
Parameters
----------
index : `dict`
The selected options. It must be a `dict` with the following keys:
* ``min`` : (`int`) The minimum value (e.g. ``0``).
* ``max`` : (`int`) The maximum value (e.g. ``100``).
* ``step`` : (`int`) The index step (e.g. ``1``).
* ``index`` : (`int`) The index value (e.g. ``10``).
allow_callback : `bool`, optional
If ``True``, it allows triggering of any callback functions.
"""
# Keep old value
old_value = self.selected_values
# Check if update is required
if (index['index'] != self.selected_values or
index['min'] != self.min or
index['max'] != self.max or
index['step'] != self.step):
# temporarily remove render callback
render_function = self._render_function
self.remove_render_function()
# update
if self.play_stop_toggle.value:
self.stop_animation()
if self.index_style == 'slider':
self.index_wid.set_widget_state(index, allow_callback=False)
else:
self.index_wid.set_widget_state(
index, loop_enabled=self.loop_toggle.value,
text_editable=True, allow_callback=False)
self.selected_values = index['index']
self.min = index['min']
self.max = index['max']
self.step = index['step']
# re-assign render callback
self.add_render_function(render_function)
# trigger render function if allowed
if allow_callback:
self.call_render_function(old_value, self.selected_values)
[docs] def stop_animation(self):
r"""
Method that stops an active annotation by setting
``self.play_stop_toggle.value = False``.
"""
self.play_stop_toggle.value = False
[docs]class ChannelOptionsWidget(MenpoWidget):
r"""
Creates a widget for selecting channel options for rendering an image. The
widget consists of the following objects from `ipywidgets` and
:ref:`api-tools-index`:
== =========================== ======================== =====================
No Object Property (`self.`) Description
== =========================== ======================== =====================
1 :map:`SlicingCommandWidget` `channels_wid` The channels selector
2 `Checkbox` `masked_checkbox` Controls masked mode
3 `Checkbox` `rgb_checkbox` View as RGB
4 `Checkbox` `sum_checkbox` View sum of channels
5 `Checkbox` `glyph_checkbox` View glyph
6 `BoundedIntText` `glyph_block_size_text` Glyph block size
7 `Checkbox` `glyph_use_neg_checkbox` Use negative values
8 `Latex` `no_options_latex` No options message
9 `VBox` `glyph_options_box` Contains 6, 7
10 `HBox` `glyph_box` Contains 5, 9
11 `HBox` `rgb_masked_options_box` Contains 3, 2
12 `HBox` `glyph_sum_options_box` Contains 4, 10
13 `VBox` `checkboxes_box` Contains 11, 12
== =========================== ======================== =====================
Note that:
* To update the state of the widget, please refer to the
:meth:`set_widget_state` method.
* The widget has **memory** about the properties of the objects that are
passed into it through :meth:`set_widget_state`. Each image object has a
unique key id assigned through :meth:`get_key`. Then, the options that
correspond to each key are stored in the ``self.default_options`` `dict`.
* The selected values of the current image object are stored in the
``self.selected_values`` `trait`. It is a `dict` with the following keys:
* ``channels`` : (`list`) The selected channels.
* ``glyph_enabled`` : (`bool`) Whether to render as glyph.
* ``glyph_block_size`` : (`int`) The glyph's block size.
* ``glyph_use_negative`` : (`bool`) Whether to use negative values in glyph
* ``sum_enabled`` : (`bool`) Whether to render as sum of channels.
* ``masked_enabled`` : (`bool`) Whether to render as masked.
* When an unseen image object is passed in (i.e. a key that is not included
in the ``self.default_options`` `dict`), it gets the following initial
options by default:
* ``channels = [0] if n_channels == 3 else None``
* ``glyph_enabled = False``
* ``glyph_block_size = 3``
* ``glyph_use_negative = False``
* ``sum_enabled = False``
* ``masked_enabled = image_is_masked``
* To set the styling of this widget please refer to the :meth:`style` and
:meth:`predefined_style` methods.
* To update the handler callback function of the widget, please refer to the
:meth:`replace_render_function` method.
Parameters
----------
n_channels : `int`
The number of channels of the initial image object.
image_is_masked : `bool`
Whether the initial image object is masked or not. If ``True``, then the
image is assumed to be a `menpo.image.MaskedImage` object.
render_function : `callable` or ``None``, optional
The render function that is executed when a widgets' value changes.
It must have signature ``render_function(change)`` where ``change`` is
a `dict` with the following keys:
* ``type`` : The type of notification (normally ``'change'``).
* ``owner`` : the `HasTraits` instance
* ``old`` : the old value of the modified trait attribute
* ``new`` : the new value of the modified trait attribute
* ``name`` : the name of the modified trait attribute.
If ``None``, then nothing is assigned.
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
Example
-------
Let's create a channels widget and then update its state. Firstly, we need
to import it:
>>> from menpowidgets.options import ChannelOptionsWidget
Now let's define a render function that will get called on every widget
change and will dynamically print the selected channels and masked flag:
>>> from menpo.visualize import print_dynamic
>>> def render_function(change):
>>> s = "Channels: {}, Masked: {}, Glyph: {}, Sum: {}".format(
>>> wid.selected_values['channels'],
>>> wid.selected_values['masked_enabled'],
>>> wid.selected_values['glyph_enabled'],
>>> wid.selected_values['sum_enabled'])
>>> print_dynamic(s)
Create the widget with some initial options and display it:
>>> wid = ChannelOptionsWidget(n_channels=30, image_is_masked=True,
>>> render_function=render_function,
>>> style='warning')
>>> wid
By playing around with the widget, printed message gets updated. Finally,
let's change the widget status with a new object:
>>> wid.set_widget_state(n_channels=10, image_is_masked=False,
>>> allow_callback=False)
Remember that the widget is **mnemonic**, i.e. it remembers the objects it
has seen and their corresponding options. These can be retrieved as:
>>> wid.default_options
"""
def __init__(self, n_channels, image_is_masked, render_function=None,
style='minimal'):
# Initialise default options dictionary
self.default_options = {}
# Assign properties
self.n_channels = None
self.image_is_masked = None
# Create children
slice_options = {'command': '0', 'length': n_channels}
self.channels_wid = SlicingCommandWidget(
slice_options, description='Channels:', render_function=None,
example_visible=True, continuous_update=False,
orientation='horizontal')
self.masked_checkbox = ipywidgets.Checkbox(description='Masked',
margin='0.1cm')
self.rgb_checkbox = ipywidgets.Checkbox(description='RGB',
margin='0.1cm')
self.sum_checkbox = ipywidgets.Checkbox(description='Sum',
margin='0.1cm')
self.glyph_checkbox = ipywidgets.Checkbox(description='Glyph',
margin='0.1cm')
self.glyph_block_size_text = ipywidgets.BoundedIntText(
description='Block size', min=1, max=25, width='1.5cm')
self.glyph_use_negative_checkbox = ipywidgets.Checkbox(
description='Negative')
self.no_options_latex = ipywidgets.Latex(value='No options available')
# Group widgets
self.glyph_options_box = ipywidgets.VBox(
children=[self.glyph_block_size_text,
self.glyph_use_negative_checkbox], margin='0.1cm',
visible=False)
self.glyph_box = ipywidgets.HBox(children=[self.glyph_checkbox,
self.glyph_options_box],
align='start')
self.rgb_masked_options_box = ipywidgets.HBox(
children=[self.rgb_checkbox, self.masked_checkbox])
self.glyph_sum_options_box = ipywidgets.HBox(
children=[self.sum_checkbox, self.glyph_box])
self.checkboxes_box = ipywidgets.VBox(
children=[self.rgb_masked_options_box, self.glyph_sum_options_box])
# Create final widget
children = [self.channels_wid, self.checkboxes_box,
self.no_options_latex]
super(ChannelOptionsWidget, self).__init__(
children, Dict, {}, render_function=render_function,
orientation='horizontal', align='start')
# Set values
self.set_widget_state(n_channels, image_is_masked, allow_callback=False)
# Set style
self.predefined_style(style)
[docs] def add_callbacks(self):
r"""
Function that adds the handler callback functions in all the widget
components, which are necessary for the internal functionality.
"""
self.glyph_block_size_text.observe(self._save_options, names='value',
type='change')
self.glyph_use_negative_checkbox.observe(
self._save_options, names='value', type='change')
self.masked_checkbox.observe(self._save_options, names='value',
type='change')
self.channels_wid.observe(self._save_channels, names='selected_values',
type='change')
self.rgb_checkbox.observe(self._save_rgb, names='value', type='change')
self.sum_checkbox.observe(self._save_sum, names='value', type='change')
self.glyph_checkbox.observe(self._save_glyph, names='value',
type='change')
[docs] def remove_callbacks(self):
r"""
Function that removes all the internal handler callback functions.
"""
self.glyph_block_size_text.unobserve(self._save_options, names='value',
type='change')
self.glyph_use_negative_checkbox.unobserve(
self._save_options, names='value', type='change')
self.masked_checkbox.unobserve(self._save_options, names='value',
type='change')
self.channels_wid.unobserve(self._save_channels, names='selected_values',
type='change')
self.rgb_checkbox.unobserve(self._save_rgb, names='value', type='change')
self.sum_checkbox.unobserve(self._save_sum, names='value', type='change')
self.glyph_checkbox.unobserve(self._save_glyph, names='value',
type='change')
def _save_options(self, change):
# get channels value
channels_val = self.channels_wid.selected_values
if self.rgb_checkbox.value:
channels_val = None
# update selected values
self.selected_values = {
'channels': channels_val,
'glyph_enabled': self.glyph_checkbox.value,
'glyph_block_size': self.glyph_block_size_text.value,
'glyph_use_negative': self.glyph_use_negative_checkbox.value,
'sum_enabled': self.sum_checkbox.value,
'masked_enabled': self.masked_checkbox.value}
# update default values
current_key = self.get_key(self.n_channels, self.image_is_masked)
self.default_options[current_key] = self.selected_values
def _save_channels(self, change):
if self.n_channels == 3:
# temporarily remove rgb callback
self.rgb_checkbox.unobserve(self._save_rgb, names='value',
type='change')
# set value
self.rgb_checkbox.value = False
# re-assign rgb callback
self.rgb_checkbox.observe(self._save_rgb, names='value',
type='change')
self._save_options({})
def _save_rgb(self, change):
if change['new']:
# temporarily remove channels callback
self.channels_wid.unobserve(
self._save_channels, names='selected_values', type='change')
# update channels widget
self.channels_wid.set_widget_state(
{'command': '0, 1, 2', 'length': self.n_channels},
allow_callback=False)
# re-assign channels callback
self.channels_wid.observe(
self._save_channels, names='selected_values', type='change')
self._save_options({})
def _save_sum(self, change):
if change['new'] and self.glyph_checkbox.value:
# temporarily remove glyph callback
self.glyph_checkbox.unobserve(self._save_glyph, names='value',
type='change')
# set glyph to False
self.glyph_checkbox.value = False
self.glyph_options_box.visible = False
# re-assign glyph callback
self.glyph_checkbox.observe(self._save_glyph, names='value',
type='change')
self._save_options({})
def _save_glyph(self, change):
if change['new'] and self.sum_checkbox.value:
# temporarily remove sum callback
self.sum_checkbox.unobserve(self._save_sum, names='value',
type='change')
# set glyph to false
self.sum_checkbox.value = False
# re-assign sum callback
self.sum_checkbox.observe(self._save_sum, names='value',
type='change')
# set visibility
self.glyph_options_box.visible = change['new']
self._save_options({})
[docs] def set_visibility(self):
r"""
Function that sets the visibility of the various components of the
widget, depending on the properties of the current image object, i.e.
``self.n_channels`` and ``self.image_is_masked``.
"""
self.channels_wid.visible = self.n_channels > 1
self.glyph_sum_options_box.visible = self.n_channels > 1
self.rgb_checkbox.visible = self.n_channels == 3
self.masked_checkbox.visible = self.image_is_masked
self.no_options_latex.visible = (self.n_channels == 1 and
not self.image_is_masked)
self.glyph_options_box.visible = self.glyph_checkbox.value
[docs] def get_key(self, n_channels, image_is_masked):
r"""
Function that returns a unique key based on the properties of the
provided image object.
Parameters
----------
n_channels : `int`
The number of channels.
image_is_masked : `bool`
Whether the image object is masked or not. If ``True``, then the
image is assumed to be a `menpo.image.MaskedImage` object.
Returns
-------
key : `str`
The key that has the format ``'{n_channels}_{image_is_masked}'``.
"""
return "{}_{}".format(n_channels, image_is_masked)
[docs] def get_default_options(self, n_channels, image_is_masked):
r"""
Function that returns a `dict` with default options given the properties
of an image object, i.e. `n_channels` and `image_is_masked`. The function
returns the `dict` of options but also updates the
``self.default_options`` `dict`.
Parameters
----------
n_channels : `int`
The number of channels.
image_is_masked : `bool`
Whether the image object is masked or not. If ``True``, then the
image is assumed to be a `menpo.image.MaskedImage` object.
Returns
-------
default_options : `dict`
A `dict` with the default options. It contains:
* ``channels`` : (`list`) The selected channels.
* ``glyph_enabled`` : (`bool`) Whether to render as glyph.
* ``glyph_block_size`` : (`int`) The glyph's block size.
* ``glyph_use_negative`` : (`bool`) Whether to use negative values.
* ``sum_enabled`` : (`bool`) Whether to render as sum of channels.
* ``masked_enabled`` : (`bool`) Whether to render as masked.
If the object is not seen before by the widget, then it automatically
gets the following default options:
* ``channels = [0] if n_channels == 3 else None``
* ``glyph_enabled = False``
* ``glyph_block_size = 3``
* ``glyph_use_negative = False``
* ``sum_enabled = False``
* ``masked_enabled = image_is_masked``
"""
# create key
key = self.get_key(n_channels, image_is_masked)
# if the key does not exist in the default options dict, then add it
if key not in self.default_options:
# if image has 3 channels, visualise it as RGB, else render only the
# first channel
channels = None if n_channels == 3 else [0]
# if image is masked, render it as masked
masked_enabled = image_is_masked
# update default options dictionary
self.default_options[key] = {'channels': channels,
'glyph_enabled': False,
'glyph_block_size': 3,
'glyph_use_negative': False,
'sum_enabled': False,
'masked_enabled': masked_enabled}
return self.default_options[key]
def _parse_channels_value(self, channels):
if channels is None:
return '0, 1, 2'
elif isinstance(channels, list):
return str(channels).strip('[]')
else:
return str(channels)
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='solid', border_width=1, border_radius=0, padding=0,
margin=0, font_family='', font_size=None, font_style='',
font_weight='', slider_width='4cm'):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
slider_width : `str`, optional
The width of the slider.
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
self.channels_wid.single_slider.width = slider_width
self.channels_wid.multiple_slider.width = slider_width
format_font(self, font_family, font_size, font_style, font_weight)
format_font(self.rgb_checkbox, font_family, font_size, font_style,
font_weight)
format_font(self.masked_checkbox, font_family, font_size, font_style,
font_weight)
format_font(self.sum_checkbox, font_family, font_size, font_style,
font_weight)
format_font(self.glyph_checkbox, font_family, font_size, font_style,
font_weight)
format_font(self.glyph_block_size_text, font_family, font_size,
font_style, font_weight)
format_font(self.glyph_use_negative_checkbox, font_family, font_size,
font_style, font_weight)
format_font(self.no_options_latex, font_family, font_size, font_style,
font_weight)
self.channels_wid.style(
box_style=box_style, border_visible=False, text_box_style=None,
text_box_background_colour=None, text_box_width=None,
font_family=font_family, font_size=font_size, font_style=font_style,
font_weight=font_weight)
[docs] def predefined_style(self, style):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if style == 'minimal':
self.style(box_style=None, border_visible=True,
border_colour='black', border_style='solid',
border_width=1, border_radius=0, padding='0.2cm',
margin='0.3cm', font_family='', font_size=None,
font_style='', font_weight='', slider_width='4cm')
format_box(self.glyph_options_box, box_style='',
border_visible=False, border_colour='',
border_style='solid', border_width=1, border_radius=0,
padding='0.1cm', margin=0)
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
self.style(box_style=style, border_visible=True,
border_colour=map_styles_to_hex_colours(style),
border_style='solid', border_width=1, border_radius=10,
padding='0.2cm', margin='0.3cm', font_family='',
font_size=None, font_style='', font_weight='',
slider_width='4cm')
format_box(self.glyph_options_box, box_style=style,
border_visible=True,
border_colour=map_styles_to_hex_colours(style),
border_style='solid', border_width=1, border_radius=10,
padding='0.1cm', margin=0)
else:
raise ValueError('style must be minimal or info or success or '
'danger or warning')
[docs] def set_widget_state(self, n_channels, image_is_masked, allow_callback=True):
r"""
Method that updates the state of the widget, if the key generated with
:meth:`get_key` based on the provided `n_channels` and `image_is_masked`
is different than the current key based on ``self.n_channels`` and
``self.image_is_masked``.
Parameters
----------
n_channels : `int`
The number of channels of the initial image object.
image_is_masked : `bool`
Whether the initial image object is masked or not. If ``True``, then
the image is assumed to be a `menpo.image.MaskedImage` object.
allow_callback : `bool`, optional
If ``True``, it allows triggering of any callback functions.
"""
# keep old value
old_value = self.selected_values
# check if updates are required
if (not self.default_options or
self.get_key(self.n_channels, self.image_is_masked) !=
self.get_key(n_channels, image_is_masked)):
# temporarily remove callbacks
render_function = self._render_function
self.remove_render_function()
self.remove_callbacks()
# Assign properties
self.n_channels = n_channels
self.image_is_masked = image_is_masked
# Get initial options
channel_options = self.get_default_options(n_channels,
image_is_masked)
# Parse channels value
channels = self._parse_channels_value(channel_options['channels'])
# Set both sum and glyph to False, to make sure that there is no
# conflict
self.sum_checkbox.value = False
self.glyph_checkbox.value = False
# Update widgets' state
slice_options = {'command': channels, 'length': self.n_channels}
self.channels_wid.set_widget_state(slice_options,
allow_callback=False)
self.masked_checkbox.value = channel_options['masked_enabled']
self.rgb_checkbox.value = (self.n_channels == 3 and
channel_options['channels'] is None)
self.sum_checkbox.value = channel_options['sum_enabled']
self.glyph_checkbox.value = channel_options['glyph_enabled']
self.glyph_block_size_text.value = \
channel_options['glyph_block_size']
self.glyph_use_negative_checkbox.value = \
channel_options['glyph_use_negative']
# Set widget's visibility
self.set_visibility()
# Get values
self._save_options({})
# Re-assign callbacks
self.add_callbacks()
self.add_render_function(render_function)
# trigger render function if allowed
if allow_callback:
self.call_render_function(old_value, self.selected_values)
[docs]class LandmarkOptionsWidget(MenpoWidget):
r"""
Creates a widget for selecting landmark options. The widget consists of the
following objects from `ipywidgets`:
== =============== =========================== ==============================
No Object Property (`self.`) Description
== =============== =========================== ==============================
1 `Latex` `no_landmarks_msg` No landmarks available msg.
2 `Checkbox` `render_landmarks_checkbox` Render landmarks checkbox
3 `Latex` `group_description` Landmark group title
4 `IntSlider` `group_slider` Landmark group selector
5 `Dropdown` `group_dropdown` Landmark group selector
6 `Latex` `group_latex` Landmark group text
7 `HBox` `group_selection_box` Contains 3, 4, 5, 6
8 `Latex` `labels_text` Labels title
9 `ToggleButtons` `labels_toggles` list with the labels per group
10 `HBox` `labels_box` Contains all 9
11 `HBox` `labels_and_text_box` Contains 8 and 10
12 `VBox` `options_box` Contains 7, 11
13 `HBox` `render_and_options_box` Contains 2, 12
== =============== =========================== ==============================
Note that:
* To update the state of the widget, please refer to the
:meth:`set_widget_state` method.
* The widget has **memory** about the properties of the objects that are
passed into it through :meth:`set_widget_state`. Each landmarks object has
a unique key id assigned through :meth:`get_key`. Then, the options that
correspond to each key are stored in the ``self.default_options`` `dict`.
* The selected values of the current landmarks object are stored in the
``self.selected_values`` `trait`. It is a `dict` with the following keys:
* ``group`` : (`str` or ``None``) The selected group.
* ``with_labels`` : (`list` or ``None``) The selected labels.
* ``render_landmarks`` : (`bool`) Whether to render the landmarks.
* When an unseen landmarks object is passed in (i.e. a key that is not
included in the ``self.default_options`` `dict`), it gets the following
initial options by default:
* ``group = None if group_keys is None else group_keys[0]``
* ``with_labels = None if group_keys is None else labels_keys[0]``
* ``render_landmarks = False if group_keys is None else True``
* To set the styling of this widget please refer to the :meth:`style` and
:meth:`predefined_style` methods.
* To update the handler callback function of the widget, please refer to the
:meth:`replace_render_function` method.
Parameters
----------
group_keys : `list` of `str` or ``None``
The `list` of landmark groups. If ``None``, then no landmark groups are
available.
labels_keys : `list` of `list` of `str` or ``None``
The `list` of labels per landmark group. If ``None``, then no labels are
available.
render_function : `callable` or ``None``, optional
The render function that is executed when a widgets' value changes.
It must have signature ``render_function(change)`` where ``change`` is
a `dict` with the following keys:
* ``type`` : The type of notification (normally ``'change'``).
* ``owner`` : the `HasTraits` instance
* ``old`` : the old value of the modified trait attribute
* ``new`` : the new value of the modified trait attribute
* ``name`` : the name of the modified trait attribute.
If ``None``, then nothing is assigned.
renderer_widget : :map:`RendererOptionsWidget` or ``None``, optional
The :map:`RendererOptionsWidget` that is created and needs to be linked
with this widget. If ``None``, then nothing is assigned.
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
Example
-------
Let's create a landmarks widget and then update its state. Firstly, we need
to import it:
>>> from menpowidgets.options import LandmarkOptionsWidget
Now let's define a render function that will get called on every widget
change and will dynamically print the selected index:
>>> from menpo.visualize import print_dynamic
>>> def render_function(change):
>>> s = "Group: {}, Labels: {}".format(
>>> wid.selected_values['group'],
>>> wid.selected_values['with_labels'])
>>> print_dynamic(s)
Create the widget with some initial options and display it:
>>> wid = LandmarkOptionsWidget(
>>> group_keys=['PTS', 'ibug_face_68'],
>>> labels_keys=[['all'], ['jaw', 'eye', 'mouth']],
>>> render_function=render_function, style='danger')
>>> wid
By playing around with the widget, the printed message gets updated.
Finally, let's change the widget status with a new set of options:
>>> wid.set_widget_state(group_keys=['new_group'],
>>> labels_keys=[['1', '2', '3']],
>>> allow_callback=False)
Remember that the widget is **mnemonic**, i.e. it remembers the objects it
has seen and their corresponding options. These can be retrieved as:
>>> wid.default_options
"""
def __init__(self, group_keys, labels_keys, render_function=None,
renderer_widget=None, style='minimal'):
# Initialise default options dictionary
self.default_options = {}
# Assign properties
self.renderer_widget = renderer_widget
self.style_option = style
self.group_keys = []
self.labels_keys = []
# Create children
# Render landmarks checkbox and no landmarks message
self.no_landmarks_msg = ipywidgets.Latex(
value='No landmarks available.')
self.render_landmarks_checkbox = ipywidgets.Checkbox(
description='Render landmarks', margin='0.3cm')
# Create group description, dropdown and slider
self.group_description = ipywidgets.Latex(value='Group', margin='0.1cm')
self.group_slider = ipywidgets.IntSlider(
margin='0.1cm', readout=False, width='3cm', value=0,
continuous_update=False, min=0)
self.group_dropdown = ipywidgets.Dropdown(
options={'0': 0}, description='', margin='0.1cm', value=0)
self.group_latex = ipywidgets.Latex(padding='0.2cm')
self.group_selection_box = ipywidgets.HBox(
children=[self.group_description, self.group_slider,
self.group_dropdown, self.group_latex], align='center')
# Link the values of group dropdown and slider
self.link_group_dropdown_and_slider = link(
(self.group_dropdown, 'value'), (self.group_slider, 'value'))
# Create labels
self.labels_toggles = [[]]
self.labels_text = ipywidgets.Latex(value='Labels')
self.labels_box = ipywidgets.HBox(children=self.labels_toggles[0],
padding='0.3cm')
self.labels_and_text_box = ipywidgets.HBox(
children=[self.labels_text, self.labels_box], align='center')
self.options_box = ipywidgets.VBox(
children=[self.group_selection_box, self.labels_and_text_box],
margin='0.2cm')
self.render_and_options_box = ipywidgets.HBox(
children=[self.render_landmarks_checkbox, self.options_box])
# Create final widget
children = [self.render_and_options_box, self.no_landmarks_msg]
super(LandmarkOptionsWidget, self).__init__(
children, Dict, {}, render_function=render_function,
orientation='horizontal', align='start')
# Set values, add callbacks before setting widget state
self.add_callbacks()
self.set_widget_state(group_keys, labels_keys, allow_callback=False)
# Set style
self.predefined_style(style)
[docs] def add_callbacks(self):
r"""
Function that adds the handler callback functions in all the widget
components, which are necessary for the internal functionality.
"""
self.render_landmarks_checkbox.observe(
self._render_landmarks_fun, names='value', type='change')
self.group_dropdown.observe(self._group_fun, names='value',
type='change')
self._add_function_to_labels_toggles(self._labels_fun)
[docs] def remove_callbacks(self):
r"""
Function that removes all the internal handler callback functions.
"""
self.render_landmarks_checkbox.unobserve(
self._render_landmarks_fun, names='value', type='change')
self.group_dropdown.unobserve(self._group_fun, names='value',
type='change')
self._remove_function_from_labels_toggles(self._labels_fun)
def _save_options(self, change):
if self.group_keys is None:
self.selected_values = {
'group': None, 'render_landmarks': False, 'with_labels': None}
else:
tmp_labels = self._get_with_labels()
self.selected_values = {
'group': self.group_keys[self.group_dropdown.value],
'render_landmarks': self.render_landmarks_checkbox.value,
'with_labels': tmp_labels}
# update default values
current_key = self.get_key(self.group_keys, self.labels_keys)
self.default_options[current_key] = self.selected_values
def _render_landmarks_fun(self, change):
# If render is True, then check whether all the labels are disabled.
# If they are, then enable all of them
if change['new']:
if len(self._get_with_labels()) == 0:
for ww in self.labels_box.children:
# temporarily remove render function
ww.unobserve(self._labels_fun, names='value', type='change')
# set value
ww.value = True
# re-add render function
ww.observe(self._labels_fun, names='value', type='change')
# set visibility
self.options_box.visible = change['new']
# save options
self._save_options({})
def _group_fun(self, change):
value = change['new']
# assign the correct children to the labels toggles
self.labels_box.children = self.labels_toggles[value]
# if a renderer widget was provided, update it
if self.renderer_widget is not None:
self.renderer_widget.set_widget_state(self.labels_keys[value],
allow_callback=False)
# save options
self._save_options({})
def _labels_fun(self, change):
# if all labels toggles are False, set render landmarks checkbox to
# False
if len(self._get_with_labels()) == 0:
# temporarily remove render function
self.render_landmarks_checkbox.unobserve(
self._render_landmarks_fun, names='value', type='change')
# set value
self.render_landmarks_checkbox.value = False
# set visibility
self.options_box.visible = False
# re-add render function
self.render_landmarks_checkbox.observe(
self._render_landmarks_fun, names='value', type='change')
# save options
self._save_options({})
[docs] def set_visibility(self):
r"""
Function that sets the visibility of the various components of the
widget, depending on the properties of the current landmarks, i.e.
``self.group_keys``.
"""
self.no_landmarks_msg.visible = self.group_keys is None
self.render_and_options_box.visible = self.group_keys is not None
if self.group_keys is not None:
# control group visibility
self.group_slider.visible = len(self.group_keys) > 1
self.group_dropdown.visible = len(self.group_keys) > 1
self.group_latex.visible = len(self.group_keys) == 1
# render_landmarks visibility
self.options_box.visible = self.selected_values['render_landmarks']
[docs] def get_key(self, group_keys, labels_keys):
r"""
Function that returns a unique key based on the properties of the
provided landmarks.
Parameters
----------
group_keys : `list` of `str` or ``None``
The `list` of landmark groups. If ``None``, then no landmark groups
are available.
labels_keys : `list` of `list` of `str` or ``None``
The `list` of labels per landmark group. If ``None``, then no labels
are available.
Returns
-------
key : `str`
The key that has the format ``'{group_keys}_{labels_keys}'``.
"""
return "{}_{}".format(group_keys, labels_keys)
[docs] def get_default_options(self, group_keys, labels_keys):
r"""
Function that returns a `dict` with default options based on the
properties of the provided landmarks. The function returns the `dict` of
options but also updates the ``self.default_options`` `dict`.
Parameters
----------
group_keys : `list` of `str` or ``None``
The `list` of landmark groups. If ``None``, then no landmark groups
are available.
labels_keys : `list` of `list` of `str` or ``None``
The `list` of labels per landmark group. If ``None``, then no labels
are available.
Returns
-------
default_options : `dict`
A `dict` with the default options. It contains:
* ``group`` : (`str` or ``None``) The selected group.
* ``with_labels`` : (`list` or ``None``) The selected labels.
* ``render_landmarks`` : (`bool`) Whether to render the landmarks.
If the object is not seen before by the widget, then it automatically
gets the following default options:
* ``group = None if group_keys is None else group_keys[0]``
* ``with_labels = None if group_keys is None else labels_keys[0]``
* ``render_landmarks = False if group_keys is None else True``
"""
# create key
key = self.get_key(group_keys, labels_keys)
# if the key does not exist in the default options dict, then add it
if key not in self.default_options:
if group_keys is None:
self.default_options[key] = {'group': None,
'with_labels': None,
'render_landmarks': False}
else:
self.default_options[key] = {'group': group_keys[0],
'with_labels': labels_keys[0],
'render_landmarks': True}
return self.default_options[key]
def _get_with_labels(self):
with_labels = []
for ww in self.labels_box.children:
if ww.value:
with_labels.append(str(ww.description))
return with_labels
def _add_function_to_labels_toggles(self, fun):
for s_group in self.labels_toggles:
for w in s_group:
w.observe(fun, names='value', type='change')
def _remove_function_from_labels_toggles(self, fun):
for s_group in self.labels_toggles:
for w in s_group:
w.unobserve(fun, names='value', type='change')
def _set_labels_toggles_values(self, with_labels):
for w in self.labels_box.children:
w.value = w.description in with_labels
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='solid', border_width=1, border_radius=0, padding=0,
margin=0, font_family='', font_size=None, font_style='',
font_weight='', labels_buttons_style=''):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
labels_buttons_style : `str` or ``None`` (see below), optional
Style options:
'success', 'info', 'warning', 'danger', 'primary', '', None
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
format_font(self, font_family, font_size, font_style, font_weight)
format_font(self.render_landmarks_checkbox, font_family, font_size,
font_style, font_weight)
format_font(self.group_dropdown, font_family, font_size, font_style,
font_weight)
format_font(self.group_description, font_family, font_size, font_style,
font_weight)
for s_group in self.labels_toggles:
for w in s_group:
format_font(w, font_family, font_size, font_style, font_weight)
w.button_style = labels_buttons_style
format_font(self.labels_text, font_family, font_size, font_style,
font_weight)
self.group_slider.slider_color = map_styles_to_hex_colours(
box_style, background=False)
self.group_slider.background_color = map_styles_to_hex_colours(
box_style, background=False)
[docs] def predefined_style(self, style):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if style == 'minimal':
self.style(box_style=None, border_visible=True,
border_colour='black', border_style='solid',
border_width=1, border_radius=0, padding='0.2cm',
margin='0.3cm', font_family='', font_size=None,
font_style='', font_weight='', labels_buttons_style='')
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
self.style(box_style=style, border_visible=True,
border_colour=map_styles_to_hex_colours(style),
border_style='solid', border_width=1, border_radius=10,
padding='0.2cm', margin='0.3cm', font_family='',
font_size=None, font_style='', font_weight='',
labels_buttons_style='primary')
else:
raise ValueError('style must be minimal or info or success or '
'danger or warning')
[docs] def set_widget_state(self, group_keys, labels_keys, allow_callback=True):
r"""
Method that updates the state of the widget, if the key generated with
:meth:`get_key` based on the provided `group_keys` and `labels_keys`
is different than the current key based on ``self.group_keys`` and
``self.labels_keys``.
Parameters
----------
group_keys : `list` of `str` or ``None``
The `list` of landmark groups. If ``None``, then no landmark groups
are available.
labels_keys : `list` of `list` of `str` or ``None``
The `list` of labels per landmark group. If ``None``, then no labels
are available.
allow_callback : `bool`, optional
If ``True``, it allows triggering of any callback functions.
"""
# keep old value
old_value = self.selected_values
# check if updates are required
if (not self.default_options or
self.get_key(self.group_keys, self.labels_keys) !=
self.get_key(group_keys, labels_keys)):
# temporarily remove callbacks
render_function = self._render_function
self.remove_render_function()
self.remove_callbacks()
# Assign properties
self.group_keys = group_keys
self.labels_keys = labels_keys
# Update widgets' state
if group_keys is not None:
# Get options to set
landmark_options = self.get_default_options(group_keys,
labels_keys)
# Update
self.group_slider.max = len(group_keys) - 1
dropdown_dict = OrderedDict()
for gn, gk in enumerate(group_keys):
dropdown_dict[gk] = gn
self.group_dropdown.options = dropdown_dict
self.group_latex.value = group_keys[0]
group_idx = group_keys.index(landmark_options['group'])
if (group_idx == self.group_dropdown.value and
len(group_keys) > 1):
if self.group_dropdown.value == 0:
self.group_dropdown.value = 1
else:
self.group_dropdown.value = 0
self.group_dropdown.value = group_idx
self.labels_toggles = [
[ipywidgets.ToggleButton(description=k, value=True)
for k in s_keys] for s_keys in labels_keys]
self.labels_box.children = self.labels_toggles[group_idx]
self._set_labels_toggles_values(landmark_options['with_labels'])
self.render_landmarks_checkbox.value = \
landmark_options['render_landmarks']
# if a renderer widget was provided, update it
if self.renderer_widget is not None:
self.renderer_widget.set_widget_state(
self.labels_keys[group_idx], allow_callback=False)
# Get values
self._save_options({})
# Set widget's visibility
self.set_visibility()
# Set style
self.predefined_style(self.style_option)
# Re-assign callbacks
self.add_callbacks()
self.add_render_function(render_function)
# trigger render function if allowed
if allow_callback:
self.call_render_function(old_value, self.selected_values)
[docs]class TextPrintWidget(ipywidgets.FlexBox):
r"""
Creates a widget for printing text. Specifically, it consists of a `list`
of `ipywidgets.Latex` objects, i.e. one per text line.
Note that:
* To set the styling please refer to the :meth:`style` and
:meth:`predefined_style` methods.
* To update the state of the widget, please refer to the
:meth:`set_widget_state` method.
Parameters
----------
text_per_line : `list` of `str`
The text to be printed per line.
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
Example
-------
Let's create an text widget and then update its state. Firstly, we need
to import it:
>>> from menpowidgets.options import TextPrintWidget
Create the widget with some initial options and display it:
>>> text_per_line = ['> The', '> Menpo', '> Team']
>>> wid = TextPrintWidget(text_per_line, style='success')
>>> wid
The style of the widget can be changed as:
>>> wid.predefined_style('danger')
Update the widget state as:
>>> wid.set_widget_state(['M', 'E', 'N', 'P', 'O'])
"""
def __init__(self, text_per_line, style='minimal'):
n_lines = len(text_per_line)
self.latex_texts = [ipywidgets.Latex(value=text_per_line[i])
for i in range(n_lines)]
super(TextPrintWidget, self).__init__(children=self.latex_texts)
self.align = 'start'
# Assign options
self.n_lines = n_lines
self.text_per_line = text_per_line
# Set style
self.predefined_style(style)
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='solid', border_width=1, border_radius=0, padding=0,
margin=0, font_family='', font_size=None, font_style='',
font_weight=''):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
format_font(self, font_family, font_size, font_style, font_weight)
for i in range(self.n_lines):
format_font(self.latex_texts[i], font_family, font_size,
font_style, font_weight)
[docs] def predefined_style(self, style):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if style == 'minimal':
self.style(box_style=None, border_visible=True,
border_colour='black', border_style='solid',
border_width=1, border_radius=0, padding='0.1cm',
margin='0.3cm', font_family='', font_size=None,
font_style='', font_weight='')
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
self.style(box_style=style, border_visible=True,
border_colour=map_styles_to_hex_colours(style),
border_style='solid', border_width=1, border_radius=10,
padding='0.1cm', margin='0.3cm', font_family='',
font_size=None, font_style='', font_weight='')
else:
raise ValueError('style must be minimal or info or success or '
'danger or warning')
[docs] def set_widget_state(self, text_per_line):
r"""
Method that updates the state of the widget with a new `list` of lines.
Parameters
----------
text_per_line : `list` of `str`
The text to be printed per line.
"""
# Check if n_lines has changed
n_lines = len(text_per_line)
if n_lines != self.n_lines:
self.latex_texts = [ipywidgets.Latex(value=text_per_line[i])
for i in range(n_lines)]
self.children = self.latex_texts
else:
for i in range(n_lines):
self.latex_texts[i].value = text_per_line[i]
self.n_lines = n_lines
self.text_per_line = text_per_line
[docs]class RendererOptionsWidget(MenpoWidget):
r"""
Creates a widget for selecting rendering options. The widget consists of the
following objects from `ipywidgets` and :ref:`api-tools-index`:
== ============================= =========================== ================
No Object Property (`self.`) Description
== ============================= =========================== ================
1 :map:`LineOptionsWidget` `options_widgets` `list` that
:map:`MarkerOptionsWidget` contains the
:map:`ImageOptionsWidget` rendering
:map:`NumberingOptionsWidget` sub-options
:map:`ZoomOneScaleWidget` widgets
:map:`ZoomTwoScalesWidget`
:map:`AxesOptionsWidget`
:map:`LegendOptionsWidget`
:map:`GridOptionsWidget`
2 Tab `suboptions_tab` Contains all 2
== ============================= =========================== ================
Note that:
* To update the state of the widget, please refer to the
:meth:`set_widget_state` method.
* The widget has **memory** about the properties of the objects that are
passed into it through :meth:`set_widget_state`. Each object has a unique
key id assigned through :meth:`get_key`. Then, the options that correspond
to each key are stored in the ``self.default_options`` `dict`.
* The selected values of the current object object are stored in the
``self.selected_values`` `trait`.
* When an unseen image object is passed in (i.e. a key that is not included
in the ``self.default_options`` `dict`), it gets the following initial
options by default:
* ``lines``
- ``render_lines = True``
- ``line_width = 1``
- ``line_style = '-``
- ``line_colour = ['red'] if labels is None else colours``
* ``markers``
- ``render_markers = True``
- ``marker_size = 5``
- ``marker_style = 'o'``
- ``marker_face_colour = ['red'] if labels is None else colours``
- ``marker_edge_colour = ['black'] if labels is None else colours``
- ``marker_edge_width = 1``
where ``colours = sample_colours_from_colourmap(len(labels), 'jet')``
* ``image``
- ``interpolation = 'bilinear'``
- ``cmap_name = None``
- ``alpha = 1.``
* ``numbering``
- ``render_numbering = False``
- ``numbers_font_name = 'sans-serif'``
- ``numbers_font_size = 10``
- ``numbers_font_style = 'normal'``
- ``numbers_font_weight = 'normal'``
- ``numbers_font_colour = ['black']``
- ``numbers_horizontal_align = 'center'``
- ``numbers_vertical_align = 'bottom'``
* ``zoom_one = 1.``
* ``zoom_two = [1., 1.]``
* ``axes``
- ``render_axes = False``
- ``axes_font_name = 'sans-serif'``
- ``axes_font_size = 10``
- ``axes_font_style = 'normal'``
- ``axes_font_weight = 'normal'``
- ``axes_x_ticks = None``
- ``axes_y_ticks = None``
- ``axes_x_limits = axes_x_limits``
- ``axes_y_limits = axes_y_limits``
* ``legend``
- ``render_legend = False``
- ``legend_title = ''``
- ``legend_font_name = 'sans-serif'``
- ``legend_font_style = 'normal'``
- ``legend_font_size = 10``
- ``legend_font_weight = 'normal'``
- ``legend_marker_scale = 1.``
- ``legend_location = 2``
- ``legend_bbox_to_anchor = (1.05, 1.)``
- ``legend_border_axes_pad = 1.``
- ``legend_n_columns = 1``
- ``legend_horizontal_spacing = 1.``
- ``legend_vertical_spacing = 1.``
- ``legend_border = True``
- ``legend_border_padding = 0.5``
- ``legend_shadow = False``
- ``legend_rounded_corners = False``
* ``grid``
- ``render_grid = False``
- ``grid_line_width = 0.5``
- ``grid_line_style = '--'``
* To set the styling of this widget please refer to the :meth:`style` and
:meth:`predefined_style` methods.
* To update the handler callback function of the widget, please refer to the
:meth:`replace_render_function` method.
Parameters
----------
options_tabs : `list` of `str`
`List` that defines the ordering of the options tabs. Possible values
are:
=============== ========================
Value Returned object
=============== ========================
``'lines'`` :map:`LineOptionsWidget`
``'markers'`` :map:`MarkerOptionsWidget`
``'numbering'`` :map:`NumberingOptionsWidget`
``'zoom_one'`` :map:`ZoomOneScaleWidget`
``'zoom_two'`` :map:`ZoomTwoScalesWidget`
``'legend'`` :map:`LegendOptionsWidget`
``'grid'`` :map:`GridOptionsWidget`
``'image'`` :map:`ImageOptionsWidget`
``'axes'`` :map:`AxesOptionsWidget`
=============== ========================
labels : `list` or ``None``, optional
The `list` of labels used in all :map:`ColourSelectionWidget` objects.
axes_x_limits : `float` or (`float`, `float`) or ``None``, optional
The limits of the x axis. If `float`, then it sets padding on the right
and left as a percentage of the rendered object's width. If `tuple` or
`list`, then it defines the axis limits. If ``None``, then the limits
are set automatically.
axes_y_limits : (`float`, `float`) `tuple` or ``None``, optional
The limits of the y axis. If `float`, then it sets padding on the
top and bottom as a percentage of the rendered object's height. If
`tuple` or `list`, then it defines the axis limits. If ``None``, then
the limits are set automatically.
render_function : `callable` or ``None``, optional
The render function that is executed when a widgets' value changes.
It must have signature ``render_function(change)`` where ``change`` is
a `dict` with the following keys:
* ``type`` : The type of notification (normally ``'change'``).
* ``owner`` : the `HasTraits` instance
* ``old`` : the old value of the modified trait attribute
* ``new`` : the new value of the modified trait attribute
* ``name`` : the name of the modified trait attribute.
If ``None``, then nothing is assigned.
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
tabs_style : `str` (see below), optional
Sets a predefined style at the tabs of the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
Example
-------
Let's create a rendering options widget and then update its state. Firstly,
we need to import it:
>>> from menpowidgets.options import RendererOptionsWidget
Let's set some initial options:
>>> options_tabs = ['markers', 'lines', 'grid']
>>> labels = ['jaw', 'eyes']
Now let's define a render function that will get called on every widget
change and will dynamically print the selected marker face colour and line
width:
>>> from menpo.visualize import print_dynamic
>>> def render_function(change):
>>> s = "Marker face colour: {}, Line width: {}".format(
>>> wid.selected_values['markers']['marker_face_colour'],
>>> wid.selected_values['lines']['line_width'])
>>> print_dynamic(s)
Create the widget with the initial options and display it:
>>> wid = RendererOptionsWidget(options_tabs, labels=labels,
>>> render_function=render_function,
>>> style='info')
>>> wid
By playing around, the printed message gets updated. The style of the widget
can be changed as:
>>> wid.predefined_style('minimal', 'info')
Finally, let's change the widget status with a new set of labels:
>>> wid.set_widget_state(labels=['1'], allow_callback=True)
Remember that the widget is **mnemonic**, i.e. it remembers the objects it
has seen and their corresponding options. These can be retrieved as:
>>> wid.default_options
"""
def __init__(self, options_tabs, labels, axes_x_limits=None,
axes_y_limits=None, render_function=None, style='minimal',
tabs_style='minimal'):
# Initialise default options dictionary
self.default_options = {}
self.global_options = {}
# Assign properties
self.labels = labels
self.options_tabs = options_tabs
# Get initial options
self.initialise_global_options(axes_x_limits, axes_y_limits)
renderer_options = self.get_default_options(labels)
# Create children
self.options_widgets = []
self.tab_titles = []
for o in options_tabs:
if o == 'lines':
self.options_widgets.append(LineOptionsWidget(
renderer_options[o], render_function=None,
render_checkbox_title='Render lines', labels=labels))
self.tab_titles.append('Lines')
elif o == 'markers':
self.options_widgets.append(MarkerOptionsWidget(
renderer_options[o], render_function=None,
render_checkbox_title='Render markers', labels=labels))
self.tab_titles.append('Markers')
elif o == 'image':
self.options_widgets.append(ImageOptionsWidget(
self.global_options[o], render_function=None))
self.tab_titles.append('Image')
elif o == 'numbering':
self.options_widgets.append(NumberingOptionsWidget(
self.global_options[o], render_function=None,
render_checkbox_title='Render numbering'))
self.tab_titles.append('Numbering')
elif o == 'zoom_two':
tmp = {'min': 0.1, 'max': 4., 'step': 0.05,
'zoom': self.global_options[o],
'lock_aspect_ratio': False}
self.options_widgets.append(ZoomTwoScalesWidget(
tmp, render_function=None,
description='Scale: ',
minus_description='fa-search-minus',
plus_description='fa-search-plus',
continuous_update=False))
self.tab_titles.append('Zoom')
elif o == 'zoom_one':
tmp = {'min': 0.1, 'max': 4., 'step': 0.05,
'zoom': self.global_options[o]}
self.options_widgets.append(ZoomOneScaleWidget(
tmp, render_function=None,
description='Scale: ',
minus_description='fa-search-minus',
plus_description='fa-search-plus',
continuous_update=False))
self.tab_titles.append('Zoom')
elif o == 'axes':
self.options_widgets.append(AxesOptionsWidget(
self.global_options[o], render_function=None,
render_checkbox_title='Render axes'))
self.tab_titles.append('Axes')
elif o == 'legend':
self.options_widgets.append(LegendOptionsWidget(
self.global_options[o], render_function=None,
render_checkbox_title='Render legend'))
self.tab_titles.append('Legend')
elif o == 'grid':
self.options_widgets.append(GridOptionsWidget(
self.global_options[o], render_function=None,
render_checkbox_title='Render grid'))
self.tab_titles.append('Grid')
self.suboptions_tab = ipywidgets.Tab(children=self.options_widgets)
# set titles
for (k, tl) in enumerate(self.tab_titles):
self.suboptions_tab.set_title(k, tl)
# Create final widget
initial_options = renderer_options.copy()
initial_options.update(self.global_options)
children = [self.suboptions_tab]
super(RendererOptionsWidget, self).__init__(
children, Dict, initial_options, render_function=render_function,
orientation='vertical', align='start')
# Set values
self.set_widget_state(labels, allow_callback=False)
# Set style
self.predefined_style(style, tabs_style)
# Add callbacks
self.add_callbacks()
def _save_options(self, change):
# update selected values
self.selected_values = {o: self.options_widgets[i].selected_values
for i, o in enumerate(self.options_tabs)}
# update default values
current_key = self.get_key(self.labels)
if 'lines' in self.options_tabs:
self.default_options[current_key]['lines'] = \
self.selected_values['lines'].copy()
if 'markers' in self.options_tabs:
self.default_options[current_key]['markers'] = \
self.selected_values['markers'].copy()
# update global values
if 'image' in self.options_tabs:
self.global_options['image'] = self.selected_values['image']
if 'numbering' in self.options_tabs:
self.global_options['numbering'] = self.selected_values['numbering']
if 'zoom_one' in self.options_tabs:
self.global_options['zoom_one'] = self.selected_values['zoom_one']
if 'zoom_two' in self.options_tabs:
self.global_options['zoom_two'] = self.selected_values['zoom_two']
if 'grid' in self.options_tabs:
self.global_options['grid'] = self.selected_values['grid']
if 'legend' in self.options_tabs:
self.global_options['legend'] = self.selected_values['legend']
if 'axes' in self.options_tabs:
self.global_options['axes'] = self.selected_values['axes']
[docs] def add_callbacks(self):
r"""
Function that adds the handler callback functions in all the widget
components, which are necessary for the internal functionality.
"""
for wid in self.options_widgets:
wid.observe(self._save_options, names='selected_values',
type='change')
[docs] def remove_callbacks(self):
r"""
Function that removes all the internal handler callback functions.
"""
for wid in self.options_widgets:
wid.unobserve(self._save_options, names='selected_values',
type='change')
[docs] def get_key(self, labels):
r"""
Function that returns a unique key based on the provided labels.
Parameters
----------
labels : `list` or ``None``, optional
The `list` of labels used in all :map:`ColourSelectionWidget` objects
Returns
-------
key : `str`
The key that has the format ``'{labels}'``.
"""
return "{}".format(labels)
[docs] def initialise_global_options(self, axes_x_limits, axes_y_limits):
r"""
Function that returns a `dict` with global options, i.e. options that do
not depend on `labels`. The functions updates ``self.global_options``
`dict` with:
* ``image`` : (`dict`) It has the following keys:
- ``interpolation`` : (`str`) The interpolation method.
- ``cmap_name`` : (`str`) The colourmap.
- ``alpha`` : (`float`) The alpha transparency value.
* ``numbering`` : (`dict`) It has the following keys:
- ``render_numbering`` : (`bool`) Flag for rendering the numbers.
- ``numbers_font_name`` : (`str`) The font name.
- ``numbers_font_size`` : (`int`) The font size.
- ``numbers_font_style`` : (`str`) The font style.
- ``numbers_font_weight`` : (`str`) The font weight.
- ``numbers_font_colour`` : (`list`) The font colour.
- ``numbers_horizontal_align`` : (`str`) The horizontal alignment.
- ``numbers_vertical_align`` : (`str`) The vertical alignment.
* ``zoom_one`` : (`float`) The zoom value.
* ``zoom_two`` : (`list` of `float`) The zoom values.
* ``axes`` : (`dict`) It has the following keys:
- ``render_axes`` : (`bool`) Flag for rendering the axes.
- ``axes_font_name`` : (`str`) The axes font name.
- ``axes_font_size`` : (`int`) The axes font size.
- ``axes_font_style`` : (`str`) The axes font style
- ``axes_font_weight`` : (`str`) The font weight.
- ``axes_x_ticks`` : (`list` or ``None``) The x ticks.
- ``axes_y_ticks`` : (`list` or ``None``) The y ticks.
- ``axes_x_limits`` : (`float` or [`float`, `float`] or ``None``)
The x limits.
- ``axes_y_limits`` : (`float` or [`float`, `float`] or ``None``)
The y limits.
* ``legend`` : (`dict`) It has the following keys:
- ``render_legend`` : (`bool`) Flag for rendering the legend.
- ``legend_title`` : (`str`) The legend title.
- ``legend_font_name`` : (`str`) The font name.
- ``legend_font_style`` : (`str`) The font style.
- ``legend_font_size`` : (`str`) The font size.
- ``legend_font_weight`` : (`str`) The font weight.
- ``legend_marker_scale`` : (`float`) The marker scale.
- ``legend_location`` : (`int`) The legend location.
- ``legend_bbox_to_anchor`` : (`tuple`) Bbox to anchor.
- ``legend_border_axes_pad`` : (`float`) Border axes pad.
- ``legend_n_columns`` : (`int`) The number of columns.
- ``legend_horizontal_spacing`` : (`float`) Horizontal spacing.
- ``legend_vertical_spacing`` : (`float`) Vetical spacing.
- ``legend_border`` : (`bool`) Flag for adding border to the legend
- ``legend_border_padding`` : (`float`) The border padding
- ``legend_shadow`` : (`bool`) Flag for adding shadow to the legend
- ``legend_rounded_corners`` : (`bool`) Flag for adding rounded
corners to the legend.
* ``gird`` : (`dict`) It has the following keys:
- ``render_grid`` : (`bool`) Flag for rendering the grid.
- ``grid_line_width`` : (`int`) The line width.
- ``grid_line_style`` : (`str`) The line style.
If the object is not seen before by the widget, then it automatically
gets the following default options:
* ``image``
- ``interpolation = 'bilinear'``
- ``cmap_name = None``
- ``alpha = 1.``
* ``numbering``
- ``render_numbering = False``
- ``numbers_font_name = 'sans-serif'``
- ``numbers_font_size = 10``
- ``numbers_font_style = 'normal'``
- ``numbers_font_weight = 'normal'``
- ``numbers_font_colour = ['black']``
- ``numbers_horizontal_align = 'center'``
- ``numbers_vertical_align = 'bottom'``
* ``zoom_one = 1.``
* ``zoom_two = [1., 1.]``
* ``axes``
- ``render_axes = False``
- ``axes_font_name = 'sans-serif'``
- ``axes_font_size = 10``
- ``axes_font_style = 'normal'``
- ``axes_font_weight = 'normal'``
- ``axes_x_ticks = None``
- ``axes_y_ticks = None``
- ``axes_x_limits = axes_x_limits``
- ``axes_y_limits = axes_y_limits``
* ``legend``
- ``render_legend = False``
- ``legend_title = ''``
- ``legend_font_name = 'sans-serif'``
- ``legend_font_style = 'normal'``
- ``legend_font_size = 10``
- ``legend_font_weight = 'normal'``
- ``legend_marker_scale = 1.``
- ``legend_location = 2``
- ``legend_bbox_to_anchor = (1.05, 1.)``
- ``legend_border_axes_pad = 1.``
- ``legend_n_columns = 1``
- ``legend_horizontal_spacing = 1.``
- ``legend_vertical_spacing = 1.``
- ``legend_border = True``
- ``legend_border_padding = 0.5``
- ``legend_shadow = False``
- ``legend_rounded_corners = False``
* ``grid``
- ``render_grid = False``
- ``grid_line_width = 0.5``
- ``grid_line_style = '--'``
Parameters
----------
axes_x_limits : `float` or (`float`, `float`) or ``None``, optional
The limits of the x axis. If `float`, then it sets padding on the
right and left as a percentage of the rendered object's width. If
`tuple` or `list`, then it defines the axis limits. If ``None``,
then the limits are set automatically.
axes_y_limits : (`float`, `float`) `tuple` or ``None``, optional
The limits of the y axis. If `float`, then it sets padding on the
top and bottom as a percentage of the rendered object's height. If
`tuple` or `list`, then it defines the axis limits. If ``None``, then
the limits are set automatically.
"""
self.global_options = {}
for o in self.options_tabs:
if o == 'image':
self.global_options[o] = {
'interpolation': 'bilinear', 'cmap_name': None,
'alpha': 1.}
elif o == 'numbering':
self.global_options[o] = {
'render_numbering': False,
'numbers_font_name': 'sans-serif',
'numbers_font_size': 10, 'numbers_font_style': 'normal',
'numbers_font_weight': 'normal',
'numbers_font_colour': 'black',
'numbers_horizontal_align': 'center',
'numbers_vertical_align': 'bottom'}
elif o == 'zoom_one':
self.global_options[o] = 1.
elif o == 'zoom_two':
self.global_options[o] = [1., 1.]
elif o == 'axes':
self.global_options[o] = {
'render_axes': False, 'axes_font_name': 'sans-serif',
'axes_font_size': 10, 'axes_font_style': 'normal',
'axes_font_weight': 'normal',
'axes_x_limits': axes_x_limits,
'axes_y_limits': axes_y_limits,
'axes_x_ticks': None, 'axes_y_ticks': None}
elif o == 'legend':
self.global_options[o] = {
'render_legend': False, 'legend_title': '',
'legend_font_name': 'sans-serif',
'legend_font_style': 'normal', 'legend_font_size': 10,
'legend_font_weight': 'normal',
'legend_marker_scale': 1., 'legend_location': 2,
'legend_bbox_to_anchor': (1.05, 1.),
'legend_border_axes_pad': 1., 'legend_n_columns': 1,
'legend_horizontal_spacing': 1.,
'legend_vertical_spacing': 1., 'legend_border': True,
'legend_border_padding': 0.5, 'legend_shadow': False,
'legend_rounded_corners': False}
elif o == 'grid':
self.global_options[o] = {
'render_grid': False, 'grid_line_style': '--',
'grid_line_width': 0.5}
[docs] def get_default_options(self, labels):
r"""
Function that returns a `dict` with default options given a `list` of
labels. The function returns the `dict` of options but also updates the
``self.default_options`` `dict`.
Parameters
----------
labels : `list` or ``None``, optional
The `list` of labels used in all :map:`ColourSelectionWidget` objects
Returns
-------
default_options : `dict`
A `dict` with the default options. It contains:
* ``lines`` : (`dict`) It has the following keys:
- ``render_lines`` : (`bool`) Whether to render the lines.
- ``line_width`` : (`float`) The width of the lines.
- ``line_style`` : (`str`) The style of the lines.
- ``line_colour`` : (`list`) The colour per label.
* ``markers`` : (`dict`) It has the following keys:
- ``render_markers`` : (`bool`) Whether to render the markers.
- ``marker_size`` : (`int`) The size of the markers.
- ``marker_style`` : (`str`) The style of the markers.
- ``marker_face_colour`` : (`list`) The face colour per label.
- ``marker_edge_colour`` : (`list`) The edge colour per label.
- ``marker_edge_width`` : (`float`) The edge width of the markers.
If the object is not seen before by the widget, then it automatically
gets the following default options:
* ``lines``
- ``render_lines = True``
- ``line_width = 1``
- ``line_style = '-``
- ``line_colour = ['red'] if labels is None else colours``
* ``markers``
- ``render_markers = True``
- ``marker_size = 5``
- ``marker_style = 'o'``
- ``marker_face_colour = ['red'] if labels is None else colours``
- ``marker_edge_colour = ['black'] if labels is None else colours``
- ``marker_edge_width = 1``
where ``colours = sample_colours_from_colourmap(len(labels), 'jet')``
"""
# create key
key = self.get_key(labels)
# if the key does not exist in the default options dict, then add it
if key not in self.default_options:
self.default_options[key] = {}
if 'lines' in self.options_tabs:
lc = ['red']
if labels is not None:
lc = sample_colours_from_colourmap(len(labels), 'jet')
self.default_options[key]['lines'] = {
'render_lines': True, 'line_width': 1,
'line_colour': lc, 'line_style': '-'}
if 'markers' in self.options_tabs:
fc = ['red']
ec = ['black']
if labels is not None and len(labels) > 1:
fc = sample_colours_from_colourmap(len(labels), 'jet')
ec = sample_colours_from_colourmap(len(labels), 'jet')
self.default_options[key]['markers'] = {
'render_markers': True, 'marker_size': 5,
'marker_face_colour': fc, 'marker_edge_colour': ec,
'marker_style': 'o', 'marker_edge_width': 1}
return self.default_options[key]
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='solid', border_width=1, border_radius=0,
padding='0.2cm', margin=0, tabs_box_style=None,
tabs_border_visible=True, tabs_border_colour='black',
tabs_border_style='solid', tabs_border_width=1,
tabs_border_radius=1, tabs_padding=0, tabs_margin=0,
font_family='', font_size=None, font_style='', font_weight=''):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
tabs_box_style : `str` or ``None`` (see below), optional
Possible tab widgets style options::
'success', 'info', 'warning', 'danger', '', None
tabs_border_visible : `bool`, optional
Defines whether to draw the border line around the tab widgets.
tabs_border_colour : `str`, optional
The color of the border around the tab widgets.
tabs_border_style : `str`, optional
The line style of the border around the tab widgets.
tabs_border_width : `float`, optional
The line width of the border around the tab widgets.
tabs_border_radius : `float`, optional
The radius of the corners of the box of the tab widgets.
tabs_padding : `float`, optional
The padding around the tab widgets.
tabs_margin : `float`, optional
The margin around the tab widgets.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
for wid in self.options_widgets:
wid.style(box_style=tabs_box_style,
border_visible=tabs_border_visible,
border_colour=tabs_border_colour,
border_style=tabs_border_style,
border_width=tabs_border_width,
border_radius=tabs_border_radius, padding=tabs_padding,
margin=tabs_margin, font_family=font_family,
font_size=font_size, font_style=font_style,
font_weight=font_weight)
format_font(self, font_family, font_size, font_style, font_weight)
[docs] def predefined_style(self, style, tabs_style='minimal'):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
tabs_style : `str` (see below)
Tabs style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if tabs_style == 'minimal' or tabs_style == '':
tabs_style = ''
tabs_border_visible = False
tabs_border_colour = 'black'
tabs_border_radius = 0
tabs_padding = 0
else:
tabs_style = tabs_style
tabs_border_visible = not style == tabs_style
tabs_border_colour = map_styles_to_hex_colours(tabs_style)
tabs_border_radius = 10
tabs_padding = '0.3cm'
if style == 'minimal':
self.style(box_style='', border_visible=True, border_colour='black',
border_style='solid', border_width=1, border_radius=0,
padding='0.2cm', margin='0.5cm', font_family='',
font_size=None, font_style='', font_weight='',
tabs_box_style=tabs_style,
tabs_border_visible=tabs_border_visible,
tabs_border_colour=tabs_border_colour,
tabs_border_style='solid', tabs_border_width=1,
tabs_border_radius=tabs_border_radius,
tabs_padding=tabs_padding, tabs_margin='0.1cm')
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
self.style(box_style=style, border_visible=True,
border_colour=map_styles_to_hex_colours(style),
border_style='solid', border_width=1, border_radius=10,
padding='0.2cm', margin='0.5cm', font_family='',
font_size=None, font_style='', font_weight='',
tabs_box_style=tabs_style,
tabs_border_visible=tabs_border_visible,
tabs_border_colour=tabs_border_colour,
tabs_border_style='solid', tabs_border_width=1,
tabs_border_radius=tabs_border_radius,
tabs_padding=tabs_padding, tabs_margin='0.1cm')
else:
raise ValueError('style must be minimal or info or success or '
'danger or warning')
[docs] def set_widget_state(self, labels, allow_callback=True):
r"""
Method that updates the state of the widget, if the provided `labels`
are different than ``self.labels``.
Parameters
----------
labels : `list` or ``None``, optional
The `list` of labels used in all :map:`ColourSelectionWidget` objects
allow_callback : `bool`, optional
If ``True``, it allows triggering of any callback functions.
"""
# keep old value
old_value = self.selected_values
# check if updates are required
if (not self.default_options or
self.get_key(self.labels) != self.get_key(labels)):
# Temporarily remove callbacks
render_function = self._render_function
self.remove_render_function()
self.remove_callbacks()
# Get options
renderer_options = self.get_default_options(labels)
# Assign properties
self.labels = labels
# Update subwidgets
if 'lines' in self.options_tabs:
i = self.options_tabs.index('lines')
self.options_widgets[i].set_widget_state(
renderer_options['lines'], labels=labels,
allow_callback=False)
if 'markers' in self.options_tabs:
i = self.options_tabs.index('markers')
self.options_widgets[i].set_widget_state(
renderer_options['markers'], labels=labels,
allow_callback=False)
# Get values
self._save_options({})
# Add callbacks
self.add_callbacks()
self.add_render_function(render_function)
# trigger render function if allowed
if allow_callback:
self.call_render_function(old_value, self.selected_values)
[docs]class SaveFigureOptionsWidget(ipywidgets.FlexBox):
r"""
Creates a widget for saving a figure to file. The widget consists of the
following objects from `ipywidgets` and :ref:`api-tools-index`:
== ============================ ====================== =====================
No Object Property (`self.`) Description
== ============================ ====================== =====================
1 `Select` `file_format_select` Image format selector
2 `FloatText` `dpi_text` DPI selector
3 `Dropdown` `orientation_dropdown` Paper orientation
4 `Select` `papertype_select` Paper type selector
5 `Checkbox` `transparent_checkbox` Transparency setter
6 :map:`ColourSelectionWidget` `facecolour_widget` Face colour selector
7 :map:`ColourSelectionWidget` `edgecolour_widget` Edge colour selector
8 `FloatText` `pad_inches_text` Padding in inches
9 `Text` `filename_text` Path and filename
10 `Checkbox` `overwrite_checkbox` Overwrite flag
11 `Latex` `error_latex` Error message area
12 `Button` `save_button` Save button
13 `VBox` `path_box` Contains 9, 1, 10, 4
14 `VBox` `page_box` Contains 3, 2, 8
15 `VBox` `colour_box` Contains 6, 7, 5
16 `Tab` `options_tabs` Contains 13, 14, 15
17 `HBox` `save_box` Contains 12, 11
18 `VBox` `options_box` Contains 16, 17
== ============================ ====================== =====================
To set the styling of this widget please refer to the :meth:`style` and
:meth:`predefined_style` methods.
Parameters
----------
renderer : `menpo.visualize.Renderer` or subclass or ``None``
The renderer object that was used to render the figure.
file_format : `str`, optional
The initial value of the file format.
dpi : `float` or ``None``, optional
The initial value of the dpi. If ``None``, then dpi is set to ``0``.
orientation : ``{'portrait', 'landscape'}``, optional
The initial value of the paper orientation.
paper_type : `str`, optional
The initial value of the paper type. Possible options are::
'letter', 'legal', 'executive', 'ledger', 'a0', 'a1', 'a2', 'a3',
'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'a10', 'b0', 'b1', 'b2', 'b3',
'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'b10'
transparent : `bool`, optional
The initial value of the transparency flag.
face_colour : `str` or `list` of `float`, optional
The initial value of the face colour.
edge_colour : `str` or `list` of `float`, optional
The initial value of the edge colour.
pad_inches : `float`, optional
The initial value of the figure padding in inches.
overwrite : `bool`, optional
The initial value of the overwrite flag.
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
def __init__(self, renderer=None, file_format='png', dpi=None,
orientation='portrait', paper_type='letter', transparent=False,
face_colour='white', edge_colour='white', pad_inches=0.,
overwrite=False, style='minimal'):
from os import getcwd
from os.path import join, splitext
# Create widgets
file_format_dict = OrderedDict()
file_format_dict['png'] = 'png'
file_format_dict['jpg'] = 'jpg'
file_format_dict['pdf'] = 'pdf'
file_format_dict['eps'] = 'eps'
file_format_dict['postscript'] = 'ps'
file_format_dict['svg'] = 'svg'
self.file_format_select = ipywidgets.Select(
options=file_format_dict, value=file_format, description='Format',
width='3cm')
if dpi is None:
dpi = 0
self.dpi_text = ipywidgets.FloatText(description='DPI', value=dpi,
min=0.)
orientation_dict = OrderedDict()
orientation_dict['portrait'] = 'portrait'
orientation_dict['landscape'] = 'landscape'
self.orientation_dropdown = ipywidgets.Dropdown(
options=orientation_dict, value=orientation,
description='Orientation')
papertype_dict = OrderedDict()
papertype_dict['letter'] = 'letter'
papertype_dict['legal'] = 'legal'
papertype_dict['executive'] = 'executive'
papertype_dict['ledger'] = 'ledger'
papertype_dict['a0'] = 'a0'
papertype_dict['a1'] = 'a1'
papertype_dict['a2'] = 'a2'
papertype_dict['a3'] = 'a3'
papertype_dict['a4'] = 'a4'
papertype_dict['a5'] = 'a5'
papertype_dict['a6'] = 'a6'
papertype_dict['a7'] = 'a7'
papertype_dict['a8'] = 'a8'
papertype_dict['a9'] = 'a9'
papertype_dict['a10'] = 'a10'
papertype_dict['b0'] = 'b0'
papertype_dict['b1'] = 'b1'
papertype_dict['b2'] = 'b2'
papertype_dict['b3'] = 'b3'
papertype_dict['b4'] = 'b4'
papertype_dict['b5'] = 'b5'
papertype_dict['b6'] = 'b6'
papertype_dict['b7'] = 'b7'
papertype_dict['b8'] = 'b8'
papertype_dict['b9'] = 'b9'
papertype_dict['b10'] = 'b10'
self.papertype_select = ipywidgets.Select(
options=papertype_dict, value=paper_type, description='Paper type',
visible=file_format == 'ps', width='3cm')
self.transparent_checkbox = ipywidgets.Checkbox(
description='Transparent', value=transparent)
self.facecolour_widget = ColourSelectionWidget(
[face_colour], render_function=None, description='Face colour')
self.edgecolour_widget = ColourSelectionWidget(
[edge_colour], render_function=None, description='Edge colour')
self.pad_inches_text = ipywidgets.FloatText(description='Pad (inch)',
value=pad_inches)
self.filename_text = ipywidgets.Text(
description='Path', value=join(getcwd(), 'Untitled.' + file_format),
width='10cm')
self.overwrite_checkbox = ipywidgets.Checkbox(
description='Overwrite if file exists', value=overwrite)
self.error_latex = ipywidgets.Latex(value="", font_weight='bold',
font_style='italic')
self.save_button = ipywidgets.Button(description=' Save',
icon='fa-floppy-o', margin='0.2cm')
# Group widgets
self.path_box = ipywidgets.VBox(
children=[self.filename_text, self.file_format_select,
self.papertype_select, self.overwrite_checkbox],
align='end', margin='0.2cm')
self.page_box = ipywidgets.VBox(
children=[self.orientation_dropdown, self.dpi_text,
self.pad_inches_text], margin='0.2cm')
self.colour_box = ipywidgets.VBox(
children=[self.facecolour_widget, self.edgecolour_widget,
self.transparent_checkbox], margin='0.2cm')
self.options_tabs = ipywidgets.Tab(
children=[self.path_box, self.page_box, self.colour_box],
margin=0, padding='0.1cm')
self.options_tabs_box = ipywidgets.VBox(
children=[self.options_tabs], border_width=1, border_color='black',
margin='0.3cm', padding='0.2cm')
tab_titles = ['Path', 'Page setup', 'Image colour']
for (k, tl) in enumerate(tab_titles):
self.options_tabs.set_title(k, tl)
self.save_box = ipywidgets.HBox(
children=[self.save_button, self.error_latex], align='center')
self.options_box = ipywidgets.VBox(
children=[self.options_tabs, self.save_box], align='center')
super(SaveFigureOptionsWidget, self).__init__(
children=[self.options_box])
self.align = 'start'
# Assign renderer
if renderer is None:
from menpo.visualize.viewmatplotlib import MatplotlibImageViewer2d
renderer = MatplotlibImageViewer2d(figure_id=None, new_figure=True,
image=np.zeros((10, 10)))
self.renderer = renderer
# Set style
self.predefined_style(style)
# Set functionality
def paper_type_visibility(change):
self.papertype_select.visible = change['new'] == 'ps'
self.file_format_select.observe(paper_type_visibility, names='value',
type='change')
def set_extension(change):
file_name, file_extension = splitext(self.filename_text.value)
self.filename_text.value = file_name + '.' + change['new']
self.file_format_select.observe(set_extension, names='value',
type='change')
def save_function(name):
# set save button state
self.error_latex.value = ''
self.save_button.description = ' Saving...'
self.save_button.disabled = True
# save figure
selected_dpi = self.dpi_text.value
if self.dpi_text.value == 0:
selected_dpi = None
try:
self.renderer.save_figure(
filename=self.filename_text.value, dpi=selected_dpi,
face_colour=
self.facecolour_widget.selected_values[0],
edge_colour=
self.edgecolour_widget.selected_values[0],
orientation=self.orientation_dropdown.value,
paper_type=self.papertype_select.value,
format=self.file_format_select.value,
transparent=self.transparent_checkbox.value,
pad_inches=self.pad_inches_text.value,
overwrite=self.overwrite_checkbox.value)
self.error_latex.value = ''
except ValueError as e:
e = str(e)
if (e == 'File already exists. Please set the overwrite kwarg '
'if you wish to overwrite the file.'):
self.error_latex.value = 'File exists! ' \
'Tick overwrite to replace it.'
else:
self.error_latex.value = e
# set save button state
self.save_button.description = ' Save'
self.save_button.disabled = False
self.save_button.on_click(save_function)
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='solid', border_width=1, border_radius=0, padding=0,
margin=0, font_family='', font_size=None, font_style='',
font_weight=''):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
format_font(self, font_family, font_size, font_style, font_weight)
format_font(self.file_format_select, font_family, font_size, font_style,
font_weight)
format_font(self.dpi_text, font_family, font_size, font_style,
font_weight)
format_font(self.orientation_dropdown, font_family, font_size,
font_style, font_weight)
format_font(self.papertype_select, font_family, font_size, font_style,
font_weight)
format_font(self.transparent_checkbox, font_family, font_size,
font_style, font_weight)
format_font(self.pad_inches_text, font_family, font_size, font_style,
font_weight)
format_font(self.filename_text, font_family, font_size, font_style,
font_weight)
format_font(self.overwrite_checkbox, font_family, font_size, font_style,
font_weight)
format_font(self.save_button, font_family, font_size, font_style,
font_weight)
self.facecolour_widget.style(
box_style=None, border_visible=False, font_family=font_family,
font_size=font_size, font_weight=font_weight, font_style=font_style)
self.edgecolour_widget.style(
box_style=None, border_visible=False, font_family=font_family,
font_size=font_size, font_weight=font_weight, font_style=font_style)
[docs] def predefined_style(self, style):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if style == 'minimal':
self.style(box_style='', border_visible=True, border_colour='black',
border_style='solid', border_width=1, border_radius=0,
padding='0.2cm', margin='0.3cm', font_family='',
font_size=None, font_style='', font_weight='')
self.save_button.button_style = ''
self.save_button.font_weight = 'normal'
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
self.style(box_style=style, border_visible=True,
border_colour= map_styles_to_hex_colours(style),
border_style='solid', border_width=1, border_radius=10,
padding='0.2cm', margin='0.3cm', font_family='',
font_size=None, font_style='', font_weight='')
self.save_button.button_style = 'primary'
self.save_button.font_weight = 'bold'
else:
raise ValueError('style must be minimal or info or success or '
'danger or warning')
[docs]class FeatureOptionsWidget(ipywidgets.FlexBox):
r"""
Creates a widget for selecting feature options. The widget consists of the
following objects from `ipywidgets` and :ref:`api-tools-index`:
== ========================= ========================= =====================
No Object Property (`self.`) Description
== ========================= ========================= =====================
1 `RadioButtons` `feature_radiobuttons` Feature type selector
2 :map:`DSIFTOptionsWidget` `dsift_options_widget` DSIFT options
3 :map:`HOGOptionsWidget` `hog_options_widget` HOG options
4 :map:`IGOOptionsWidget` `igo_options_widget` IGO options
5 :map:`LBPOptionsWidget` `lbp_options_widget` LBP options
6 :map:`DaisyOptionsWidget` `daisy_options_widget` Daisy options
7 `Latex` `no_options_widget` No options available
8 `Box` `per_feature_options_box` Contains 2 - 7
9 `Image` `preview_image` Contains 6, 7
10 `Latex` `preview_input_latex` Contains 5, 9
11 `Latex` `preview_output_latex` Contains 3, 2
12 `Latex` `preview_time_latex` Contains 4, 10
13 `VBox` `preview_box` Contains 9 - 12
14 `Tab` `options_box` Contains 1, 8, 13
== ========================= ========================= =====================
Note that:
* To set the styling please refer to the :meth:`style` and
:meth:`predefined_style` methods.
* The widget stores the features `function` to ``self.features_function``,
the features options `dict` in ``self.features_options`` and the `partial`
function with the options as ``self.function``.
Parameters
----------
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
def __init__(self, style='minimal'):
# import features methods and time
import time
from functools import partial
from menpo.feature import (dsift, hog, lbp, igo, es, daisy, gradient,
no_op)
from menpo.image import Image
import menpo.io as mio
from menpo.feature.visualize import sum_channels
from .style import convert_image_to_bytes
# Create widgets
tmp = OrderedDict()
tmp['DSIFT'] = dsift
tmp['HOG'] = hog
tmp['IGO'] = igo
tmp['ES'] = es
tmp['Daisy'] = daisy
tmp['LBP'] = lbp
tmp['Gradient'] = gradient
tmp['None'] = no_op
self.feature_radiobuttons = ipywidgets.RadioButtons(
value=no_op, options=tmp, description='Feature type:')
dsift_options_dict = {'window_step_horizontal': 1,
'window_step_vertical': 1,
'num_bins_horizontal': 2, 'num_bins_vertical': 2,
'num_or_bins': 9, 'cell_size_horizontal': 6,
'cell_size_vertical': 6, 'fast': True}
self.dsift_options_widget = DSIFTOptionsWidget(dsift_options_dict)
self.dsift_options_widget.style(box_style=None, border_visible=False,
margin='0.2cm')
hog_options_dict = {'mode': 'dense', 'algorithm': 'dalaltriggs',
'num_bins': 9, 'cell_size': 8, 'block_size': 2,
'signed_gradient': True, 'l2_norm_clip': 0.2,
'window_height': 1, 'window_width': 1,
'window_unit': 'blocks', 'window_step_vertical': 1,
'window_step_horizontal': 1,
'window_step_unit': 'pixels', 'padding': True}
self.hog_options_widget = HOGOptionsWidget(hog_options_dict)
self.hog_options_widget.style(box_style=None, border_visible=False,
margin='0.2cm')
igo_options_dict = {'double_angles': True}
self.igo_options_widget = IGOOptionsWidget(igo_options_dict)
self.igo_options_widget.style(box_style=None, border_visible=False,
margin='0.2cm')
lbp_options_dict = {'radius': list(range(1, 5)), 'samples': [8] * 4,
'mapping_type': 'u2', 'window_step_vertical': 1,
'window_step_horizontal': 1,
'window_step_unit': 'pixels', 'padding': True}
self.lbp_options_widget = LBPOptionsWidget(lbp_options_dict)
self.lbp_options_widget.style(box_style=None, border_visible=False,
margin='0.2cm')
daisy_options_dict = {'step': 1, 'radius': 15, 'rings': 2,
'histograms': 2, 'orientations': 8,
'normalization': 'l1', 'sigmas': None,
'ring_radii': None}
self.daisy_options_widget = DaisyOptionsWidget(daisy_options_dict)
self.daisy_options_widget.style(box_style=None, border_visible=False,
margin='0.2cm')
self.no_options_widget = ipywidgets.Latex(value='No options available.')
# Load and rescale preview image (lenna)
self.image = mio.import_builtin_asset.lenna_png()
self.image = self.image.crop_to_landmarks_proportion(0.18)
self.image = self.image.as_greyscale()
# Group widgets
self.per_feature_options_box = ipywidgets.Box(
children=[self.dsift_options_widget, self.hog_options_widget,
self.igo_options_widget, self.lbp_options_widget,
self.daisy_options_widget, self.no_options_widget])
self.preview_image = ipywidgets.Image(
value=convert_image_to_bytes(self.image), visible=False)
self.preview_input_latex = ipywidgets.Latex(
value="Input: {}W x {}H x {}C".format(
self.image.width, self.image.height, self.image.n_channels),
visible=False)
self.preview_output_latex = ipywidgets.Latex(value="")
self.preview_time_latex = ipywidgets.Latex(value="")
self.preview_box = ipywidgets.VBox(
children=[self.preview_image, self.preview_input_latex,
self.preview_output_latex, self.preview_time_latex])
self.options_box = ipywidgets.Tab(
children=[self.feature_radiobuttons, self.per_feature_options_box,
self.preview_box])
tab_titles = ['Feature', 'Options', 'Preview']
for (k, tl) in enumerate(tab_titles):
self.options_box.set_title(k, tl)
super(FeatureOptionsWidget, self).__init__(children=[self.options_box])
self.align = 'start'
# Initialize output
options = {}
self.function = partial(no_op, **options)
self.features_function = no_op
self.features_options = options
# Set style
self.predefined_style(style)
# Set functionality
def per_feature_options_visibility(change):
value = change['new']
if value == dsift:
self.igo_options_widget.visible = False
self.lbp_options_widget.visible = False
self.daisy_options_widget.visible = False
self.no_options_widget.visible = False
self.hog_options_widget.visible = False
self.dsift_options_widget.visible = True
elif value == hog:
self.igo_options_widget.visible = False
self.lbp_options_widget.visible = False
self.daisy_options_widget.visible = False
self.no_options_widget.visible = False
self.dsift_options_widget.visible = False
self.hog_options_widget.visible = True
elif value == igo:
self.hog_options_widget.visible = False
self.lbp_options_widget.visible = False
self.daisy_options_widget.visible = False
self.no_options_widget.visible = False
self.dsift_options_widget.visible = False
self.igo_options_widget.visible = True
elif value == lbp:
self.hog_options_widget.visible = False
self.igo_options_widget.visible = False
self.daisy_options_widget.visible = False
self.no_options_widget.visible = False
self.dsift_options_widget.visible = False
self.lbp_options_widget.visible = True
elif value == daisy:
self.hog_options_widget.visible = False
self.igo_options_widget.visible = False
self.lbp_options_widget.visible = False
self.no_options_widget.visible = False
self.dsift_options_widget.visible = False
self.daisy_options_widget.visible = True
else:
self.hog_options_widget.visible = False
self.igo_options_widget.visible = False
self.lbp_options_widget.visible = False
self.daisy_options_widget.visible = False
self.dsift_options_widget.visible = False
self.no_options_widget.visible = True
for name, f in tmp.items():
if f == value:
self.no_options_widget.value = \
"{}: No available options.".format(name)
self.feature_radiobuttons.observe(per_feature_options_visibility,
names='value', type='change')
per_feature_options_visibility({'new': no_op})
def get_function(change):
# get options
if self.feature_radiobuttons.value == dsift:
opts = self.dsift_options_widget.selected_values
elif self.feature_radiobuttons.value == hog:
opts = self.hog_options_widget.selected_values
elif self.feature_radiobuttons.value == igo:
opts = self.igo_options_widget.selected_values
elif self.feature_radiobuttons.value == lbp:
opts = self.lbp_options_widget.selected_values
elif self.feature_radiobuttons.value == daisy:
opts = self.daisy_options_widget.selected_values
else:
opts = {}
# get features function closure
func = partial(self.feature_radiobuttons.value, **opts)
# store function
self.function = func
self.features_function = self.feature_radiobuttons.value
self.features_options = opts
self.feature_radiobuttons.observe(get_function, names='value',
type='change')
self.options_box.observe(get_function, names='selected_index',
type='change')
def preview_function(change):
if change['new'] == 2:
# extracting features message
val1 = ''
for name, f in tmp.items():
if f == self.function.func:
val1 = name
self.preview_output_latex.value = \
'Previewing {} features...'.format(val1)
self.preview_time_latex.value = ''
# extract feature and time it
t = time.time()
feat_image = self.function(self.image)
t = time.time() - t
# store feature image shape and n_channels
val2 = feat_image.width
val3 = feat_image.height
val4 = feat_image.n_channels
# compute sum of feature image and normalize its pixels in range
# (0, 1) because it is required by as_PILImage
feat_image = sum_channels(feat_image, channels=None)
# feat_image = np.sum(feat_image.pixels, axis=2)
feat_image = feat_image.pixels
feat_image -= np.min(feat_image)
feat_image /= np.max(feat_image)
feat_image = Image(feat_image)
# update preview
self.preview_image.value = convert_image_to_bytes(feat_image)
self.preview_input_latex.visible = True
self.preview_image.visible = True
# set info
self.preview_output_latex.value = \
"{}: {}W x {}H x {}C".format(val1, val2, val3, val4)
self.preview_time_latex.value = "{0:.2f} secs elapsed".format(t)
if change['old'] == 2:
self.preview_input_latex.visible = False
self.preview_image.visible = False
self.options_box.observe(preview_function, names='selected_index',
type='change')
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='solid', border_width=1, border_radius=0, padding=0,
margin=0, font_family='', font_size=None, font_style='',
font_weight=''):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
format_font(self, font_family, font_size, font_style, font_weight)
format_font(self.feature_radiobuttons, font_family, font_size,
font_style, font_weight)
format_font(self.no_options_widget, font_family, font_size, font_style,
font_weight)
format_font(self.preview_input_latex, font_family, font_size,
font_style, font_weight)
format_font(self.preview_output_latex, font_family, font_size,
font_style, font_weight)
format_font(self.preview_time_latex, font_family, font_size, font_style,
font_weight)
self.dsift_options_widget.style(
box_style=None, border_visible=False, margin='0.2cm',
font_family=font_family, font_size=font_size, font_style=font_style,
font_weight=font_weight)
self.hog_options_widget.style(
box_style=None, border_visible=False, margin='0.2cm',
font_family=font_family, font_size=font_size, font_style=font_style,
font_weight=font_weight)
self.igo_options_widget.style(
box_style=None, border_visible=False, margin='0.2cm',
font_family=font_family, font_size=font_size, font_style=font_style,
font_weight=font_weight)
self.lbp_options_widget.style(
box_style=None, border_visible=False, margin='0.2cm',
font_family=font_family, font_size=font_size, font_style=font_style,
font_weight=font_weight)
self.daisy_options_widget.style(
box_style=None, border_visible=False, margin='0.2cm',
font_family=font_family, font_size=font_size, font_style=font_style,
font_weight=font_weight)
self.no_options_widget.margin = '0.2cm'
[docs] def predefined_style(self, style):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if style == 'minimal':
self.style(box_style='', border_visible=True, border_colour='black',
border_style='solid', border_width=1, border_radius=0,
padding='0.2cm', margin='0.3cm', font_family='',
font_size=None, font_style='', font_weight='')
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
self.style(box_style=style, border_visible=True,
border_colour= map_styles_to_hex_colours(style),
border_style='solid', border_width=1, border_radius=10,
padding='0.2cm', margin='0.3cm', font_family='',
font_size=None, font_style='', font_weight='')
else:
raise ValueError('style must be minimal or info or success or '
'danger or warning')
[docs]class PatchOptionsWidget(MenpoWidget):
r"""
Creates a widget for selecting patches options when rendering a patch-based
image. The widget consists of the following objects from `ipywidgets` and
:ref:`api-tools-index`:
== =========================== ========================= ====================
No Object Property (`self.`) Description
== =========================== ========================= ====================
1 `Dropdown` `offset_dropdown` Offset index
2 `Checkbox` `render_centers_checkbox` Render centers flag
3 `Checkbox` `render_patches_checkbox` Render patches flag
4 `ToggleButton` `background_toggle` Background colour
5 `Latex` `background_title` Background title
6 :map:`SlicingCommandWidget` `slicing_wid` Patch index selector
7 :map:`LineOptionsWidget` `bboxes_line_options_wid` Bboxes options
8 `HBox` `background_box` Contains 5, 4
9 `Box` `render_checkboxes_box` Contains 2, 3
10 `HBox` `render_box` Contains 8, 9
11 `VBox` `offset_patches_box` Contains 6, 1, 10
== =========================== ========================= ====================
Note that:
* To update the state of the widget, please refer to the
:meth:`set_widget_state` method.
* The widget has **memory** about the properties of the objects that are
passed into it through :meth:`set_widget_state`. Each patches object has a
unique key id assigned through :meth:`get_key`. Then, the options that
correspond to each key are stored in the ``self.default_options`` `dict`.
* The selected values of the current patches object are stored in the
``self.selected_values`` `trait`. It is a `dict` with the following keys:
* ``patches_indices`` : (`list` or `int`) The selected patches
(e.g. ``list(range(n_patches))``).
* ``offset_index`` : (`int`) The selected offset
* ``background`` : (`str`) The background colour (e.g. ``'white'``).
* ``render_patches`` : (`bool`) Whether to render the patches.
* ``render_patches_bboxes`` : (`bool`) Whether to render boxes around the
patches.
* ``bboxes_line_colour`` : (`list`) The boxes line colour (e.g. ``['red']``)
* ``bboxes_line_style`` : (`str`) The boxes line style (e.g. ``'-'``).
* ``bboxes_line_width`` : (`float`) The boxes line width (e.g. ``1``).
* ``render_centers`` : (`bool`) Whether to render the patches' centers.
* When an unseen patches object is passed in (i.e. a key that is not included
in the ``self.default_options`` `dict`), it gets the following initial
options by default:
* ``patches_indices = list(range(n_patches))``
* ``offset_index = 0``
* ``background = 'white'``
* ``render_patches = True``
* ``render_patches_bboxes = True``
* ``bboxes_line_colour = ['red']``
* ``bboxes_line_style = '-'``
* ``bboxes_line_width = 1``
* ``render_centers = True``
* To set the styling of this widget please refer to the :meth:`style` and
:meth:`predefined_style` methods.
* To update the handler callback function of the widget, please refer to the
:meth:`replace_render_function` method.
Parameters
----------
n_patches : `int`
The number of patches of the initial object.
n_offsets : `int`
The number of offsets of the initial object.
render_function : `callable` or ``None``, optional
The render function that is executed when a widgets' value changes.
It must have signature ``render_function(change)`` where ``change`` is
a `dict` with the following keys:
* ``type`` : The type of notification (normally ``'change'``).
* ``owner`` : the `HasTraits` instance
* ``old`` : the old value of the modified trait attribute
* ``new`` : the new value of the modified trait attribute
* ``name`` : the name of the modified trait attribute.
If ``None``, then nothing is assigned.
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
subwidgets_style : `str` (see below), optional
Sets a predefined style at the widget's patches and bboxes options.
Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
Example
-------
Let's create a patches widget and then update its state. Firstly, we need
to import it:
>>> from menpowidgets.options import PatchOptionsWidget
Now let's define a render function that will get called on every widget
change and will dynamically print the selected patches and bboxes flag:
>>> from menpo.visualize import print_dynamic
>>> def render_function(change):
>>> s = "Patches: {}, BBoxes: {}".format(
>>> wid.selected_values['patches']['indices'],
>>> wid.selected_values['bboxes']['render_lines'])
>>> print_dynamic(s)
Create the widget with some initial options and display it:
>>> wid = PatchOptionsWidget(n_patches=68, n_offsets=5,
>>> render_function=render_function,
>>> style='info', subwidgets_style='danger')
>>> wid
By playing around with the widget, printed message gets updated. Finally,
let's change the widget status with a new set of options:
>>> wid.set_widget_state(n_patches=49, n_offsets=1, allow_callback=False)
Remember that the widget is **mnemonic**, i.e. it remembers the objects it
has seen and their corresponding options. These can be retrieved as:
>>> wid.default_options
"""
def __init__(self, n_patches, n_offsets, render_function=None,
style='minimal', subwidgets_style='minimal'):
# Initialise default options dictionary
self.default_options = {}
# Assign properties
self.n_offsets = n_offsets
self.n_patches = n_patches
# Create children
self.offset_dropdown = ipywidgets.Dropdown(
options={'0': 0}, value=0, description='Offset:', width='2cm')
self.render_centers_checkbox = ipywidgets.Checkbox(
description='Render centres')
self.render_patches_checkbox = ipywidgets.Checkbox(
description='Render patches')
self.background_toggle = ipywidgets.ToggleButton(
description='white', color='#000000', value=True,
background_color='#FFFFFF')
def change_toggle_description(change):
if change['new']:
self.background_toggle.description = 'white'
self.background_toggle.background_colour = '#FFFFFF'
self.background_toggle.color = '#000000'
else:
self.background_toggle.description = 'black'
self.background_toggle.background_colour = '#000000'
self.background_toggle.color = '#FFFFFF'
self.background_toggle.observe(change_toggle_description, names='value',
type='change')
self.background_title = ipywidgets.Latex(value='Background:',
margin='0.1cm')
slice_options = {'command': "range({})".format(n_patches),
'length': n_patches}
self.slicing_wid = SlicingCommandWidget(
slice_options, description='Patches:',
orientation='vertical', example_visible=True,
continuous_update=False)
self.bboxes_line_options_wid = LineOptionsWidget(
{'render_lines': True, 'line_colour': ['red'], 'line_style': '-',
'line_width': 1}, render_checkbox_title='Render bounding boxes')
# Group widgets
self.background_box = ipywidgets.HBox(children=[
self.background_title, self.background_toggle], align='center',
margin='0.5cm')
self.render_checkboxes_box = ipywidgets.Box(children=[
self.render_patches_checkbox, self.render_centers_checkbox],
margin='0.2cm')
self.render_box = ipywidgets.HBox(children=[
self.background_box, self.render_checkboxes_box], align='center')
self.offset_patches_box = ipywidgets.VBox(
children=[self.slicing_wid, self.offset_dropdown, self.render_box])
# Create final widget
children = [self.offset_patches_box, self.bboxes_line_options_wid]
super(PatchOptionsWidget, self).__init__(
children, Dict, {}, render_function=render_function,
orientation='horizontal', align='start')
# Set values
self.add_callbacks()
self.set_widget_state(n_patches, n_offsets, allow_callback=False)
# Set style
self.predefined_style(style, subwidgets_style)
[docs] def add_callbacks(self):
r"""
Function that adds the handler callback functions in all the widget
components, which are necessary for the internal functionality.
"""
self.slicing_wid.observe(self._save_options, names='selected_values',
type='change')
self.offset_dropdown.observe(self._save_options, names='value',
type='change')
self.background_toggle.observe(self._save_options, names='value',
type='change')
self.render_patches_checkbox.observe(self._save_options, names='value',
type='change')
self.render_centers_checkbox.observe(self._save_options, names='value',
type='change')
self.bboxes_line_options_wid.observe(
self._save_options, names='selected_values', type='change')
[docs] def remove_callbacks(self):
r"""
Function that removes all the internal handler callback functions.
"""
self.slicing_wid.unobserve(self._save_options, names='selected_values',
type='change')
self.offset_dropdown.unobserve(self._save_options, names='value',
type='change')
self.background_toggle.unobserve(self._save_options, names='value',
type='change')
self.render_patches_checkbox.unobserve(self._save_options,
names='value', type='change')
self.render_centers_checkbox.unobserve(self._save_options,
names='value', type='change')
self.bboxes_line_options_wid.unobserve(self._save_options,
names='selected_values',
type='change')
def _save_options(self, change):
# set background attributes
bc, c, description = self._background_args_wrt_value(
self.background_toggle.value)
self.background_toggle.background_color = bc
self.background_toggle.color = c
self.background_toggle.description = description
# update selected values
self.selected_values = {
'patches_indices': self.slicing_wid.selected_values,
'offset_index': int(self.offset_dropdown.value),
'background': description,
'render_patches': self.render_patches_checkbox.value,
'render_centers': self.render_centers_checkbox.value,
'render_patches_bboxes':
self.bboxes_line_options_wid.selected_values['render_lines'],
'bboxes_line_colour':
self.bboxes_line_options_wid.selected_values['line_colour'][0],
'bboxes_line_style':
self.bboxes_line_options_wid.selected_values['line_style'],
'bboxes_line_width':
self.bboxes_line_options_wid.selected_values['line_width']}
# update default values
current_key = self.get_key(self.n_patches, self.n_offsets)
self.default_options[current_key] = {
'patches_indices': self.slicing_wid.selected_values,
'offset_index': int(self.offset_dropdown.value),
'background': description,
'render_patches': self.render_patches_checkbox.value,
'render_centers': self.render_centers_checkbox.value,
'render_patches_bboxes':
self.bboxes_line_options_wid.selected_values['render_lines'],
'bboxes_line_colour':
self.bboxes_line_options_wid.selected_values['line_colour'],
'bboxes_line_style':
self.bboxes_line_options_wid.selected_values['line_style'],
'bboxes_line_width':
self.bboxes_line_options_wid.selected_values['line_width']}
[docs] def get_key(self, n_patches, n_offsets):
r"""
Function that returns a unique key based on the properties of the
provided patches object.
Parameters
----------
n_patches : `int`
The number of patches.
n_offsets : `int`
The number of offsets.
Returns
-------
key : `str`
The key that has the format ``'{n_patches}_{n_offsets}'``.
"""
return "{}_{}".format(n_patches, n_offsets)
[docs] def get_default_options(self, n_patches, n_offsets):
r"""
Function that returns a `dict` with default options given the properties
of a patches object, i.e. `n_patches` and `n_offsets`. The function
returns the `dict` of options but also updates the
``self.default_options`` `dict`.
Parameters
----------
n_patches : `int`
The number of patches.
n_offsets : `int`
The number of offsets.
Returns
-------
default_options : `dict`
A `dict` with the default options. It contains:
* ``patches_indices`` : (`list` or `int`) The selected patches.
* ``offset_index`` : (`int`) The selected offset.
* ``background`` : (`str`) The background colour.
* ``render_patches`` : (`bool`) Whether to render the patches.
* ``render_patches_bboxes`` : (`bool`) Whether to render boxes around the
patches.
* ``bboxes_line_colour`` : (`list`) The boxes line colour.
* ``bboxes_line_style`` : (`str`) The boxes line style.
* ``bboxes_line_width`` : (`float`) The boxes line width.
* ``render_centers`` : (`bool`) Whether to render the patches centers
If the object is not seen before by the widget, then it automatically
gets the following default options:
* ``patches_indices = list(range(n_patches))``
* ``offset_index = 0``
* ``background = 'white'``
* ``render_patches = True``
* ``render_patches_bboxes = True``
* ``bboxes_line_colour = ['red']``
* ``bboxes_line_style = '-'``
* ``bboxes_line_width = 1``
* ``render_centers = True``
"""
# create key
key = self.get_key(n_patches, n_offsets)
# if the key does not exist in the default options dict, then add it
if key not in self.default_options:
# update default options dictionary
self.default_options[key] = {
'patches_indices': list(range(n_patches)), 'offset_index': 0,
'background': 'white', 'render_patches': True,
'render_patches_bboxes': True, 'bboxes_line_colour': ['red'],
'bboxes_line_style': '-', 'bboxes_line_width': 1,
'render_centers': True}
return self.default_options[key]
def _background_args_wrt_description(self, description):
background_colour = '#FFFFFF'
color = '#000000'
value = True
if description == 'black':
background_colour = '#000000'
color = '#FFFFFF'
value = False
return background_colour, color, value
def _background_args_wrt_value(self, value):
background_colour = '#FFFFFF'
color = '#000000'
description = 'white'
if not value:
background_colour = '#000000'
color = '#FFFFFF'
description = 'black'
return background_colour, color, description
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='dashed', border_width=1, border_radius=0, padding=0,
margin=0, font_family='', font_size=None, font_style='',
font_weight='', bboxes_box_style=None,
bboxes_border_visible=False, bboxes_border_colour='black',
bboxes_border_style='solid', bboxes_border_width=1,
bboxes_border_radius=0, bboxes_padding=0, bboxes_margin=0,
patches_box_style=None, patches_border_visible=False,
patches_border_colour='black', patches_border_style='solid',
patches_border_width=1, patches_border_radius=0,
patches_padding=0, patches_margin=0):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
bboxes_box_style : `str` or ``None`` (see below), optional
Style options for the bounding boxes:
'success', 'info', 'warning', 'danger', '', None
bboxes_border_visible : `bool`, optional
Defines whether to draw the border line around the bounding boxes
options.
bboxes_border_colour : `str`, optional
The color of the border around the bounding boxes options.
bboxes_border_style : `str`, optional
The line style of the border around the bounding boxes options.
bboxes_border_width : `float`, optional
The line width of the border around the bounding boxes options.
bboxes_border_radius : `float`, optional
The radius of the corners of the box of the bounding boxes options.
bboxes_padding : `float`, optional
The padding around the bounding boxes options.
bboxes_margin : `float`, optional
The margin around the bounding boxes options.
patches_box_style : `str` or ``None`` (see below), optional
Style options of the patches and offset options:
'success', 'info', 'warning', 'danger', '', None
patches_border_visible : `bool`, optional
Defines whether to draw the border line around the patches and
offset options.
patches_border_colour : `str`, optional
The color of the border around the patches and offset options.
patches_border_style : `str`, optional
The line style of the border around the patches and offset options.
patches_border_width : `float`, optional
The line width of the border around the patches and offset options.
patches_border_radius : `float`, optional
The radius of the corners of the box of the patches and offset
options.
patches_padding : `float`, optional
The padding around the patches and offset options.
patches_margin : `float`, optional
The margin around the patches and offset options.
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
format_font(self, font_family, font_size, font_style, font_weight)
format_font(self.offset_dropdown, font_family, font_size, font_style,
font_weight)
format_font(self.render_patches_checkbox, font_family, font_size,
font_style, font_weight)
format_font(self.render_centers_checkbox, font_family, font_size,
font_style, font_weight)
format_font(self.background_toggle, font_family, font_size,
font_style, font_weight)
format_font(self.background_title, font_family, font_size,
font_style, font_weight)
self.bboxes_line_options_wid.style(
box_style=bboxes_box_style, border_visible=bboxes_border_visible,
border_colour=bboxes_border_colour, border_style=bboxes_border_style,
border_width=bboxes_border_width,
border_radius=bboxes_border_radius, padding=bboxes_padding,
margin=bboxes_margin, font_family=font_family, font_size=font_size,
font_style=font_style, font_weight=font_weight)
self.slicing_wid.style(
box_style=patches_box_style, text_box_style=None,
text_box_background_colour=None, text_box_width=None,
font_family=font_family, font_size=font_size, font_style=font_style,
font_weight=font_weight)
format_box(self.offset_patches_box, box_style=patches_box_style,
border_visible=patches_border_visible,
border_colour=patches_border_colour,
border_style=patches_border_style,
border_width=patches_border_width,
border_radius=patches_border_radius,
padding=patches_padding, margin=patches_margin)
[docs] def predefined_style(self, style, subwidgets_style):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
subwidgets_style : `str` (see below)
Sub-widgets (patches and bounding boxes) style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if style == 'minimal':
box_style = None
border_visible = False
border_colour = 'black'
border_radius = 0
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
box_style = style
border_visible = True
border_colour = map_styles_to_hex_colours(style)
border_radius = 10
else:
raise ValueError('style and must be minimal or info or success '
'or danger or warning')
if subwidgets_style == 'minimal':
bboxes_box_style = None
bboxes_border_colour = 'black'
bboxes_border_radius = 0
patches_box_style = None
patches_border_colour = 'black'
patches_border_radius = 0
elif (subwidgets_style == 'info' or subwidgets_style == 'success' or
subwidgets_style == 'danger' or subwidgets_style == 'warning'):
bboxes_box_style = subwidgets_style
bboxes_border_colour = map_styles_to_hex_colours(subwidgets_style)
bboxes_border_radius = 10
patches_box_style = subwidgets_style
patches_border_colour = map_styles_to_hex_colours(subwidgets_style)
patches_border_radius = 10
else:
raise ValueError('subwidgets_style and must be minimal or info '
'or success or danger or warning')
self.style(
box_style=box_style, border_visible=border_visible,
border_colour=border_colour, border_style='solid', border_width=1,
border_radius=border_radius, padding='0.2cm', margin='0.3cm',
font_family='', font_size=None, font_style='', font_weight='',
bboxes_box_style=bboxes_box_style, bboxes_border_visible=True,
bboxes_border_colour=bboxes_border_colour,
bboxes_border_style='solid', bboxes_border_width=1,
bboxes_border_radius=bboxes_border_radius, bboxes_padding='0.2cm',
bboxes_margin='0.1cm', patches_box_style=patches_box_style,
patches_border_visible=True,
patches_border_colour=patches_border_colour,
patches_border_style='solid', patches_border_width=1,
patches_border_radius=patches_border_radius,
patches_padding='0.2cm', patches_margin='0.1cm')
[docs] def set_widget_state(self, n_patches, n_offsets, allow_callback=True):
r"""
Method that updates the state of the widget, if the key generated with
:meth:`get_key` based on the provided `n_patches` and `n_offsets`
is different than the current key based on ``self.n_patches`` and
``self.n_offsets``.
Parameters
----------
n_patches : `int`
The number of patches.
n_offsets : `int`
The number of offsets.
allow_callback : `bool`, optional
If ``True``, it allows triggering of any callback functions.
"""
# keep old value
old_value = self.selected_values
# check if updates are required
if (not self.default_options or
self.get_key(self.n_patches, self.n_offsets) !=
self.get_key(n_patches, n_offsets)):
# temporarily remove callbacks
render_function = self._render_function
self.remove_render_function()
self.remove_callbacks()
# Assign properties
self.n_patches = n_patches
self.n_offsets = n_offsets
# Get initial options
patch_options = self.get_default_options(n_patches, n_offsets)
# Update widgets' state
offsets_dict = OrderedDict()
for i in range(self.n_offsets):
offsets_dict[str(i)] = i
self.offset_dropdown.options = offsets_dict
self.offset_dropdown.value = patch_options['offset_index']
self.render_patches_checkbox.value = patch_options['render_patches']
self.render_centers_checkbox.value = patch_options['render_centers']
background_colour = '#FFFFFF'
color = '#000000'
value = True
if patch_options['background'] == 'black':
background_colour = '#000000'
color = '#FFFFFF'
value = False
self.background_toggle.description = patch_options['background']
self.background_toggle.color = color
self.background_toggle.background_color = background_colour
self.background_toggle.value = value
slice_options = {'command': str(patch_options['patches_indices']),
'length': self.n_patches}
self.slicing_wid.set_widget_state(slice_options,
allow_callback=False)
line_opts = {
'render_lines': patch_options['render_patches_bboxes'],
'line_colour': patch_options['bboxes_line_colour'],
'line_style': patch_options['bboxes_line_style'],
'line_width': patch_options['bboxes_line_width']}
self.bboxes_line_options_wid.set_widget_state(
line_opts, labels=None, allow_callback=False)
# Get values
self._save_options({})
# Re-assign callbacks
self.add_callbacks()
self.add_render_function(render_function)
# trigger render function if allowed
if allow_callback:
self.call_render_function(old_value, self.selected_values)
[docs]class PlotOptionsWidget(MenpoWidget):
r"""
Creates a widget for selecting options for rendering various curves in a
graph. The widget consists of the following objects from `ipywidgets` and
:ref:`api-tools-index`:
== ========================== ====================== =====================
No Object Property (`self.`) Description
== ========================== ====================== =====================
1 :map:`LineOptionsWidget` `lines_wid` Line options widget
2 :map:`MarkerOptionsWidget` `markers_wid` Marker options widget
3 `Dropdown` `curves_dropdown` Curve selector
4 `Tab` `lines_markers_tab` Contains 1, 2
5 `VBox` `lines_markers_box` Contains 3, 4
6 :map:`LegendOptionsWidget` `legend_wid` Legend options widget
7 :map:`AxesOptionsWidget` `axes_wid` Axes options widget
8 :map:`ZoomTwoScalesWidget` `zoom_wid` Zoom options widget
9 :map:`GridOptionsWidget` `grid_wid` Grid options widget
10 `Text` `x_label` X label text
11 `Text` `y_label` Y label text
12 `Text` `title` Title text
13 `Textarea` `legend_entries_text` Legend entries text
14 `VBox` `plot_related_options` Contains 10 - 13
15 `Tab` `options_tab` Contains 14, 5 - 9
== ========================== ====================== =====================
Note that:
* The widget has **memory** about the properties of the objects that are
passed into it through `legend_entries`.
* The selected values of the current object object are stored in the
``self.selected_values`` `trait`.
* To set the styling of this widget please refer to the :meth:`style` and
:meth:`predefined_style` methods.
* To update the handler callback function of the widget, please refer to the
:meth:`replace_render_function` method.
Parameters
----------
legend_entries : `list` of `str`
The `list` of legend entries per curve.
render_function : `callable` or ``None``, optional
The render function that is executed when a widgets' value changes.
It must have signature ``render_function(change)`` where ``change`` is
a `dict` with the following keys:
* ``type`` : The type of notification (normally ``'change'``).
* ``owner`` : the `HasTraits` instance
* ``old`` : the old value of the modified trait attribute
* ``new`` : the new value of the modified trait attribute
* ``name`` : the name of the modified trait attribute.
If ``None``, then nothing is assigned.
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
tabs_style : `str` (see below), optional
Sets a predefined style at the tabs of the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
Example
-------
Let's create a plot options widget. Firstly, we need to import it:
>>> from menpowidgets.options import PlotOptionsWidget
Let's set some legend entries:
>>> legend_entries = ['method_1', 'method_2']
Now let's define a render function that will get called on every widget
change and will dynamically print the selected marker face colour and line
width:
>>> from menpo.visualize import print_dynamic
>>> def render_function(change):
>>> s = "Marker edge colours: {}, Line widths: {}".format(
>>> wid.selected_values['marker_edge_colour'],
>>> wid.selected_values['line_width'])
>>> print_dynamic(s)
Create the widget with the initial options and display it:
>>> wid = PlotOptionsWidget(legend_entries,
>>> render_function=render_function,
>>> style='danger', tabs_style='info')
>>> wid
By playing around, the printed message gets updated. The style of the widget
can be changed as:
>>> wid.predefined_style('minimal', 'info')
"""
def __init__(self, legend_entries, render_function=None, style='minimal',
tabs_style='minimal'):
# Assign properties
self.legend_entries = legend_entries
self.n_curves = len(legend_entries)
# Create default options
default_options = self.create_default_options()
# Create children
self.lines_wid = LineOptionsWidget(
{'render_lines': default_options['render_lines'][0],
'line_width': default_options['line_width'][0],
'line_colour': [default_options['line_colour'][0]],
'line_style': default_options['line_style'][0]},
render_function=None, render_checkbox_title='Render lines',
labels=None)
self.markers_wid = MarkerOptionsWidget(
{'render_markers': default_options['render_markers'][0],
'marker_style': default_options['marker_style'][0],
'marker_size': default_options['marker_size'][0],
'marker_face_colour': [default_options['marker_face_colour'][0]],
'marker_edge_colour': [default_options['marker_edge_colour'][0]],
'marker_edge_width': default_options['marker_edge_width'][0]},
render_function=None, render_checkbox_title='Render markers',
labels=None)
curves_dict = {}
for i, s in enumerate(self.legend_entries):
curves_dict[s] = i
self.curves_dropdown = ipywidgets.Dropdown(
description='Curve: ', options=curves_dict, value=0)
self.lines_markers_tab = ipywidgets.Tab(
children=[self.lines_wid, self.markers_wid], margin='0.2cm')
self.lines_markers_tab.set_title(0, 'Lines')
self.lines_markers_tab.set_title(1, 'Markers')
self.lines_markers_box = ipywidgets.VBox(
children=[self.curves_dropdown, self.lines_markers_tab],
align='start')
self.legend_wid = LegendOptionsWidget(
{'render_legend': default_options['render_legend'],
'legend_title': default_options['legend_title'],
'legend_font_name': default_options['legend_font_name'],
'legend_font_style': default_options['legend_font_style'],
'legend_font_size': default_options['legend_font_size'],
'legend_font_weight': default_options['legend_font_weight'],
'legend_marker_scale': default_options['legend_marker_scale'],
'legend_location': default_options['legend_location'],
'legend_bbox_to_anchor': default_options['legend_bbox_to_anchor'],
'legend_border_axes_pad': default_options['legend_border_axes_pad'],
'legend_n_columns': default_options['legend_n_columns'],
'legend_horizontal_spacing': default_options['legend_horizontal_spacing'],
'legend_vertical_spacing': default_options['legend_vertical_spacing'],
'legend_border': default_options['legend_border'],
'legend_border_padding': default_options['legend_border_padding'],
'legend_shadow': default_options['legend_shadow'],
'legend_rounded_corners': default_options['legend_rounded_corners']},
render_function=None, render_checkbox_title='Render legend')
self.axes_wid = AxesOptionsWidget(
{'render_axes': default_options['render_axes'],
'axes_font_name': default_options['axes_font_name'],
'axes_font_size': default_options['axes_font_size'],
'axes_font_style': default_options['axes_font_style'],
'axes_font_weight': default_options['axes_font_weight'],
'axes_x_limits': default_options['axes_x_limits'],
'axes_y_limits': default_options['axes_y_limits'],
'axes_x_ticks': default_options['axes_x_ticks'],
'axes_y_ticks': default_options['axes_y_ticks']},
render_function=None, render_checkbox_title='Render axes')
self.zoom_wid = ZoomTwoScalesWidget(
{'zoom': default_options['zoom'], 'min': 0.1, 'max': 4.,
'step': 0.05, 'lock_aspect_ratio': False}, render_function=None,
description='Scale: ', continuous_update=False)
self.grid_wid = GridOptionsWidget(
{'render_grid': default_options['render_grid'],
'grid_line_width': default_options['grid_line_width'],
'grid_line_style': default_options['grid_line_style']},
render_function=None, render_checkbox_title='Render grid')
self.x_label = ipywidgets.Text(description='X label', margin='0.05cm',
value=default_options['x_label'])
self.y_label = ipywidgets.Text(description='Y label', margin='0.05cm',
value=default_options['y_label'])
self.title = ipywidgets.Text(description='Title', margin='0.05cm',
value=default_options['title'])
self.legend_entries_text = ipywidgets.Textarea(
description='Legend', width='73mm', margin='0.05cm',
value=self._convert_list_to_legend_entries(self.legend_entries))
self.plot_related_options = ipywidgets.VBox(
children=[self.x_label, self.y_label, self.title,
self.legend_entries_text])
# Group widgets
self.options_tab = ipywidgets.Tab(
children=[self.plot_related_options, self.lines_markers_box,
self.legend_wid, self.axes_wid, self.zoom_wid,
self.grid_wid])
self.options_tab.set_title(0, 'Figure')
self.options_tab.set_title(1, 'Renderer')
self.options_tab.set_title(2, 'Legend')
self.options_tab.set_title(3, 'Axes')
self.options_tab.set_title(4, 'Zoom')
self.options_tab.set_title(5, 'Grid')
# Create final widget
children = [self.options_tab]
super(PlotOptionsWidget, self).__init__(
children, Dict, default_options, render_function=render_function,
orientation='vertical', align='start')
# Set style
self.predefined_style(style, tabs_style)
# Set functionality
def get_legend_entries(change):
# get legend entries
tmp_entries = str(self.legend_entries_text.value).splitlines()
if len(tmp_entries) < self.n_curves:
n_missing = self.n_curves - len(tmp_entries)
for j in range(n_missing):
kk = j + len(tmp_entries)
tmp_entries.append("curve {}".format(kk))
self.legend_entries = tmp_entries[:self.n_curves]
# update dropdown menu
curves_dir = {}
for j, le in enumerate(self.legend_entries):
curves_dir[le] = j
self.curves_dropdown.options = curves_dir
if self.curves_dropdown.value == 0 and self.n_curves > 1:
self.curves_dropdown.value = 1
self.curves_dropdown.value = 0
self.legend_entries_text.observe(get_legend_entries, names='value',
type='change')
def save_options(change):
# get lines and markers options
k = self.curves_dropdown.value
render_lines = list(self.selected_values['render_lines'])
render_lines[k] = self.lines_wid.selected_values['render_lines']
line_colour = list(self.selected_values['line_colour'])
line_colour[k] = self.lines_wid.selected_values['line_colour'][0]
line_style = list(self.selected_values['line_style'])
line_style[k] = self.lines_wid.selected_values['line_style']
line_width = list(self.selected_values['line_width'])
line_width[k] = self.lines_wid.selected_values['line_width']
render_markers = list(self.selected_values['render_markers'])
render_markers[k] = self.markers_wid.selected_values['render_markers']
marker_style = list(self.selected_values['marker_style'])
marker_style[k] = self.markers_wid.selected_values['marker_style']
marker_size = list(self.selected_values['marker_size'])
marker_size[k] = self.markers_wid.selected_values['marker_size']
marker_face_colour = list(self.selected_values['marker_face_colour'])
marker_face_colour[k] = self.markers_wid.selected_values['marker_face_colour'][0]
marker_edge_colour = list(self.selected_values['marker_edge_colour'])
marker_edge_colour[k] = self.markers_wid.selected_values['marker_edge_colour'][0]
marker_edge_width = list(self.selected_values['marker_edge_width'])
marker_edge_width[k] = self.markers_wid.selected_values['marker_edge_width']
self.selected_values = {
'legend_entries': self.legend_entries,
'title': str(self.title.value),
'x_label': str(self.x_label.value),
'y_label': str(self.y_label.value),
'render_lines': render_lines, 'line_colour': line_colour,
'line_style': line_style, 'line_width': line_width,
'render_markers': render_markers, 'marker_style': marker_style,
'marker_size': marker_size,
'marker_face_colour': marker_face_colour,
'marker_edge_colour': marker_edge_colour,
'marker_edge_width': marker_edge_width,
'render_legend': self.legend_wid.selected_values['render_legend'],
'legend_title': self.legend_wid.selected_values['legend_title'],
'legend_font_name': self.legend_wid.selected_values['legend_font_name'],
'legend_font_style': self.legend_wid.selected_values['legend_font_style'],
'legend_font_size': self.legend_wid.selected_values['legend_font_size'],
'legend_font_weight': self.legend_wid.selected_values['legend_font_weight'],
'legend_marker_scale': self.legend_wid.selected_values['legend_marker_scale'],
'legend_location': self.legend_wid.selected_values['legend_location'],
'legend_bbox_to_anchor': self.legend_wid.selected_values['legend_bbox_to_anchor'],
'legend_border_axes_pad': self.legend_wid.selected_values['legend_border_axes_pad'],
'legend_n_columns': self.legend_wid.selected_values['legend_n_columns'],
'legend_horizontal_spacing': self.legend_wid.selected_values['legend_horizontal_spacing'],
'legend_vertical_spacing': self.legend_wid.selected_values['legend_vertical_spacing'],
'legend_border': self.legend_wid.selected_values['legend_border'],
'legend_border_padding': self.legend_wid.selected_values['legend_border_padding'],
'legend_shadow': self.legend_wid.selected_values['legend_shadow'],
'legend_rounded_corners': self.legend_wid.selected_values['legend_rounded_corners'],
'render_axes': self.axes_wid.selected_values['render_axes'],
'axes_font_name': self.axes_wid.selected_values['axes_font_name'],
'axes_font_size': self.axes_wid.selected_values['axes_font_size'],
'axes_font_style': self.axes_wid.selected_values['axes_font_style'],
'axes_font_weight': self.axes_wid.selected_values['axes_font_weight'],
'axes_x_limits': self.axes_wid.selected_values['axes_x_limits'],
'axes_y_limits': self.axes_wid.selected_values['axes_y_limits'],
'axes_x_ticks': self.axes_wid.selected_values['axes_x_ticks'],
'axes_y_ticks': self.axes_wid.selected_values['axes_y_ticks'],
'zoom': self.zoom_wid.selected_values,
'render_grid': self.grid_wid.selected_values['render_grid'],
'grid_line_style': self.grid_wid.selected_values['grid_line_style'],
'grid_line_width': self.grid_wid.selected_values['grid_line_width']}
self.title.observe(save_options, names='value', type='change')
self.x_label.observe(save_options, names='value', type='change')
self.y_label.observe(save_options, names='value', type='change')
self.legend_entries_text.observe(save_options, names='value',
type='change')
self.lines_wid.observe(save_options, names='selected_values',
type='change')
self.markers_wid.observe(save_options, names='selected_values',
type='change')
self.axes_wid.observe(save_options, names='selected_values',
type='change')
self.legend_wid.observe(save_options, names='selected_values',
type='change')
self.grid_wid.observe(save_options, names='selected_values',
type='change')
self.zoom_wid.observe(save_options, names='selected_values',
type='change')
def update_lines_markers(change):
k = self.curves_dropdown.value
# remove save options callback
self.lines_wid.unobserve(save_options, names='selected_values',
type='change')
self.markers_wid.unobserve(save_options, names='selected_values',
type='change')
# update lines
self.lines_wid.set_widget_state(
{'render_lines': self.selected_values['render_lines'][k],
'line_width': self.selected_values['line_width'][k],
'line_colour': [self.selected_values['line_colour'][k]],
'line_style': self.selected_values['line_style'][k]},
labels=None, allow_callback=False)
# update markers
self.markers_wid.set_widget_state(
{'render_markers': self.selected_values['render_markers'][k],
'marker_style': self.selected_values['marker_style'][k],
'marker_size': self.selected_values['marker_size'][k],
'marker_face_colour': [self.selected_values['marker_face_colour'][k]],
'marker_edge_colour': [self.selected_values['marker_edge_colour'][k]],
'marker_edge_width': self.selected_values['marker_edge_width'][k]},
labels=None, allow_callback=False)
# add save options callback
self.lines_wid.observe(save_options, names='selected_values',
type='change')
self.markers_wid.observe(save_options, names='selected_values',
type='change')
self.curves_dropdown.observe(update_lines_markers, names='value',
type='change')
[docs] def create_default_options(self):
r"""
Function that returns a `dict` with default options. The returned
`dict` has the following default keys and values:
* ``title = ''``
* ``x_label = ''``
* ``y_label = ''``
* ``render_legend = True``
* ``legend_title = ''``
* ``legend_font_name = 'sans-serif'``
* ``legend_font_style = 'normal'``
* ``legend_font_size = 10``
* ``legend_font_weight = 'normal'``
* ``legend_marker_scale = 1.``
* ``legend_location = 2``
* ``legend_bbox_to_anchor = (1.05, 1.)``
* ``legend_border_axes_pad = 1.``
* ``legend_n_columns = 1``
* ``legend_horizontal_spacing = 1.``
* ``legend_vertical_spacing = 1.``
* ``legend_border = True``
* ``legend_border_padding = 0.5``
* ``legend_shadow = False``
* ``legend_rounded_corners = False``
* ``render_axes = True``
* ``axes_font_name = 'sans-serif'``
* ``axes_font_size = 10``
* ``axes_font_style = 'normal'``
* ``axes_font_weight = 'normal'``
* ``axes_x_limits = None``
* ``axes_y_limits = None``
* ``axes_x_ticks = None``
* ``axes_y_ticks = None``
* ``render_grid = True``
* ``grid_line_style = '--'``
* ``grid_line_width = 0.5``
* ``render_lines = [True] * self.n_curves``
* ``line_width = [1] * self.n_curves``
* ``line_colour = colours if self.n_curves > 1 else ['red']``
* ``line_style = ['-'] * self.n_curves``
* ``render_markers = [True] * self.n_curves``
* ``marker_size = [7] * self.n_curves``
* ``marker_face_colour = ['white'] * self.n_curves``
* ``marker_edge_colour = colours if self.n_curves > 1 else ['red']``
* ``marker_style = ['s'] * self.n_curves``
* ``marker_edge_width = [2.] * self.n_curves``
* ``zoom = [1., 1.]``
where ``colours = sample_colours_from_colourmap(self.n_curves, 'Paired')``.
"""
render_lines = [True] * self.n_curves
line_style = ['-'] * self.n_curves
line_width = [1] * self.n_curves
render_markers = [True] * self.n_curves
marker_style = ['s'] * self.n_curves
marker_size = [7] * self.n_curves
marker_face_colour = ['white'] * self.n_curves
marker_edge_width = [2.] * self.n_curves
line_colour = ['red']
marker_edge_colour = ['red']
if self.n_curves > 1:
line_colour = sample_colours_from_colourmap(self.n_curves, 'Paired')
marker_edge_colour = sample_colours_from_colourmap(
self.n_curves, 'Paired')
return {'title': '', 'x_label': '', 'y_label': '', 'render_legend': True,
'legend_entries': self.legend_entries,
'legend_title': '', 'legend_font_name': 'sans-serif',
'legend_font_style': 'normal', 'legend_font_size': 10,
'legend_font_weight': 'normal', 'legend_marker_scale': 1.,
'legend_location': 2, 'legend_bbox_to_anchor': (1.05, 1.),
'legend_border_axes_pad': 1., 'legend_n_columns': 1,
'legend_horizontal_spacing': 1., 'legend_vertical_spacing': 1.,
'legend_border': True, 'legend_border_padding': 0.5,
'legend_shadow': False, 'legend_rounded_corners': False,
'render_axes': True, 'axes_font_name': 'sans-serif',
'axes_font_size': 10, 'axes_font_style': 'normal',
'axes_font_weight': 'normal', 'axes_x_limits': 0.,
'axes_y_limits': 0., 'axes_x_ticks': None,
'axes_y_ticks': None, 'render_grid': True,
'grid_line_style': '--', 'grid_line_width': 0.5,
'render_lines': render_lines, 'line_width': line_width,
'line_colour': line_colour, 'line_style': line_style,
'render_markers': render_markers, 'marker_size': marker_size,
'marker_face_colour': marker_face_colour,
'marker_edge_colour': marker_edge_colour,
'marker_style': marker_style,
'marker_edge_width': marker_edge_width, 'zoom': [1., 1.]}
def _convert_list_to_legend_entries(self, l):
tmp_lines = []
for k in l:
tmp_lines.append(k)
tmp_lines.append('\n')
tmp_lines = tmp_lines[:-1]
return unicode().join(tmp_lines)
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='solid', border_width=1, border_radius=0,
padding='0.2cm', margin=0, tabs_box_style=None,
tabs_border_visible=True, tabs_border_colour='black',
tabs_border_style='solid', tabs_border_width=1,
tabs_border_radius=1, tabs_padding=0, tabs_margin=0,
font_family='', font_size=None, font_style='', font_weight=''):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
tabs_box_style : See Below, optional
Possible tab widgets style options::
'success', 'info', 'warning', 'danger', '', None
tabs_border_visible : `bool`, optional
Defines whether to draw the border line around the tab widgets.
tabs_border_colour : `str`, optional
The colour of the border around the tab widgets.
tabs_border_style : `str`, optional
The line style of the border around the tab widgets.
tabs_border_width : `float`, optional
The line width of the border around the tab widgets.
tabs_border_radius : `float`, optional
The radius of the corners of the box of the tab widgets.
tabs_padding : `float`, optional
The padding around the tab widgets.
tabs_margin : `float`, optional
The margin around the tab widgets.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
format_box(self.lines_markers_box, box_style=tabs_box_style,
border_visible=tabs_border_style,
border_colour=tabs_border_colour,
border_style=tabs_border_style,
border_width=tabs_border_width,
border_radius=tabs_border_radius, padding=tabs_padding,
margin=tabs_margin)
format_box(self.plot_related_options, box_style=tabs_box_style,
border_visible=tabs_border_style,
border_colour=tabs_border_colour,
border_style=tabs_border_style,
border_width=tabs_border_width,
border_radius=tabs_border_radius, padding=tabs_padding,
margin=tabs_margin)
self.lines_wid.style(
box_style=tabs_box_style, border_visible=False, padding=0,
margin=0, font_family=font_family, font_size=font_size,
font_weight=font_weight, font_style=font_style)
self.markers_wid.style(
box_style=tabs_box_style, border_visible=False, padding=0,
margin=0, font_family=font_family, font_size=font_size,
font_weight=font_weight, font_style=font_style)
self.legend_wid.style(
box_style=tabs_box_style, border_visible=tabs_border_visible,
border_colour=tabs_border_colour, border_style=tabs_border_style,
border_width=tabs_border_width, border_radius=tabs_border_radius,
padding=tabs_padding, margin=tabs_margin, font_family=font_family,
font_size=font_size, font_weight=font_weight, font_style=font_style)
self.zoom_wid.style(
box_style=tabs_box_style, border_visible=tabs_border_visible,
border_colour=tabs_border_colour, border_style=tabs_border_style,
border_width=tabs_border_width, border_radius=tabs_border_radius,
padding=tabs_padding, margin=tabs_margin, font_family=font_family,
font_size=font_size, font_weight=font_weight, font_style=font_style)
self.axes_wid.style(
box_style=tabs_box_style, border_visible=tabs_border_visible,
border_colour=tabs_border_colour, border_style=tabs_border_style,
border_width=tabs_border_width, border_radius=tabs_border_radius,
padding=tabs_padding, margin=tabs_margin, font_family=font_family,
font_size=font_size, font_weight=font_weight, font_style=font_style)
self.grid_wid.style(
box_style=tabs_box_style, border_visible=tabs_border_visible,
border_colour=tabs_border_colour, border_style=tabs_border_style,
border_width=tabs_border_width, border_radius=tabs_border_radius,
padding=tabs_padding, margin=tabs_margin, font_family=font_family,
font_size=font_size, font_weight=font_weight, font_style=font_style)
format_font(self, font_family, font_size, font_style, font_weight)
format_font(self.x_label, font_family, font_size, font_style,
font_weight)
format_font(self.y_label, font_family, font_size, font_style,
font_weight)
format_font(self.title, font_family, font_size, font_style,
font_weight)
format_font(self.legend_entries_text, font_family, font_size, font_style,
font_weight)
format_font(self.curves_dropdown, font_family, font_size, font_style,
font_weight)
[docs] def predefined_style(self, style, tabs_style='minimal'):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
tabs_style : `str` (see below)
Tabs style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if tabs_style == 'minimal' or tabs_style == '':
tabs_style = ''
tabs_border_visible = True
tabs_border_colour = 'black'
tabs_border_radius = 0
tabs_padding = 0
else:
tabs_style = tabs_style
tabs_border_visible = True
tabs_border_colour = map_styles_to_hex_colours(tabs_style)
tabs_border_radius = 10
tabs_padding = '0.2cm'
if style == 'minimal':
self.style(box_style='', border_visible=True, border_colour='black',
border_style='solid', border_width=1, border_radius=0,
padding='0.2cm', margin='0.5cm', font_family='',
font_size=None, font_style='', font_weight='',
tabs_box_style=tabs_style,
tabs_border_visible=tabs_border_visible,
tabs_border_colour=tabs_border_colour,
tabs_border_style='solid', tabs_border_width=1,
tabs_border_radius=tabs_border_radius,
tabs_padding=tabs_padding, tabs_margin='0.3cm')
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
self.style(box_style=style, border_visible=True,
border_colour=map_styles_to_hex_colours(style),
border_style='solid', border_width=1, border_radius=10,
padding='0.2cm', margin='0.5cm', font_family='',
font_size=None, font_style='', font_weight='',
tabs_box_style=tabs_style,
tabs_border_visible=tabs_border_visible,
tabs_border_colour=tabs_border_colour,
tabs_border_style='solid', tabs_border_width=1,
tabs_border_radius=tabs_border_radius,
tabs_padding=tabs_padding, tabs_margin='0.3cm')
else:
raise ValueError('style must be minimal or info or success or '
'danger or warning')
[docs]class LinearModelParametersWidget(MenpoWidget):
r"""
Creates a widget for selecting parameters values when visualizing a linear
model (e.g. PCA model). The widget has options for animating through various
parameters values. It consists of the following objects from `ipywidgets`:
== ============== ====================== ========================
No Object Property (`self.`) Description
== ============== ====================== ========================
1 `Button` `plot_button` The plot variance button
2 `Button` `reset_button` The reset button
3 `HBox` `plot_and_reset` Contains 1, 2
4 `ToggleButton` `play_stop_toggle` The play/stop button
5 `Button` `fast_forward_button` Increase speed
6 `Button` `fast_backward_button` Decrease speed
7 `ToggleButton` `loop_toggle` Repeat mode
8 `HBox` `animation_buttons` Contains 4, 5, 6, 7
9 `HBox` `buttons_box` Contains 3, 8
== ============== ====================== ========================
If ``mode = 'single'``, then:
== ============= ================== ==========================
No Object Property (`self.`) Description
== ============= ================== ==========================
4 `FloatSlider` `slider` The parameter value slider
5 `Dropdown` `dropdown_params` The parameter selector
6 `HBox` `parameters_wid` Contains 4, 5
== ============= ================== ==========================
If ``mode = 'multiple'``, then:
== ============= ================== ==========================
No Object Property (`self.`) Description
== ============= ================== ==========================
7 `FloatSlider` `sliders` `list` of all sliders
8 `VBox` `parameters_wid` Contains all 7
== ============= ================== ==========================
Note that:
* To update the state of the widget, please refer to the
:meth:`set_widget_state` method.
* The selected values are stored in the ``self.selected_values`` `trait`
which is a `list`.
* To set the styling of this widget please refer to the :meth:`style` and
:meth:`predefined_style` methods.
* To update the handler callback functions of the widget, please refer to
the :meth:`replace_render_function` and :meth:`replace_variance_function`
methods.
Parameters
----------
n_parameters : `int`
The `list` of initial parameters values.
render_function : `callable` or ``None``, optional
The render function that is executed when a widgets' value changes.
It must have signature ``render_function(change)`` where ``change`` is
a `dict` with the following keys:
* ``type`` : The type of notification (normally ``'change'``).
* ``owner`` : the `HasTraits` instance
* ``old`` : the old value of the modified trait attribute
* ``new`` : the new value of the modified trait attribute
* ``name`` : the name of the modified trait attribute.
If ``None``, then nothing is assigned.
mode : ``{'single', 'multiple'}``, optional
If ``'single'``, only a single slider is constructed along with a
dropdown menu that allows the parameter selection.
If ``'multiple'``, a slider is constructed for each parameter.
params_str : `str`, optional
The string that will be used as description of the slider(s). The final
description has the form ``"{}{}".format(params_str, p)``, where ``p``
is the parameter number.
params_bounds : (`float`, `float`), optional
The minimum and maximum bounds, in std units, for the sliders.
params_step : `float`, optional
The step, in std units, of the sliders.
plot_variance_visible : `bool`, optional
Defines whether the button for plotting the variance will be visible
upon construction.
plot_variance_function : `callable` or ``None``, optional
The plot function that is executed when the plot variance button is
clicked. If ``None``, then nothing is assigned.
animation_visible : `bool`, optional
Defines whether the animation options will be visible.
loop_enabled : `bool`, optional
If ``True``, then the repeat mode of the animation is enabled.
interval : `float`, optional
The interval between the animation progress in seconds.
interval_step : `float`, optional
The interval step (in seconds) that is applied when fast
forward/backward buttons are pressed.
animation_step : `float`, optional
The parameters step that is applied when animation is enabled.
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
continuous_update : `bool`, optional
If ``True``, then the render function is called while moving a
slider's handle. If ``False``, then the the function is called only
when the handle (mouse click) is released.
Example
-------
Let's create a linear model parameters values widget and then update its
state. Firstly, we need to import it:
>>> from menpowidgets.options import LinearModelParametersWidget
Now let's define a render function that will get called on every widget
change and will dynamically print the selected parameters:
>>> from menpo.visualize import print_dynamic
>>> def render_function(change):
>>> s = "Selected parameters: {}".format(wid.selected_values)
>>> print_dynamic(s)
Create the widget with some initial options and display it:
>>> wid = LinearModelParametersWidget(n_parameters=5,
>>> render_function=render_function,
>>> params_str='Parameter ',
>>> mode='multiple',
>>> params_bounds=(-3., 3.),
>>> plot_variance_visible=True,
>>> style='info')
>>> wid
By moving the sliders, the printed message gets updated. Finally, let's
change the widget status with a new set of options:
>>> wid.set_widget_state(n_parameters=10, params_str='',
>>> params_step=0.1, params_bounds=(-10, 10),
>>> plot_variance_visible=False,
>>> allow_callback=True)
"""
def __init__(self, n_parameters, render_function=None, mode='multiple',
params_str='', params_bounds=(-3., 3.), params_step=0.1,
plot_variance_visible=True, plot_variance_function=None,
animation_visible=True, loop_enabled=False, interval=0.,
interval_step=0.05, animation_step=0.5, style='minimal',
continuous_update=False):
from time import sleep
from IPython import get_ipython
# Get the kernel to use it later in order to make sure that the widgets'
# traits changes are passed during a while-loop
kernel = get_ipython().kernel
# If only one slider requested, then set mode to multiple
if n_parameters == 1:
mode = 'multiple'
# Create children
if mode == 'multiple':
self.sliders = [
ipywidgets.FloatSlider(
description="{}{}".format(params_str, p),
min=params_bounds[0], max=params_bounds[1],
step=params_step, value=0.,
continuous_update=continuous_update)
for p in range(n_parameters)]
self.parameters_wid = ipywidgets.VBox(children=self.sliders,
margin='0.2cm')
else:
vals = OrderedDict()
for p in range(n_parameters):
vals["{}{}".format(params_str, p)] = p
self.slider = ipywidgets.FloatSlider(
description='', min=params_bounds[0], max=params_bounds[1],
step=params_step, value=0., readout=False, margin='0.2cm',
continuous_update=continuous_update)
self.dropdown_params = ipywidgets.Dropdown(options=vals,
margin='0.2cm')
self.parameters_wid = ipywidgets.HBox(
children=[self.dropdown_params, self.slider])
self.plot_button = ipywidgets.Button(
description='Variance', margin='0.05cm',
visible=plot_variance_visible)
self.reset_button = ipywidgets.Button(description='Reset',
margin='0.05cm')
self.plot_and_reset = ipywidgets.HBox(
children=[self.reset_button, self.plot_button], margin='0.2cm')
self.play_stop_toggle = ipywidgets.ToggleButton(
icon='fa-play', description='', value=False, margin='0.05cm',
tooltip='Play animation')
self._toggle_play_style = '' if style == 'minimal' else 'success'
self._toggle_stop_style = '' if style == 'minimal' else 'danger'
self.fast_forward_button = ipywidgets.Button(
icon='fa-fast-forward', description='', margin='0.05cm',
tooltip='Increase animation speed')
self.fast_backward_button = ipywidgets.Button(
icon='fa-fast-backward', description='', margin='0.05cm',
tooltip='Decrease animation speed')
loop_icon = 'fa-repeat' if loop_enabled else 'fa-long-arrow-right'
self.loop_toggle = ipywidgets.ToggleButton(
icon=loop_icon, description='', value=loop_enabled,
margin='0.05cm', tooltip='Repeat animation')
self.animation_buttons = ipywidgets.HBox(
children=[self.play_stop_toggle, self.loop_toggle,
self.fast_backward_button, self.fast_forward_button],
margin='0.2cm', visible=animation_visible)
self.buttons_box = ipywidgets.HBox(children=[self.animation_buttons,
self.plot_and_reset])
self.options_box = ipywidgets.VBox(
children=[self.parameters_wid, self.buttons_box], align='start')
# Create final widget
children = [self.options_box]
super(LinearModelParametersWidget, self).__init__(
children, List, [0.] * n_parameters,
render_function=render_function, align='start')
# Assign output
self.n_parameters = n_parameters
self.mode = mode
self.params_str = params_str
self.params_bounds = params_bounds
self.params_step = params_step
self.plot_variance_visible = plot_variance_visible
self.loop_enabled = loop_enabled
self.continuous_update = continuous_update
self.interval = interval
self.interval_step = interval_step
self.animation_step = animation_step
self.animation_visible = animation_visible
# Set style
self.predefined_style(style)
# Set functionality
if mode == 'single':
# Assign slider value to parameters values list
def save_slider_value(change):
current_parameters = list(self.selected_values)
current_parameters[self.dropdown_params.value] = change['new']
self.selected_values = current_parameters
self.slider.observe(save_slider_value, names='value', type='change')
# Set correct value to slider when drop down menu value changes
def set_slider_value(change):
# Temporarily remove render callback
render_fun = self._render_function
self.remove_render_function()
# Set slider value
self.slider.value = self.selected_values[change['new']]
# Re-assign render callback
self.add_render_function(render_fun)
self.dropdown_params.observe(set_slider_value, names='value',
type='change')
else:
# Assign saving values and main plotting function to all sliders
for w in self.sliders:
w.observe(self._save_slider_value_from_id, names='value',
type='change')
def reset_parameters(name):
# Keep old value
old_value = self.selected_values
# Temporarily remove render callback
render_fun = self._render_function
self.remove_render_function()
# Set parameters to 0
self.selected_values = [0.0] * self.n_parameters
if mode == 'multiple':
for ww in self.parameters_wid.children:
ww.value = 0.
else:
self.parameters_wid.children[0].value = 0
self.parameters_wid.children[1].value = 0.
# Re-assign render callback and trigger it
self.add_render_function(render_fun)
self.call_render_function(old_value, self.selected_values)
self.reset_button.on_click(reset_parameters)
# Set functionality
def play_stop_pressed(change):
value = change['new']
if value:
# Animation was not playing, so Play was pressed.
# Change the button style
self.play_stop_toggle.button_style = self._toggle_stop_style
# Change the icon and tooltip to Stop
self.play_stop_toggle.icon = 'fa-stop'
self.play_stop_toggle.tooltip = 'Stop animation'
# Disable buttons
self.reset_button.disabled = True
self.plot_button.disabled = True
else:
# Animation was playing, so Stop was pressed.
# Change the button style
self.play_stop_toggle.button_style = self._toggle_play_style
# Change the icon and tooltip to Play
self.play_stop_toggle.icon = 'fa-play'
self.play_stop_toggle.tooltip = 'Play animation'
# Enable buttons
self.reset_button.disabled = False
self.plot_button.disabled = False
self.play_stop_toggle.observe(play_stop_pressed, names='value',
type='change')
def loop_pressed(change):
if change['new']:
self.loop_toggle.icon = 'fa-repeat'
else:
self.loop_toggle.icon = 'fa-long-arrow-right'
kernel.do_one_iteration()
self.loop_toggle.observe(loop_pressed, names='value', type='change')
def fast_forward_pressed(name):
tmp = self.interval
tmp -= self.interval_step
if tmp < 0:
tmp = 0
self.interval = tmp
kernel.do_one_iteration()
self.fast_forward_button.on_click(fast_forward_pressed)
def fast_backward_pressed(name):
self.interval += self.interval_step
kernel.do_one_iteration()
self.fast_backward_button.on_click(fast_backward_pressed)
def animate(change):
reset_parameters('')
if mode == 'multiple':
n_sliders = self.n_parameters
slider_id = 0
while slider_id < n_sliders and self.play_stop_toggle.value:
# animate from 0 to min
slider_val = 0.
while (slider_val > self.params_bounds[0] and
self.play_stop_toggle.value):
# update slider value
slider_val -= self.animation_step
# set value
self.parameters_wid.children[slider_id].value = slider_val
# Run IPython iteration.
kernel.do_one_iteration()
# wait
sleep(self.interval)
# animate from min to max
slider_val = self.params_bounds[0]
while (slider_val < self.params_bounds[1] and
self.play_stop_toggle.value):
# update slider value
slider_val += self.animation_step
# set value
self.parameters_wid.children[slider_id].value = slider_val
# Run IPython iteration.
kernel.do_one_iteration()
# wait
sleep(self.interval)
# animate from max to 0
slider_val = self.params_bounds[1]
while slider_val > 0. and self.play_stop_toggle.value:
# update slider value
slider_val -= self.animation_step
# set value
self.parameters_wid.children[slider_id].value = slider_val
# Run IPython iteration.
kernel.do_one_iteration()
# wait
sleep(self.interval)
# reset value
self.parameters_wid.children[slider_id].value = 0.
# update slider id
if self.loop_toggle.value and slider_id == n_sliders - 1:
slider_id = 0
else:
slider_id += 1
if not self.loop_toggle.value and slider_id >= n_sliders:
self.stop_animation()
else:
n_sliders = self.n_parameters
slider_id = 0
while slider_id < n_sliders and self.play_stop_toggle.value:
# set dropdown value
self.parameters_wid.children[0].value = slider_id
# animate from 0 to min
slider_val = 0.
while (slider_val > self.params_bounds[0] and
self.play_stop_toggle.value):
# update slider value
slider_val -= self.animation_step
# set value
self.parameters_wid.children[1].value = slider_val
# Run IPython iteration.
kernel.do_one_iteration()
# wait
sleep(self.interval)
# animate from min to max
slider_val = self.params_bounds[0]
while (slider_val < self.params_bounds[1] and
self.play_stop_toggle.value):
# update slider value
slider_val += self.animation_step
# set value
self.parameters_wid.children[1].value = slider_val
# Run IPython iteration.
kernel.do_one_iteration()
# wait
sleep(self.interval)
# animate from max to 0
slider_val = self.params_bounds[1]
while slider_val > 0. and self.play_stop_toggle.value:
# update slider value
slider_val -= self.animation_step
# set value
self.parameters_wid.children[1].value = slider_val
# Run IPython iteration.
kernel.do_one_iteration()
# wait
sleep(self.interval)
# reset value
self.parameters_wid.children[1].value = 0.
# update slider id
if self.loop_toggle.value and slider_id == n_sliders - 1:
slider_id = 0
else:
slider_id += 1
if not self.loop_toggle.value and slider_id >= n_sliders:
self.stop_animation()
self.play_stop_toggle.observe(animate, names='value', type='change')
# Set plot variance function
self._variance_function = None
self.add_variance_function(plot_variance_function)
def _save_slider_value_from_id(self, change):
current_parameters = list(self.selected_values)
description = change['owner'].description
i = int(description[len(self.params_str)::])
current_parameters[i] = change['new']
self.selected_values = current_parameters
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='solid', border_width=1, border_radius=0, padding=0,
margin=0, font_family='', font_size=None, font_style='',
font_weight='', slider_width='', slider_handle_colour=None,
slider_bar_colour=None, buttons_style=''):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
slider_width : `str`, optional
The width of the slider(s).
slider_handle_colour : `str`, optional
The colour of the handle(s) of the slider(s).
slider_bar_colour : `str`, optional
The bar colour of the slider(s).
buttons_style : `str` or ``None`` (see below), optional
Style options:
'success', 'info', 'warning', 'danger', 'primary', '', None
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
format_font(self, font_family, font_size, font_style, font_weight)
format_font(self.reset_button, font_family, font_size, font_style,
font_weight)
format_font(self.plot_button, font_family, font_size, font_style,
font_weight)
if self.mode == 'single':
format_slider(self.slider, slider_width=slider_width,
slider_handle_colour=slider_handle_colour,
slider_bar_colour=slider_bar_colour,
slider_text_visible=True)
format_font(self.slider, font_family, font_size, font_style,
font_weight)
format_font(self.dropdown_params, font_family, font_size,
font_style, font_weight)
else:
for sl in self.sliders:
format_slider(sl, slider_width=slider_width,
slider_handle_colour=slider_handle_colour,
slider_bar_colour=slider_bar_colour,
slider_text_visible=True)
format_font(sl, font_family, font_size, font_style,
font_weight)
self.reset_button.button_style = buttons_style
self.plot_button.button_style = buttons_style
[docs] def predefined_style(self, style):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if style == 'minimal':
self.play_stop_toggle.button_style = ''
self.fast_forward_button.button_style = ''
self.fast_backward_button.button_style = ''
self.loop_toggle.button_style = ''
self._toggle_play_style = ''
self._toggle_stop_style = ''
self.style(box_style=None, border_visible=True,
border_colour='black', border_style='solid',
border_width=1, border_radius=0, padding='0.2cm',
margin='0.3cm', font_family='', font_size=None,
font_style='', font_weight='', slider_width='',
slider_handle_colour=None, slider_bar_colour=None,
buttons_style='')
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
self.play_stop_toggle.button_style = 'success'
self.fast_forward_button.button_style = 'info'
self.fast_backward_button.button_style = 'info'
self.loop_toggle.button_style = 'info'
self._toggle_play_style = 'success'
self._toggle_stop_style = 'danger'
self.style(box_style=style, border_visible=True,
border_colour=map_styles_to_hex_colours(style),
border_style='solid', border_width=1, border_radius=10,
padding='0.2cm', margin='0.3cm', font_family='',
font_size=None, font_style='', font_weight='',
slider_width='',
slider_handle_colour=map_styles_to_hex_colours(style),
slider_bar_colour=None, buttons_style='primary')
else:
raise ValueError('style must be minimal or info or success or '
'danger or warning')
def stop_animation(self):
r"""
Method that stops an active annotation by setting
``self.play_stop_toggle.value = False``.
"""
self.play_stop_toggle.value = False
[docs] def add_variance_function(self, variance_function):
r"""
Method that adds a `variance_function()` to the `Variance` button of the
widget. The given function is also stored in `self._variance_function`.
Parameters
----------
variance_function : `callable` or ``None``, optional
The variance function that behaves as a callback. If ``None``,
then nothing is added.
"""
self._variance_function = variance_function
if self._variance_function is not None:
self.plot_button.on_click(self._variance_function)
[docs] def remove_variance_function(self):
r"""
Method that removes the current `self._variance_function()` from
the `Variance` button of the widget and sets
``self._variance_function = None``.
"""
self.plot_button.on_click(self._variance_function, remove=True)
self._variance_function = None
[docs] def replace_variance_function(self, variance_function):
r"""
Method that replaces the current `self._variance_function()` of the
`Variance` button of the widget with the given `variance_function()`.
Parameters
----------
variance_function : `callable` or ``None``, optional
The variance function that behaves as a callback. If ``None``,
then nothing happens.
"""
# remove old function
self.remove_variance_function()
# add new function
self.add_variance_function(variance_function)
[docs] def set_widget_state(self, n_parameters=None, params_str=None,
params_bounds=None, params_step=None,
plot_variance_visible=True, animation_step=0.5,
allow_callback=True):
r"""
Method that updates the state of the widget with a new set of options.
Parameters
----------
n_parameters : `int`
The `list` of initial parameters values.
params_str : `str`, optional
The string that will be used as description of the slider(s). The
final description has the form ``"{}{}".format(params_str, p)``,
where ``p`` is the parameter number.
params_bounds : (`float`, `float`), optional
The minimum and maximum bounds, in std units, for the sliders.
params_step : `float`, optional
The step, in std units, of the sliders.
plot_variance_visible : `bool`, optional
Defines whether the button for plotting the variance will be visible
upon construction.
animation_step : `float`, optional
The parameters step that is applied when animation is enabled.
allow_callback : `bool`, optional
If ``True``, it allows triggering of any callback functions.
"""
# Keep old value
old_value = self.selected_values
# Temporarily remove render callback
render_function = self._render_function
self.remove_render_function()
# Parse given options
if n_parameters is None:
n_parameters = self.n_parameters
if params_str is None:
params_str = ''
if params_bounds is None:
params_bounds = self.params_bounds
if params_step is None:
params_step = self.params_step
# Set plot variance visibility
self.plot_button.visible = plot_variance_visible
self.animation_step = animation_step
# Update widget
if n_parameters == self.n_parameters:
# The number of parameters hasn't changed
if self.mode == 'multiple':
for p, sl in enumerate(self.sliders):
sl.description = "{}{}".format(params_str, p)
sl.min = params_bounds[0]
sl.max = params_bounds[1]
sl.step = params_step
else:
self.slider.min = params_bounds[0]
self.slider.max = params_bounds[1]
self.slider.step = params_step
if not params_str == '':
vals = OrderedDict()
for p in range(n_parameters):
vals["{}{}".format(params_str, p)] = p
self.dropdown_params.options = vals
else:
# The number of parameters has changed
self.selected_values = [0.] * n_parameters
if self.mode == 'multiple':
# Create new sliders
self.sliders = [
ipywidgets.FloatSlider(
description="{}{}".format(params_str, p),
min=params_bounds[0], max=params_bounds[1],
step=params_step, value=0.)
for p in range(n_parameters)]
# Set sliders as the children of the container
self.parameters_wid.children = self.sliders
# Assign saving values and main plotting function to all sliders
for w in self.sliders:
w.observe(self._save_slider_value_from_id, names='value',
type='change')
# Set style
if self.box_style is None:
self.predefined_style('minimal')
else:
self.predefined_style(self.box_style)
else:
self.slider.min = params_bounds[0]
self.slider.max = params_bounds[1]
self.slider.step = params_step
vals = OrderedDict()
for p in range(n_parameters):
vals["{}{}".format(params_str, p)] = p
if self.dropdown_params.value == 0 and n_parameters > 1:
self.dropdown_params.value = 1
self.dropdown_params.value = 0
self.dropdown_params.options = vals
self.slider.value = 0.
# Re-assign render callback
self.add_render_function(render_function)
# Assign new selected options
self.n_parameters = n_parameters
self.params_str = params_str
self.params_bounds = params_bounds
self.params_step = params_step
self.plot_variance_visible = plot_variance_visible
# trigger render function if allowed
if allow_callback:
self.call_render_function(old_value, self.selected_values)
[docs]class CameraSnapshotWidget(MenpoWidget):
r"""
Creates a webcam widget for taking screenshots. The widget consists of the
following objects from `ipywidgets` and :ref:`api-tools-index`:
== ========================= ================== ======================
No Object Property (`self.`) Description
== ========================= ================== ======================
1 :map:`CameraWidget` `camera_wid` The webcam widget
2 `Latex` `n_snapshots_text` Number of snapshots
3 `Button` `snapshot_but` Take snapshot button
4 `VBox` `snapshot_box` Contains 3, 2
5 `Button` `close_but` Close widget button
8 :map:`ZoomOneScaleWidget` `zoom_widget` Resolution controller
9 `HBox` `buttons_box` Contains 3, 5, 8
10 `Image` `preview_children` List of preview images
11 `HBox` `preview` Contains all 10
== ========================= ================== ======================
Note that:
* The selected values are stored in the ``self.selected_values`` `trait`.
* To set the styling of this widget please refer to the :meth:`style` and
:meth:`predefined_style` methods.
* To update the handler callback function of the widget, please refer to the
:meth:`replace_render_function` method.
Parameters
----------
canvas_width : `int`, optional
The initial width of the rendered canvas. Note that this doesn't actually
change the webcam resolution. It simply rescales the rendered image, as
well as the size of the returned screenshots.
hd : `bool`, optional
If ``True``, then the webcam will be set to high definition (HD), i.e.
720 x 1280. Otherwise the default resolution will be used.
n_preview_windows : `int`, optional
The number of preview thumbnails that will be used as a FIFO stack to
show the captured screenshots. It must be at least 4.
preview_windows_margin : `int`, optional
The margin between the preview thumbnails in pixels.
render_function : `callable` or ``None``, optional
The render function that is executed when a widgets' value changes.
It must have signature ``render_function(change)`` where ``change`` is
a `dict` with the following keys:
* ``type`` : The type of notification (normally ``'change'``).
* ``owner`` : the `HasTraits` instance
* ``old`` : the old value of the modified trait attribute
* ``new`` : the new value of the modified trait attribute
* ``name`` : the name of the modified trait attribute.
If ``None``, then nothing is assigned.
style : `str` (see below), optional
Sets a predefined style at the widget. Possible options are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
preview_style : `str` (see below), optional
Sets a predefined style at the widget's preview box. Possible options
are:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
Example
-------
Let's create a webcam widget. Firstly, we need to import it:
>>> from menpowidgets.options import CameraSnapshotWidget
Create the widget with some initial options and display it:
>>> wid = CameraSnapshotWidget(canvas_width=640, hd=True,
>>> n_preview_windows=5,
>>> preview_windows_margin=1, style='info')
>>> wid
By pressing the "Take snapshot" button, the snapshots appear in the
thumbnails below the stream. The video stream can be interrupted by pressing
the "Close" button.
"""
javascript_exported = False
def __init__(self, canvas_width=640, hd=True, n_preview_windows=5,
preview_windows_margin=3, render_function=None,
style='minimal', preview_style='minimal'):
# Publish javascript - only occurs once on construction of first
# webcam widget
if not self.javascript_exported:
import os.path
from pathlib import Path
menpowidgets_path = Path(os.path.abspath(__file__)).parent
with open(str(menpowidgets_path / 'js' / 'webcam.js'), 'r') as f:
display(Javascript(data=f.read()))
self.javascript_exported = True
# Check arguments
if n_preview_windows < 4:
n_preview_windows = 4
if preview_windows_margin < 0:
preview_windows_margin = 0
# Create widgets
self.logo_wid = LogoWidget(style=style)
self.logo_wid.margin = '0.1cm'
self.camera_wid = CameraWidget(canvas_width=canvas_width, hd=hd)
self.camera_wid.margin = '0.1cm'
self.camera_logo_box = ipywidgets.VBox(
children=[self.logo_wid, self.camera_wid], align='center')
self.n_snapshots_text = ipywidgets.Latex(value='', margin=2,
visible=False)
self.snapshot_but = ipywidgets.Button(
icon='fa-camera', description=' Take Snapshot',
tooltip='Take snapshot')
self.snapshot_but.style = 'primary'
self.snapshot_box = ipywidgets.VBox(
children=[self.snapshot_but, self.n_snapshots_text],
align='center', margin='0.1cm')
self.close_but = ipywidgets.Button(
icon='fa-close', description=' Close', tooltip='Close the widget',
margin='0.1cm')
self.zoom_widget = ZoomOneScaleWidget(
{'min': 0.1, 'max': 2.1, 'step': 0.05, 'zoom': 1.},
continuous_update=False)
self.zoom_widget.title.visible = False
self.zoom_widget.zoom_text.visible = False
self.zoom_widget.button_plus.tooltip = 'Increase video resolution'
self.zoom_widget.button_minus.tooltip = 'Decrease video resolution'
self.zoom_widget.margin = '0.1cm'
self.resolution_text = ipywidgets.Latex(
value="{}W x {}H".format(self.camera_wid.canvas_width,
self.camera_wid.canvas_height),
margin='0.1cm')
self.resolution_text.font_family = 'monospace'
self.zoom_and_resolution_box = ipywidgets.HBox(
children=[self.zoom_widget, self.resolution_text], align='center')
self.buttons_box = ipywidgets.HBox(
children=[self.snapshot_box, self.close_but,
self.zoom_and_resolution_box], align='start')
width_per_preview = int((canvas_width - preview_windows_margin * 2 *
n_preview_windows) / n_preview_windows)
preview_children = [
ipywidgets.Image(width=width_per_preview,
margin=preview_windows_margin, visible=False)
for k in range(n_preview_windows)]
self.preview = ipywidgets.HBox(children=preview_children, align='start',
visible=False)
# Create final widget
children = [self.camera_logo_box, self.buttons_box, self.preview]
super(CameraSnapshotWidget, self).__init__(
children, List, [], render_function=render_function,
orientation='vertical', align='center')
# Assign properties
self.selected_values = self.camera_wid.snapshots
# Assign take screenshot callback
def take_snapshot(_):
self.camera_wid.take_snapshot = not self.camera_wid.take_snapshot
self.snapshot_but.on_click(take_snapshot)
# Assign close callback
def close(_):
self.close()
self.close_but.on_click(close)
# Assign preview callback
def update_preview(_):
# Convert image to bytes
img = self.camera_wid.imageurl.encode('utf-8')
img = b64decode(img[len('data:image/png;base64,'):])
# Increase n_snapshots text
n_snapshots = len(self.selected_values)
if n_snapshots == 1:
self.n_snapshots_text.value = "1 snapshot"
self.n_snapshots_text.visible = True
self.preview.visible = True
else:
self.n_snapshots_text.value = "{} snapshots".format(n_snapshots)
# Update preview thumbnails
if n_snapshots <= n_preview_windows:
self.preview.children[n_snapshots-1].value = img
self.preview.children[n_snapshots-1].visible = True
else:
for k in range(n_preview_windows-1):
self.preview.children[k].value = \
self.preview.children[k+1].value
self.preview.children[n_preview_windows-1].value = img
self.camera_wid.observe(update_preview, names='imageurl', type='change')
# Assign zoom resolution callback
def change_resolution(change):
self.camera_wid.canvas_width = int(canvas_width * change['new'])
self.resolution_text.value = "{}W x {}H".format(
self.camera_wid.canvas_width, self.camera_wid.canvas_height)
self.zoom_widget.observe(change_resolution, names='selected_values',
type='change')
# Assign resolution text callback
def set_resolution_text(_):
self.resolution_text.value = "{}W x {}H".format(
self.camera_wid.canvas_width, self.camera_wid.canvas_height)
self.camera_wid.observe(set_resolution_text, names='canvas_height',
type='change')
# Set style
self.predefined_style(style, preview_style)
[docs] def style(self, box_style=None, border_visible=False, border_colour='black',
border_style='solid', border_width=1, border_radius=0,
padding='0.2cm', margin=0, preview_box_style=None,
preview_border_visible=True, preview_border_colour='black',
preview_border_style='solid', preview_border_width=1,
preview_border_radius=1, preview_padding=0, preview_margin=0,
font_family='', font_size=None, font_style='', font_weight=''):
r"""
Function that defines the styling of the widget.
Parameters
----------
box_style : `str` or ``None`` (see below), optional
Possible widget style options::
'success', 'info', 'warning', 'danger', '', None
border_visible : `bool`, optional
Defines whether to draw the border line around the widget.
border_colour : `str`, optional
The colour of the border around the widget.
border_style : `str`, optional
The line style of the border around the widget.
border_width : `float`, optional
The line width of the border around the widget.
border_radius : `float`, optional
The radius of the border around the widget.
padding : `float`, optional
The padding around the widget.
margin : `float`, optional
The margin around the widget.
preview_box_style : `str` or ``None`` (see below), optional
Possible tab widgets style options::
'success', 'info', 'warning', 'danger', '', None
preview_border_visible : `bool`, optional
Defines whether to draw the border line around the preview.
preview_border_colour : `str`, optional
The color of the border around the preview.
preview_border_style : `str`, optional
The line style of the border around the preview.
preview_border_width : `float`, optional
The line width of the border around the preview.
preview_border_radius : `float`, optional
The radius of the corners of the box of the preview.
preview_padding : `float`, optional
The padding around the preview box.
preview_margin : `float`, optional
The margin around the preview box.
font_family : `str` (see below), optional
The font family to be used. Example options::
'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace',
'helvetica'
font_size : `int`, optional
The font size.
font_style : `str` (see below), optional
The font style. Example options::
'normal', 'italic', 'oblique'
font_weight : See Below, optional
The font weight. Example options::
'ultralight', 'light', 'normal', 'regular', 'book', 'medium',
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy',
'extra bold', 'black'
"""
format_box(self, box_style, border_visible, border_colour, border_style,
border_width, border_radius, padding, margin)
format_box(self.preview, preview_box_style, preview_border_visible,
preview_border_colour, preview_border_style,
preview_border_width, preview_border_radius, preview_padding,
preview_margin)
format_font(self, font_family, font_size, font_style, font_weight)
format_font(self.preview, font_family, font_size, font_style,
font_weight)
[docs] def predefined_style(self, style, preview_style='minimal'):
r"""
Function that sets a predefined style on the widget.
Parameters
----------
style : `str` (see below)
Style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
preview_style : `str` (see below)
Preview box style options:
============= ============================
Style Description
============= ============================
``'minimal'`` Simple black and white style
``'success'`` Green-based style
``'info'`` Blue-based style
``'warning'`` Yellow-based style
``'danger'`` Red-based style
``''`` No style
============= ============================
"""
if preview_style == 'minimal' or preview_style == '':
preview_style = ''
preview_border_visible = True
preview_border_colour = 'black'
preview_border_radius = 0
preview_padding = 0
else:
preview_style = preview_style
preview_border_visible = not style == preview_style
preview_border_colour = map_styles_to_hex_colours(preview_style)
preview_border_radius = 10
preview_padding = '0.3cm'
if style == 'minimal':
self.snapshot_but.button_style = ''
self.close_but.button_style = ''
self.zoom_widget.button_minus.button_style = ''
self.zoom_widget.button_plus.button_style = ''
self.resolution_text.color = map_styles_to_hex_colours(
'minimal', background=False)
self.n_snapshots_text.color = map_styles_to_hex_colours(
'minimal', background=False)
format_slider(self.zoom_widget.zoom_slider, slider_width='2cm',
slider_handle_colour=map_styles_to_hex_colours('minimal'),
slider_bar_colour=map_styles_to_hex_colours('minimal'),
slider_text_visible=False)
self.style(box_style='', border_visible=False,
border_colour='black', border_style='solid',
border_width=1, border_radius=0, padding=0, margin=0,
font_family='', font_size=None, font_style='',
font_weight='', preview_box_style=preview_style,
preview_border_visible=preview_border_visible,
preview_border_colour=preview_border_colour,
preview_border_style='solid', preview_border_width=1,
preview_border_radius=preview_border_radius,
preview_padding=preview_padding, preview_margin='0.1cm')
elif (style == 'info' or style == 'success' or style == 'danger' or
style == 'warning'):
self.snapshot_but.button_style = 'primary'
self.close_but.button_style = 'danger'
self.zoom_widget.button_minus.button_style = 'warning'
self.zoom_widget.button_plus.button_style = 'warning'
self.resolution_text.color = map_styles_to_hex_colours(
'warning', background=False)
self.n_snapshots_text.color = map_styles_to_hex_colours(
'info', background=False)
format_slider(self.zoom_widget.zoom_slider, slider_width='2cm',
slider_handle_colour=map_styles_to_hex_colours('warning'),
slider_bar_colour=map_styles_to_hex_colours('warning'),
slider_text_visible=False)
self.style(box_style=style, border_visible=True,
border_colour=map_styles_to_hex_colours(style),
border_style='solid', border_width=1, border_radius=10,
padding=0, margin=0, font_family='',
font_size=None, font_style='', font_weight='',
preview_box_style=preview_style,
preview_border_visible=preview_border_visible,
preview_border_colour=preview_border_colour,
preview_border_style='solid', preview_border_width=1,
preview_border_radius=preview_border_radius,
preview_padding=preview_padding, preview_margin='0.1cm')
else:
raise ValueError('style must be minimal or info or success or '
'danger or warning')