Jupyter widgets 布局#

本笔记本展示了如何布局Jupyter交互式小部件,以构建丰富且响应式的基于小部件的应用程序。

您可以直接跳转到这些部分:

layout 属性#

Jupyter 交互式小部件有一个 layout 属性,暴露了多个影响小部件布局的 CSS 属性。

暴露的CSS属性#

以下属性映射到同名CSS属性的值(下划线替换为连字符),应用于对应小部件的顶部DOM元素。

尺寸#

  • 高度

  • 宽度

  • max_height

  • max_width

  • min_height

  • min_width

显示#

  • 可见性

  • 展示

  • overflow 溢出

盒模型#

  • 边框

  • margin(页边距)

  • 内边距

定位#

  • 顶部

  • 底部

  • 右侧

弹性盒模型#

  • 顺序

  • flex_flow

  • align_items

  • 弹性

  • align_self

  • align_content

  • justify_content

网格布局#

  • grid_auto_columns

  • grid_auto_flow

  • grid_auto_rows

  • 网格间隙

  • grid_template

  • grid_row

  • grid_column

简写CSS属性#

您可能已经注意到,某些CSS属性如margin-[top/right/bottom/left]似乎缺失了。同样的情况也适用于padding-[top/right/bottom/left]等等。

实际上,你可以通过单独使用 margin 属性,原子性地指定 [top/right/bottom/left] 边距,通过传递字符串 '100px 150px 100px 80px' 来分别为上、右、下和左边距设置 10015010080 像素。

类似地,flex 属性可以包含 flex-growflex-shrinkflex-basis 的值。border 属性是 border-widthborder-style (必需)border-color 的简写属性。

简单示例#

以下示例展示了如何调整一个Button的大小,使其视图的高度为80px,宽度为可用空间的50%

from ipywidgets import Button, Layout

b = Button(description='(50% width, 80px height) button',
           layout=Layout(width='50%', height='80px'))
b

layout 属性可以在多个小部件之间共享并直接分配。

Button(description='Another button with the same layout', layout=b.layout)

描述#

您可能已经注意到,长描述被截断了。这是因为描述长度默认是固定的。

from ipywidgets import IntSlider

IntSlider(description='A too long description')

你可以调整描述的长度以适配描述文本。但这会使小组件本身变短。你可以通过调整描述宽度和小组件宽度(使用小组件的样式)来同时改变两者。

style = {'description_width': 'initial'}
IntSlider(description='A too long description', style=style)

如果您需要更多灵活性来布局小部件和描述,可以直接使用Label小部件。

from ipywidgets import HBox, Label

HBox([Label('A too long description'), IntSlider()])

自然尺寸以及使用HBox和VBox的布局#

大部分核心小部件都有默认的高度和宽度,能够很好地平铺在一起。这允许基于 HBoxVBox 辅助函数的简单布局自然对齐:

from ipywidgets import Button, HBox, VBox

words = ['correct', 'horse', 'battery', 'staple']
items = [Button(description=w) for w in words]
left_box = VBox([items[0], items[1]])
right_box = VBox([items[2], items[3]])
HBox([left_box, right_box])

LaTeX#

小部件(widgets)如滑块和文本输入具有一个描述属性,可以渲染Latex公式。Label小部件也可以渲染Latex公式。

from ipywidgets import IntSlider, Label
IntSlider(description=r'\(\int_0^t f\)')
Label(value=r'\(e=mc^2\)')

数值格式化#

滑块具有读数显示区域,可使用Python的格式规范迷你语言进行格式化。若滑块数值的字符串表示所需空间超过读数区域可用宽度,则会应用不同样式以显示并非所有数字都可见。

Flexbox布局#

上面的 HBoxVBox 类是 Box 控件的特例。

Box 部件实现了完整的 CSS flexbox 规范以及 Grid 布局规范,使得在 Jupyter 笔记本中能够实现丰富的响应式布局。其目标是为容器内项目提供一种高效的布局、对齐和空间分配方式。

再次强调,整个弹性盒子规范通过容器部件(Box)和所含项目的layout属性展现。可以在所有所含项目之间共享相同的layout属性。

致谢#

以下关于弹性盒布局的弹性盒教程遵循克里斯·科伊尔(Chris Coyier)的文章A Complete Guide to Flexbox的思路,并经许可使用了该文章中的文本和各种图像。

基础与术语#

由于flexbox是一个完整的模块而非单一属性,它涉及许多内容,包括一整套属性。其中一些属性用于设置容器(父元素,称为“flex容器”),而其他属性则用于设置子元素(称为“flex items”)。

如果常规布局基于块和内联流动方向,弹性布局则基于“弹性流动方向”。请查看规范中的这张图,它解释了弹性布局背后的主要思想。

Flexbox

基本上,元素将沿着main axis(从main-startmain-end)或cross axis(从cross-startcross-end)进行布局。

  • main axis - flex容器的主轴是flex项目布局的主要轴线。注意,它不一定是水平的;这取决于flex-direction属性(见下文)。

  • main-start | main-end - 弹性项目放置在容器内,从main-start开始到main-end结束。

  • main size - 弹性项目的宽度或高度,取决于主轴方向,即为项目的主尺寸。弹性项目的主尺寸属性为'width'或'height'属性,取决于主轴方向。 cross axis - 与主轴垂直的轴称为交叉轴。其方向取决于主轴方向。

  • cross-start | cross-end - Flex 行从 flex 容器的 cross-start 侧开始填充项目,并朝 cross-end 侧放置。

  • cross size - 弹性项目的交叉尺寸,即处于交叉轴方向的宽度或高度。交叉尺寸属性是指处于交叉轴方向的'width'或'height'属性。

父级属性#

Container

显示#

display 可以是 flexinline-flex。这定义了一个弹性容器(块级或内联)。

flex-flow#

flex-flowflex-directionflex-wrap 属性的简写,它们共同定义弹性容器的主轴和交叉轴。默认值为 row nowrap

  • flex-direction (column-reverse | column | row | row-reverse )

    这定义了主轴方向,从而决定了弹性项目在弹性容器中的排列方向。弹性盒布局(除了可选的换行外)是一个单向布局概念。可以将弹性项目想象为主要在水平行或垂直列中排列。 Direction

  • flex-wrap (nowrap | wrap | wrap-reverse)

    默认情况下,弹性项目会全部尝试放在一行上。您可以通过此属性更改此行为,允许项目根据需要换行。方向在此也起到作用,决定了新行的堆叠方向。 Wrap

justify-content#

justify-content 可以是下列值之一:flex-start, flex-end, center, space-between, space-around。这一定义了沿主轴的对齐方式。当一行中的所有弹性项目为不可伸缩时,或为可伸缩但已达到其最大尺寸时,它有助于分配剩余的自由空间。当项目溢出该行时,它还会对项目的对齐方式施加一定的控制。 Justify

align-items#

align-items 可以是 flex-start, flex-end, center, baseline, stretch 中的一个。这定义了 flex 项目在当前行的交叉轴上布局的默认行为。可以将其视为交叉轴(垂直于主轴)上的 justify-content 版本。 Items

align-content#

align-content 可以是 flex-start, flex-end, center, baseline, stretch 中的一个。这会在交叉轴上有额外空间时对齐弹性容器的行,类似于 justify-content 在主轴上对齐各个项目的方式。 Items

注意:当只有一行弹性项目时,此属性无效。

项目的属性#

Item

如果父元素不是flexbox容器(即具有等于display属性为flexinline-flex),则与flexbox相关的CSS属性对项目没有影响。

订单#

默认情况下,弹性项目按照源码顺序排列。不过,order属性控制它们在弹性容器中的显示顺序。 Order

弹性布局#

flex 是三个属性的简写,包括 flex-growflex-shrinkflex-basis 的组合。第二和第三个参数(flex-shrinkflex-basis)是可选的。默认值为 0 1 auto

  • flex-grow

    这定义了弹性项目在必要时增长的能力。它接受一个无单位的值作为比例。它规定了项目应该占用弹性容器内可用空间的多少。

    如果所有项目的 flex-grow 都设置为 1,容器中的剩余空间将平均分配给所有子项。如果其中一个子项值为 2,该子项将占用其他子项两倍的剩余空间(或者至少会尝试这样做)。 Grow

  • flex-shrink

    这定义了弹性项目在必要时收缩的能力。

  • flex-basis

    这定义了在分配剩余空间之前元素的默认大小。它可以是长度(例如 20%, 5rem 等)或关键字。auto 关键字意为 "查看我的宽度或高度属性"

align-self#

align-self 允许为单独的弹性项目覆盖默认对齐方式(或由 align-items 指定的对齐方式)。

Align

VBox与HBox辅助工具#

VBoxHBox 辅助类提供了简单的默认设置,用于在垂直和水平盒子中排列子部件。它们大致等同于:

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

示例#

四个按钮在一个VBox中。项目拉伸至最大宽度,位于垂直框中,占据可用空间的50%

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='50%')

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

三个按钮放入一个HBox中。项目按它们的权重比例伸缩。

from ipywidgets import Layout, Button, Box, VBox

# Items flex proportionally to the weight and the left over space around the text 
items_auto = [
    Button(description='weight=1; auto', layout=Layout(flex='1 1 auto', width='auto'), button_style='danger'),
    Button(description='weight=3; auto', layout=Layout(flex='3 1 auto', width='auto'), button_style='danger'),
    Button(description='weight=1; auto', layout=Layout(flex='1 1 auto', width='auto'), button_style='danger'),
 ]

# Items flex proportionally to the weight 
items_0 = [
    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(display='flex',
                    flex_flow='row', 
                    align_items='stretch', 
                    width='70%')
box_auto = Box(children=items_auto, layout=box_layout)
box_0 = Box(children=items_0, layout=box_layout)
VBox([box_auto, box_0])

一个更高级的示例:反应式表单。

表单是一个宽度为'50%'的VBox。VBox中的每一行都是一个HBox,用于在内容之间用空格进行对齐。

from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, IntSlider

form_item_layout = Layout(
    display='flex',
    flex_flow='row',
    justify_content='space-between'
)

form_items = [
    Box([Label(value='Age of the captain'), IntSlider(min=40, max=60)], layout=form_item_layout),
    Box([Label(value='Egg style'), 
         Dropdown(options=['Scrambled', 'Sunny side up', 'Over easy'])], layout=form_item_layout),
    Box([Label(value='Ship size'), 
         FloatText()], layout=form_item_layout),
    Box([Label(value='Information'), 
         Textarea()], layout=form_item_layout)
]

form = Box(form_items, layout=Layout(
    display='flex',
    flex_flow='column',
    border='solid 2px',
    align_items='stretch',
    width='50%'
))
form

更高级的示例:轮播组件。

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='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])

网格布局#

GridBox 类是 Box 小部件的一个特例。

Box 小部件支持完整的 CSS flexbox 规范,能够在 Jupyter notebook 中实现丰富的响应式布局。它旨在提供一种高效的方式来布置、对齐和分配容器内项目之间的空间。

再次说明,整个网格布局规范通过容器部件(Box)和所包含项的layout属性公开。可以在所有包含项之间共享相同的layout属性。

以下关于flexbox布局的flexbox教程遵循Chris House的文章A Complete Guide to Grid的思路,并使用该篇文章with permission中的文字和各种图片。

基础与浏览器支持#

要开始使用,您需要将容器元素定义为具有 display: grid 的网格,使用 grid-template-rows、grid-template-columns 和 grid_template_areas 设置列和行的大小,然后使用 grid-column 和 grid-row 将其子元素放置到网格中。与 flexbox 类似,网格项目的源顺序并不重要。您的 CSS 可以以任何顺序排列它们,这使得使用媒体查询重新排列网格变得非常容易。想象一下定义整个页面的布局,然后只需几行 CSS 代码即可完全重新排列以适应不同的屏幕宽度。网格是有史以来最强大的 CSS 模块之一。

截至2017年3月,大多数浏览器都推出了对CSS Grid的原生、无前缀支持:Chrome(包括安卓版)、Firefox、Safari(包括iOS版)和Opera。另一方面,Internet Explorer 10和11虽然支持,但实现方式较旧,语法已过时。现在是使用网格构建的时候了!

重要术语#

在深入了解网格概念之前,理解术语非常重要。由于这里涉及的术语在概念上都有点相似,如果不先记住网格规范中定义的含义,很容易将它们混淆。但别担心,术语并不多。

网格容器

应用了 display: grid 的元素。它是所有网格项的直接父级。在此示例中,container 是网格容器。

<div class="container">
  <div class="item item-1"></div>
  <div class="item item-2"></div>
  <div class="item item-3"></div>
</div>

网格项

网格容器的子元素(例如直接后代)。这里的项目元素是网格项,但子项不是。

<div class="container">
  <div class="item"></div> 
  <div class="item">
  	<p class="sub-item"></p>
  </div>
  <div class="item"></div>
</div>

网格线

构成网格结构的分隔线。它们可以是垂直的(“列网格线”)或水平的(“行网格线”),并位于行或列的任一侧。这里的黄线是列网格线的一个示例。

grid-line

网格轨道

两个相邻网格线之间的空间。你可以把它们看作是网格的列或行。这是第二和第三行网格线之间的网格轨道。

grid-track

网格单元格

两个相邻行和两个相邻列网格线之间的空间。它是网格的单个“单元”。这是位于行网格线1和2以及列网格线2和3之间的网格单元。

grid-cell

网格区域

四个网格线所围成的总空间。一个网格区域可以由任意数量的网格单元组成。这里是第1行到第3行的网格线和第1列到第3列的网格线之间的网格区域。

grid-area

父级属性#

grid-template-rows, grid-template-columns

通过一个以空格分隔的值列表定义网格的列和行。这些值代表轨道尺寸,它们之间的空格代表网格线。

值:

  • <track-size> - 可以是长度、百分比或网格中可用空间的一部分(使用 fr 单位)

  • <line-name> - 您选择的任意名称

grid-template-areas

通过引用由grid-area属性指定的网格区域名称来定义网格模板。重复网格区域的名称会使内容跨越这些单元格。句点表示空单元格。该语法本身提供了网格结构的可视化。

值:

  • <grid-area-name> - 使用grid-area指定的网格区域名称

  • . - 一个句点表示一个空的网格单元格

  • none - 未定义任何网格区域

网格间距

grid-row-gapgrid-column-gap的简写

值:

  • <grid-row-gap>, <grid-column-gap> - 长度数值

其中 grid-row-gapgrid-column-gap 指定了网格线的尺寸。你可以将其理解为设置列与行之间的沟槽宽度。

  • <line-size> - 一个长度值

注意:grid-前缀将被移除,grid-gap将重命名为gap。无前缀属性已在Chrome 68+、Safari 11.2 Release 50+和Opera 54+中得到支持。

align-items

沿块(列)轴对齐网格项(与沿内联(行)轴对齐的 justify-items 相对)。此值适用于容器内的所有网格项。

值:

  • start - 将项目对齐到其单元格的起始边缘

  • end - 将项目对齐到其单元格的末端边缘

  • center - 将项目在其单元格内居中对齐

  • stretch - 填充单元格的整个高度(此为默认设置)

justify-items

将网格项目沿内联(行)轴对齐(与沿块(列)轴对齐的align-items相反)。此值适用于容器内的所有网格项目。

值:

  • start - 将项目对齐到其单元格的起始边缘

  • end - 将项目对齐到其单元格的末端边缘

  • center - 将项目在其单元格内居中对齐

  • stretch - 填充单元格的整个宽度(这是默认设置)

align-content

有时网格的总尺寸可能小于其网格容器的尺寸。如果所有网格项都使用非弹性单位(如px)进行大小调整,则可能发生这种情况。在这种情况下,您可以设置网格在网格容器内的对齐方式。该属性使网格沿块(列)轴对齐(与justify-content属性相反,后者使网格沿内联(行)轴对齐)。

值:

  • start - 将网格对齐到网格容器的起始边缘

  • end - 将网格对齐到网格容器的末端边缘

  • center - 将网格对齐到网格容器的中心

  • stretch - 调整网格项的大小,使网格能够填满网格容器的全部高度

  • space-around - 在每个网格项目之间均匀分布空间,两端留有半尺寸空间

  • space-between - 在每个网格项之间放置相等的空间,但在最远端不留空间

  • space-evenly - 在每个网格项目之间均匀分布间距,包括最远端

justify-content

有时,网格的总大小可能小于其网格容器的大小。如果所有网格项都使用非弹性单位(如px)调整大小,则可能发生这种情况。在这种情况下,您可以设置网格在容器内的对齐方式。此属性沿内联(行)轴对齐网格(与align-content属性相反,后者沿块(列)轴对齐网格)。

值:

  • start - 将网格对齐到网格容器的起始边缘

  • end - 将网格对齐到网格容器的末端边缘

  • center - 将网格对齐到网格容器的中心

  • stretch - 调整网格项大小以使网格填满网格容器的整个宽度

  • space-around - 在每个网格项目之间均匀分布空间,两端留有半尺寸空间

  • space-between - 在每个网格项之间放置相等的空间,但在最远端不留空间

  • space-evenly - 在每个网格项目之间均匀分布间距,包括最远端

grid-auto-columns, grid-auto-rows

指定任何自动生成的网格轨道(又称隐式网格轨道)的大小。当网格项目数量超过网格中的单元格,或者网格项目被放置于显式网格之外时,会创建隐式轨道。(详见显式网格与隐式网格的区别)

值:

  • <track-size> - 可以是长度、百分比或网格中可用空间的一部分(使用 fr 单位)

项目的属性#

注意:floatdisplay: inline-blockdisplay: table-cellvertical-aligncolumn-?? 属性对网格项无效。

grid-column, grid-row

通过引用特定的网格线来确定网格项目在网格中的位置。grid-column-start/grid-row-start是项目开始的行,而grid-column-end/grid-row-end是项目结束的行。

值:

  • <line> - 可以是一个数字,引用带编号的网格线,或者是一个名称,引用命名的网格线

  • span <number> - 该项将跨越所提供的网格轨道数量

  • span <name> - 该条目将跨越直到遇到具有所提供名称的下一行

  • auto - 表示自动放置、自动跨度或默认跨度为1

.item {
  grid-column: <number> | <name> | span <number> | span <name> | auto / 
               <number> | <name> | span <number> | span <name> | auto
  grid-row: <number> | <name> | span <number> | span <name> | auto /
            <number> | <name> | span <number> | span <name> | auto
}

示例:

.item-a {
  grid-column: 2 / five;
  grid-row: row1-start / 3;
}

grid-start-end-a

.item-b {
  grid-column: 1 / span col4-start;
  grid-row: 2 / span 2;
}

grid-start-end-b

如果未声明grid-column / grid-row,项目默认将跨越1个轨道。

项目可以相互重叠。你可以使用 z-index 来控制它们的堆叠顺序。

网格区域

给项目命名,以便可以通过使用grid-template-areas属性创建的模板来引用。或者,这个属性可以作为grid-row-start + grid-column-start + grid-row-end + grid-column-end的更为简短的简写形式。

值:

  • <name> - 您选择的名称

  • <row-start> / <column-start> / <row-end> / <column-end> - 可以是数字或命名线

.item {
  grid-area: <name> | <row-start> / <column-start> / <row-end> / <column-end>;
}

示例:

作为一种为项目命名的方式:

.item-d {
  grid-area: header
}

作为 grid-row-start + grid-column-start + grid-row-end + grid-column-end 的简写:

.item-d {
  grid-area: 1 / col4-start / last-line / 6
}

grid-start-end-d

justify-self

将网格项在单元格内沿行内(行)轴对齐(与沿块(列)轴对齐的 align-self 相反)。此值适用于单个单元格内的网格项。

值:

  • start - 将网格项对齐到单元格的起始边缘

  • end - 将网格项与单元格的末端边缘对齐

  • center - 将网格项在单元格中居中对齐

  • stretch - 填充单元格的整个宽度(这是默认设置)

.item {
  justify-self: start | end | center | stretch;
}

示例:

.item-a {
  justify-self: start;
}

Example of  set to start

.item-a {
  justify-self: end;
}

Example of  set to end

.item-a {
  justify-self: center;
}

Example of  set to center

.item-a {
  justify-self: stretch;
}

Example of  set to stretch

要为网格中所有项目设置对齐方式,此行为也可以通过网格容器上的justify-items属性进行设置。

from ipywidgets import Button, GridBox, Layout, ButtonStyle

按名称放置项目:

header  = Button(description='Header',
                 layout=Layout(width='auto', grid_area='header'),
                 style=ButtonStyle(button_color='lightblue'))
main    = Button(description='Main',
                 layout=Layout(width='auto', grid_area='main'),
                 style=ButtonStyle(button_color='moccasin'))
sidebar = Button(description='Sidebar',
                 layout=Layout(width='auto', grid_area='sidebar'),
                 style=ButtonStyle(button_color='salmon'))
footer  = Button(description='Footer',
                 layout=Layout(width='auto', grid_area='footer'),
                 style=ButtonStyle(button_color='olive'))

GridBox(children=[header, main, sidebar, footer],
        layout=Layout(
            width='50%',
            grid_template_rows='auto auto auto',
            grid_template_columns='25% 25% 25% 25%',
            grid_template_areas='''
            "header header header header"
            "main main . sidebar "
            "footer footer footer footer"
            ''')
       )

设置行和列模板以及间隙

GridBox(children=[Button(layout=Layout(width='auto', height='auto'),
                         style=ButtonStyle(button_color='darkseagreen')) for i in range(9)
                 ],
        layout=Layout(
            width='50%',
            grid_template_columns='100px 50px 100px',
            grid_template_rows='80px auto 80px', 
            grid_gap='5px 10px')
       )

Image 布局和尺寸调整#

由于历史原因(HTML标签img在CSS之前就已存在)和实际原因(图像具有固有尺寸),图像的布局和大小与其他元素略有不同。

图像尺寸设置特别令人困惑,因为有两种看似合理的方法来指定图像大小。Image 小部件具有 widthheight 属性,这些属性对应于 HTML img 标签中同名的属性。此外,Image 小部件与所有其他小部件一样,具有一个 layout,该布局同样包含 widthheight 属性。

此外,对图像应用了一些不适用于其他小部件的CSS样式:max_width设置为100%height设置为auto

您不应依赖 Image.widthImage.height 来确定图像小部件的显示宽度和高度。任何CSS样式,无论是通过 Image.layout 还是其他来源,都将覆盖 Image.widthImage.height

当单独显示一个Image控件时,将Image.layout.width设置为所需宽度,将显示具有与原始图像相同宽高比的Image控件。

当将Image放置在Box(或HBoxVBox)内时,结果取决于是否在Box上设置了宽度。如果已设置,则图像将被拉伸(或压缩)以适应盒子,因为Image.layout.max_width100%,所以图像会填充容器。这通常不会保持图像的宽高比。

控制容器内Image的显示#

使用 Image.layout.object_fit 来控制图像在容器(如盒子)内的缩放方式。可能的取值为:

  • 'contain': 将图像适配到其内容框中,同时保持宽高比。如果容器的任何部分未被图像覆盖,则会显示容器的背景。内容框的大小是:如果容器比图像小,则为容器的大小;如果容器比图像大,则为图像的大小。

  • 'cover': 完全填充内容框,同时保持图像的宽高比,必要时裁切图像。

  • 'fill': 完全填充内容框,根据需要拉伸/压缩图像。

  • 'none': 不调整大小;图像将被容器裁剪.

  • 'scale-down': 执行与containnone相同的操作,使用能产生更小显示图像的选项。

  • None (Python 值): 从布局中移除 object_fit; 效果与 'fill' 相同。

使用 Image.layout.object_position 控制图像在容器(如盒子)内的定位方式。默认值确保图像在盒子中居中。在某些情况下,Image.layout.object_position 的效果取决于 Image.layout.object_fit 的值。

有多种方式可以指定 object_position 的值,如下所述。

object_fit 示例#

在下面的示例中,一张图片被显示在一个绿色框内,以展示object_fit的每个值。

为保持示例的一致性,在此定义通用代码。

from ipywidgets import Layout, Box, VBox, HBox, HTML, Image

fit_options = ['contain', 'cover', 'fill', 'scale-down', 'none', None]

hbox_layout = Layout()
hbox_layout.width = '100%'
hbox_layout.justify_content = 'space-around'

green_box_layout = Layout()
green_box_layout.width = '100px'
green_box_layout.height = '100px'
green_box_layout.border = '2px solid green'


def make_box_for_grid(image_widget, fit):
    """
    Make a VBox to hold caption/image for demonstrating
    option_fit values.
    """
    # Make the caption
    if fit is not None:
        fit_str = "'{}'".format(fit)
    else:
        fit_str = str(fit)
        
    h = HTML(value='' + str(fit_str) + '')

    # Make the green box with the image widget inside it
    boxb = Box()
    boxb.layout = green_box_layout
    boxb.children = [image_widget]
    
    # Compose into a vertical box
    vb = VBox()
    vb.layout.align_items = 'center'
    vb.children = [h, boxb]
    return vb

# Use this margin to eliminate space between the image and the box
image_margin = '0 0 0 0'

# Set size of captions in figures below
caption_size = 'h4'

在比原始图像更小的Box中使用object_fit#

每种效果都可以在下图中看到。每种情况下,图像都位于带有绿色边框的框内。原始图像尺寸为600x300,图像中的网格框为正方形。由于图像比框宽度更宽,因此内容框的大小即为容器的尺寸。

with open('images/gaussian_with_grid.png', 'rb') as f:
    im_600_300 = f.read()
boxes = []
for fit in fit_options:
    ib = Image(value=im_600_300)
    ib.layout.object_fit = fit
    ib.layout.margin = image_margin

    boxes.append(make_box_for_grid(ib, fit))

vb = VBox()
h = HTML(value='<{size}>Examples of <code>object_fit</code> with large image</{size}>'.format(size=caption_size))
vb.layout.align_items = 'center'
hb = HBox()
hb.layout = hbox_layout
hb.children = boxes

vb.children = [h, hb]
vb

object_fit 在一个大于原始图像的 Box#

每种的效果都可以在下图中看到。在每种情况下,图像都位于带有绿色边框的盒子中。原始图像为50x25,图像中的网格框是正方形。

with open('images/gaussian_with_grid_tiny.png', 'rb') as f:
    im_50_25 = f.read()
boxes = []
for fit in fit_options:
    ib = Image(value=im_50_25)
    ib.layout.object_fit = fit
    ib.layout.margin = image_margin
    boxes.append(make_box_for_grid(ib, fit))

vb = VBox()
h = HTML(value='<{size}>Examples of <code>object_fit</code> with small image</{size}>'.format(size=caption_size))
vb.layout.align_items = 'center'
hb = HBox()
hb.layout = hbox_layout
hb.children = boxes

vb.children = [h, hb]
vb

或许会令人惊讶,尽管对option_fit值的描述如此,但在所有情况下,图像实际上都没有填满整个框。原因是底层图像只有50像素宽,是框宽度的一半,所以fillcover意味着“填充/覆盖由图像大小决定的内容框”。

object_fit 在比原始图像大的 Box 中:使用图像布局宽度 100% 来填充容器#

如果图像的布局宽度设置为100%,它将填充其所放置的框。此示例还说明了'contain''scale-down'之间的区别。'scale-down'的效果要么与'contain'相同,要么与'none'相同,具体取决于哪个会导致显示更小的图像。在这种情况下,较小的图像来自于未进行任何调整,因此显示的是这样的图像。

boxes = []
for fit in fit_options:
    ib = Image(value=im_50_25)
    ib.layout.object_fit = fit
    ib.layout.margin = image_margin

    # NOTE WIDTH IS SET TO 100%
    ib.layout.width = '100%'
    
    boxes.append(make_box_for_grid(ib, fit))

vb = VBox()
h = HTML(value='<{size}>Examples of <code>object_fit</code> with image '
               'smaller than container</{size}>'.format(size=caption_size))
vb.layout.align_items = 'center'
hb = HBox()
hb.layout = hbox_layout
hb.children = boxes

vb.children = [h, hb]
vb

object_position 示例#

有几种设置对象位置的方法:

  • 使用诸如topleft之类的关键词来描述图像应如何放置在容器中。

  • 使用两个位置(以像素为单位),用于从容器左上角到图像左上角的偏移量。偏移量可以是正数或负数,可用于将图像定位在框的外部。

  • 在每个方向使用百分比作为偏移量。如果图像小于容器,该百分比是图像周围垂直或水平空白区域的占比;如果图像大于容器,则是图像超出容器部分的占比。

  • 像素与百分比偏移的混合。

  • 关键词和偏移量的混合。

object_fit 决定的图像缩放在某些情况下会优先于定位。例如,如果 object_fitfill,意味着图像将填充容器而不保持宽高比,那么 object_position 将不起作用,因为实际上没有定位需要执行。

另一种理解方式是:object_position 指定了当特定方向上有空白空间时,图像周围的空白空间在容器中应如何分布。

使用关键词指定 object_position#

这种形式的 object_position 接受两个关键字,一个用于图像在容器中的水平位置,一个用于垂直位置,按此顺序。

  • 水平位置必须是以下之一:

    • 'left': 图像的左侧应与容器的左侧对齐

    • 'center': 图像应在容器中水平居中。

    • 'right':图像的右侧应与容器的右侧对齐。

  • 垂直位置必须为以下之一

    • 'top': 图像顶部应与容器顶部对齐。

    • center’: 图像应在容器内垂直居中显示。

    • 'bottom': 图像的底部应与容器的底部对齐。

每种效果如下所示,一次用于图像小于容器的情况,一次用于图像大于容器的情况。

在以下示例中,object_fit 被设置为 'none',以便图像不被缩放。

object_fit = 'none'
image_value = [im_600_300, im_50_25]
horz_keywords = ['left', 'center', 'right']
vert_keywords = ['top', 'center', 'bottom']

rows = []
for image, caption  in zip(image_value, ['600 x 300 image', '50 x 25 image']):
    cols = []
    for horz in horz_keywords:
        for vert in vert_keywords:
            ib = Image(value=image)
            ib.layout.object_position = '{horz} {vert}'.format(horz=horz, vert=vert)
            ib.layout.margin = image_margin
            ib.layout.object_fit = object_fit
            # ib.layout.height = 'inherit'
            ib.layout.width = '100%'
            cols.append(make_box_for_grid(ib, ib.layout.object_position))
    hb = HBox()
    hb.layout = hbox_layout
    hb.children = cols
    rows.append(hb)

vb = VBox()

h1 = HTML(value='<{size}><code> object_position </code> by '
                'keyword with large image</{size}>'.format(size=caption_size))
h2 = HTML(value='<{size}><code> object_position </code> by '
                'keyword with small image</{size}>'.format(size=caption_size))

vb.children = [h1, rows[0], h2, rows[1]]
vb.layout.height = '400px'
vb.layout.justify_content = 'space-around'
vb.layout.align_items = 'center'
vb

通过像素偏移指定 object_position#

可以指定图像左上角相对于容器左上角的偏移量,单位为像素。两个偏移量中的第一个是水平方向的,第二个是垂直方向的,任何一个都可以为负数。如果使用足够大的偏移量使图像位于容器外部,将会导致图像被隐藏。

图片首先使用 object_fit(如果未指定任何值,则默认为 fill)的值进行缩放,然后应用偏移量。

偏移量可以通过结合关键字和像素偏移从底部和/或右侧指定。例如,right 10px bottom 20px 将图像的右侧从容器右边缘偏移10像素,图像底部从容器底部偏移20像素。

object_fit = ['none', 'contain', 'fill', 'cover']
offset = '20px 10px'
image_value = [im_600_300]

boxes = []
for image, caption  in zip(image_value, ['600 x 300 image', ]):
    for fit in object_fit:
        ib = Image(value=image)
        ib.layout.object_position = offset
        ib.layout.margin = image_margin
        ib.layout.object_fit = fit
        # ib.layout.height = 'inherit'
        ib.layout.width = '100%'
        title = 'object_fit: {}'.format(ib.layout.object_fit)
        boxes.append(make_box_for_grid(ib, title))

vb = VBox()
h = HTML(value='<{size}><code>object_position</code> by '
               'offset {offset} with several '
               '<code>object_fit</code>s with large image</{size}>'.format(size=caption_size,
                                                         offset=offset))
vb.layout.align_items = 'center'
hb = HBox()
hb.layout = hbox_layout
hb.children = boxes

vb.children = [h, hb]
vb

指定偏移量作为百分比的object_position#

可以指定图像左上角相对于容器左上角的偏移量,以百分比表示。两个偏移量中第一个是水平方向的,第二个是垂直方向的,任一偏移量都可以为负数。使用足够大的偏移量使图像位于容器外部将导致图像被隐藏。

需要理解的重要一点是,当图像小于容器时,这是每个方向上空白区域的百分比,因此50% 50%会将图像居中。

如果图像在使用object_fit缩放后大于容器,那么偏移量是图像超出容器溢出部分的百分比。这意味着50% 50%也会将大于容器的图像居中。值为10% 90%会将图像超出容器部分的10%放在左边缘左侧,90%垂直放在顶边缘上方。

与通过关键字指定object_position一样,object_fit可以防止对图像应用任何偏移。