Jupyter小部件的布局#
本笔记本展示了如何布局 Jupyter 交互式小部件,以构建丰富且具有 响应性 的基于小部件的应用程序。
layout
属性#
Jupyter 交互式小部件具有一个 layout
属性,它暴露了一些影响小部件布局的 CSS 属性。
暴露的 CSS 属性#
尺寸#
height
width
max_height
max_width
min_height
min_width
显示#
visibility
display
overflow
盒模型#
border
margin
padding
定位#
top
left
bottom
right
弹性盒子#
order
flex_flow
align_items
flex
align_self
align_content
justify_content
网格布局#
grid_auto_columns
grid_auto_flow
grid_auto_rows
grid_gap
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'
来分别设置 top
、right
、bottom
和 left
边距为 100
、150
、100
和 80
像素。
同样,flex
属性可以包含 flex-grow
、flex-shrink
和 flex-basis
的值。border
属性是 border-width
、border-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)
如果您需要更大的灵活性来布局小部件和描述,您可以直接使用标签小部件。
from ipywidgets import HBox, Label
HBox([Label('A too long description'), IntSlider()])
自然尺寸,以及使用 HBox 和 VBox 的排列#
大多数核心小部件都有默认的高度和宽度,可以很好地拼接在一起。这使得基于 HBox
和 VBox
辅助函数的简单布局能够自然对齐:
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#
像滑块和文本输入这样的小部件具有可以渲染 LaTeX 方程的描述属性。Label
小部件也可以渲染 LaTeX 方程。
from ipywidgets import IntSlider, Label
IntSlider(description=r'\(\int_0^t f\)')
Label(value=r'\(e=mc^2\)')
数字格式化#
滑块有一个读数区域,可以使用 Python 的 格式说明小型语言 进行格式化。如果读数区域的空间对于滑块值的字符串表示来说太窄,将应用不同的样式以显示并非所有数字都可见。
弹性盒子布局#
上述的HBox
和VBox
类是Box
小部件的特殊案例。
Box
小部件实现了完整的CSS弹性盒子规范以及网格布局规范,使得在 Jupyter 笔记本中可以实现丰富的响应式布局。它旨在提供一种高效的方式,用于在容器中对项目进行布局、对齐和分配空间。
再次强调,整个弹性盒子规范可以通过容器小部件(Box
)的layout
属性以及包含的项目来暴露。可以在所有包含的项目之间共享相同的layout
属性。
致谢#
以下关于弹性盒子布局的教程遵循了Chris Coyier的文章《A Complete Guide to Flexbox》 的思路,并且在获得了许可的情况下,使用了该文章的文本和各种图片。
基本概念和术语#
由于flexbox是一个完整的模块,而不仅仅是一个属性,它涉及到很多方面,包括它的一整套属性。其中一些属性是设置在容器(父元素,被称为"flex容器")上的,而其他的则是设置在子元素(被称为"flex项目")上的。
如果常规布局是基于块和内联流动方向的,那么flex布局则是基于"flex流动方向"。请参考规范中的这张图,解释了flex布局背后的主要思想。
基本上,项目将按照"主轴"(从main-start到main-end)或"交叉轴"(从cross-start到cross-end)进行布局。
“主轴” - flex容器的主轴是沿其排列flex项目的主要轴。请注意,它不一定是水平的;它取决于flex-direction属性(见下文)。
“main-start | main-end” - flex项目从容器的main-start开始放置,向main-end方向延伸。
“主轴尺寸” - flex项目的宽度或高度(取决于主轴维度)是项目的主轴尺寸。flex项目的主轴尺寸属性是’width’或’height’属性,取决于主轴维度。 交叉轴 - 与主轴垂直的轴称为交叉轴。其方向取决于主轴方向。
“cross-start | cross-end” - flex线用项目填充,并从flex容器的cross-start一侧开始放置,向cross-end一侧延伸。
“交叉尺寸” - flex项目的宽度或高度(取决于交叉维度)是项目的交叉尺寸。交叉尺寸属性是’width’或’height’,取决于交叉维度。
父元素的属性#
display#
display
可以是flex
或inline-flex
。这定义了一个flex容器(块级或内联)。
flex-flow#
flex-flow
是flex-direction
和flex-wrap
属性的简写,它们共同定义了flex容器的主轴和交叉轴。默认值是row nowrap
。
flex-direction
(column-reverse | column | row | row-reverse )这建立了主轴,从而定义了flex项目在flex容器中的放置方向。Flexbox是一个(除了可选的包裹外)单方向布局概念。可以将flex项目视为主要是水平行或垂直列排列。
flex-wrap
(nowrap | wrap | wrap-reverse)默认情况下,flex项目都会尝试适应一行。你可以使用这个属性改变这一点,允许项目根据需要进行换行。方向在这里也起作用,决定了新行堆叠的方向。
justify-content#
justify-content
可以是flex-start
、flex-end
、center
、space-between
、space-around
之一。这定义了沿着主轴的对齐方式。它有助于分配剩余的空闲空间,当一行上的所有flex项目都是不可变的,或者它们是灵活的但已达到最大大小时。当项目溢出该行时,它也会对项目的对齐产生一些控制。
align-items#
align-items
可以是flex-start
、flex-end
、center
、baseline
、stretch
之一。这定义了在当前行上沿着交叉轴布局flex项目的默认行为。可以将其视为交叉轴(与主轴垂直)版本的justify-content。
align-content#
align-content
可以是flex-start
、flex-end
、center
、baseline
、stretch
之一。当交叉轴有多余的空间时,它会对flex容器的行进行对齐,类似于justify-content如何沿着主轴对齐单个项目。
注意:当只有一行flex项目时,此属性无效。
项目的属性#
如果父元素不是flexbox容器(即其display属性等于flex或inline-flex),则项目的与flexbox相关的CSS属性不会有任何影响。
order#
默认情况下,flex项目按源顺序布局。然而,order
属性控制它们在flex容器中出现的顺序。
<img alt="Order" style="width: 500px;"/>
flex#
flex
是三个属性的简写,分别是flex-grow
、flex-shrink
和flex-basis
。第二个和第三个参数(flex-shrink
和flex-basis
)是可选的。默认值是0 1 auto
。
flex-grow
这定义了flex项目在必要时增长的能力。它接受一个无单位的值,作为比例。它指示了项目应该在flex容器中占据多少可用空间。
如果所有项目的flex-grow设置为1,则容器中剩余的空间将平均分配给所有子元素。如果其中一个子元素的值为2,则剩余的空间将占据其他空间的两倍(或至少会尝试这样做)。
flex-shrink
这定义了flex项目在必要时缩小的能力。
flex-basis
这定义了元素在剩余空间分配之前的默认大小。它可以是一个长度(例如
20%
、5rem
等)或一个关键字。auto
关键字意味着"查看我的宽度或高度属性"。
align-self#
align-self
允许默认对齐(或由align-items指定的对齐)被单个flex项目覆盖。
VBox和HBox助手#
VBox 和 HBox 助手类提供了简单的默认设置,以垂直和水平方式排列子小部件。它们大致相当于:
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弹性盒子规范,使得在Jupyter笔记本中可以实现丰富的响应式布局。它旨在提供一种高效的方式,用于在容器中对项目进行布局、对齐和分配空间。
再次强调,整个网格布局规范可以通过容器小部件(Box
)的layout
属性以及包含的项目来暴露。可以在所有包含的项目之间共享相同的layout
属性。
以下的弹性盒子教程遵循了Chris House的文章《A Complete Guide to Grid》的思路,并在获得许可的情况下,使用了该文章的文本和各种图像。
基础知识和浏览器支持#
要开始使用,你必须将一个容器元素定义为网格,使用display: grid
,通过grid-template-rows
、grid-template-columns
和grid_template_areas
设置行和列的大小,然后使用grid-column
和grid-row
将其子元素放置到网格中。与弹性盒子类似,网格项目的来源顺序并不重要。你的CSS可以以任何顺序放置它们,这使得使用媒体查询重新排列网格变得非常容易。想象一下定义整个页面的布局,然后完全重新安排它以适应不同的屏幕宽度,只需几行CSS代码。网格是迄今为止引入的最强大的CSS模块之一。
截至2017年3月,大多数浏览器都本机支持无前缀的CSS Grid:Chrome(包括在Android上)、Firefox、Safari(包括在iOS上)和Opera。另一方面,Internet Explorer 10和11支持它,但使用的是过时的语法。现在就是使用网格构建的时候!
重要术语#
在深入了解网格的概念之前,了解术语非常重要。由于这里涉及的术语在概念上都有些相似,如果你不先记住网格规范定义的含义,就很容易将它们混淆。不过别担心,它们并不多。
网格容器
应用display: grid
的元素。它是所有网格项目的直接父级。在这个例子中,容器是网格容器。
<div class="container">
<div class="item item-1"></div>
<div class="item item-2"></div>
<div class="item item-3"></div>
</div>
网格项目
网格容器的子元素(例如直接后代)。这里的item元素是网格项目,但sub-item不是。
<div class="container">
<div class="item"></div>
<div class="item">
<p class="sub-item"></p>
</div>
<div class="item"></div>
</div>
网格线
构成网格结构的分界线。它们可以是垂直的(“列网格线”)或水平的(“行网格线”),位于行或列的任一侧。这里的黄色线是列网格线的一个例子。
网格轨道
两个相邻网格线之间的空间。你可以将它们看作是网格的列或行。这里是第二和第三行网格线之间的网格轨道。
网格单元格
两个相邻行和两个相邻列网格线之间的空间。它是网格的一个"单元"。这里是行网格线1和2,以及列网格线2和3之间的网格单元格。
网格区域
由四条网格线包围的总空间。一个网格区域可能由任意数量的网格单元格组成。这里是行网格线1和3,以及列网格线1和3之间的网格区域。
父级的属性#
grid-template-rows, grid-template-colums
使用空格分隔的值列表定义网格的列和行。这些值代表轨道大小,它们之间的空格代表网格线。
值:
<track-size>
- 可以是长度、百分比或网格中可用空间的分数(使用fr
单位)<line-name>
- 你选择的任意网格区域名称
grid-template-areas
通过引用使用grid-area
属性指定的网格区域的名称来定义网格模板。重复网格区域的名称会导致内容跨越这些单元格。句点表示空单元格。语法本身提供了网格结构的可视化。
值:
<grid-area-name>
- 使用grid-area
指定的网格区域的名称.
- 句点表示空的网格单元格none
- 没有定义网格区域
grid-gap
grid-row-gap
和grid-column-gap
的简写
值:
<grid-row-gap>
,<grid-column-gap>
- 长度值
其中grid-row-gap
和grid-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
单位)
网格项的属性#
注意:float
、display: inline-block
、display: table-cell
、vertical-align
和column-??
属性对网格项无效。
grid-column, grid-row
通过引用特定的网格线来确定网格项在网格中的位置。grid-column-start
/grid-row-start
是项开始的线,grid-column-end
/grid-row-end
是项结束的线。
值:
<line>
- 可以是一个数字,引用编号的网格线,或者是一个名称,引用命名的网格线span <number>
- 项将跨越提供的数目的网格轨道span <name>
- 项将一直跨越直到遇到下一个具有提供名称的线auto
- 表示自动放置,自动跨度或默认跨度为一
.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;
}
.item-b {
grid-column: 1 / span col4-start;
grid-row: 2 / span 2;
}
如果没有声明grid-column
/ grid-row
,项将默认跨越1个轨道。
项可以重叠。你可以使用z-index
来控制它们的堆叠顺序。
grid-area
给一个项命名,以便可以通过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
}
justify-self
沿着内联(行)轴对齐单元格内的网格项(与align-self相反,后者沿着块(列)轴对齐)。此值适用于单个单元格内的网格项。
值:
start
- 将网格项与单元格的起始边缘对齐end
- 将网格项与单元格的结束边缘对齐center
- 在单元格的中心对齐网格项stretch
- 填充整个单元格的宽度(这是默认值)
.item {
justify-self: start | end | center | stretch;
}
示例:
.item-a {
justify-self: start;
}
.item-a {
justify-self: end;
}
.item-a {
justify-self: center;
}
.item-a {
justify-self: 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')
)
图像布局和大小调整与其他元素略有不同,这是由于历史原因(HTML标签img
在CSS之前就存在了)和实际原因(图像具有固有的大小)。
图像大小调整尤其令人困惑,因为有两种方式可以指定图像的大小。Image
小部件具有与HTML img
标签同名的属性width
和height
。此外,Image
小部件(像其他所有小部件一样)具有layout
,它也具有width
和height
。
另外,一些CSS样式被应用到图像上,而这些样式不会被应用到其他小部件上:max_width
被设置为100%
,height
被设置为auto
。
你不应该依赖Image.width
或Image.height
来确定图像小部件的显示宽度和高度。任何CSS样式,无论是通过Image.layout
还是其他来源,都会覆盖Image.width
和Image.height
。
当单独显示一个Image
小部件时,将Image.layout.width
设置为所需的宽度将按原始图像的宽高比显示Image
小部件。
当将Image
放入Box
(或HBox
或VBox
)中时,结果取决于是否在Box
上设置了宽度。如果设置了宽度,那么图像将被拉伸(或压缩)以适应盒子,因为Image.layout.max_width
是100%
,所以图像会填充容器。这通常不会保留图像的宽高比。
控制容器内Image
的显示#
使用Image.layout.object_fit
来控制图像在容器(如盒子)内的缩放方式。可能的取值有:
'contain'
:在保持宽高比的同时,将图像适应到其内容框中。如果容器的任何部分没有被图像覆盖,则显示容器的背景。内容框的大小取决于容器和图像的大小,如果容器小于图像,则为容器的大小;如果容器大于图像,则为图像的大小。'cover'
:在保持图像宽高比的同时,完全填充内容框,必要时裁剪图像。'fill'
:完全填充内容框,根据需要拉伸/压缩图像。'none'
:不进行大小调整;图像将被容器裁剪。'scale-down'
:执行与contain
或none
相同的操作,使用两者中显示图像较小的那个。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'
object_fit
在比原始图像小的Box
中#
在下面的图像中可以看到每个效果。在每种情况下,图像位于一个带有绿色边框的盒子中。原始图像是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像素宽,是盒子宽度的一半,所以fill
和cover
的意思是“填充/覆盖由图像大小决定的内容框”。
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
的示例#
有几种方法可以设置对象位置:
使用关键字,如
top
和left
,来描述图像应如何放置在容器中。使用两个位置值(以像素为单位),表示从容器的左上角到图像的左上角的偏移量。偏移量可以是正数或负数,可以用来将图像定位在盒子外部。
在每个方向上使用百分比作为偏移量。如果图像小于容器,则百分比是指围绕图像的垂直或水平留白部分的比例;如果图像大于容器,则是指图像超出容器的部分的比例。
混合使用像素和百分比偏移量。
混合使用关键字和偏移量。
由object_fit
决定的图像缩放在某些情况下会优先于定位。例如,如果object_fit
是fill
,以便图像应该填充容器而不保持宽高比,则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
将图像的右侧从容器的右边缘偏移10px,并将图像的底部从容器的底部偏移20px。
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
可能会阻止任何偏移应用于图像。