The flexbox layout enables you to make extensive changes to the appearance of a widget by making changes to its layout.
The following material on the flexbox layout follows the lines of the article A Complete Guide to Flexbox by Chris Coyier, and uses text and various images from the article with permission.
The flexbox layout spec is excellent for laying out items in a single direction, either horizontally or vertically.
Since flexbox is a whole module and not a single property, it involves a lot of things including its whole set of properties. Some of them are meant to be set on the container (parent element, known as "flex container") whereas the others are meant to be set on the children (known as "flex items"). If regular layout is based on both block and inline flow directions, the flex layout is based on "flex-flow directions". Please have a look at this figure from the specification, explaining the main idea behind the flex layout.
Basically, items will be laid out following either the main axis
(from main-start
to main-end
) or the cross axis
(from cross-start
to cross-end
).
main axis
- The main axis of a flex container is the primary axis along which flex items are laid out. Beware, it is not necessarily horizontal; it depends on the flex-direction property (see below).main-start | main-end
- The flex items are placed within the container starting from main-start and going to main-end.main size
- A flex item's width or height, whichever is in the main dimension, is the item's main size. The flex item's main size property is either the ‘width’ or ‘height’ property, whichever is in the main dimension.cross axis - The axis perpendicular to the main axis is called the cross axis. Its direction depends on the main axis direction.
cross-start | cross-end
- Flex lines are filled with items and placed into the container starting on the cross-start side of the flex container and going toward the cross-end side.cross size
- The width or height of a flex item, whichever is in the cross dimension, is the item's cross size. The cross size property is whichever of ‘width’ or ‘height’ that is in the cross dimension.The VBox
and HBox
helper classes provide simple defaults to arrange child widgets in vertical and horizontal boxes. They are roughly equivalent to:
def VBox(*pargs, **kwargs):
"""Displays multiple widgets vertically using the flexible box model."""
box = Box(*pargs, **kwargs)
box.layout.display = 'flex'
box.layout.flex_flow = 'column'
box.layout.align_items = 'stretch'
return box
def HBox(*pargs, **kwargs):
"""Displays multiple widgets horizontally using the flexible box model."""
box = Box(*pargs, **kwargs)
box.layout.display = 'flex'
box.layout.align_items = 'stretch'
return box
Note in particular that it is the flex_flow
that controls whether box children are arranged in a row (for HBox
) or a column (for VBox
).
from ipywidgets import Layout, Button, HBox
items = [
Button(description='weight=1; 0%', layout=Layout(flex='1 1 0%', width='auto'), button_style='danger'),
Button(description='weight=3; 0%', layout=Layout(flex='3 1 0%', width='auto'), button_style='danger'),
Button(description='weight=1; 0%', layout=Layout(flex='1 1 0%', width='auto'), button_style='danger'),
]
box_layout = Layout(align_items='stretch', width='70%')
HBox(children=items, layout=box_layout)
from ipywidgets import Button, HBox, VBox
left_box = VBox([Button(description="left {}".format(i)) for i in range(1,3)])
right_box = VBox([Button(description="right {}".format(i)) for i in range(1,3)])
HBox([left_box, right_box])
left_box.children[0].style.button_color = 'lightblue'
right_box.children = [Button(description="new right {}".format(i)) for i in range(1,5)]
right_box.layout.flex_flow = 'column-reverse'
left_box.layout.justify_content = 'flex-end'
left_box.layout.justify_content = 'space-between'
left_box.children[0].layout.flex = '1'
left_box.children[1].layout.flex = '1'
from ipywidgets import Valid
tools = [Button(description="Tool {}".format(i)) for i in range(1,4)]
status = Valid(value=True, description='Status')
HBox(tools + [status])
Align status to right
toolbar = HBox([HBox(tools), status], layout={'justify_content': 'space-between'})
toolbar
from ipywidgets import Layout, Button, Box, Label, VBox
item_layout = Layout(height='100px', min_width='40px')
items = [Button(layout=item_layout, description=str(i), button_style='warning') for i in range(40)]
box_layout = Layout(overflow='scroll hidden',
border='3px solid black',
width='500px',
height='',
flex_flow='row',
display='flex')
carousel = Box(children=items, layout=box_layout)
VBox([Label('Scroll horizontally:'), carousel])
Use the dropdowns and sliders in the widget to change the layout of the box containing the colored buttons. Many of the CSS layout options described above are available, and the Python code to generate a Layout
object reflecting the settings is in a TextArea
in the widget.
A few questions to answer after the demonstration of this (see the detailed flexbox guide for a longer discussion):
justify_content
affect? You may find it easier to answer this if you set wrap
to wrap
.align_items
affect?align_content
different than align_items
?from layout_preview import layout
layout
This example, from earlier in this notebook, lays out 4 buttons vertically.
from ipywidgets import Layout, Button, Box
items_layout = Layout(width='auto') # override the default width of the button to 'auto' to let the button grow
box_layout = Layout(display='flex',
flex_flow='column',
align_items='stretch',
border='solid',
width='20%')
words = ['correct', 'horse', 'battery', 'staple']
items = [Button(description=word, layout=items_layout, button_style='danger') for word in words]
box = Box(children=items, layout=box_layout)
box
Flexbox allows you to change the order and orientation of the children items in the flexbox without changing the children themselves.
flex_flow
so that the buttons are displayed in a single column in reverse order.flex_flow
so that the buttons are displayed in a single row instead of a column.align_items
and describe how it affects the display of the buttons.width
, then change flex_flow
to lay out the buttons in rows that wrap so that there is a 2x2 grid of buttons.Feel free to figure out the layout using the tool above and copy/paste the layout here!
The code that generated the carousel is reproduced below. Run the cell, then continue reading.
from ipywidgets import Layout, Button, VBox, Label
item_layout = Layout(height='100px', min_width='40px')
items = [Button(layout=item_layout, description=str(i), button_style='warning') for i in range(40)]
box_layout = Layout(overflow_x='scroll',
border='3px solid black',
width='500px',
height='',
flex_flow='row',
display='flex')
carousel = Box(children=items, layout=box_layout)
VBox([Label('Scroll horizontally:'), carousel])
To do:
min_width
for one of the items
, say the first one. Does it affect only the first one, or all of them? Why?height
of only the first button. Hint: It needs its own Layout
.items[0].layout.min_width = 'FILL IN WITH A WIDTH'