In [ ]:
%%html
<style>
.example-container { background: #999999; padding: 2px; min-height: 100px; }
.example-container.sm { min-height: 50px; }
.example-box { background: #9999FF; width: 50px; height: 50px; text-align: center; vertical-align: middle; color: white; font-weight: bold; margin: 2px;}
.example-box.med { width: 65px; height: 65px; }
.example-box.lrg { width: 80px; height: 80px; }
</style>
Since the representation of the widget you see is a browser element, Cascading Style Sheets (CSS) are used for styling. Widgets have a set_css
method that allows you to add and remove CSS properties from your elements. The following example shows had set_css
can be used to set the background color of a TextWidget
.
In [ ]:
from IPython.html import widgets
text = widgets.TextWidget(value="Hello World!")
text.set_css('background', 'lime')
text
In the example above, the color lime
is specified by name. CSS also supports specifying colors by a 3 byte hexadecimal string. The first byte is red, second green, and third blue (RGB). The following example sets the TextWidget
's background to blue.
In [ ]:
text.set_css('background', '#0000FF')
In CSS the font color is color
.
In [ ]:
text.set_css('color', '#FFFFFF')
CSS is also used to set the height and width of controls. The set_css
method also can accept a single dictionary with multiple CSS properties (as seen below).
In [ ]:
btn = widgets.ButtonWidget()
btn.set_css({
'width': '100px',
'height': '100px',
'background': 'red',
})
btn
To remove the styling, you can call set_css
again, but use an empty string instead of a color value.
In [ ]:
btn.set_css('background', '')
For more information about what can be done with CSS, please refer to the Mozilla Developer Network's series on it.
To display widget A inside widget B, widget A must be a child of widget B. Only one instance of any particular widget can be child of another (this limitation will be removed in IPython 3.0). In other words, widget A cannot have widget B listed twice in it's list of children.
Widgets that can contain other widgets have a children
attribute. This attribute can be set via a keyword argument in the widget's constructor or after construction. Calling display on an object with children automatically displays those children, too.
In [ ]:
from IPython.display import display
float_range = widgets.FloatSliderWidget()
string = widgets.TextWidget(value='hi')
container = widgets.ContainerWidget(children=[float_range, string])
container.set_css('border', '3px dotted red')
display(container) # Displays the `container` and all of it's children.
Children can be added to parents after the parent has been displayed. The parent is responsible for rendering its children.
In [ ]:
container = widgets.ContainerWidget()
container.set_css('border', '3px dotted red')
display(container)
int_range = widgets.IntSliderWidget()
container.children=[int_range]
If you need to display a more complicated set of widgets, there are specialized containers that you can use. To display multiple sets of widgets, you can use an AccordionWidget
or a TabWidget
in combination with one ContainerWidget
per set of widgets (as seen below). The "pages" of these widgets are their children. To set the titles of the pages, one must call set_title
after the widget has been displayed.
In [ ]:
name1 = widgets.TextWidget(description='Location:')
zip1 = widgets.BoundedIntTextWidget(description='Zip:', min=0, max=99999)
page1 = widgets.ContainerWidget(children=[name1, zip1])
name2 = widgets.TextWidget(description='Location:')
zip2 = widgets.BoundedIntTextWidget(description='Zip:', min=0, max=99999)
page2 = widgets.ContainerWidget(children=[name2, zip2])
accord = widgets.AccordionWidget(children=[page1, page2])
display(accord)
accord.set_title(0, 'From')
accord.set_title(1, 'To')
In [ ]:
name = widgets.TextWidget(description='Name:')
color = widgets.DropdownWidget(description='Color:', values=['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'])
page1 = widgets.ContainerWidget(children=[name, color])
age = widgets.IntSliderWidget(description='Age:', min=0, max=120, value=50)
gender = widgets.RadioButtonsWidget(description='Gender:', values=['male', 'female'])
page2 = widgets.ContainerWidget(children=[age, gender])
tabs = widgets.TabWidget(children=[page1, page2])
display(tabs)
tabs.set_title(0, 'Name')
tabs.set_title(1, 'Details')
Unlike the other two special containers, the PopupWidget
is only designed to display one set of widgets. The PopupWidget
can be used to display widgets outside of the widget area.
In [ ]:
counter = widgets.IntTextWidget(description='Counter:')
popup = widgets.PopupWidget(children=[counter], description='Popup Demo', button_text='Popup Button')
display(popup)
In [ ]:
counter.value += 1
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
counter.value += 1
In [ ]:
popup.close()
Most widgets have a description
attribute, which allows a label for the widget to be defined.
The label of the widget has a fixed minimum width.
The text of the label is always right aligned and the widget is left aligned:
In [ ]:
display(widgets.TextWidget(description="a:"))
display(widgets.TextWidget(description="aa:"))
display(widgets.TextWidget(description="aaa:"))
If a label is longer than the minimum width, the widget is shifted to the right:
In [ ]:
display(widgets.TextWidget(description="a:"))
display(widgets.TextWidget(description="aa:"))
display(widgets.TextWidget(description="aaa:"))
display(widgets.TextWidget(description="aaaaaaaaaaaaaaaaaa:"))
If a description
is not set for the widget, the label is not displayed:
In [ ]:
display(widgets.TextWidget(description="a:"))
display(widgets.TextWidget(description="aa:"))
display(widgets.TextWidget(description="aaa:"))
display(widgets.TextWidget())
IPython defines a large number of DOM (document object model) classes that you can apply to your widgets. Applying a DOM class causes all of the CSS associated with that class to be applied to the element. Classes can be applied and removed using the add_class
and remove_class
methods after a widget has been displayed. The majority of DOM classes defined by IPython are actually Bootstrap classes. For more information on Bootstrap classes and CSS, please refer to Bootstrap's website.
Both add_class
and remove_class
allow you to use CSS selectors to pick which sub elements of your widget get styled. Because of this, the add_class
and remove_class
methods are path dependent (order specific). The following example shows the same three calls made in three different orders and the resulting output. All three differ.
In [ ]:
%%html
<style>
div.cube { display: inline; padding: 5px; }
div.red { background: red; }
div.blue { background: blue; }
</style>
In [ ]:
from IPython.html import widgets
from IPython.display import display
html = '<br />'.join([''.join(['<div class="cube">x</div>' for i in range(8)]) for j in range(8)])
widget = [widgets.HTMLWidget(value=html) for i in range(3)]
display(widget[0])
widget[0].add_class('red', 'div.cube:nth-child(even)')
widget[0].remove_class('red', 'div.red:nth-child(7n+1)')
widget[0].add_class('blue', 'div.red:nth-child(3n+1)')
In [ ]:
display(widget[1])
widget[1].remove_class('red', 'div.red:nth-child(7n+1)')
widget[1].add_class('blue', 'div.red:nth-child(3n+1)')
widget[1].add_class('red', 'div.cube:nth-child(even)')
In [ ]:
display(widget[2])
widget[2].add_class('red', 'div.cube:nth-child(even)')
widget[2].add_class('blue', 'div.red:nth-child(3n+1)')
widget[2].remove_class('red', 'div.red:nth-child(7n+1)')
Widgets can be aligned using IPython alignment classes. These classes should work with most widgets, but were designed to be applied to ContainerWidget
s. Examples of these classes follow:
Widget containers default to this orientation.
These examples use the hbox layout to show packing. Packing is the alignment of the widgets along the the axis that they are displayed on.
These examples use the hbox layout to show alignment. Packing is the alignment of the widgets along the the axis perpendicular to the one that they are displayed on.
To specify how "greedy" a container is when filling in the remaining space of its parent, the box-flexN
classes are used (where N is 0, 1, or 2). The higher the value of N, the more greedy the child is. box-flex0
is the default behavior, which is to not fill the parent.
Widget containers default to vbox alignment.
In [ ]:
buttons = [widgets.ButtonWidget(description=str(i)) for i in range(3)]
container = widgets.ContainerWidget(children=buttons)
display(container)
To make a widget container display its widgets horizontally, you need to remove the vbox
class from the container and add the hbox
class in its place.
In [ ]:
container = widgets.ContainerWidget(children=buttons)
display(container)
container.remove_class('vbox')
container.add_class('hbox')
By setting the width of the container to 100% and adding the center
class to it, you can center the buttons.
In [ ]:
container.set_css('width', '100%')
container.add_class('center')
In addition to alignment classes, the classes defined by Bootstrap can also be used. This tutorial will only cover a few of the most common classes. For a full list of Bootstrap classes, please refer to Bootstrap's website.
In [ ]:
# List of the bootstrap button styles
classes = [
'btn',
'btn-primary',
'btn-info',
'btn-success',
'btn-warning',
'btn-danger',
'btn-inverse',
'btn-link'
]
# Display the buttons in a hbox
container = widgets.ContainerWidget(children=[widgets.ButtonWidget(description=c) for c in classes])
display(container)
# Apply classes after display
container.remove_class('vbox')
container.add_class('hbox')
ret = [container.children[i].add_class(c) for i, c in enumerate(classes)]
In [ ]:
def create_label(cls):
class_name = widgets.HTMLWidget(value=cls)
container = widgets.ContainerWidget(children=[class_name])
display(container)
container.add_class(cls)
ret = [create_label(c) for c in [
'alert',
'alert alert-error',
'alert alert-success',
'alert alert-info'
]]
In [ ]:
classes = [
'progress-info',
'progress-success',
'progress-warning',
'progress-danger',
'progress-info progress-striped',
'progress-success progress-striped',
'progress-warning progress-striped',
'progress-danger progress-striped',
'active progress-info progress-striped',
'active progress-success progress-striped',
'active progress-warning progress-striped',
'active progress-danger progress-striped',
]
ws = [widgets.IntProgressWidget(value=50, description=c) for c in classes]
ret = [display(w) for w in ws]
ret = [ws[i].add_class(c) for i, cs in enumerate(classes) for c in cs.split(' ')]
Sometimes it is necessary to hide or show widgets in place, without having to re-display the widget.
The visibility
property of widgets can be used to hide or show widgets that have already been displayed (as seen below).
In [ ]:
string = widgets.LatexWidget(value="Hello World!")
display(string)
In [ ]:
string.visible=False
In [ ]:
string.visible=True
In the example below, a form is rendered, which conditionally displays widgets depending on the state of other widgets. Try toggling the student checkbox.
In [ ]:
form = widgets.ContainerWidget()
first = widgets.TextWidget(description="First Name:")
last = widgets.TextWidget(description="Last Name:")
student = widgets.CheckboxWidget(description="Student:", value=False)
school_info = widgets.ContainerWidget(visible=False, children=[
widgets.TextWidget(description="School:"),
widgets.IntTextWidget(description="Grade:", min=0, max=12)
])
pet = widgets.TextWidget(description="Pet's Name:")
form.children = [first, last, student, school_info, pet]
display(form)
def on_student_toggle(name, value):
if value:
school_info.visible = True
else:
school_info.visible = False
student.on_trait_change(on_student_toggle, 'value')