使用布局模板#

正如我们在Jupyter小部件的布局中展示的,可以使用灵活的 GridBox 规范将多个小部件排列在一起。然而,使用该规范需要对CSS属性有一定的了解,可能会带来陡峭的学习曲线。在这里,我们将描述基于GridBox构建的布局模板,这些模板简化了常见小部件布局的创建。

# Utils widgets
from ipywidgets import Button, Layout, jslink, IntText, IntSlider

def create_expanded_button(description, button_style):
    return Button(description=description, button_style=button_style, layout=Layout(height='auto', width='auto'))

top_left_button = create_expanded_button("Top left", 'info')
top_right_button = create_expanded_button("Top right", 'success')
bottom_left_button = create_expanded_button("Bottom left", 'danger')
bottom_right_button = create_expanded_button("Bottom right", 'warning')

top_left_text = IntText(description='Top left', layout=Layout(width='auto', height='auto'))
top_right_text = IntText(description='Top right', layout=Layout(width='auto', height='auto'))
bottom_left_slider = IntSlider(description='Bottom left', layout=Layout(width='auto', height='auto'))
bottom_right_slider = IntSlider(description='Bottom right', layout=Layout(width='auto', height='auto'))

2x2 Grid#

您可以轻松地使用TwoByTwoLayout小部件创建一个布局,该布局在2x2矩阵上排列了4个小部件:

from ipywidgets import TwoByTwoLayout

TwoByTwoLayout(top_left=top_left_button,
               top_right=top_right_button,
               bottom_left=bottom_left_button,
               bottom_right=bottom_right_button)

如果你没有为某些插槽定义小部件,布局将通过合并相邻的单元格自动重新配置自己。

TwoByTwoLayout(top_left=top_left_button,
               bottom_left=bottom_left_button,
               bottom_right=bottom_right_button)

如果你不希望这种行为,你可以在TwoByTwoLayout构造函数的参数中传递merge=False

TwoByTwoLayout(top_left=top_left_button,
               bottom_left=bottom_left_button,
               bottom_right=bottom_right_button,
               merge=False)

即使在布局初始化之后,你也可以添加缺失的小部件:

layout_2x2 = TwoByTwoLayout(top_left=top_left_button,
                            bottom_left=bottom_left_button,
                            bottom_right=bottom_right_button)
layout_2x2
layout_2x2.top_right = top_right_button

你还可以使用小部件的链接特性来根据另一个小部件更新某个小部件的某些属性:

app = TwoByTwoLayout(top_left=top_left_text, top_right=top_right_text,
                     bottom_left=bottom_left_slider, bottom_right=bottom_right_slider)
                     
link_left = jslink((app.top_left, 'value'), (app.bottom_left, 'value'))
link_right = jslink((app.top_right, 'value'), (app.bottom_right, 'value'))
app.bottom_right.value = 30
app.top_left.value = 25
app

你可以使用自定义小部件轻松创建更复杂的布局。例如,你可以使用bqplot Figure小部件来添加图表:

import bqplot as bq
import numpy as np
size = 100
np.random.seed(0)

x_data = range(size)
y_data = np.random.randn(size)
y_data_2 = np.random.randn(size)
y_data_3 = np.cumsum(np.random.randn(size) * 100.)

x_ord = bq.OrdinalScale()
y_sc = bq.LinearScale()

bar = bq.Bars(x=np.arange(10), y=np.random.rand(10), scales={'x': x_ord, 'y': y_sc})
ax_x = bq.Axis(scale=x_ord)
ax_y = bq.Axis(scale=y_sc, tick_format='0.2f', orientation='vertical')

fig = bq.Figure(marks=[bar], axes=[ax_x, ax_y], padding_x=0.025, padding_y=0.025,
                layout=Layout(width='auto', height='90%'))
from ipywidgets import FloatSlider

max_slider = FloatSlider(min=0, max=10, default_value=2, description="Max: ",
                         layout=Layout(width='auto', height='auto'))
min_slider = FloatSlider(min=-1, max=10, description="Min: ",
                         layout=Layout(width='auto', height='auto'))
app = TwoByTwoLayout(top_left=min_slider,
                     bottom_left=max_slider, 
                     bottom_right=fig,
                     align_items="center", 
                     height='700px')

jslink((y_sc, 'max'), (max_slider, 'value'))
jslink((y_sc, 'min'), (min_slider, 'value'))
jslink((min_slider, 'max'), (max_slider, 'value'))
jslink((max_slider, 'min'), (min_slider, 'value'))

max_slider.value = 1.5
app

AppLayout#

AppLayout 是一个小部件布局模板,允许你创建类似应用程序的小部件排列。它由一个头部、一个底部、两个侧边栏和一个中央窗格组成:

from ipywidgets import AppLayout, Button, Layout
header_button = create_expanded_button('Header', 'success')
left_button = create_expanded_button('Left', 'info')
center_button = create_expanded_button('Center', 'warning')
right_button = create_expanded_button('Right', 'info')
footer_button = create_expanded_button('Footer', 'success')
AppLayout(header=header_button,
          left_sidebar=left_button,
          center=center_button,
          right_sidebar=right_button,
          footer=footer_button)

然而,有了自动合并特性,可以实现许多其他布局:

AppLayout(header=None,
          left_sidebar=None,
          center=center_button,
          right_sidebar=None,
          footer=None)
AppLayout(header=header_button,
          left_sidebar=left_button,
          center=center_button,
          right_sidebar=right_button,
          footer=None)
AppLayout(header=None,
          left_sidebar=left_button,
          center=center_button,
          right_sidebar=right_button,
          footer=None)
AppLayout(header=header_button,
          left_sidebar=left_button,
          center=center_button,
          right_sidebar=None,
          footer=footer_button)
AppLayout(header=header_button,
          left_sidebar=None,
          center=center_button,
          right_sidebar=right_button,
          footer=footer_button)
AppLayout(header=header_button,
          left_sidebar=None,
          center=center_button,
          right_sidebar=None,
          footer=footer_button)
AppLayout(header=header_button,
          left_sidebar=left_button,
          center=None,
          right_sidebar=right_button,
          footer=footer_button)

你还可以使用pane_widthspane_heights参数来修改窗格的相对和绝对宽度和高度。两者都接受一个包含三个元素的序列,每个元素可以是一个整数(相当于给定给行/列的权重),也可以是格式为'1fr'(与整数相同)或'100px'(绝对大小)的字符串。

AppLayout(header=header_button,
          left_sidebar=left_button,
          center=center_button,
          right_sidebar=right_button,
          footer=footer_button,
          pane_widths=[3, 3, 1],
          pane_heights=[1, 5, '60px'])

Grid layout#

GridspecLayout是一个N乘M的网格布局,允许使用类似于matplotlib的GridSpec的API来灵活定义布局。

你可以使用GridspecLayout来定义一个简单的等间距网格。例如,要创建一个4x3的布局:

from ipywidgets import GridspecLayout

grid = GridspecLayout(4, 3)

for i in range(4):
    for j in range(3):
        grid[i, j] = create_expanded_button('Button {} - {}'.format(i, j), 'warning')
grid

要使一个小部件跨越几个列和/或行,你可以使用切片表示法:

grid = GridspecLayout(4, 3, height='300px')
grid[:3, 1:] = create_expanded_button('One', 'success')
grid[:, 0] = create_expanded_button('Two', 'info')
grid[3, 1] = create_expanded_button('Three', 'warning')
grid[3, 2] = create_expanded_button('Four', 'danger')
grid

你仍然可以使用相同的索引表示法来更改存储在网格中的小部件的属性。

grid = GridspecLayout(4, 3, height='300px')
grid[:3, 1:] = create_expanded_button('One', 'success')
grid[:, 0] = create_expanded_button('Two', 'info')
grid[3, 1] = create_expanded_button('Three', 'warning')
grid[3, 2] = create_expanded_button('Four', 'danger')

grid
grid[0, 0].description = "I am the blue one"

注意:只需传递感兴趣的小部件所占据的网格单元之一的索引即可。在这种情况下不支持切片。

如果已经有一个与要添加的小部件位置冲突的小部件,它将从网格中移除:

grid = GridspecLayout(4, 3, height='300px')
grid[:3, 1:] = create_expanded_button('One', 'info')
grid[:, 0] = create_expanded_button('Two', 'info')
grid[3, 1] = create_expanded_button('Three', 'info')
grid[3, 2] = create_expanded_button('Four', 'info')

grid
grid[3, 1] = create_expanded_button('New button!!', 'danger')

注意:在这种情况下支持切片。

grid[:3, 1:] = create_expanded_button('I am new too!!!!!', 'warning')

使用GridspecLayout创建散点图#

在这些示例中,我们将演示如何使用GridspecLayoutbqplot小部件创建一个多面板散点图。要运行此示例,您需要安装bqplot包。

例如,您可以使用以下代码片段来获取多个维度的散点图:

import bqplot as bq
import numpy as np
from ipywidgets import GridspecLayout, Button, Layout

n_features = 5
data = np.random.randn(100, n_features)
data[:50, 2] += 4 * data[:50, 0] **2
data[50:, :] += 4

A = np.random.randn(n_features, n_features)/5

data = np.dot(data,A)

scales_x = [bq.LinearScale() for i in range(n_features)]
scales_y = [bq.LinearScale() for i in range(n_features)]

gs = GridspecLayout(n_features, n_features)
for i in range(n_features):
    for j in range(n_features):
        
        if i != j:
            sc_x = scales_x[j]
            sc_y = scales_y[i]

            scatt = bq.Scatter(x=data[:, j], y=data[:, i], scales={'x': sc_x, 'y': sc_y}, default_size=1)

            gs[i, j] = bq.Figure(marks=[scatt], layout=Layout(width='auto', height='auto'),
                                 fig_margin=dict(top=0, bottom=0, left=0, right=0))
        else:
            sc_x = scales_x[j]
            sc_y = bq.LinearScale()
        
            hist = bq.Hist(sample=data[:,i], scales={'sample': sc_x, 'count': sc_y})
            
            gs[i, j] = bq.Figure(marks=[hist], layout=Layout(width='auto', height='auto'),
                                 fig_margin=dict(top=0, bottom=0, left=0, right=0))
gs

Style 属性#

您可以指定额外的样式属性来修改布局。例如,您可以使用heightwidth参数来更改整个布局的大小。

AppLayout(header=None,
          left_sidebar=left_button,
          center=center_button,
          right_sidebar=right_button,
          footer=None,
          height="200px", width="50%")

使用grid_gap参数可以增大或减小窗格之间的间隙:

AppLayout(header=None,
          left_sidebar=left_button,
          center=center_button,
          right_sidebar=right_button,
          footer=None,
          height="200px", width="50%",
          grid_gap="10px")

此外,您可以使用justify_contentalign_items属性来控制小部件在布局中的对齐方式:

from ipywidgets import Text, HTML
TwoByTwoLayout(top_left=top_left_button, top_right=top_right_button,
               bottom_right=bottom_right_button,
               justify_items='center',
               width="50%",
               align_items='center')

对于其他对齐选项,可以使用常见的名称(topbottom)或它们的CSS等效项(flex-startflex-end):

TwoByTwoLayout(top_left=top_left_button, top_right=top_right_button,
               bottom_right=bottom_right_button,
               justify_items='center',
               width="50%",
               align_items='top')

示例#

在这个笔记本中,您将找到一个使用AppLayout的完整示例。