Source code for menpo.visualize.widgets.options

from collections import OrderedDict
from functools import partial
import numpy as np

import IPython.html.widgets as ipywidgets
from IPython.utils.traitlets import link

from .tools import (_format_box, _format_font, _convert_image_to_bytes,
                    IndexButtonsWidget, IndexSliderWidget, LineOptionsWidget,
                    MarkerOptionsWidget, ImageOptionsWidget,
                    NumberingOptionsWidget, FigureOptionsOneScaleWidget,
                    FigureOptionsTwoScalesWidget, LegendOptionsWidget,
                    GridOptionsWidget, ColourSelectionWidget, HOGOptionsWidget,
                    DaisyOptionsWidget, LBPOptionsWidget, IGOOptionsWidget,
                    DSIFTOptionsWidget, _map_styles_to_hex_colours)


[docs]class ChannelOptionsWidget(ipywidgets.FlexBox): r""" Creates a widget for selecting channel options when rendering an image. The widget consists of the following parts from `IPython.html.widgets`: == ============== ================================== ======================= No Object Variable (`self.`) Description == ============== ================================== ======================= 1 RadioButtons `mode_radiobuttons` The mode selector 'Single' or 'Multiple' 2 Checkbox `masked_checkbox` Controls masked mode 3 IntSlider `single_slider` Single channel selector 4 IntRangeSlider `multiple_slider` Channels range selector 5 Checkbox `rgb_checkbox` View as RGB 6 Checkbox `sum_checkbox` View sum of channels 7 Checkbox `glyph_checkbox` View glyph 8 BoundedIntText `glyph_block_size_text` Glyph block size 9 Checkbox `glyph_use_negative_checkbox` Use negative values 10 VBox `glyph_options_box` Contains 8, 9 11 VBox `glyph_box` Contains 7, 10 12 HBox `multiple_options_box` Contains 6, 11, 5 13 Box `sliders_box` Contains 3, 4 14 Box `sliders_and_multiple_options_box` Contains 13, 12 15 VBox `mode_and_masked_box` Contains 1, 2 == ============== ================================== ======================= Note that: * The selected values are stored in the ``self.selected_values`` `dict`. * To set the styling please refer to the ``style()`` and ``predefined_style()`` methods. * To update the state of the widget, please refer to the ``set_widget_state()`` method. * To update the callback function please refer to the ``replace_render_function()`` method. Parameters ---------- channel_options : `dict` The dictionary with the initial options. For example :: channel_options = {'n_channels': 10, 'image_is_masked': True, 'channels': 0, 'glyph_enabled': False, 'glyph_block_size': 3, 'glyph_use_negative': False, 'sum_enabled': False, 'masked_enabled': True} render_function : `function` or ``None``, optional The render function that is executed when a widgets' value changes. If ``None``, then nothing is assigned. style : 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 menpo.visualize.widgets import ChannelOptionsWidget >>> from IPython.display import display 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(name, value): >>> s = "Channels: {}, Masked: {}".format( >>> wid.selected_values['channels'], >>> wid.selected_values['masked_enabled']) >>> print_dynamic(s) Create the widget with some initial options and display it: >>> channel_options = {'n_channels': 30, >>> 'image_is_masked': True, >>> 'channels': [0, 10], >>> 'glyph_enabled': False, >>> 'glyph_block_size': 3, >>> 'glyph_use_negative': False, >>> 'sum_enabled': True, >>> 'masked_enabled': True} >>> wid = ChannelOptionsWidget(channel_options, >>> render_function=render_function, >>> style='warning') >>> display(wid) By playing around with the widget, printed message gets updated. Finally, let's change the widget status with a new dictionary of options: >>> new_options = {'n_channels': 10, >>> 'image_is_masked': True, >>> 'channels': [7, 8, 9], >>> 'glyph_enabled': True, >>> 'glyph_block_size': 3, >>> 'glyph_use_negative': True, >>> 'sum_enabled': False, >>> 'masked_enabled': False} >>> wid.set_widget_state(new_options, allow_callback=False) """ def __init__(self, channel_options, render_function=None, style='minimal'): # If image_is_masked is False, then masked_enabled should be False too if not channel_options['image_is_masked']: channel_options['masked_enabled'] = False # Parse channels mode_default, single_slider_default, multiple_slider_default = \ self._parse_options(channel_options['channels']) # Check sum and glyph options channel_options['sum_enabled'], channel_options['glyph_enabled'] = \ self._parse_sum_glyph(mode_default, channel_options['sum_enabled'], channel_options['glyph_enabled']) # Create widgets self.mode_radiobuttons = ipywidgets.RadioButtons( options=['Single', 'Multiple'], value=mode_default, description='Mode:', disabled=channel_options['n_channels'] == 1) self.masked_checkbox = ipywidgets.Checkbox( value=channel_options['masked_enabled'], description='Masked', visible=channel_options['image_is_masked']) self.single_slider = ipywidgets.IntSlider( min=0, max=channel_options['n_channels']-1, step=1, value=single_slider_default, description='Channel', visible=self._single_slider_visible(mode_default), disabled=channel_options['n_channels'] == 1) self.multiple_slider = ipywidgets.IntRangeSlider( min=0, max=channel_options['n_channels']-1, step=1, value=multiple_slider_default, description='Channels', visible=self._multiple_slider_visible(mode_default)) self.rgb_checkbox = ipywidgets.Checkbox( value=(channel_options['n_channels'] == 3 and channel_options['channels'] is None), description='RGB', visible=self._rgb_checkbox_visible( mode_default, channel_options['n_channels'])) self.sum_checkbox = ipywidgets.Checkbox( value=channel_options['sum_enabled'], description='Sum', visible=self._sum_checkbox_visible( mode_default, channel_options['n_channels'])) self.glyph_checkbox = ipywidgets.Checkbox( value=channel_options['glyph_enabled'], description='Glyph', visible=self._glyph_checkbox_visible( mode_default, channel_options['n_channels'])) self.glyph_block_size_text = ipywidgets.BoundedIntText( description='Block size', min=1, max=25, value=channel_options['glyph_block_size'], width='1.5cm') self.glyph_use_negative_checkbox = ipywidgets.Checkbox( description='Negative', value=channel_options['glyph_use_negative']) # Group widgets self.glyph_options_box = ipywidgets.VBox( children=[self.glyph_block_size_text, self.glyph_use_negative_checkbox], visible=self._glyph_options_visible( mode_default, channel_options['n_channels'], channel_options['glyph_enabled'])) self.glyph_box = ipywidgets.VBox(children=[self.glyph_checkbox, self.glyph_options_box], align='start') self.multiple_options_box = ipywidgets.HBox( children=[self.sum_checkbox, self.glyph_box, self.rgb_checkbox]) self.sliders_box = ipywidgets.Box( children=[self.single_slider, self.multiple_slider]) self.sliders_and_multiple_options_box = ipywidgets.Box( children=[self.sliders_box, self.multiple_options_box]) self.mode_and_masked_box = ipywidgets.VBox( children=[self.mode_radiobuttons, self.masked_checkbox]) super(ChannelOptionsWidget, self).__init__( children=[self.mode_and_masked_box, self.sliders_and_multiple_options_box]) self.align = 'start' self.orientation = 'horizontal' # Assign output self.selected_values = channel_options # Set style self.predefined_style(style) # Set functionality def mode_selection(name, value): # Temporarily remove render function self.sum_checkbox.on_trait_change(self._render_function, 'value', remove=True) self.glyph_checkbox.on_trait_change(self._render_function, 'value', remove=True) # Control visibility of widgets if value == 'Single': self.multiple_slider.visible = False self.single_slider.visible = True self.sum_checkbox.visible = False self.sum_checkbox.value = False self.glyph_checkbox.visible = False self.glyph_checkbox.value = False self.glyph_options_box.visible = False self.rgb_checkbox.visible = \ self.selected_values['n_channels'] == 3 else: self.single_slider.visible = False self.multiple_slider.visible = True self.sum_checkbox.visible = True self.sum_checkbox.value = False self.glyph_checkbox.visible = \ self.selected_values['n_channels'] > 1 self.glyph_checkbox.value = False self.glyph_options_box.visible = False self.rgb_checkbox.visible = False # Add render function if self._render_function is not None: self.sum_checkbox.on_trait_change(self._render_function, 'value') self.glyph_checkbox.on_trait_change(self._render_function, 'value') self.mode_radiobuttons.on_trait_change(mode_selection, 'value') def glyph_options_visibility(name, value): # Temporarily remove render function self.sum_checkbox.on_trait_change(self._render_function, 'value', remove=True) # Check value of sum checkbox if value: self.sum_checkbox.value = False # Add render function if self._render_function is not None: self.sum_checkbox.on_trait_change(self._render_function, 'value') # Control glyph options visibility self.glyph_options_box.visible = value self.glyph_checkbox.on_trait_change(glyph_options_visibility, 'value') self.link_rgb_checkbox_and_single_slider = link( (self.rgb_checkbox, 'value'), (self.single_slider, 'disabled')) def sum_fun(name, value): # Temporarily remove render function self.glyph_checkbox.on_trait_change(self._render_function, 'value', remove=True) # Check value of glyph checkbox if value: self.glyph_checkbox.value = False # Add render function if self._render_function is not None: self.glyph_checkbox.on_trait_change(self._render_function, 'value') self.sum_checkbox.on_trait_change(sum_fun, 'value') def get_glyph_options(name, value): self.selected_values['glyph_enabled'] = self.glyph_checkbox.value self.selected_values['sum_enabled'] = self.sum_checkbox.value self.selected_values['glyph_use_negative'] = \ self.glyph_use_negative_checkbox.value self.selected_values['glyph_block_size'] = \ self.glyph_block_size_text.value self.glyph_checkbox.on_trait_change(get_glyph_options, 'value') self.sum_checkbox.on_trait_change(get_glyph_options, 'value') self.glyph_use_negative_checkbox.on_trait_change(get_glyph_options, 'value') self.glyph_block_size_text.on_trait_change(get_glyph_options, 'value') def get_channels(name, value): if self.mode_radiobuttons.value == "Single": if self.rgb_checkbox.value: self.selected_values['channels'] = None else: self.selected_values['channels'] = self.single_slider.value else: self.selected_values['channels'] = range( self.multiple_slider.lower, self.multiple_slider.upper + 1) self.single_slider.on_trait_change(get_channels, 'value') self.multiple_slider.on_trait_change(get_channels, 'value') self.rgb_checkbox.on_trait_change(get_channels, 'value') self.mode_radiobuttons.on_trait_change(get_channels, 'value') def get_masked(name, value): self.selected_values['masked_enabled'] = value self.masked_checkbox.on_trait_change(get_masked, 'value') # Set render function self._render_function = None self.add_render_function(render_function) def _parse_options(self, channels): if isinstance(channels, list): if len(channels) == 1: mode_value = 'Single' single_slider_value = channels[0] multiple_slider_value = (0, 1) else: mode_value = 'Multiple' single_slider_value = 0 multiple_slider_value = (min(channels), max(channels)) elif channels is None: mode_value = 'Single' single_slider_value = 0 multiple_slider_value = (0, 1) else: mode_value = 'Single' single_slider_value = channels multiple_slider_value = (0, 1) return mode_value, single_slider_value, multiple_slider_value def _parse_sum_glyph(self, mode_value, sum_enabled, glyph_enabled): if mode_value == 'Single' or (sum_enabled and glyph_enabled): sum_enabled = False glyph_enabled = False return sum_enabled, glyph_enabled def _single_slider_visible(self, mode): return mode == 'Single' def _multiple_slider_visible(self, mode): return mode == 'Multiple' def _rgb_checkbox_visible(self, mode, n_channels): return mode == 'Single' and n_channels == 3 def _sum_checkbox_visible(self, mode, n_channels): return mode == 'Multiple' and n_channels > 1 def _glyph_checkbox_visible(self, mode, n_channels): return mode == 'Multiple' and n_channels > 1 def _glyph_options_visible(self, mode, n_channels, glyph_value): return mode == 'Multiple' and n_channels > 1 and glyph_value
[docs] def style(self, box_style=None, border_visible=False, border_color='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_colour=''): r""" Function that defines the styling of the widget. Parameters ---------- box_style : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ border_visible : `bool`, optional Defines whether to draw the border line around the widget. border_color : `str`, optional The color 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 corners of the box. padding : `float`, optional The padding around the widget. margin : `float`, optional The margin around the widget. font_family : 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 : {``'normal'``, ``'italic'``, ``'oblique'``}, optional The font style. 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. slider_colour : `str`, optional The colour of the sliders. """ _format_box(self, box_style, border_visible, border_color, border_style, border_width, border_radius, padding, margin) self.single_slider.width = slider_width self.multiple_slider.width = slider_width _format_font(self, font_family, font_size, font_style, font_weight) _format_font(self.mode_radiobuttons, font_family, font_size, font_style, font_weight) _format_font(self.single_slider, font_family, font_size, font_style, font_weight) _format_font(self.multiple_slider, font_family, font_size, font_style, font_weight) _format_font(self.masked_checkbox, font_family, font_size, font_style, font_weight) _format_font(self.rgb_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_use_negative_checkbox, font_family, font_size, font_style, font_weight) _format_font(self.glyph_block_size_text, font_family, font_size, font_style, font_weight) self.single_slider.slider_color = slider_colour self.single_slider.background_color = slider_colour self.multiple_slider.slider_color = slider_colour self.multiple_slider.background_color = slider_colour
[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_color='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='5cm', slider_colour='') _format_box(self.glyph_options_box, box_style='', border_visible=False, border_color='', 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_color=_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='5cm', slider_colour=_map_styles_to_hex_colours(style)) _format_box(self.glyph_options_box, box_style=style, border_visible=True, border_color=_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 add_render_function(self, render_function): r""" Method that adds a `render_function()` to the widget. The signature of the given function is also stored in `self._render_function`. Parameters ---------- render_function : `function` or ``None``, optional The render function that behaves as a callback. If ``None``, then nothing is added. """ self._render_function = render_function if self._render_function is not None: self.mode_radiobuttons.on_trait_change(self._render_function, 'value') self.masked_checkbox.on_trait_change(self._render_function, 'value') self.single_slider.on_trait_change(self._render_function, 'value') self.multiple_slider.on_trait_change(self._render_function, 'value') self.rgb_checkbox.on_trait_change(self._render_function, 'value') self.sum_checkbox.on_trait_change(self._render_function, 'value') self.glyph_checkbox.on_trait_change(self._render_function, 'value') self.glyph_block_size_text.on_trait_change(self._render_function, 'value') self.glyph_use_negative_checkbox.on_trait_change( self._render_function, 'value')
[docs] def remove_render_function(self): r""" Method that removes the current `self._render_function()` from the widget and sets ``self._render_function = None``. """ self.mode_radiobuttons.on_trait_change(self._render_function, 'value', remove=True) self.masked_checkbox.on_trait_change(self._render_function, 'value', remove=True) self.single_slider.on_trait_change(self._render_function, 'value', remove=True) self.multiple_slider.on_trait_change(self._render_function, 'value', remove=True) self.rgb_checkbox.on_trait_change(self._render_function, 'value', remove=True) self.sum_checkbox.on_trait_change(self._render_function, 'value', remove=True) self.glyph_checkbox.on_trait_change(self._render_function, 'value', remove=True) self.glyph_block_size_text.on_trait_change(self._render_function, 'value', remove=True) self.glyph_use_negative_checkbox.on_trait_change(self._render_function, 'value', remove=True) self._render_function = None
[docs] def replace_render_function(self, render_function): r""" Method that replaces the current `self._render_function()` of the widget with the given `render_function()`. Parameters ---------- render_function : `function` or ``None``, optional The render function that behaves as a callback. If ``None``, then nothing is happening. """ # remove old function self.remove_render_function() # add new function self.add_render_function(render_function)
[docs] def set_widget_state(self, channel_options, allow_callback=True): r""" Method that updates the state of the widget with a new set of values. Parameters ---------- channel_options : `dict` The dictionary with the new options to be used. For example :: channel_options = {'n_channels': 10, 'image_is_masked': True, 'channels': 0, 'glyph_enabled': False, 'glyph_block_size': 3, 'glyph_use_negative': False, 'sum_enabled': False, 'masked_enabled': True} allow_callback : `bool`, optional If ``True``, it allows triggering of any callback functions. """ # temporarily remove render callback render_function = self._render_function self.remove_render_function() # If image_is_masked is False, then masked_enabled should be False too if not channel_options['image_is_masked']: channel_options['masked_enabled'] = False # Parse channels mode_default, single_slider_default, multiple_slider_default = \ self._parse_options(channel_options['channels']) # Check sum and glyph options channel_options['sum_enabled'], channel_options['glyph_enabled'] = \ self._parse_sum_glyph(mode_default, channel_options['sum_enabled'], channel_options['glyph_enabled']) # Update widgets' state self.mode_radiobuttons.value = mode_default self.mode_radiobuttons.disabled = channel_options['n_channels'] == 1 self.masked_checkbox.value = channel_options['masked_enabled'] self.masked_checkbox.visible = channel_options['image_is_masked'] self.single_slider.max = channel_options['n_channels'] - 1 self.single_slider.value = single_slider_default self.single_slider.visible = self._single_slider_visible(mode_default) self.single_slider.disabled = channel_options['n_channels'] == 1 self.multiple_slider.max = channel_options['n_channels'] - 1 self.multiple_slider.value = multiple_slider_default self.multiple_slider.visible = self._multiple_slider_visible( mode_default) self.rgb_checkbox.value = (channel_options['n_channels'] == 3 and channel_options['channels'] is None) self.rgb_checkbox.visible = self._rgb_checkbox_visible( mode_default, channel_options['n_channels']) self.sum_checkbox.value = channel_options['sum_enabled'] self.sum_checkbox.visible = self._sum_checkbox_visible( mode_default, channel_options['n_channels']) self.glyph_checkbox.value = channel_options['glyph_enabled'] self.glyph_checkbox.visible = self._glyph_checkbox_visible( mode_default, channel_options['n_channels']) self.glyph_block_size_text.value = channel_options['glyph_block_size'] self.glyph_use_negative_checkbox.value = \ channel_options['glyph_use_negative'] self.glyph_options_box.visible = self._glyph_options_visible( mode_default, channel_options['n_channels'], channel_options['glyph_enabled']) # Re-assign render callback self.add_render_function(render_function) # Assign new options dict to selected_values self.selected_values = channel_options # trigger render function if allowed if allow_callback: self._render_function('', True)
[docs]class LandmarkOptionsWidget(ipywidgets.FlexBox): r""" Creates a widget for animating through a list of objects. The widget consists of the following parts from `IPython.html.widgets`: == ============= ================================ ========================== No Object Variable (`self.`) Description == ============= ================================ ========================== 1 Latex `no_landmarks_msg` Message in case there are no landmarks available. 2 Checkbox `render_landmarks_checkbox` Render landmarks 3 Box `landmarks_checkbox_and_msg_box` Contains 2, 1 4 Dropdown `group_dropdown` Landmark group selector 5 ToggleButtons `labels_toggles` `list` of `lists` with the labels per group 6 Latex `labels_text` Labels title text 7 HBox `labels_box` Contains all 5 8 HBox `labels_and_text_box` Contains 6, 7 9 VBox `group_and_labels_and_text_box` Contains 4, 8 == ============= ================================ ========================== Note that: * The selected values are stored in the ``self.selected_values`` `dict`. * To set the styling please refer to the ``style()`` and ``predefined_style()`` methods. * To update the state of the widget, please refer to the ``set_widget_state()`` method. * To update the callback function please refer to the ``replace_render_function()`` and ``replace_update_function()`` methods. Parameters ---------- landmark_options : `dict` The dictionary with the initial options. For example :: landmark_options = {'has_landmarks': True, 'render_landmarks': True, 'group_keys': ['PTS', 'ibug_face_68'], 'labels_keys': [['all'], ['jaw', 'eye']], 'group': 'PTS', 'with_labels': ['all']} render_function : `function` or ``None``, optional The render function that is executed when a widgets' value changes. If ``None``, then nothing is assigned. update_function : `function` or ``None``, optional The update function that is executed when the index value changes. If ``None``, then nothing is assigned. style : `str` (see below) 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 from menpo.visualize.widgets import LandmarkOptionsWidget >>> from IPython.display import display 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(name, value): >>> 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: >>> landmark_options = {'has_landmarks': True, >>> 'render_landmarks': True, >>> 'group_keys': ['PTS', 'ibug_face_68'], >>> 'labels_keys': [['all'], ['jaw', 'eye', 'mouth']], >>> 'group': 'ibug_face_68', >>> 'with_labels': ['eye', 'jaw', 'mouth']} >>> wid = LandmarkOptionsWidget(landmark_options, >>> render_function=render_function, >>> style='danger') >>> display(wid) By playing around with the widget, the printed message gets updated. Finally, let's change the widget status with a new dictionary of options: >>> new_options = {'has_landmarks': True, >>> 'render_landmarks': True, >>> 'group_keys': ['new_group'], >>> 'labels_keys': [['1', '2', '3']], >>> 'group': 'new_group', >>> 'with_labels': None} >>> wid.set_widget_state(new_options, allow_callback=False) """ def __init__(self, landmark_options, render_function=None, update_function=None, style='minimal'): # Check given options landmark_options = self._parse_landmark_options_dict(landmark_options) # Temporarily store visible and disabled values tmp_visible = self._options_visible(landmark_options['has_landmarks']) tmp_disabled = not landmark_options['render_landmarks'] tmp_slider_visible = self._group_slider_visible( landmark_options['group_keys']) # Get selected group value index group_idx = landmark_options['group_keys'].index( landmark_options['group']) # Create widgets # Render landmarks checkbox and no landmarks message self.no_landmarks_msg = ipywidgets.Latex( value='No landmarks available.', visible=self._no_landmarks_msg_visible( landmark_options['has_landmarks'])) self.render_landmarks_checkbox = ipywidgets.Checkbox( description='Render landmarks', value=landmark_options['render_landmarks'], visible=tmp_visible) self.landmarks_checkbox_and_msg_box = ipywidgets.Box( children=[self.render_landmarks_checkbox, self.no_landmarks_msg]) # Create group description, dropdown and slider self.group_description = ipywidgets.Latex(value='Group', margin='0.1cm') dropdown_dict = OrderedDict() for gn, gk in enumerate(landmark_options['group_keys']): dropdown_dict[gk] = gn self.group_slider = ipywidgets.IntSlider( min=0, max=len(landmark_options['group_keys']) - 1, margin='0.1cm', font_size=0, value=group_idx, disabled=tmp_disabled, width='3cm', visible=tmp_slider_visible) self.group_dropdown = ipywidgets.Dropdown( options=dropdown_dict, description='', disabled=tmp_disabled, value=group_idx, margin='0.1cm') self.group_selection_box = ipywidgets.HBox( children=[self.group_description, self.group_slider, self.group_dropdown], visible=tmp_visible, 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 = [ [ipywidgets.ToggleButton(description=k, value=True, visible=tmp_visible, disabled=tmp_disabled) for k in s_keys] for s_keys in landmark_options['labels_keys']] self.labels_text = ipywidgets.Latex(value='Labels', visible=tmp_visible) self.labels_box = ipywidgets.HBox( children=self.labels_toggles[group_idx]) self.labels_and_text_box = ipywidgets.HBox(children=[self.labels_text, self.labels_box], align='center') self._set_labels_toggles_values(landmark_options['with_labels']) self.group_and_labels_and_text_box = ipywidgets.VBox( children=[self.group_selection_box, self.labels_and_text_box]) super(LandmarkOptionsWidget, self).__init__( children=[self.landmarks_checkbox_and_msg_box, self.group_and_labels_and_text_box]) self.align = 'start' self.labels_box.padding = '0.3cm' # Assign output self.selected_values = landmark_options # Set style self.predefined_style(style) # Set functionality def render_landmarks_fun(name, value): # save render_landmarks value self.selected_values['render_landmarks'] = value # disable group drop down menu self.group_dropdown.disabled = not value self.group_slider.disabled = not value # set disability of all labels toggles for s_keys in self.labels_toggles: for k in s_keys: k.disabled = not value # if all currently selected labels toggles are False, set them all # to True self._all_labels_false_1() self.render_landmarks_checkbox.on_trait_change(render_landmarks_fun, 'value') def group_fun(name, value): # save group value self.selected_values['group'] = \ self.selected_values['group_keys'][value] # assign the correct children to the labels toggles self.labels_box.children = self.labels_toggles[value] # save with_labels value self._save_with_labels() self.group_dropdown.on_trait_change(group_fun, 'value') def labels_fun(name, value): # if all labels toggles are False, set render landmarks checkbox to # False self._all_labels_false_2() # save with_labels value self._save_with_labels() # assign labels_fun to all labels toggles (even hidden ones) self._add_function_to_labels_toggles(labels_fun) # Store functions self._render_landmarks_fun = render_landmarks_fun self._group_fun = group_fun self._labels_fun = labels_fun # Set render function self._update_function = None self.add_update_function(update_function) self._render_function = None self.add_render_function(render_function) def _parse_landmark_options_dict(self, landmark_options): if (len(landmark_options['group_keys']) == 1 and landmark_options['group_keys'][0] == ' '): landmark_options['has_landmarks'] = False if not landmark_options['has_landmarks']: landmark_options['render_landmarks'] = False landmark_options['group_keys'] = [' '] landmark_options['group'] = ' ' landmark_options['labels_keys'] = [[' ']] landmark_options['with_labels'] = [' '] else: if (landmark_options['with_labels'] is not None and len(landmark_options['with_labels']) == 0): landmark_options['with_labels'] = None return landmark_options def _no_landmarks_msg_visible(self, has_landmarks): return not has_landmarks def _options_visible(self, has_landmarks): return has_landmarks def _group_slider_visible(self, group_keys): return len(group_keys) > 1 def _all_labels_false_1(self): r""" If all currently selected labels toggles are ``False``, set them all to ``True``. """ # get all values of current labels toggles all_values = [ww.value for ww in self.labels_box.children] # if all of them are False if all(item is False for item in all_values): for ww in self.labels_box.children: # temporarily remove render function ww.on_trait_change(self._render_function, 'value', remove=True) # set value ww.value = True # re-add render function ww.on_trait_change(self._render_function, 'value') def _all_labels_false_2(self): r""" If all currently selected labels toggles are ``False``, set `render_landmarks_checkbox` to ``False``. """ # get all values of current labels toggles all_values = [ww.value for ww in self.labels_box.children] # if all of them are False if all(item is False for item in all_values): # temporarily remove render function self.render_landmarks_checkbox.on_trait_change( self._render_function, 'value', remove=True) # set value self.render_landmarks_checkbox.value = False # re-add render function self.render_landmarks_checkbox.on_trait_change( self._render_function, 'value') def _save_with_labels(self): r""" Saves `with_labels` value to the `selected_values` dictionary. """ self.selected_values['with_labels'] = [] for ww in self.labels_box.children: if ww.value: self.selected_values['with_labels'].append( str(ww.description)) def _set_labels_toggles_values(self, with_labels): for w in self.labels_box.children: if w.description not in with_labels: w.value = False else: w.value = True def _add_function_to_labels_toggles(self, fun): r""" Adds a function callback to all labels toggles. """ for s_group in self.labels_toggles: for w in s_group: w.on_trait_change(fun, 'value') def _remove_function_from_labels_toggles(self, fun): r""" Removes a function callback from all labels toggles. """ for s_group in self.labels_toggles: for w in s_group: w.on_trait_change(fun, 'value', remove=True)
[docs] def style(self, box_style=None, border_visible=False, border_color='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 : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ border_visible : `bool`, optional Defines whether to draw the border line around the widget. border_color : `str`, optional The color 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 corners of the box. padding : `float`, optional The padding around the widget. margin : `float`, optional The margin around the widget. font_family : 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 : {``'normal'``, ``'italic'``, ``'oblique'``}, optional The font style. 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 : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'primary' Blue-based style 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ """ _format_box(self, box_style, border_visible, border_color, 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)
[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_color='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_color=_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 add_render_function(self, render_function): r""" Method that adds a `render_function()` to the widget. The signature of the given function is also stored in `self._render_function`. Parameters ---------- render_function : `function` or ``None``, optional The render function that behaves as a callback. If ``None``, then nothing is added. """ self._render_function = render_function if self._render_function is not None: self.render_landmarks_checkbox.on_trait_change( self._render_function, 'value') self.group_dropdown.on_trait_change(self._render_function, 'value') self._add_function_to_labels_toggles(self._render_function)
[docs] def remove_render_function(self): r""" Method that removes the current `self._render_function()` from the widget and sets ``self._render_function = None``. """ self.render_landmarks_checkbox.on_trait_change(self._render_function, 'value', remove=True) self.group_dropdown.on_trait_change(self._render_function, 'value', remove=True) self._remove_function_from_labels_toggles(self._render_function) self._render_function = None
[docs] def replace_render_function(self, render_function): r""" Method that replaces the current `self._render_function()` of the widget with the given `render_function()`. Parameters ---------- render_function : `function` or ``None``, optional The render function that behaves as a callback. If ``None``, then nothing is happening. """ # remove old function self.remove_render_function() # add new function self.add_render_function(render_function)
[docs] def add_update_function(self, update_function): r""" Method that adds an `update_function()` to the widget. The signature of the given function is also stored in `self._update_function`. Parameters ---------- update_function : `function` or ``None``, optional The update function that behaves as a callback. If ``None``, then nothing is added. """ self._update_function = update_function if self._update_function is not None: self.render_landmarks_checkbox.on_trait_change( self._update_function, 'value') self.group_dropdown.on_trait_change(self._update_function, 'value') self._add_function_to_labels_toggles(self._update_function)
[docs] def remove_update_function(self): r""" Method that removes the current `self._update_function()` from the widget and sets ``self._update_function = None``. """ self.render_landmarks_checkbox.on_trait_change(self._update_function, 'value', remove=True) self.group_dropdown.on_trait_change(self._update_function, 'value', remove=True) self._remove_function_from_labels_toggles(self._update_function) self._update_function = None
[docs] def replace_update_function(self, update_function): r""" Method that replaces the current `self._update_function()` of the widget with the given `update_function()`. Parameters ---------- update_function : `function` or ``None``, optional The update function that behaves as a callback. If ``None``, then nothing is happening. """ # remove old function self.remove_update_function() # add new function self.add_update_function(update_function)
def _compare_groups_and_labels(self, groups, labels): r""" Function that compares the provided landmarks groups and labels with `self.selected_values['group_keys']` and `self.selected_values['labels_keys']`. Parameters ---------- groups : `list` of `str` The new `list` of landmark groups. labels : `list` of `list` of `str` The new `list` of `list`s of each landmark group's labels. Returns ------- _compare_groups_and_labels : `bool` ``True`` if the groups and labels are identical with the ones stored in `self.selected_values['group_keys']` and `self.selected_values['labels_keys']`. """ # function that compares two lists without taking into account the order def comp_lists(l1, l2): len_match = len(l1) == len(l2) return len_match and np.all([g1 == g2 for g1, g2 in zip(l1, l2)]) # comparison of the given groups groups_same = comp_lists(groups, self.selected_values['group_keys']) # if groups are the same, then compare the labels if groups_same: len_match = len(labels) == len(self.selected_values['labels_keys']) tmp = [comp_lists(g1, g2) for g1, g2 in zip(labels, self.selected_values['labels_keys'])] return len_match and np.all(tmp) else: return False
[docs] def set_widget_state(self, landmark_options, allow_callback=True): r""" Method that updates the state of the widget with a new set of values. Parameters ---------- landmark_options : `dict` The dictionary with the new options to be used. For example :: landmark_options = {'has_landmarks': True, 'render_landmarks': True, 'group_keys': ['PTS', 'ibug_face_68'], 'labels_keys': [['all'], ['jaw', 'eye']], 'group': 'PTS', 'with_labels': ['all']} allow_callback : `bool`, optional If ``True``, it allows triggering of any callback functions. """ # temporarily remove render callback render_function = self._render_function self.remove_render_function() update_function = self._update_function self.remove_update_function() # temporarily remove the rest of the callbacks self.render_landmarks_checkbox.on_trait_change( self._render_landmarks_fun, 'value', remove=True) self.group_dropdown.on_trait_change(self._group_fun, 'value', remove=True) self._remove_function_from_labels_toggles(self._labels_fun) # Check given options landmark_options = self._parse_landmark_options_dict(landmark_options) # Temporarily store visible and disabled values tmp_visible = self._options_visible(landmark_options['has_landmarks']) tmp_disabled = not landmark_options['render_landmarks'] tmp_slider_visible = self._group_slider_visible( landmark_options['group_keys']) # Update widgets self.no_landmarks_msg.visible = self._no_landmarks_msg_visible( landmark_options['has_landmarks']) self.render_landmarks_checkbox.value = \ landmark_options['render_landmarks'] self.render_landmarks_checkbox.visible = tmp_visible self.labels_text.visible = tmp_visible # Check if group_keys and labels_keys are the same with the existing # ones if not self._compare_groups_and_labels(landmark_options['group_keys'], landmark_options['labels_keys']): if landmark_options['group'] is None: landmark_options['group'] = landmark_options['group_keys'][0] group_idx = landmark_options['group_keys'].index( landmark_options['group']) dropdown_dict = OrderedDict() for gn, gk in enumerate(landmark_options['group_keys']): dropdown_dict[gk] = gn self.group_selection_box.visible = tmp_visible self.group_dropdown.options = dropdown_dict self.group_dropdown.disabled = tmp_disabled self.group_slider.max = len(landmark_options['group_keys']) - 1 self.group_slider.disabled = tmp_disabled self.group_slider.visible = tmp_slider_visible self.group_dropdown.value = group_idx self.labels_toggles = [ [ipywidgets.ToggleButton(description=k, disabled=tmp_disabled, visible=tmp_visible, value=True) for k in s_keys] for s_keys in landmark_options['labels_keys']] self.labels_box.children = self.labels_toggles[group_idx] if landmark_options['with_labels'] is None: landmark_options['with_labels'] = \ landmark_options['labels_keys'][group_idx] self._set_labels_toggles_values(landmark_options['with_labels']) else: self.group_selection_box.visible = tmp_visible self.group_slider.disabled = tmp_disabled self.group_dropdown.disabled = tmp_disabled if landmark_options['group'] is None: landmark_options['group'] = self.selected_values['group'] group_idx = landmark_options['group_keys'].index( landmark_options['group']) self.group_dropdown.value = group_idx if landmark_options['with_labels'] is None: landmark_options['with_labels'] = \ self.selected_values['with_labels'] self._set_labels_toggles_values(landmark_options['with_labels']) for w in self.labels_box.children: w.disabled = tmp_disabled w.visible = tmp_visible # Re-assign the rest of the callbacks self.render_landmarks_checkbox.on_trait_change( self._render_landmarks_fun, 'value') self.group_dropdown.on_trait_change(self._group_fun, 'value') self._add_function_to_labels_toggles(self._labels_fun) # Assign new options dict to selected_values self.selected_values = landmark_options # Re-assign render callback self.add_update_function(update_function) self.add_render_function(render_function) # trigger render function if allowed if allow_callback: self._update_function('', True) self._render_function('', True)
[docs]class TextPrintWidget(ipywidgets.FlexBox): r""" Creates a widget for printing text. Specifically, it consists of a `list` of `IPython.html.widgets.Latex` objects, i.e. one per text line. Note that: * To set the styling please refer to the ``style()`` and ``predefined_style()`` methods. * To update the state of the widget, please refer to the ``set_widget_state()`` method. Parameters ---------- n_lines : `int` The number of lines of the text to be printed. text_per_line : `list` of length `n_lines` The text to be printed per line. style : 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 menpo.visualize.widgets import TextPrintWidget >>> from IPython.display import display Create the widget with some initial options and display it: >>> n_lines = 3 >>> text_per_line = ['> The', '> Menpo', '> Team'] >>> wid = TextPrintWidget(n_lines, text_per_line, style='success') >>> display(wid) The style of the widget can be changed as: >>> wid.predefined_style('danger') Update the widget state as: >>> wid.set_widget_state(5, ['M', 'E', 'N', 'P', 'O']) """ def __init__(self, n_lines, text_per_line, style='minimal'): 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_color='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 : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ border_visible : `bool`, optional Defines whether to draw the border line around the widget. border_color : `str`, optional The color 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 corners of the box. padding : `float`, optional The padding around the widget. margin : `float`, optional The margin around the widget. font_family : 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 : {``'normal'``, ``'italic'``, ``'oblique'``}, optional The font style. 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_color, 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_color='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_color=_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, n_lines, text_per_line): r""" Method that updates the state of the widget with a new set of values. Parameters ---------- n_lines : `int` The number of lines of the text to be printed. text_per_line : `list` of length `n_lines` The text to be printed per line. """ # Check if n_lines has changed 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 AnimationOptionsWidget(ipywidgets.FlexBox): r""" Creates a widget for animating through a list of objects. The widget consists of the following parts from `IPython.html.widgets` and `menpo.visualize.widgets.tools`: == ================== ===================== ==================== No Object Variable (`self.`) Description == ================== ===================== ==================== 1 ToggleButton `play_stop_toggle` The play/stop button 2 ToggleButton `play_options_toggle` Button that toggles the options menu 3 Checkbox `loop_checkbox` Repeat mode 4 FloatText `interval_text` Interval (secs) 5 VBox `loop_interval_box` Contains 3, 4 6 VBox `play_options_box` Contains 2, 5 7 HBox `animation_box` Contains 1, 6 8 IndexButtonsWidget `index_wid` The index selector IndexSliderWidget widget == ================== ===================== ==================== Note that: * The selected values are stored in the ``self.selected_values`` `dict`. * To set the styling please refer to the ``style()`` and ``predefined_style()`` methods. * To update the state of the widget, please refer to the ``set_widget_state()`` method. * To update the callback function please refer to the ``replace_render_function()`` and ``replace_update_function()`` methods. Parameters ---------- index : `dict` The dictionary with the initial options. For example :: index = {'min': 0, 'max': 100, 'step': 1, 'index': 10} render_function : `function` or ``None``, optional The render function that is executed when a widgets' value changes. If ``None``, then nothing is assigned. update_function : `function` or ``None``, optional The update function that is executed when the index value changes. If ``None``, then nothing is assigned. index_style : {``'buttons'``, ``'slider'``}, optional If ``'buttons'``, then `IndexButtonsWidget()` class is called. If ``'slider'``, then 'IndexSliderWidget()' class is called. interval : `float`, optional The interval between the animation progress. description : `str`, optional The title of the widget. minus_description : `str`, optional The title of the button that decreases the index. plus_description : `str`, optional The title of the button that increases the index. 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. text_editable : `bool`, optional Flag that determines whether the index text will be editable. style : 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 animation widget and then update its state. Firstly, we need to import it: >>> from menpo.visualize.widgets import AnimationOptionsWidget >>> from IPython.display import display 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(name, value): >>> s = "Selected index: {}".format(wid.selected_values['index']) >>> 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') >>> display(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, update_function=None, index_style='buttons', interval=0.5, description='Index: ', minus_description='-', plus_description='+', loop_enabled=True, text_editable=True, style='minimal'): 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) elif index_style == 'buttons': self.index_wid = IndexButtonsWidget( index, description=description, minus_description=minus_description, plus_description=plus_description, loop_enabled=loop_enabled, text_editable=text_editable) 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.15cm') # Create other widgets self.play_stop_toggle = ipywidgets.ToggleButton(description='Play >', value=False) self._toggle_play_style = 'success' self._toggle_stop_style = 'danger' if style == 'minimal': self._toggle_play_style = '' self._toggle_stop_style = '' self.play_options_toggle = ipywidgets.ToggleButton( description='Options', value=False, button_style=self._toggle_play_style) self.loop_checkbox = ipywidgets.Checkbox(description='Loop', value=loop_enabled) self.interval_text = ipywidgets.FloatText(description='Interval (sec)', value=interval) self.loop_interval_box = ipywidgets.VBox( children=[self.interval_text, self.loop_checkbox], visible=False, margin='0.1cm', padding='0.1cm', border_color='black', border_style='solid', border_width=1) self.play_options_box = ipywidgets.VBox( children=[self.play_options_toggle, self.loop_interval_box]) self.animation_box = ipywidgets.HBox( children=[self.play_stop_toggle, self.play_options_box], margin='0.15cm', padding=0) super(AnimationOptionsWidget, self).__init__( children=[self.index_wid, self.animation_box]) self.align = 'start' self.orientation = 'horizontal' # Assign output self.selected_values = index self.index_style = index_style # Set style self.predefined_style(style) # Set functionality def play_stop_pressed(name, value): 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 description to Stop self.play_stop_toggle.description = 'Stop -' # Make sure that play options are off self.play_options_toggle.value = False else: # Animation was playing, so Stop was pressed. # Change the button style self.play_stop_toggle.button_style = self._toggle_play_style # Change the description to Play self.play_stop_toggle.description = 'Play >' self.play_options_toggle.disabled = value self.play_stop_toggle.on_trait_change(play_stop_pressed, 'value') def play_options_visibility(name, value): self.loop_interval_box.visible = value self.play_options_toggle.on_trait_change(play_options_visibility, 'value') def animate(name, value): if self.loop_checkbox.value: # loop is enabled i = self.selected_values['index'] if i < self.selected_values['max']: i += self.selected_values['step'] else: i = self.selected_values['min'] while (i <= self.selected_values['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 i < self.selected_values['max']: i += self.selected_values['step'] else: i = self.selected_values['min'] # wait sleep(self.interval_text.value) else: # loop is disabled i = self.selected_values['index'] i += self.selected_values['step'] while (i <= self.selected_values['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 i += self.selected_values['step'] # wait sleep(self.interval_text.value) if i > self.selected_values['max']: self.play_stop_toggle.value = False self.play_stop_toggle.on_trait_change(animate, 'value') # Set render and update functions self._update_function = None self.add_update_function(update_function) self._render_function = None self.add_render_function(render_function)
[docs] def style(self, box_style=None, border_visible=False, border_color='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 : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ border_visible : `bool`, optional Defines whether to draw the border line around the widget. border_color : `str`, optional The color 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 corners of the box. padding : `float`, optional The padding around the widget. margin : `float`, optional The margin around the widget. font_family : 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 : {``'normal'``, ``'italic'``, ``'oblique'``}, optional The font style. 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_color, border_style, border_width, border_radius, padding, margin) _format_font(self, font_family, font_size, font_style, font_weight) _format_font(self.play_stop_toggle, font_family, font_size, font_style, font_weight) _format_font(self.play_options_toggle, font_family, font_size, font_style, font_weight) _format_font(self.loop_checkbox, font_family, font_size, font_style, font_weight) _format_font(self.interval_text, 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.15cm', 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.15cm', 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.play_stop_toggle.font_weight = 'normal' self.play_options_toggle.button_style = '' _format_box(self.loop_interval_box, '', False, 'black', 'solid', 1, 10, '0.1cm', '0.1cm') 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 = '' elif self.index_style == 'slider': self.index_wid.slider.slider_color = '' self.index_wid.slider.background_color = '' 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.play_stop_toggle.font_weight = 'bold' self.play_options_toggle.button_style = 'info' _format_box(self.loop_interval_box, 'info', True, _map_styles_to_hex_colours('info'), 'solid', 1, 10, '0.1cm', '0.1cm') 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 add_render_function(self, render_function): r""" Method that adds a `render_function()` to the widget. The signature of the given function is also stored in `self._render_function`. Parameters ---------- render_function : `function` or ``None``, optional The render function that behaves as a callback. If ``None``, then nothing is added. """ self._render_function = render_function if self._render_function is not None: self.index_wid.add_render_function(self._render_function)
[docs] def remove_render_function(self): r""" Method that removes the current `self._render_function()` from the widget and sets ``self._render_function = None``. """ self.index_wid.remove_render_function() self._render_function = None
[docs] def replace_render_function(self, render_function): r""" Method that replaces the current `self._render_function()` of the widget with the given `render_function()`. Parameters ---------- render_function : `function` or ``None``, optional The render function that behaves as a callback. If ``None``, then nothing is happening. """ # remove old function self.remove_render_function() # add new function self.add_render_function(render_function)
[docs] def add_update_function(self, update_function): r""" Method that adds an `update_function()` to the widget. The signature of the given function is also stored in `self._update_function`. Parameters ---------- update_function : `function` or ``None``, optional The update function that behaves as a callback. If ``None``, then nothing is added. """ self._update_function = update_function if self._update_function is not None: self.index_wid.add_update_function(self._update_function)
[docs] def remove_update_function(self): r""" Method that removes the current `self._update_function()` from the widget and sets ``self._update_function = None``. """ self.index_wid.remove_update_function() self._update_function = None
[docs] def replace_update_function(self, update_function): r""" Method that replaces the current `self._update_function()` of the widget with the given `update_function()`. Parameters ---------- update_function : `function` or ``None``, optional The update function that behaves as a callback. If ``None``, then nothing is happening. """ # remove old function self.remove_update_function() # add new function self.add_update_function(update_function)
[docs] def set_widget_state(self, index, allow_callback=True): r""" Method that updates the state of the widget with a new set of values. Parameters ---------- index : `dict` The dictionary with the new options to be used. For example :: index = {'min': 0, 'max': 100, 'step': 1, 'index': 10} allow_callback : `bool`, optional If ``True``, it allows triggering of any callback functions. """ if self.play_stop_toggle.value: self.play_stop_toggle.value = False if self.index_style == 'slider': self.index_wid.set_widget_state(index, allow_callback=allow_callback) else: self.index_wid.set_widget_state( index, loop_enabled=self.index_wid.loop_enabled, text_editable=self.index_wid.text_editable, allow_callback=allow_callback) self.selected_values = index
[docs]class RendererOptionsWidget(ipywidgets.FlexBox): r""" Creates a widget for selecting rendering options. The widget consists of the following parts from `IPython.html.widgets` and `menpo.visualize.widgets.tools`: == ====================== =========================== =================== No Object Variable (`self.`) Description == ====================== =========================== =================== 1 Dropdown `object_selection_dropdown` The object selector 2 LineOptionsWidget `options_widgets` `list` with the MarkerOptionsWidget various rendering ImageOptionsWidget sub-options widgets NumberingOptionsWidget FigureOptionsWidget LegendOptionsWidget GridOptionsWidget 3 Tab `suboptions_tab` Contains all 2 == ====================== =========================== =================== Note that: * The selected values are stored in the ``self.selected_values`` `dict`. * To set the styling please refer to the ``style()`` and ``predefined_style()`` methods. * To update the state of the widget, please refer to the ``set_widget_state()`` method. * To update the callback function please refer to the ``replace_render_function()`` methods. Parameters ---------- renderer_options : `list` of `dict` The initial rendering options per object. The `list` must have length `n_objects` and contain a `dict` of rendering options per object. For example, in case we had two objects to render :: lines_options = {'render_lines': True, 'line_width': 1, 'line_colour': ['b', 'r'], 'line_style': '-'} markers_options = {'render_markers': True, 'marker_size': 20, 'marker_face_colour': ['w', 'w'], 'marker_edge_colour': ['b', 'r'], 'marker_style': 'o', 'marker_edge_width': 1} numbering_options = {'render_numbering': True, 'numbers_font_name': 'serif', 'numbers_font_size': 10, 'numbers_font_style': 'normal', 'numbers_font_weight': 'normal', 'numbers_font_colour': ['k'], 'numbers_horizontal_align': 'center', 'numbers_vertical_align': 'bottom'} legend_options = {'render_legend': True, 'legend_title': '', 'legend_font_name': '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': True} figure_options = {'x_scale': 1., 'y_scale': 1., 'render_axes': True, 'axes_font_name': 'serif', 'axes_font_size': 10, 'axes_font_style': 'normal', 'axes_font_weight': 'normal', 'axes_x_limits': None, 'axes_y_limits': None} grid_options = {'render_grid': True, 'grid_line_style': '--', 'grid_line_width': 0.5} image_options = {'alpha': 1., 'interpolation': 'bilinear', 'cmap_name': 'gray'} rendering_dict = {'lines': lines_options, 'markers': markers_options, 'numbering': numbering_options, 'legend': legend_options, 'figure': figure_options, 'grid': grid_options, 'image': image_options} renderer_options = [rendering_dict, rendering_dict] options_tabs : `list` of `str` `List` that defines the ordering of the options tabs. Possible values are ============= =============================== Value Returned class ============= =============================== 'lines' `LineOptionsWidget` 'markers' `MarkerOptionsWidget` 'numbering' `NumberingOptionsWidget` 'figure_one' `FigureOptionsOneScaleWidget` 'figure_two' `FigureOptionsTwoScalesWidget` 'legend' `LegendOptionsWidget` 'grid' `GridOptionsWidget` 'image' `ImageOptionsWidget` ============= =============================== objects_names : `list` of `str` or ``None``, optional A `list` with the names of the objects that will be used in the selection dropdown menu. If ``None``, then the names will have the format ``%d``. labels_per_object : `list` of `list` or ``None``, optional A `list` that contains a `list` of labels for each object. Those `labels` are employed by the `ColourSelectionWidget`. An example for which this option is useful is in the case we wish to create rendering options for multiple :map:`LandmarkGroup` objects and each one of them has a different set of `labels`. If ``None``, then `labels_per_object` is a `list` of length `n_objects` with ``None``. selected_object : `int`, optional The object for which to show the rendering options in the beginning, when the widget is created. object_selection_dropdown_visible : `bool`, optional Controls the visibility of the object selection dropdown (`self.object_selection_dropdown`). render_function : `function` or ``None``, optional The render function that is executed when a widgets' value changes. If ``None``, then nothing is assigned. style : 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 : 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 menpo.visualize.widgets import RendererOptionsWidget >>> from IPython.display import display Let's set some initial options: >>> options_tabs = ['markers', 'lines', 'grid'] >>> objects_names = ['james', 'patrick'] >>> labels_per_object = [['jaw', 'eyes'], None] >>> selected_object = 1 >>> object_selection_dropdown_visible = True Now let's define a render function that will get called on every widget change and will dynamically print the selected marker face colour for both objects: >>> from menpo.visualize import print_dynamic >>> def render_function(name, value): >>> s = "{}: {}, {}: {}".format( >>> wid.objects_names[0], >>> wid.selected_values[0]['markers']['marker_face_colour'], >>> wid.objects_names[1], >>> wid.selected_values[1]['markers']['marker_face_colour']) >>> print_dynamic(s) Create the widget with some initial options and display it: >>> # 1st dictionary >>> markers_options = {'render_markers': True, 'marker_size': 20, >>> 'marker_face_colour': ['w', 'w'], >>> 'marker_edge_colour': ['b', 'r'], >>> 'marker_style': 'o', 'marker_edge_width': 1} >>> lines_options = {'render_lines': True, 'line_width': 1, >>> 'line_colour': ['b', 'r'], 'line_style': '-'} >>> grid_options = {'render_grid': True, 'grid_line_style': '--', >>> 'grid_line_width': 0.5} >>> rendering_dict_1 = {'lines': lines_options, 'grid': grid_options, >>> 'markers': markers_options} >>> >>> # 2nd dictionary >>> markers_options = {'render_markers': True, 'marker_size': 200, >>> 'marker_face_colour': [[0.1, 0.2, 0.3]], >>> 'marker_edge_colour': ['m'], 'marker_style': 'x', >>> 'marker_edge_width': 1} >>> lines_options = {'render_lines': True, 'line_width': 100, >>> 'line_colour': [[0.1, 0.2, 0.3]], 'line_style': '-'} >>> grid_options = {'render_grid': False, 'grid_line_style': '--', >>> 'grid_line_width': 0.5} >>> rendering_dict_2 = {'lines': lines_options, 'grid': grid_options, >>> 'markers': markers_options} >>> >>> # Final list >>> rendering_options = [rendering_dict_1, rendering_dict_2] >>> >>> # Create and display widget >>> wid = AnimationOptionsWidget(index, index_style='buttons', >>> render_function=render_function, >>> style='info') >>> display(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 dictionary of options: >>> # 1st dictionary >>> markers_options = {'render_markers': False, 'marker_size': 20, >>> 'marker_face_colour': ['k'], >>> 'marker_edge_colour': ['c'], >>> 'marker_style': 'o', 'marker_edge_width': 1} >>> lines_options = {'render_lines': False, 'line_width': 1, >>> 'line_colour': ['r'], 'line_style': '-'} >>> grid_options = {'render_grid': True, 'grid_line_style': '--', >>> 'grid_line_width': 0.5} >>> rendering_dict_1 = {'lines': lines_options, 'grid': grid_options, >>> 'markers': markers_options} >>> >>> # 2nd dictionary >>> markers_options = {'render_markers': True, 'marker_size': 200, >>> 'marker_face_colour': [[0.123, 0.234, 0.345], 'r'], >>> 'marker_edge_colour': ['m', 'm'], >>> 'marker_style': 'x', 'marker_edge_width': 1} >>> lines_options = {'render_lines': True, 'line_width': 100, >>> 'line_colour': [[0.1, 0.2, 0.3], 'b'], 'line_style': '-'} >>> grid_options = {'render_grid': False, 'grid_line_style': '--', >>> 'grid_line_width': 0.5} >>> rendering_dict_2 = {'lines': lines_options, 'grid': grid_options, >>> 'markers': markers_options} >>> >>> # Final list >>> new_options = [rendering_dict_1, rendering_dict_2] >>> >>> # Set new labels per object >>> labels_per_object = [['1'], ['jaw', 'eyes']] >>> >>> # Update widget state >>> wid.set_widget_state(new_options, labels_per_object, >>> allow_callback=True) """ def __init__(self, renderer_options, options_tabs, objects_names=None, labels_per_object=None, selected_object=0, object_selection_dropdown_visible=True, render_function=None, style='minimal', tabs_style='minimal'): # Make sure that renderer_options is a list even with one member if not isinstance(renderer_options, list): renderer_options = [renderer_options] # Get number of objects to be rendered self.n_objects = len(renderer_options) # Check labels_per_object if labels_per_object is None: labels_per_object = [None] * self.n_objects # Check objects_names if objects_names is None: objects_names = [str(k) for k in range(self.n_objects)] # Create widgets # object selection dropdown objects_dict = OrderedDict() for k, g in enumerate(objects_names): objects_dict[g] = k tmp_visible = self._selection_dropdown_visible( object_selection_dropdown_visible) self.object_selection_dropdown = ipywidgets.Dropdown( options=objects_dict, value=selected_object, description='Select', visible=tmp_visible, margin='0.1cm') # options widgets options_widgets = [] tab_titles = [] for o in options_tabs: # get the options to pass to the sub-options constructors if o == 'figure_one' or o == 'figure_two': tmp_options = renderer_options[selected_object]['figure'] else: tmp_options = renderer_options[selected_object][o] # get the labels to pass in where required tmp_labels = labels_per_object[selected_object] # call sub-options classes if o == 'lines': options_widgets.append(LineOptionsWidget( tmp_options, render_function=render_function, render_checkbox_title='Render lines', labels=tmp_labels)) tab_titles.append('Lines') elif o == 'markers': options_widgets.append(MarkerOptionsWidget( tmp_options, render_function=render_function, render_checkbox_title='Render markers', labels=tmp_labels)) tab_titles.append('Markers') elif o == 'image': options_widgets.append(ImageOptionsWidget( tmp_options, render_function=render_function)) tab_titles.append('Image') elif o == 'numbering': options_widgets.append(NumberingOptionsWidget( tmp_options, render_function=render_function, render_checkbox_title='Render numbering')) tab_titles.append('Numbering') elif o == 'figure_one': options_widgets.append(FigureOptionsOneScaleWidget( tmp_options, render_function=render_function, figure_scale_visible=True, axes_visible=True)) tab_titles.append('Figure/Axes') elif o == 'figure_two': options_widgets.append(FigureOptionsTwoScalesWidget( tmp_options, render_function=render_function, figure_scale_visible=True, axes_visible=True, coupled_default=False)) tab_titles.append('Figure/Axes') elif o == 'legend': options_widgets.append(LegendOptionsWidget( tmp_options, render_function=render_function, render_checkbox_title='Render legend')) tab_titles.append('Legend') elif o == 'grid': options_widgets.append(GridOptionsWidget( tmp_options, render_function=render_function, render_checkbox_title='Render grid')) tab_titles.append('Grid') self.options_widgets = options_widgets self.tab_titles = tab_titles self.suboptions_tab = ipywidgets.Tab(children=options_widgets) # set titles for (k, tl) in enumerate(self.tab_titles): self.suboptions_tab.set_title(k, tl) self.options_box = ipywidgets.VBox( children=[self.object_selection_dropdown, self.suboptions_tab], align='center', padding='0.2cm') super(RendererOptionsWidget, self).__init__(children=[self.options_box]) self.align = 'start' # Assign output self.selected_values = renderer_options self.options_tabs = options_tabs self.objects_names = objects_names self.labels_per_object = labels_per_object self.object_selection_dropdown_visible = \ object_selection_dropdown_visible # Set style self.predefined_style(style, tabs_style) # Set functionality def update_widgets(name, value): for i, tab in enumerate(self.options_tabs): # get the options to pass to the sub-options update functions if tab == 'figure_one' or tab == 'figure_two': tmp_options = self.selected_values[value]['figure'] else: tmp_options = self.selected_values[value][tab] # call sub-options classes if tab == 'lines' or tab == 'markers': self.options_widgets[i].set_widget_state( tmp_options, labels=self.labels_per_object[value], allow_callback=False) else: self.options_widgets[i].set_widget_state( tmp_options, allow_callback=False) self.object_selection_dropdown.on_trait_change(update_widgets, 'value') # Set render function self._render_function = render_function def _selection_dropdown_visible(self, object_selection_dropdown_visible): return object_selection_dropdown_visible and self.n_objects > 1
[docs] def style(self, box_style=None, border_visible=False, border_color='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_color='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 : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ border_visible : `bool`, optional Defines whether to draw the border line around the widget. border_color : `str`, optional The color 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 corners of the box. padding : `float`, optional The padding around the widget. margin : `float`, optional The margin around the widget. tabs_box_style : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ tabs_border_visible : `bool`, optional Defines whether to draw the border line around the tab widgets. tabs_border_color : `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 : 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 : {``'normal'``, ``'italic'``, ``'oblique'``}, optional The font style. 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_color, 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_color=tabs_border_color, 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) _format_font(self.object_selection_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), optional 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_color = 'black' tabs_border_radius = 0 tabs_padding = 0 else: tabs_style = tabs_style tabs_border_visible = True tabs_border_color = _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_color='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_color=tabs_border_color, 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_color=_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_color=tabs_border_color, 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 add_render_function(self, render_function): r""" Method that adds a `render_function()` to the widget. The signature of the given function is also stored in `self._render_function`. Parameters ---------- render_function : `function` or ``None``, optional The render function that behaves as a callback. If ``None``, then nothing is added. """ self._render_function = render_function if self._render_function is not None: for wid in self.options_widgets: wid.add_render_function(self._render_function)
[docs] def remove_render_function(self): r""" Method that removes the current `self._render_function()` from the widget and sets ``self._render_function = None``. """ for wid in self.options_widgets: wid.remove_render_function() self._render_function = None
[docs] def replace_render_function(self, render_function): r""" Method that replaces the current `self._render_function()` of the widget with the given `render_function()`. Parameters ---------- render_function : `function` or ``None``, optional The render function that behaves as a callback. If ``None``, then nothing is happening. """ # remove old function self.remove_render_function() # add new function self.add_render_function(render_function)
[docs] def set_widget_state(self, renderer_options, labels_per_object, selected_object=None, object_selection_dropdown_visible=None, allow_callback=True): r""" Method that updates the state of the widget with a new set of values. Note that the number of objects should not change. Parameters ---------- renderer_options : `list` of `dict` The selected rendering options per object. The `list` must have length `n_objects` and contain a `dict` of rendering options per object. For example, in case we had two objects to render :: lines_options = {'render_lines': True, 'line_width': 1, 'line_colour': ['b', 'r'], 'line_style': '-'} markers_options = {'render_markers': True, 'marker_size': 20, 'marker_face_colour': ['w', 'w'], 'marker_edge_colour': ['b', 'r'], 'marker_style': 'o', 'marker_edge_width': 1} numbering_options = {'render_numbering': True, 'numbers_font_name': 'serif', 'numbers_font_size': 10, 'numbers_font_style': 'normal', 'numbers_font_weight': 'normal', 'numbers_font_colour': ['k'], 'numbers_horizontal_align': 'center', 'numbers_vertical_align': 'bottom'} legend_options = {'render_legend': True, 'legend_title': '', 'legend_font_name': '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': True} figure_options = {'x_scale': 1., 'y_scale': 1., 'render_axes': True, 'axes_font_name': 'serif', 'axes_font_size': 10, 'axes_font_style': 'normal', 'axes_font_weight': 'normal', 'axes_x_limits': None, 'axes_y_limits': None} grid_options = {'render_grid': True, 'grid_line_style': '--', 'grid_line_width': 0.5} image_options = {'alpha': 1., 'interpolation': 'bilinear', 'cmap_name': 'gray'} rendering_dict = {'lines': lines_options, 'markers': markers_options, 'numbering': numbering_options, 'legend': legend_options, 'figure': figure_options, 'grid': grid_options 'image': image_options} renderer_options = [rendering_dict, rendering_dict] labels_per_object : `list` of `list` or ``None``, optional A `list` that contains a `list` of labels for each object. Those `labels` are employed by the `ColourSelectionWidget`. An example for which this option is useful is in the case we wish to create rendering options for multiple :map:`LandmarkGroup` objects and each one of them has a different set of `labels`. If ``None``, then `labels_per_object` is a `list` of lenth `n_objects` with ``None``. selected_object : `int`, optional The object for which to show the rendering options in the beginning, when the widget is created. object_selection_dropdown_visible : `bool`, optional Controls the visibility of the object selection dropdown (`self.object_selection_dropdown`). allow_callback : `bool`, optional If ``True``, it allows triggering of any callback functions. """ # Check options if selected_object is None: selected_object = self.object_selection_dropdown.value if object_selection_dropdown_visible is not None: self.object_selection_dropdown.visible = \ self._selection_dropdown_visible( object_selection_dropdown_visible) self.object_selection_dropdown_visible = \ object_selection_dropdown_visible # Update sub-options widgets for i, tab in enumerate(self.options_tabs): # get the options to pass to the sub-options update functions if tab == 'figure_one' or tab == 'figure_two': tmp_options = renderer_options[selected_object]['figure'] else: tmp_options = renderer_options[selected_object][tab] # call sub-options classes if tab == 'lines' or tab == 'markers': self.options_widgets[i].set_widget_state( tmp_options, labels=labels_per_object[selected_object], allow_callback=False) else: self.options_widgets[i].set_widget_state( tmp_options, allow_callback=False) # Assign new options dict to selected_values self.selected_values = renderer_options self.labels_per_object = labels_per_object # trigger render function if allowed if allow_callback: self._render_function('', True)
[docs] def update_object_names(self, objects_names): r""" Method that updates the options in the dropdown menu for selecting an object. Note that the number of objects should not change. Parameters ---------- objects_names : `list` of `str` A `list` with the names of the objects that will be used in the selection dropdown menu. """ if not self.objects_names == objects_names: # update dropdown options objects_dict = OrderedDict() for k, g in enumerate(objects_names): objects_dict[g] = k self.object_selection_dropdown.options = objects_dict self.objects_names = objects_names # make sure the dropdown gets updated tmp = self.object_selection_dropdown.value if self.object_selection_dropdown.value > 0: self.object_selection_dropdown.value = 0 self.object_selection_dropdown.value = tmp elif (self.object_selection_dropdown.value == 0 and len(self.object_selection_dropdown.options) > 1): self.object_selection_dropdown.value = 1 self.object_selection_dropdown.value = 0
[docs]class GraphOptionsWidget(ipywidgets.FlexBox): r""" Creates a widget for selecting options for rendering various curves in a graph. The widget consists of the following parts from `IPython.html.widgets` and `menpo.visualize.widgets.tools`: == ===================== ======================= ======================= No Object Variable (`self.`) Description == ===================== ======================= ======================= 1 RendererOptionsWidget `renderer_widget` The rendering widget 2 FloatRangeSlider `x_limit` Sets the x limit 3 FloatRangeSlider `y_limit` Sets the y limit 4 Text `x_label` Sets the x label 5 Text `y_label` Sets the y label 6 Text `title` Sets the title 7 Textarea `legend_entries` Sets the legend entries 8 VBox `graph_related_options` Contains 2 - 7 9 Tab `options_tab` Contains 8, 1 == ===================== ======================= ======================= Note that: * The selected values are stored in the ``self.selected_values`` `dict`. * To set the styling please refer to the ``style()`` and ``predefined_style()`` methods. * To update the state of the widget, please refer to the ``set_widget_state()`` method. * To update the callback function please refer to the ``replace_render_function()`` methods. Parameters ---------- graph_options : `list` of `str` The initial options. For example, in case we had two curves to render :: graph_options = {'legend_entries': ['Nontas', 'Leda'], 'x_label': 'X', 'y_label': 'Y', 'title': 'TITLE', 'x_axis_limits': (2, 7), 'y_axis_limits': (-0.2, 0.2), 'render_lines': [True, True], 'line_colour': ['r', 'b'], 'line_style': ['--', '-'], 'line_width': [1, 3], 'render_markers': [True, False], 'marker_style': ['o', 's'], 'marker_size': [6, 12], 'marker_face_colour': ['k', 'm'], 'marker_edge_colour': ['w', 'c'], 'marker_edge_width': [1, 4], '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': 0., 'legend_n_columns': 1, 'legend_horizontal_spacing': 0, 'legend_vertical_spacing': 0, 'legend_border': True, 'legend_border_padding': 0, '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', 'figure_size': (10, 8), 'render_grid': True, 'grid_line_style': '--', 'grid_line_width': 1} x_slider_options : (`float`, `float`, `float`) The attributes of the x limit slider in the form (`min`, `max`, `step`). y_slider_options : (`float`, `float`, `float`) The attributes of the y limit slider in the form (`min`, `max`, `step`). render_function : `function` or ``None``, optional The render function that is executed when a widgets' value changes. If ``None``, then nothing is assigned. style : 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 : 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 ========= ============================ renderer_tabs_style : See Below, optional Sets a predefined style at the tabs of the renderer 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, graph_options, x_slider_options, y_slider_options, render_function=None, style='minimal', tabs_style='minimal', renderer_tabs_style='minimal'): # Get number of curves (objects) if graph_options['legend_entries'] is None: raise ValueError("legend_entries must be a list, not None") self.n_curves = len(graph_options['legend_entries']) # Check options graph_options['render_lines'] = \ self._check_option(graph_options['render_lines']) graph_options['line_style'] = \ self._check_option(graph_options['line_style']) graph_options['line_width'] = \ self._check_option(graph_options['line_width']) graph_options['render_markers'] = \ self._check_option(graph_options['render_markers']) graph_options['marker_style'] = \ self._check_option(graph_options['marker_style']) graph_options['marker_size'] = \ self._check_option(graph_options['marker_size']) graph_options['marker_edge_width'] = \ self._check_option(graph_options['marker_edge_width']) self.initial_figure_size = graph_options['figure_size'] # Create renderer dictionaries renderer_options = [] legend_options = { 'render_legend': graph_options['render_legend'], 'legend_title': graph_options['legend_title'], 'legend_font_name': graph_options['legend_font_name'], 'legend_font_style': graph_options['legend_font_style'], 'legend_font_size': graph_options['legend_font_size'], 'legend_font_weight': graph_options['legend_font_weight'], 'legend_marker_scale': graph_options['legend_marker_scale'], 'legend_location': graph_options['legend_location'], 'legend_bbox_to_anchor': graph_options['legend_bbox_to_anchor'], 'legend_border_axes_pad': graph_options['legend_border_axes_pad'], 'legend_n_columns': graph_options['legend_n_columns'], 'legend_horizontal_spacing': graph_options['legend_horizontal_spacing'], 'legend_vertical_spacing': graph_options['legend_vertical_spacing'], 'legend_border': graph_options['legend_border'], 'legend_border_padding': graph_options['legend_border_padding'], 'legend_shadow': graph_options['legend_shadow'], 'legend_rounded_corners': graph_options['legend_rounded_corners']} figure_options = {'x_scale': 1., 'y_scale': 1., 'render_axes': graph_options['render_axes'], 'axes_font_name': graph_options['axes_font_name'], 'axes_font_size': graph_options['axes_font_size'], 'axes_font_style': graph_options['axes_font_style'], 'axes_font_weight': graph_options['axes_font_weight'], 'axes_x_limits': None, 'axes_y_limits': None} grid_options = {'render_grid': graph_options['render_grid'], 'grid_line_style': graph_options['grid_line_style'], 'grid_line_width': graph_options['grid_line_width']} for i in range(self.n_curves): lines_options = {'render_lines': graph_options['render_lines'][i], 'line_width': graph_options['line_width'][i], 'line_colour': [graph_options['line_colour'][i]], 'line_style': graph_options['line_style'][i]} markers_options = { 'render_markers': graph_options['render_markers'][i], 'marker_size': graph_options['marker_size'][i], 'marker_face_colour': [graph_options['marker_face_colour'][i]], 'marker_edge_colour': [graph_options['marker_edge_colour'][i]], 'marker_style': graph_options['marker_style'][i], 'marker_edge_width': graph_options['marker_edge_width'][i]} rendering_dict = {'lines': lines_options, 'markers': markers_options, 'legend': legend_options, 'figure': figure_options, 'grid': grid_options} renderer_options.append(rendering_dict) # Create widgets options_tabs = ['lines', 'markers', 'legend', 'figure_two', 'grid'] self.renderer_widget = RendererOptionsWidget( renderer_options, options_tabs=options_tabs, objects_names=graph_options['legend_entries'], object_selection_dropdown_visible=self.n_curves > 1, render_function=None) # Make the x and y limits of the axes widget invisible. They will be # controlled by the sliders in the graph options. self.renderer_widget.options_widgets[3].axes_x_limits_box.visible = \ False self.renderer_widget.options_widgets[3].axes_y_limits_box.visible = \ False self.x_limit = ipywidgets.FloatRangeSlider( min=x_slider_options[0], max=x_slider_options[1], step=x_slider_options[2], value=graph_options['x_axis_limits'], description='X limits', margin='0.05cm', width='7.3cm') self.y_limit = ipywidgets.FloatRangeSlider( min=y_slider_options[0], max=y_slider_options[1], step=y_slider_options[2], value=graph_options['y_axis_limits'], description='Y limits', margin='0.05cm', width='7.3cm') self.x_label = ipywidgets.Text(description='X label', margin='0.05cm', value=graph_options['x_label']) self.y_label = ipywidgets.Text(description='Y label', margin='0.05cm', value=graph_options['y_label']) self.title = ipywidgets.Text(description='Title', margin='0.05cm', value=graph_options['title']) self.legend_entries = ipywidgets.Textarea( description='Legend', width='73mm', margin='0.05cm', value=self._convert_list_to_legend_entries( graph_options['legend_entries'])) self.graph_related_options = ipywidgets.VBox( children=[self.x_limit, self.y_limit, self.x_label, self.y_label, self.title, self.legend_entries]) self.options_tab = ipywidgets.Tab( children=[self.graph_related_options, self.renderer_widget]) self.options_tab.set_title(0, 'Graph') self.options_tab.set_title(1, 'Renderer') super(GraphOptionsWidget, self).__init__(children=[self.options_tab]) # Assign output self.selected_values = graph_options # Set style self.predefined_style(style, tabs_style, renderer_tabs_style) # Set functionality def legend_entries_function(name, value): tmp_entries = str(self.legend_entries.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.selected_values['legend_entries'] = tmp_entries[:self.n_curves] self.legend_entries.on_trait_change(legend_entries_function, 'value') def update_renderer_widget_objects(name, value): self.renderer_widget.update_object_names( self.selected_values['legend_entries']) self.options_tab.on_trait_change(update_renderer_widget_objects, 'selected_index') def get_graph_related_options(name, value): self.selected_values['x_label'] = str(self.x_label.value) self.selected_values['y_label'] = str(self.y_label.value) self.selected_values['title'] = str(self.title.value) self.selected_values['x_axis_limits'] = self.x_limit.value self.selected_values['y_axis_limits'] = self.y_limit.value self.x_label.on_trait_change(get_graph_related_options, 'value') self.y_label.on_trait_change(get_graph_related_options, 'value') self.title.on_trait_change(get_graph_related_options, 'value') self.x_limit.on_trait_change(get_graph_related_options, 'value') self.y_limit.on_trait_change(get_graph_related_options, 'value') # Set render function self._render_function = None self.add_render_function(render_function) def _check_option(self, val): if isinstance(val, list) and not len(val) == self.n_curves: raise ValueError("lines and markers related options must be lists " "of length equal to the number of curves.") elif val is None: raise ValueError("lines and markers related options cannot be None") elif val is not None and not isinstance(val, list): val = [val] * self.n_curves return val 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_color='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_color='black', tabs_border_style='solid', tabs_border_width=1, tabs_border_radius=1, tabs_padding=0, tabs_margin=0, renderer_tabs_box_style=None, renderer_tabs_border_visible=True, renderer_tabs_border_color='black', renderer_tabs_border_style='solid', renderer_tabs_border_width=1, renderer_tabs_border_radius=1, renderer_tabs_padding=0, renderer_tabs_margin=0, font_family='', font_size=None, font_style='', font_weight=''): r""" Function that defines the styling of the widget. Parameters ---------- box_style : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ border_visible : `bool`, optional Defines whether to draw the border line around the widget. border_color : `str`, optional The color 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 corners of the box. padding : `float`, optional The padding around the widget. margin : `float`, optional The margin around the widget. tabs_box_style : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ tabs_border_visible : `bool`, optional Defines whether to draw the border line around the tab widgets. tabs_border_color : `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. renderer_tabs_box_style : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ renderer_tabs_border_visible : `bool`, optional Defines whether to draw the border line around the tab widgets of the renderer widget. renderer_tabs_border_color : `str`, optional The color of the border around the tab widgets of the renderer widget. renderer_tabs_border_style : `str`, optional The line style of the border around the tab widgets of the renderer widget. renderer_tabs_border_width : `float`, optional The line width of the border around the tab widgets of the renderer widget. renderer_tabs_border_radius : `float`, optional The radius of the corners of the box of the tab widgets of the renderer widget. renderer_tabs_padding : `float`, optional The padding around the tab widgets of the renderer widget. renderer_tabs_margin : `float`, optional The margin around the tab widgets of the renderer widget. font_family : 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 : {``'normal'``, ``'italic'``, ``'oblique'``}, optional The font style. 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_color, border_style, border_width, border_radius, padding, margin) _format_box(self.graph_related_options, tabs_box_style, tabs_border_visible, tabs_border_color, tabs_border_style, tabs_border_width, tabs_border_radius, tabs_padding, tabs_margin) self.x_limit.slider_color = _map_styles_to_hex_colours(tabs_box_style) self.x_limit.background_color = _map_styles_to_hex_colours(tabs_box_style) self.y_limit.slider_color = _map_styles_to_hex_colours(tabs_box_style) self.y_limit.background_color = _map_styles_to_hex_colours(tabs_box_style) _format_box(self.renderer_widget, tabs_box_style, tabs_border_visible, tabs_border_color, tabs_border_style, tabs_border_width, tabs_border_radius, tabs_padding, tabs_margin) for wid in self.renderer_widget.options_widgets: wid.style(box_style=renderer_tabs_box_style, border_visible=renderer_tabs_border_visible, border_color=renderer_tabs_border_color, border_style=renderer_tabs_border_style, border_width=renderer_tabs_border_width, border_radius=renderer_tabs_border_radius, padding=renderer_tabs_padding, margin=renderer_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) _format_font(self.renderer_widget.object_selection_dropdown, font_family, font_size, font_style, font_weight) _format_font(self.graph_related_options, font_family, font_size, font_style, font_weight) _format_font(self.x_limit, font_family, font_size, font_style, font_weight) _format_font(self.y_limit, 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, font_family, font_size, font_style, font_weight)
[docs] def predefined_style(self, style, tabs_style='minimal', renderer_tabs_style='mininal'): 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), optional 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 ========= ============================ renderer_tabs_style : `str` (see below), optional 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 renderer_tabs_style == 'minimal' or renderer_tabs_style == '': renderer_tabs_style = '' renderer_tabs_border_visible = False renderer_tabs_border_color = 'black' renderer_tabs_border_radius = 0 renderer_tabs_padding = 0 else: renderer_tabs_style = renderer_tabs_style renderer_tabs_border_visible = True renderer_tabs_border_color = \ _map_styles_to_hex_colours(renderer_tabs_style) renderer_tabs_border_radius = 10 renderer_tabs_padding = '0.3cm' if tabs_style == 'minimal' or tabs_style == '': tabs_style = '' tabs_border_visible = True tabs_border_color = 'black' tabs_border_radius = 0 tabs_padding = 0 else: tabs_style = tabs_style tabs_border_visible = True tabs_border_color = _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_color='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_color=tabs_border_color, tabs_border_style='solid', tabs_border_width=1, tabs_border_radius=tabs_border_radius, tabs_padding=tabs_padding, tabs_margin='0.3cm', renderer_tabs_box_style=renderer_tabs_style, renderer_tabs_border_visible=renderer_tabs_border_visible, renderer_tabs_border_color=renderer_tabs_border_color, renderer_tabs_border_style='solid', renderer_tabs_border_width=1, renderer_tabs_border_radius=renderer_tabs_border_radius, renderer_tabs_padding=renderer_tabs_padding, renderer_tabs_margin='0.5cm') elif (style == 'info' or style == 'success' or style == 'danger' or style == 'warning'): self.style(box_style=style, border_visible=True, border_color=_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_color=tabs_border_color, tabs_border_style='solid', tabs_border_width=1, tabs_border_radius=tabs_border_radius, tabs_padding=tabs_padding, tabs_margin='0.3cm', renderer_tabs_box_style=renderer_tabs_style, renderer_tabs_border_visible=renderer_tabs_border_visible, renderer_tabs_border_color=renderer_tabs_border_color, renderer_tabs_border_style='solid', renderer_tabs_border_width=1, renderer_tabs_border_radius=renderer_tabs_border_radius, renderer_tabs_padding=renderer_tabs_padding, renderer_tabs_margin='0.5cm') else: raise ValueError('style must be minimal or info or success or ' 'danger or warning')
def _get_selected_options(self): # legend options legend_tmp = self.renderer_widget.selected_values[0]['legend'] self.selected_values.update(legend_tmp) # axes options figure_tmp = self.renderer_widget.selected_values[0]['figure'] self.selected_values['render_axes'] = figure_tmp['render_axes'] self.selected_values['axes_font_name'] = figure_tmp['axes_font_name'] self.selected_values['axes_font_size'] = figure_tmp['axes_font_size'] self.selected_values['axes_font_style'] = figure_tmp['axes_font_style'] self.selected_values['axes_font_weight'] = \ figure_tmp['axes_font_weight'] self.selected_values['figure_size'] = \ (figure_tmp['x_scale'] * self.initial_figure_size[0], figure_tmp['y_scale'] * self.initial_figure_size[1]) # grid options grid_tmp = self.renderer_widget.selected_values[0]['grid'] self.selected_values.update(grid_tmp) # lines and markers options for j in range(self.n_curves): self.selected_values['render_lines'][j] = \ self.renderer_widget.selected_values[j]['lines']['render_lines'] self.selected_values['line_colour'][j] = \ self.renderer_widget.selected_values[j]['lines']['line_colour'][0] self.selected_values['line_style'][j] = \ self.renderer_widget.selected_values[j]['lines']['line_style'] self.selected_values['line_width'][j] = \ self.renderer_widget.selected_values[j]['lines']['line_width'] self.selected_values['render_markers'][j] = \ self.renderer_widget.selected_values[j]['markers']['render_markers'] self.selected_values['marker_style'][j] = \ self.renderer_widget.selected_values[j]['markers']['marker_style'] self.selected_values['marker_size'][j] = \ self.renderer_widget.selected_values[j]['markers']['marker_size'] self.selected_values['marker_face_colour'][j] = \ self.renderer_widget.selected_values[j]['markers']['marker_face_colour'][0] self.selected_values['marker_edge_colour'][j] = \ self.renderer_widget.selected_values[j]['markers']['marker_edge_colour'][0] self.selected_values['marker_edge_width'][j] = \ self.renderer_widget.selected_values[j]['markers']['marker_edge_width']
[docs] def add_render_function(self, render_function): r""" Method that adds a `render_function()` to the widget. The signature of the given function is also stored in `self._render_function`. Parameters ---------- render_function : `function` or ``None``, optional The render function that behaves as a callback. If ``None``, then nothing is added. """ self._render_function_tmp = render_function if self._render_function_tmp is None: def render_function_with_get_options(name, value): # Get all the selected values self._get_selected_options() # Make displacements menu invisible self.plot_displacements_menu.visible = False else: def render_function_with_get_options(name, value): # Get all the selected values self._get_selected_options() # Call render function self._render_function_tmp(name, value) self._render_function = render_function_with_get_options self.renderer_widget.add_render_function(self._render_function) self.x_limit.on_trait_change(self._render_function, 'value') self.y_limit.on_trait_change(self._render_function, 'value') self.x_label.on_trait_change(self._render_function, 'value') self.y_label.on_trait_change(self._render_function, 'value') self.title.on_trait_change(self._render_function, 'value') self.legend_entries.on_trait_change(self._render_function, 'value')
[docs] def remove_render_function(self): r""" Method that removes the current `self._render_function()` from the widget and sets ``self._render_function = None``. """ self.renderer_widget.remove_render_function() self.x_limit.on_trait_change(self._render_function, 'value', remove=True) self.y_limit.on_trait_change(self._render_function, 'value', remove=True) self.x_label.on_trait_change(self._render_function, 'value', remove=True) self.y_label.on_trait_change(self._render_function, 'value', remove=True) self.title.on_trait_change(self._render_function, 'value', remove=True) self.legend_entries.on_trait_change(self._render_function, 'value', remove=True) self._render_function = None
[docs] def replace_render_function(self, render_function): r""" Method that replaces the current `self._render_function()` of the widget with the given `render_function()`. Parameters ---------- render_function : `function` or ``None``, optional The render function that behaves as a callback. If ``None``, then nothing is happening. """ # remove old function self.remove_render_function() # add new function self.add_render_function(render_function)
[docs]class SaveFigureOptionsWidget(ipywidgets.FlexBox): r""" Creates a widget for saving a figure to file. The widget consists of the following parts from `IPython.html.widgets` and `menpo.visualize.widgets.tools`: == ===================== ====================== ========================== No Object Variable (`self.`) Description == ===================== ====================== ========================== 1 Select `file_format_select` Image format selector 2 FloatText `dpi_text` DPI selector 3 Dropdown `orientation_dropdown` Paper orientation selector 4 Select `papertype_select` Paper type selector 5 Checkbox `transparent_checkbox` Transparency setter 6 ColourSelectionWidget `facecolour_widget` Face colour selector 7 ColourSelectionWidget `edgecolour_widget` Edge colour selector 8 FloatText `pad_inches_text` Padding in inches setter 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 please refer to the ``style()`` and ``predefined_style()`` methods. Parameters ---------- renderer : :map:`Renderer` class or subclass 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 orientation. papertype : `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. facecolour : `str` or `list` of `float`, optional The initial value of the face colour. edgecolour : `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 : 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, file_format='png', dpi=None, orientation='portrait', papertype='letter', transparent=False, facecolour='w', edgecolour='w', 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) 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=papertype, description='Paper type', visible=file_format == 'ps', width='3cm') self.transparent_checkbox = ipywidgets.Checkbox( description='Transparent', value=transparent) self.facecolour_widget = ColourSelectionWidget( [facecolour], description='Face colour') self.edgecolour_widget = ColourSelectionWidget( [edgecolour], 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', 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.Box( 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 self.renderer = renderer # Set style self.predefined_style(style) # Set functionality def papertype_visibility(name, value): self.papertype_select.visible = value == 'ps' self.file_format_select.on_trait_change(papertype_visibility, 'value') def set_extension(name, value): file_name, file_extension = splitext(self.filename_text.value) self.filename_text.value = file_name + '.' + value self.file_format_select.on_trait_change(set_extension, 'value') 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['colour'][0], edge_colour= self.edgecolour_widget.selected_values['colour'][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: if (e.message == '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.message # 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_color='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 : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ border_visible : `bool`, optional Defines whether to draw the border line around the widget. border_color : `str`, optional The color 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 corners of the box. padding : `float`, optional The padding around the widget. margin : `float`, optional The margin around the widget. font_family : 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 : {``'normal'``, ``'italic'``, ``'oblique'``}, optional The font style. 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_color, 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_color='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_color= _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')
class FeatureOptionsWidget(ipywidgets.FlexBox): r""" Creates a widget for selecting feature options. Specifically, it consists of: 1) RadioButtons [`self.feature_radiobuttons`]: select feature type 2) DSIFTOptionsWidget [`self.dsift_options_widget`]: dsift options widget 3) HOGOptionsWidget [`self.hog_options_widget`]: hog options widget 4) IGOOptionsWidget [`self.igo_options_widget`]: igo options widget 5) LBPOptionsWidget [`self.lbp_options_widget`]: lbp options widget 6) DaisyOptionsWidget [`self.daisy_options_widget`]: daisy options widget 7) Latex [`self.no_options_widget`]: message for no options available 8) Box [`self.per_feature_options_box`]: box that contains (2), (3), (4), (5), (6) and (7) 9) Image [`self.preview_image`]: lenna image 10) Latex [`self.preview_input_latex`]: the initial image information 11) Latex [`self.preview_output_latex`]: the output image information 12) Latex [`self.preview_time_latex`]: the timing information 13) VBox [`self.preview_box`]: box that contains (9), (10), (11), (12) 14) Tab [`self.options_box`]: box that contains (1), (8) and (13) To set the styling of this widget please refer to the `style()` method. 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) Sets a predefined style at the widget. Possible options are :: {``'minimal'``, ``'success'``, ``'info'``, ``'warning'``, ``'danger'``, ``''``} """ def __init__(self, style='minimal'): # import features methods and time import time 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 # 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': 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.crop_to_landmarks_proportion_inplace(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(name, value): 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.on_trait_change( per_feature_options_visibility, 'value') per_feature_options_visibility('', no_op) def get_function(name, value): # 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 = value self.features_options = opts self.feature_radiobuttons.on_trait_change(get_function, 'value') self.options_box.on_trait_change(get_function, 'selected_index') def preview_function(name, old_value, value): if value == 2: # extracting features message 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 old_value == 2: self.preview_input_latex.visible = False self.preview_image.visible = False self.options_box.on_trait_change(preview_function, 'selected_index') def style(self, box_style=None, border_visible=False, border_color='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 : See Below, optional Style options ========= ============================ Style Description ========= ============================ 'success' Green-based style 'info' Blue-based style 'warning' Yellow-based style 'danger' Red-based style '' Default style None No style ========= ============================ border_visible : `bool`, optional Defines whether to draw the border line around the widget. border_color : `str`, optional The color 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 corners of the box. padding : `float`, optional The padding around the widget. margin : `float`, optional The margin around the widget. font_family : 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 : {``'normal'``, ``'italic'``, ``'oblique'``}, optional The font style. 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_color, 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' 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_color='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_color= _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')