You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
4.1 KiB
Python
121 lines
4.1 KiB
Python
# Copyright (c) Jupyter Development Team.
|
|
# Distributed under the terms of the Modified BSD License.
|
|
|
|
"""SelectionContainer class.
|
|
|
|
Represents a multipage container that can be used to group other widgets into
|
|
pages.
|
|
"""
|
|
|
|
from .widget_box import Box
|
|
from .widget import register
|
|
from .widget_core import CoreWidget
|
|
from traitlets import Unicode, Dict, CInt, TraitError, validate, observe
|
|
from .trait_types import TypedTuple
|
|
from itertools import chain, repeat, islice
|
|
|
|
# Inspired by an itertools recipe: https://docs.python.org/3/library/itertools.html#itertools-recipes
|
|
def pad(iterable, padding=None, length=None):
|
|
"""Returns the sequence elements and then returns None up to the given size (or indefinitely if size is None)."""
|
|
return islice(chain(iterable, repeat(padding)), length)
|
|
|
|
class _SelectionContainer(Box, CoreWidget):
|
|
"""Base class used to display multiple child widgets."""
|
|
titles = TypedTuple(trait=Unicode(), help="Titles of the pages").tag(sync=True)
|
|
selected_index = CInt(
|
|
help="""The index of the selected page. This is either an integer selecting a particular sub-widget, or None to have no widgets selected.""",
|
|
allow_none=True,
|
|
default_value=None
|
|
).tag(sync=True)
|
|
|
|
@validate('selected_index')
|
|
def _validated_index(self, proposal):
|
|
if proposal.value is None or 0 <= proposal.value < len(self.children):
|
|
return proposal.value
|
|
else:
|
|
raise TraitError('Invalid selection: index out of bounds')
|
|
|
|
@validate('titles')
|
|
def _validate_titles(self, proposal):
|
|
return tuple(pad(proposal.value, '', len(self.children)))
|
|
|
|
@observe('children')
|
|
def _observe_children(self, change):
|
|
self._reset_selected_index()
|
|
self._reset_titles()
|
|
|
|
def _reset_selected_index(self):
|
|
if self.selected_index is not None and len(self.children) < self.selected_index:
|
|
self.selected_index = None
|
|
|
|
def _reset_titles(self):
|
|
if len(self.titles) != len(self.children):
|
|
# Run validation function
|
|
self.titles = tuple(self.titles)
|
|
|
|
def set_title(self, index, title):
|
|
"""Sets the title of a container page.
|
|
Parameters
|
|
----------
|
|
index : int
|
|
Index of the container page
|
|
title : unicode
|
|
New title
|
|
"""
|
|
titles = list(self.titles)
|
|
# for backwards compatibility with ipywidgets 7.x
|
|
if title is None:
|
|
title = ''
|
|
titles[index]=title
|
|
self.titles = tuple(titles)
|
|
|
|
def get_title(self, index):
|
|
"""Gets the title of a container page.
|
|
Parameters
|
|
----------
|
|
index : int
|
|
Index of the container page
|
|
"""
|
|
return self.titles[index]
|
|
|
|
@register
|
|
class Accordion(_SelectionContainer):
|
|
"""Displays children each on a separate accordion page."""
|
|
_view_name = Unicode('AccordionView').tag(sync=True)
|
|
_model_name = Unicode('AccordionModel').tag(sync=True)
|
|
|
|
|
|
@register
|
|
class Tab(_SelectionContainer):
|
|
"""Displays children each on a separate accordion tab."""
|
|
_view_name = Unicode('TabView').tag(sync=True)
|
|
_model_name = Unicode('TabModel').tag(sync=True)
|
|
|
|
def __init__(self, children=(), **kwargs):
|
|
if len(children) > 0 and 'selected_index' not in kwargs:
|
|
kwargs['selected_index'] = 0
|
|
super().__init__(children=children, **kwargs)
|
|
|
|
def _reset_selected_index(self):
|
|
# if there are no tabs, then none should be selected
|
|
num_children = len(self.children)
|
|
if num_children == 0:
|
|
self.selected_index = None
|
|
|
|
# if there are tabs, but none is selected, select the first one
|
|
elif self.selected_index == None:
|
|
self.selected_index = 0
|
|
|
|
# if there are tabs and a selection, but the selection is no longer
|
|
# valid, select the last tab.
|
|
elif num_children < self.selected_index:
|
|
self.selected_index = num_children - 1
|
|
|
|
|
|
|
|
@register
|
|
class Stack(_SelectionContainer):
|
|
"""Displays only the selected child."""
|
|
_view_name = Unicode('StackView').tag(sync=True)
|
|
_model_name = Unicode('StackModel').tag(sync=True)
|