kivy最新版2.3.1 (截止2025年上半年), 其提供的布局类有如下这些:
GridLayoutPageLayoutBoxLayoutStackLayout- AnchorLayout
- FloatLayout
- RelativeLayout
- ScatterLayout
布局(上)我们讨论前4个布局类
1.GridLayout(网格布局)
表现形式为多行多列的矩阵.每个被添加的组件都是自动从左向右, 从上向下依次被添加的.
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
# 创建一个集成了GridLayout的自定义类
class MyGridLayout(GridLayout):
# 初始化参数
def __init__(self, **kwargs):
super(MyGridLayout, self).__init__(**kwargs)
self.cols = 2
self.rows = 2
self.add_widget(Label(text="UserName:"))
self.add_widget(TextInput(multiline=False))
self.add_widget((Label(text='Password:')))
self.add_widget(TextInput(password=True, multiline=False))
class MyApp(App):
def build(self):
# 将构建的自定义GridLayout实例化返回
return MyGridLayout()
if __name__ == '__main__':
界面样式:
构建网格布局,必须指定cols或者rows, 否则会抛出异常: [WARNING] <__main__.MyGridLayout object at 0x106a873f0> have no cols or rows set, layout is not triggered.
API说明
类
kivy.uix.gridlayout.GridLayout
属性和方法
rows
网格的行数量
类型: NumericProperty
默认值:None
cols
网格的列数量
类型: NumericProperty
默认值:None
col_default_width
列使用的默认最小尺寸
类型:NumericProperty
默认值:0
row_default_height
行使用的默认最小尺寸
类型:NumericProperty
默认值:0
col_force_default
强制列使用默认值, 如果设置为True, 则忽略width和size_hint_x设置的值.
类型:BooleanProperty
默认值:False
row_force_default
强制行使用默认值, 如果设置为True, 则忽略height和size_hint_y设置的值.
类型:BooleanProperty
默认值:False
rows_minimum
行最小的高度
类型:DictProperty
默认值:{}
cols_minimum
列最小的宽度
类型:DictProperty
默认值:{}
do_layout(*largs)
当布局由触发器调用时,会调用此函数
minimum_size
自动计算容纳所有子项所需的最小尺寸
类型: ReferenceListProperty,由(minimum_width, minimum_height)构成, 只读
minimum_width
自动计算容纳所有子项所需的最小宽度
类型: NumericProperty
默认值:0
minimum_heigh
自动计算容纳所有子项所需的最小高度
类型: NumericProperty
默认值:0
orientation
布局的方向, 即每一个Widget都是以什么样的顺序被填充进布局的.
有效的布局有:lr-tb、tb-lr、rl-tb、tb-rl、lr-bt、bt-lr、rl-bt、bt-rl
字母的意义分别是
lr: 从左向右
tb: 从上向下
字母颠倒则方向相反
类型:OptionProperty
默认值:lr-tb
padding
布局框与每个内部Widget的填充间距.
有3种使用方法,分别是:
- [padding_left, padding_top, padding_right, padding_bottom]
- [padding_horizontal, padding_vertical]
- [padding]
类型:VariableListProperty
默认值:[0, 0, 0, 0]
spacing
子组件的间距
有2种使用方法,分别是:
- [spacing_horizontal, spacing_vertical]
- [spacing]
类型:VariableListProperty
默认值:[0, 0]
案例
import os
from kivy.app import App
from kivy.graphics import Color, Rectangle
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.core.text import LabelBase
# 可以在这里下载喜欢的字体: https://font.chinaz.com/
font_path = os.path.join(os.path.dirname(__file__), 'youaimoshouheiti-regular.ttf')
##覆盖默认的字体Roboto
LabelBase.register('Roboto', font_path)
LabelBase.register('自定义字体', font_path)
class StyledGridLayout(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 此段代码是为了给GridLayout设置一个背景色,在布局更新时, 背景色随之更新
with self.canvas.before:
Color(0.5, 0.5, 1) # 设置背景色为浅蓝色,RGB值 (0.5, 0.5, 1)
self.rect = Rectangle(size=self.size, pos=self.pos)
self.bind(size=self.update_rect, pos=self.update_rect)
self.cols = 2
self.rows = 2
#设置内部组件间的距离
# 统一40
self.spacing = 40
# 左右30, 上下50
self.spacing = [30, 50]
# 设置布局的边框距离内部组件的距离
# 统一100
self.padding = 100
# 左右100, 上下150
self.padding = [100, 150]
# 左20, 上40, 右60, 下80
self.padding = [20, 40, 60, 80]
# 强制设置宽度为300
self.col_default_width = 300
# self.col_force_default = True
# 强制设置高度为100
self.row_default_height = 100
self.row_force_default = True
self.add_widget(Button(text="壹"))
self.add_widget(Button(text="贰"))
self.add_widget(Button(text="叁"))
self.add_widget(Button(text="肆"))
def update_rect(self, *args):
self.rect.size = self.size
self.rect.pos = self.pos
class GridLayoutApp(App):
def build(self):
return StyledGridLayout()
if __name__ == '__main__':
GridLayoutApp().run()
效果图
2.PageLayout
PageLayout用于创建一个简单的可以翻页的布局.
PageLayout并不遵循这些属性: size_hint, size_hint_min, size_hit_max, pos_hint
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.pagelayout import PageLayout
class MyPageLayout(PageLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.button = Button(text='Button1', background_color = 'red')
self.add_widget(self.button)
self.swipe_threshold = .1
gl2 = GridLayout()
gl2.cols = 1
gl2.rows = 2
gl2.add_widget(Button(text='Button2', background_color = 'blue'))
gl2.add_widget(Button(text='Button3', background_color = 'yellow'))
self.add_widget(gl2)
class PageLayoutApp(App):
def build(self):
return MyPageLayout()
if __name__ == '__main__':
PageLayoutApp().run()
界面样式
此例中, PageLayout
- 首先,添加了一个Button作为第一页的内容(第一张图片);
- 其次,添加了一个GridLayout作为第二页的内容(第二张图片)
一般情况下, PageLayout是作为其它Layout的容器存在的,因此放置诸如GridLayout的布局等是推荐的方式.
API说明
类
kivy.uix.pagelayout.PageLayout
属性和方法
anim_kwargs
用于构建动画的关键参数
类型:DictProperty
默认值:{‘d’: .5, ‘t’: ‘in_quad’}
border
当前页面边框的宽度,用于在需要时显示上一页/下一页滑动区域
类型:NumericProperty
默认值:50dp
do_layout(*largs)
当布局由触发器调用时,会调用此函数. 如果自定义PageLayout的子类, 不应该覆盖此方法, 而是应该重写_trigger_layout()
类型:function
on_touch_down(touch)
当触摸按下时, 所采取的动作
类型:function
参数: kivy.input.motionevent.MotionEvent
返回值: bool, 如果为真,则触摸事件的分发将停止; 如果为假,事件将继续分发给Widget树的其余部分
on_touch_move(touch)
当触摸移动时, 所采取的动作
类型:function
参数: kivy.input.motionevent.MotionEvent
返回值: bool, 如果为真,则触摸事件的分发将停止; 如果为假,事件将继续分发给Widget树的其余部分
on_touch_up(touch)
当触摸松开时, 所采取的动作
类型:function
参数: kivy.input.motionevent.MotionEvent
返回值: bool, 如果为真,则触摸事件的分发将停止; 如果为假,事件将继续分发给Widget树的其余部分
page
想要展示的页面的索引
类型:NumericProperty
默认值:0
swipe_threshold
用于触发滑动操作的阈值,以组件大小的比例表示. 比如设置为.5, 就需要滑动下一页到整个PageLayout的50%才会触发翻页.
类型:NumericProperty
默认值:.5
3.BoxLayout
BoxLayout把其内部的Widget按照横向或者纵向的顺序依次排列.
可以通过设置子Widget的size_hint属性, 来改变子组件在BoxLayout的空间占比.
如box_layout.add_widget(Button(text='Hello', size_hint=(.7, 1)))会添加一个横向占比70%的按钮.
控制位置的属性在此组件中只能部分被使用, 这依赖于属性orientation:
如果orientation = 'vertical': x, right, center_x是生效的;
如果orientation = 'horizontal': y, top, center_y是生效的.
固定尺寸的子组件优先占用空间, 其它按"比例占用的子组件"再依据自身空间占比在剩余空间中计算尺寸.
如:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class MyBoxLayout(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# self.size = (1000, 500)
# 可以设置布局是横向or纵向
self.orientation = 'horizontal'
# self.orientation = 'vertical'
self.add_widget(Button(text='button1', background_color='red', size= (150, 500), size_hint= (None, None)))
self.add_widget(Button(text='button2', background_color='blue', size_hint=(.5, 1)))
self.add_widget(Button(text='button3', background_color='green', size_hint=(.5, 1)))
class MyBoxLayoutApp(App):
def build(self):
return MyBoxLayout()
if __name__ == '__main__':
MyBoxLayoutApp().run()
上例中优先计算button1的尺寸, 其次button2和button3再根据比例计算尺寸, 展示效果:
API说明
类
kivy.uix.boxlayout.BoxLayout
属性和方法
add_widget(widget, *args, **kwargs)
向BoxLayout中添加子Widget, 其它Layout也有同样的方法
入参:
widget:子组件;index:添加是下标位置, 默认一个一个往后加;canvas:str, widget的画布效果, 可以是before,after,None
do_layout(**largs)
当布局由触发器调用时,会调用此函数. 如果自定义PageLayout的子类, 不应该覆盖此方法, 而是应该重写_trigger_layout()
类型:function
minimum_height
自动计算出的容纳所有子元素所需的最小高度
类型:NumericProperty
默认值:0, 这是一个只读属性
minimum_width
自动计算出的容纳所有子元素所需的最小宽度
类型:NumericProperty
默认值:0, 这是一个只读属性
minimum_size
自动计算出的容纳所有子元素所需的最小尺寸
类型:ReferenceListProperty
默认值:(0,0), 这是一个只读属性
orientation
布局的方向
类型:OptionProperty
默认值:horizontal, 可选值:vertical或horizontal.
padding
布局框与每个内部Widget的填充间距.
有3种使用方法,分别是:
- [padding_left, padding_top, padding_right, padding_bottom]
- [padding_horizontal, padding_vertical]
- [padding]
类型:VariableListProperty
默认值:[0, 0, 0, 0]
spacing
子组件的间距
有2种使用方法,分别是:
- [spacing_horizontal, spacing_vertical]
- [spacing]
类型:VariableListProperty
默认值:[0, 0]
4.StackLayout
StackLayout可以以水平或者垂直的方式, 尽可能多的填充子Widget. 每个子Widget的尺寸都可以不相同.
比如我们可以使用按钮写一面有趣的墙:
它是用StackLayout实现的:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.uix.stacklayout import StackLayout
class MyStackLayout(StackLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
for i in range(10):
if i % 2 == 0:
# 某些行是有半截砖的, 2个半截构成比整砖的构成, 会多1块
bricks_each_line = 11
else:
# 如果当前行全部是整转, 就需要10块
bricks_each_line = 10
for j in range(bricks_each_line):
if (j == 0 or j == 10) and i % 2 == 0:
# 间隔的某些行左右会多出2块半截的砖(stackLayout中的Widget的大小可以不一致)
x_hint = .05
else:
# 定义每块砖的横向占比是0.1(10%), 10块砖刚好塞满100%
x_hint = .1
# 每块转按照计算的大小比例排放, 由于是放10层, 因此每层需要占0.1(即10%)
self.add_widget(Button(text='', size_hint=(x_hint, .1)))
class StackLayoutApp(App):
def build(self):
# 设置墙的宽度和高度
Window.size = (1000, 600)
return MyStackLayout()
if __name__ == '__main__':
StackLayoutApp().run()
通过子组件的size_hint属性, 可以设置其在StackLayout中的尺寸占比.
另外,可以在App的build()方法中设置Window.size = (x, y)来完成整个窗口的大小的调整.
API说明
类
kivy.uix.stacklayout.StackLayout
属性和方法
do_layout(*largs)
当布局由触发器调用时,会调用此函数. 如果自定义PageLayout的子类, 不应该覆盖此方法, 而是应该重写_trigger_layout()
类型:function
minimum_width
自动计算容纳所有子项所需的最小宽度, 它由布局自动设置, 无序调整
类型: NumericProperty
默认值:0
minimum_heigh
自动计算容纳所有子项所需的最小高度, 它由布局自动设置, 无序调整
类型: NumericProperty
默认值:0
minimum_size
自动计算出的容纳所有子元素所需的最小尺寸, 它由布局自动设置, 无序调整
类型:ReferenceListProperty
默认值:(0,0), 这是一个只读属性
orientation
布局的方向, 即每一个Widget都是以什么样的顺序被填充进布局的.
有效的布局有:lr-tb、tb-lr、rl-tb、tb-rl、lr-bt、bt-lr、rl-bt、bt-rl
字母的意义分别是
lr: 从左向右
tb: 从上向下
字母颠倒则方向相反
类型:OptionProperty
默认值:lr-tb
padding
布局框与每个内部Widget的填充间距.
有3种使用方法,分别是:
- [padding_left, padding_top, padding_right, padding_bottom]
- [padding_horizontal, padding_vertical]
- [padding]
类型:VariableListProperty
默认值:[0, 0, 0, 0]
spacing
子组件的间距
有2种使用方法,分别是:
- [spacing_horizontal, spacing_vertical]
- [spacing]