Kivy命名规则 1、类继承App的类名(小写)(除去App)+ .kv 例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 from kivy.app import Appfrom kivy.uix.gridlayout import GridLayoutfrom kivy.uix.label import Labelfrom kivy.uix.textinput import TextInputfrom kivy.uix.button import Buttonfrom kivy.graphics import Color,Rectanglefrom kivy.properties import ColorProperty, ListPropertyclass LoginScreen (GridLayout ): def __init__ (self, **kwargs ): super (LoginScreen, self ).__init__(**kwargs) self .cols = 2 self .rows = 6 self .add_widget(Label(text='User Name' )) self .username = TextInput(multiline=False ) self .add_widget(self .username) self .add_widget(Label(text='password' )) self .password = TextInput(password=True , multiline=False ) self .add_widget(self .password) self .button = Button(size=(.4 , .63 ), text='iPaoMi' , background_color=(0 , 0 , 1 ), font_size=15 , color=(1 , 0 , 1 )) self .button.bind(on_press=self .on_push) self .add_widget(self .button) with self .password.canvas.before: Color(1 , 0.5 , 0 , 0.5 ) self .password = Rectangle(size=self .password.size, pos=self .password.pos) def on_push (self, inst ): print ('inst' +inst.text) self .button.text = inst.text self .button.height=12 class MyApp (App ): def build (self ): self .root = root = LoginScreen() return root if __name__ == '__main__' : MyApp().run()
创建的kv文件则为 my.kv
代码分离规则: 定义页面: 使用<>:
创建一个页面,页面和py里的类一致。 例如: 且使用缩进标识控件和属性的隶属关系。
<LoginScreen>:
BoxLayout:
Button:
BoxLayout:
Button:
<LoginScreen>:
使用kv文件则无须使用Py文件进行编写布局,可直接如下:
1 2 class LoginScreen (App ): pass
其实kv文件读取我们也可以直接将文件内容以字符串形式写在任意变量内,任何使用,如下
from kivy.lang import Builder
Builder.load_string("""
<LoginScreen>:
BoxLayout:
Button:
BoxLayout:
Button:
<LoginScreen>:
""")
class LoginScreen(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class MyApp(App):
def build(self):
return LoginScreen() # Kivy会自动缩放根控件,
if __name__ == "__main__":
MyApp().run()
Kivy 中文显示 Kivy 属性 位置和尺寸(大小) 尺寸 (大小) 大小有两种写法一种是 size
另一种是 size_hint
size 传入的值是一个固定的宽高值
size_hint 传入的是按照当前窗口比例的宽高值,一般写做如下:
size_hint: .4, .5
或者
size_hint: 0.4, 0.5
位置 位置也有和大小一样的两种属性,一种pos
另一个是pos_hint
pos pos:
pos_hint 按照宽高比例的写法是允许省略0的写法。 比例值的计算是以左下角为坐标系起点(0,0)
,横向为x轴,纵向为y,假设宽为W,高为L,则窗内任意一点的比例值为[W/x,L/y]
相对于控件, x轴上有三条边可以确定位置:
左边界x
正中间线center_x
右边界right
同样,y轴也有三条可以确定位置:
X轴和Y轴各有三种他们两两组合共有9种写法可以确定一个位置。
pos_hint: {'x': .8, 'y': .4}
或
pos_hint: {'right': .7, 'top': .3}
或者
pos_hint: {'center_y': .7, 'center_x': .3}
以上x、y、right、top、center_y、center_x都是以控件为主,距离布局窗体的X轴和Y轴。
布局 FloatLayout py文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from kivy.app import Appfrom kivy.uix.floatlayout import FloatLayoutclass FloatLayoutWidget (FloatLayout ): def __init__ (self ): super (FloatLayoutWidget, self ).__init__() class FloatLayoutApp (App ): def build (self ): return FloatLayoutWidget() if __name__ == '__main__' : FloatLayoutApp().run()
kv文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <Button> font_size: 40 size_hint: .3 , .4 <FloatLayoutWidget> canvas: Color: rgba: [.2 , .4 , .5 , .6 ] Rectangle: size: self .size pos: self .pos Button: text: "BT1" background_color: 1 , 0 , 0 , 1 pos_hint: {'x' : 0 , 'top' : 1 } Button: text: "BT2" background_color: .2 , .3 , .4 , .5 pos_hint: {'x' : .35 , 'y' : .3 } Button: text: "BT3" background_color: .5 , .4 , .3 , .2 pos_hint: {'x' : .7 , 'bottom' : 0 } Button: text: "BT4" background_color: 1 , 0 , 0 , 1 pos_hint: {'x' : 0.7 , 'top' : 1 } Button: text: "BT5" background_color: 1 , 0 , 0 , 1 pos_hint: {'x' : 0 , 'bottom' : 1 }
BoxLayout 盒子布局,可以将部件水平或者垂直排列的布局。类似于安卓的线性布局。如果没有限制任何大小,部件将会以10px间距平分父窗口大小。
只用Python进行布局的话如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from kivy.app import Appfrom kivy.graphics import Rectangle, Colorfrom kivy.uix.boxlayout import BoxLayoutfrom kivy.uix.button import Buttonclass BoxLayoutWidget (BoxLayout ): def __init__ (self ): super (BoxLayoutWidget, self ).__init__() with self .canvas: Color(.4 , .5 , .02 , .3 ) self .rect = Rectangle(pos=self .pos, size=self .size) self .bind(pos=self .update_rect, size=self .update_rect) button1 = Button(text='Hello BoxLayout1' , size_hint=(.3 , .2 ), pos=(200 , 40 ), background_color=(1 , 1 , 1 , 1 )) button2 = Button(text='Hello BoxLayout2' , size_hint=(.2 , .2 ), pos=(200 , 40 ), background_color=(.6 , .5 , .8 , .3 )) self .add_widget(button1) self .add_widget(button2) def update_rect (self, *args ): self .rect.pos = self .pos self .rect.size = self .size class BoxLayoutApp (App ): def build (self ): return BoxLayoutWidget() if __name__ == '__main__' : BoxLayoutApp().run()
或者配合kv文件进行布局:
python代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from kivy.app import Appfrom kivy.uix.boxlayout import BoxLayoutclass BoxLayoutWidget (BoxLayout ): def __init__ (self, **kwargs ): super (BoxLayoutWidget, self ).__init__(**kwargs) class BoxLayoutTApp (App ): def build (self ): return BoxLayoutWidget() if __name__ == '__main__' : BoxLayoutTApp().run()
kv文件如下:(kv文件创建遵守kv命名规则)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <Button> font_size: 50 <BoxLayoutWidget> canvas: Color: rgba: [.2, .4, .5, .6] Button: text: "BT1" background_color: 1, 0, 0, 1 Button: text: "BT2" background_color: 1, 1, 1, 1 Button: text: "BT3" background_color: 0, 1, 0, 1 Button: text: "BT4" background_color: 1, 0, 1, 1 Button: text: "BT5" background_color: 0, 1, 1, 1
默认BoxLayout
排版是纵向排版,若需要改为横向排版则可在KV文件里添加参数orientation: "vertical"
若是使用python实现则可以将实例化BoxLayout
时,添加参数BoxLayout(orientation="vertical")
BoxLayout间距 BoxLayout布局中间距有两种形式:
布局和子级之间填充是需要使用padding
,默认为[0, 0, 0, 0]
,四个参数分别为 [padding_left, padding_top, padding_right, padding_bottom]
从右下脚开始顺时针一一对应。(左,上,右,下);同时padding还接受两个参数形式[padding_horizontal, padding_vertical]
分别是水平边距和竖直边距;或者一个参数形式[padding]
代表周围边距。
子级和子级之间填充需要使用spacing
, 默认为 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <Button> font_size: 10 <BoxLayoutWidget> orientation: "vertical" # 横向排版 padding: [10, 20, 30, 40] # 设置间距 canvas: Color: rgba: [.2, .4, .5, .6] Button: text: "BT1" # 显示文本,.KV默认是不支持中文 background_color: 1, 0, 0, 1 Button: text: "BT2" background_color: 1, 1, 1, 1 BoxLayout: orientation: "vertical" # 设置间距 spacing: 20 Button: text: "BT3" background_color: 0, 1, 0, 1 Button: text: "BT4" background_color: 1, 0, 1, 1 Button: text: "BT5" size_hint_y: .15 # 设置按钮大小 background_color: 0, 1, 1, 1
AnchorLayout布局(锚点布局) 锚点布局可以将子部件放在 左上、中上、右上、左中、正中、右中、左下、中下、右下 ,9个位置处。只需要指定anchor_x
和anchor_y
属性即可。
anchor_x
默认值为center
,可以且只可以接受left
、right
、center
,分别为左、右和中。
anchor_y
默认值为center
,可以且只可以接受top
、bottom
、center
,分别为上、下和中。
python代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 from kivy.app import Appfrom kivy.uix.anchorlayout import AnchorLayoutfrom kivy.uix.button import Buttonfrom kivy.graphics import Rectangle, Colorclass AnchorLayoutWidget (AnchorLayout ): def __init__ (self ): super (AnchorLayoutWidget, self ).__init__() with self .canvas: Color(1 , 1 , 1 , 1 ) self .rect = Rectangle(pos=self .pos, size=self .size) self .bind(pos=self .update_rect, size=self .update_rect) anchor_first = AnchorLayout(anchor_x='left' , anchor_y='top' ) anchor_first.add_widget(Button(text='left-top' , size_hint=[.3 , .3 ], background_color=[0 , 0 , 0 , 1 ])) anchor_second = AnchorLayout(anchor_x='center' , anchor_y='top' ) anchor_second.add_widget(Button(text='center-top' , size_hint=[.3 , .3 ], background_color=[0 , 0 , 1 , 1 ])) anchor_third = AnchorLayout(anchor_x='right' , anchor_y='top' ) anchor_third.add_widget(Button(text='right-top' , size_hint=[.3 , .3 ], background_color=[0 , 1 , 1 , 1 ])) anchor_fourth = AnchorLayout(anchor_x='left' , anchor_y='center' ) anchor_fourth.add_widget(Button(text='left-center' , size_hint=[.3 , .3 ], background_color=[1 , 1 , 0 , 1 ])) anchor_fifth = AnchorLayout(anchor_x='center' , anchor_y='center' ) anchor_fifth.add_widget(Button(text='center-center' , size_hint=[.3 , .3 ], background_color=[0 , 1 , 0 , 1 ])) anchor_sixth = AnchorLayout(anchor_x='right' , anchor_y='center' ) anchor_sixth.add_widget(Button(text='right-center' , size_hint=[.3 , .3 ], background_color=[1 , 0 , 1 , 1 ])) anchor_seventh = AnchorLayout(anchor_x='left' , anchor_y='bottom' ) anchor_seventh.add_widget(Button(text='left-bottom' , size_hint=[.3 , .3 ], background_color=[0 , 1 , .5 , 1 ])) anchor_eighth = AnchorLayout(anchor_x='center' , anchor_y='bottom' ) anchor_eighth.add_widget(Button(text='center-bottom' , size_hint=[.3 , .3 ], background_color=[1 , .5 , 1 , .5 ])) anchor_ninth = AnchorLayout(anchor_x='right' , anchor_y='bottom' ) anchor_ninth.add_widget(Button(text='right-bottom' , size_hint=[.3 , .3 ], background_color=[.5 , 1 , 1 , .5 ])) self .add_widget(anchor_first) self .add_widget(anchor_second) self .add_widget(anchor_third) self .add_widget(anchor_fourth) self .add_widget(anchor_fifth) self .add_widget(anchor_sixth) self .add_widget(anchor_seventh) self .add_widget(anchor_eighth) self .add_widget(anchor_ninth) def update_rect (self, *args ): self .rect.pos = self .pos self .rect.size = self .size class AnchorLayoutApp (App ): def build (self ): return AnchorLayoutWidget() if __name__ == '__main__' : AnchorLayoutApp().run()
或者配合kv文件进行布局: python代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from kivy.app import Appfrom kivy.uix.boxlayout import BoxLayoutclass BoxLayoutWidget (BoxLayout ): def __init__ (self, **kwargs ): super (BoxLayoutWidget, self ).__init__(**kwargs) class BoxLayoutTApp (App ): def build (self ): return BoxLayoutWidget() if __name__ == '__main__' : BoxLayoutTApp().run()
kv文件如下:
<Button>
font_size: 10
size_hint: .3, .3
<AnchorLayoutWidget>
padding: 20
AnchorLayout:
anchor_x: 'left'
anchor_y: 'top'
Button:
background_color: 0, 0, 0, 1
text: 'left-top'
AnchorLayout:
anchor_x: 'center'
anchor_y: 'top'
Button:
text: 'center-top'
background_color: 0, 0, 1, 1
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
Button:
text: 'right-top'
background_color: 0, 1, 1, 1
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
Button:
text: 'center-center'
background_color: 1, 1, 0, 1
AnchorLayout:
anchor_x: 'left'
anchor_y: 'center'
Button:
text: 'left-center'
background_color: 0, 1, 0, 1
AnchorLayout:
anchor_x: 'right'
anchor_y: 'center'
Button:
text: 'right-center'
background_color: 1, 0, 1, 1
AnchorLayout:
anchor_x: 'left'
anchor_y: 'bottom'
Button:
text: 'left-bottom'
background_color: 0, 1, .5, 1
AnchorLayout:
anchor_x: 'center'
anchor_y: 'bottom'
Button:
text: 'center-bottom'
background_color: 1, .5, 1, .5
AnchorLayout:
anchor_x: 'right'
anchor_y: 'bottom'
Button:
text: 'right-bottom'
background_color: .5, 1, 1, .5
GridLayout (网格布局) 可以将子部件排列成多行多列的矩阵,根据布局配置按照子部件的索引为每一个子部件分配位置。新建网格布局的时候,需要设置具体的行(cols
)和列(rows
)数,作为约束。否则设置是无效的。 设置具体的行列后,当子部件变化时,布局就会根据改值进行扩展,但是总数不会超过设置的上限值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 from kivy.app import Appfrom kivy.uix.gridlayout import GridLayoutfrom kivy.uix.button import Buttonfrom kivy.graphics import Rectangle, Colorclass GridLayoutWidget (GridLayout ): def __init__ (self ): super (GridLayoutWidget, self ).__init__() with self .canvas: Color(1 , 1 , 1 , 1 ) self .rect = Rectangle(pos=self .pos, size=self .size) self .bind(pos=self .update_rect, size=self .update_rect) self .cols = 3 self .rows = 3 for i in range (8 ): btn = Button(text=f'BTN_{i} ' , background_color=(0.2 , .2 , .4 )) self .add_widget(btn) def update_rect (self, *args ): self .rect.pos = self .pos self .rect.size = self .size class GridLayoutApp (App ): def build (self ): return GridLayoutWidget() if __name__ == '__main__' : GridLayoutApp().run()
配合kv文件代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from kivy.app import Appfrom kivy.uix.gridlayout import GridLayoutclass GridLayoutWidget (GridLayout ): def __init__ (self ): super (GridLayoutWidget, self ).__init__() class GridLayoutApp (App ): def build (self ): return GridLayoutWidget() if __name__ == '__main__' : GridLayoutApp().run()
kv代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <Button> font_size: 10 size_hint: .3, .4 <GridLayoutWidget> rows:3 cols:3 padding: 15 spacing: 20 Button: background_color: 0, 1, 0, 1 text: 'btn_1' Button: background_color: .1, 0, 1, 1 text: 'btn_2' Button: background_color: 0, .5, .4, 1 text: 'btn_3' Button: background_color: 0, 1, 0, .5 text: 'btn_4'
GridLayout设置布局大小 GridLayout布局种,可以使用部件本身属性为其指定大小,若要对部件设置固定宽度,在设置之前则需要将size_hint_x
设置为None
。
GridLayout为部件指定大小 需要使用col_force_default
属性,其默认值False
,表示不使用列默认宽度。需要将col_force_default
设置为True
。并且在没有给子部件设置width
和size_hint_x
的属性的情况下,使用默认的列宽。列宽的默认值为0
。所以还需要指定一个具体的值,使用col_default_width
可以设置成功。同上,对与设置行高也可以相同的方法将row_force_default
设置True
,并且row_default_height
设置值。
设置列宽行高 布局的宽高 可以使用cols_minimum
设置属性的列宽,cols_minimum
属性接受的值是一个字典,字典的键为列号,值为像素值。例如:cols_minimum:{0: 130, 1:140}
将第一列设置为130像素,第二列设置为140像素。 执行一下代码后,可以发现,从执行顺序的优先级来说,可以按照如下顺序: col_default_width < cols_minimum < width
。 同理,也可以通过rows_minimum
设置属性的行高rows_minimum: {0: 50}
。 可参考如下kv代码:
<Button>
font_size: 50
size_hint: .3, .4
<GridLayoutWidget>
rows:3 # 设置行数
cols:3 # 设置列数
padding: 15 # 设置间距
spacing: 20 # 设置间距
col_force_default: True # 强制使用默认列宽
col_default_width: 200 # 设置默认,列宽
row_force_default: True # 强制使用默认行高
row_default_height: '34px' # 设置默认行高
cols_minimum:{0: 130, 1:140} # 设置列宽
rows_minimum: {0: 50} # 设置行高
canvas:
Color:
rgba: .3,.3,.5,.5
Rectangle:
size: self.size
pos: self.pos
Button:
background_color: 0, 1, 0, 1
text: 'btn_1'
size_hint_x: None
width: '300px'
Button:
background_color: .1, 0, 1, 1
text: 'btn_2'
Button:
background_color: 0, .5, .4, 1
text: 'btn_3'
Button:
background_color: 0, 1, 0, .5
text: 'btn_4'
PageLayout 布局 PageLayou布局和前面几种布局方式是有差异的,他更倾向与动态布局,使用这个布局可以创建一个简单的多页面布局。可以在这些布局之间随意跳转,此布局每个子部件都作为一个单独的页面,所以此布局并不支持size_hint
和pos_hint
: 例如如下: 会生成一个可以自由左右拉的按钮。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from kivy.uix.pagelayout import PageLayoutfrom kivy.app import Appfrom kivy.uix.button import Buttonclass PageLayoutWidget (PageLayout ): def __init__ (self, **kwargs ): super (PageLayoutWidget, self ).__init__() btn = Button(text='btn1' , background_color=[0.3 ,.9 ,.2 ,1 ]) btn2 = Button(text='btn2' , background_color=[.1 ,.5 ,.4 ,1 ]) self .add_widget(btn) self .add_widget(btn2) class PageLayoutPyApp (App ): def build (self ): return PageLayoutWidget() if __name__ == '__main__' : PageLayoutPyApp().run()
KV实现布局
# pagelayout.kv
<PageLayoutWidget>
border: '100dp' # 设置边界,如果不设置默认为50dp
Button:
text:'Page0'
background_color: 0.3, .2, .5, 1
Button:
text:'Page1'
background_color: 0.4, .4, .6, 1
Button:
text:'Page2'
background_color: 0.6, .6, .8, 1
Button:
text:'Page3'
background_color: 0.8, .8, 1, 1
Python配合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from kivy.app import Appfrom kivy.uix.pagelayout import PageLayoutclass PageLayoutWidget (PageLayout ): def __init__ (self ): super (PageLayoutWidget, self ).__init__() class PageLayoutApp (App ): def build (self ): return PageLayoutWidget() if __name__ == '__main__' : PageLayoutApp().run()
PageLayout布局基本属性
border
: 指定两边边界大小,如果我们没有指定的话默认是50dp
.前面的kv中也对这进行写入,测试时可以将对应行去掉后进行测试。
page
: 设置默认显示哪一页、
swipe_threshold
: 设置翻页灵敏度 例如: swipe_threshold: .8
anim_kwargs
: 属性设置’t‘翻页动画和’d‘持续时间,例如: anim_kwargs: {'d':10, 't': 'linear'}
。 这边t中有哪些参数暂时不做过多说明,后期会细说。
可以尝试下,当以上属性都设置了,页面之间切换的动画被延长到10秒了,并且翻页的灵敏度也要滑动到80%时以上才会被判定为翻页的动作。
RelativeLayout布局 相对布局,此布局的操作方式与前面的FloatLayout基本是相同的,只是在定位的时候我们使用的x,center_x,right,y,center_y,top
这些属性是想对于他的父布局的,而不是相对于窗口。 其实任何布局,当布局的位置属性更改时,小部件都会移动。 接下来我们看看如何初始化一个RelativeLayou布局。 老样子,使用python代码和kv代码文件两种方式实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 from kivy.uix.relativelayout import RelativeLayoutfrom kivy.uix.button import Buttonfrom kivy.app import Appfrom kivy.uix.boxlayout import BoxLayoutfrom kivy.graphics import Color, Rectangleclass MyButton (Button ): def __init__ (self, **kwargs ): super (MyButton, self ).__init__(**kwargs) self .font_size = 20 self .size_hint = [.2 , .2 ] class RelativelayoutWidget (RelativeLayout ): pass class BoxLayoutWidget (BoxLayout ): def __init__ (self, **kwargs ): super (BoxLayoutWidget, self ).__init__(**kwargs) with self .canvas: Color(1 , 1 , 1 , 1 ) self .rect = Rectangle(pos=self .pos, size=self .size) self .bind(pos=self .update_rect, size=self .update_rect) box_relative = RelativelayoutWidget() btn1 = MyButton(text='btn1' , pos_hint={'right' : 1 , 'top' : 1 }, background_color=(.1 , .2 , .3 , 1 )) btn2 = MyButton(text='btn2' , pos_hint={'x' : 0 , 'top' : 1 }, background_color=(.1 , .2 , .3 , 1 )) RelativeBtn3 = MyButton(text='RelativeBtn3' , pos_hint={'center_x' : .5 , 'center_y' : .5 }, background_color=(.1 , .2 , .3 , 1 )) RelativeBtn4 = MyButton(text='RelativeBtn4' , pos_hint={'x' : 0 , 'y' : 0 }, background_color=(.1 , .2 , .3 , 1 )) RelativeBtn5 = MyButton(text='RelativeBtn5' , pos_hint={'right' : 1 , 'y' : 0 }, background_color=(.1 , .2 , .3 , 1 )) for i in [btn1, btn2, RelativeBtn3, RelativeBtn4, RelativeBtn5]: box_relative.add_widget(i) self .add_widget(BoxLayout()) self .add_widget(box_relative) def update_rect (self, *args ): self .rect.pos = self .pos self .rect.size = self .size class RelativeLayoutApp (App ): def build (self ): return BoxLayoutWidget() if __name__ == '__main__' : RelativeLayoutApp().run()
以上代码运行后,会出现半边是空白,另外半边是有在四个角落和中间五个按钮,那是因为我们把按钮在第42行的时候,全部加在了Relativelayout
布局内了,然后右边放空一个BoxLayout
布局进行占位,不加任何控件,因为Relativelayout
布局里面的控件是按照Relativelayout
布局的位置和大小进行定位的,并非按照我们最开始初始化的大的那个BoxLayout
布局进行定位的,所以里面的控件都是相对于Relativelayout
布局的位置进行放置。 接下一样,我们用kv文件配合python 的方式去实现上面的样子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from kivy.uix.boxlayout import BoxLayoutfrom kivy.app import Appclass BoxLayoutWidget (BoxLayout ): def __init__ (self, **kwargs ): super (BoxLayoutWidget, self ).__init__(**kwargs) class RelativeLayoutApp (App ): def build (self ): return BoxLayoutWidget() if __name__ == '__main__' : RelativeLayoutApp().run()
kv代码
<Button>
font_size:20
size_hint: .2,.3
<BoxLayoutWidget>
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
RelativeLayout:
Button:
text:"btn1"
background_color: .1,.2,.3,1
pos_hint:{'right':1, 'top':1}
Button:
text:"btn2"
background_color: .1,.2,.3,1
pos_hint:{'x': 0, 'top': 1}
Button:
text:"RelativeBtn3"
background_color: .1,.2,.3,1
pos_hint:{'center_x': .5, 'center_y': .5}
Button:
text:"RelativeBtn4"
background_color: .1,.2,.3,1
pos_hint:{'x': 0, 'y': 0}
Button:
text:"RelativeBtn5"
background_color: .1,.2,.3,1
pos_hint:{'right': 1, 'y': 0}
ScatterLayout 分散布局 ScatterLayout
布局与RelativeLayout
布局相似,当变更位置的时候,布局内的小部件也会随着父布局一起相对更改,因为这个布局主要由Scatter
小部件实现,所以是可以实现自由的平移,旋转,缩放等布局的。 我们来看下, 使用Python是如何实现的。
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.uix.scatterlayout import ScatterLayout
from kivy.uix.image import AsyncImage
from kivy.graphics import Rectangle, Color
class ScatterLayoutWidget(ScatterLayout):
pass
class BoxLayoutWidget(BoxLayout):
def __init__(self, **kwargs):
super(BoxLayoutWidget, self).__init__(**kwargs)
with self.canvas:
Color(1, 1, 1, 1)
self.rect = Rectangle(pos=self.pos, size=self.size)
self.bind(pos=self.update_rect, size=self.update_rect)
scatter_layout = ScatterLayoutWidget()
image = AsyncImage(source='https://blogcdn.sea-whales.cn/blog/typecho/11.jpg')
scatter_layout.add_widget(image)
self.add_widget(scatter_layout)
def update_rect(self, *args):
self.rect.pos = self.pos
self.rect.size = self.size
class ScatterLayoutApp(App):
def build(self):
return BoxLayoutWidget()
if __name__ == '__main__':
ScatterLayoutApp().run()
运行以上python代码,将会出现一个图片,且在窗体中间加载,我们可以使用鼠标单击图片,模拟手指点击进行图片的拖动和缩放、旋转操作。
kv配合python实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from kivy.app import Appfrom kivy.uix.boxlayout import BoxLayoutclass BoxLayoutWidget (BoxLayout ): def __init__ (self, **kwargs ): super (BoxLayoutWidget, self ).__init__(**kwargs) class ScatterLayoutApp (App ): def build (self ): return BoxLayoutWidget() if __name__ == '__main__' : ScatterLayoutApp().run()
kv文件
<BoxLayoutWidget>
canvas:
Color:
rgba: 1,1,1,1
ScatterLayout:
AsyncImage:
source: 'https://blogcdn.sea-whales.cn/blog/typecho/11.jpg'
ScatterLayout:
Image:
source: 'scatter_image.jpg'
StackLayout 堆栈布局 堆栈布局中只要不超过布局的范围,就可以进行垂直或者水平的排列子项,并且各个小部件的大小可以不需要相同,StackLayout
布局可以通过orientation
属性进行指定布局方向,默认值为lr-tb
。 我们看看堆栈排列有哪些排序排列。
按行排列:lr-tb
,lr-bt
,rl-tb
,rl-bt
按列排序:tb-lr
,bt-lr
,tb-rl
,bt-rl
我们看看上面的l、r、t、b 分别代表什么。
l 代表的是’left’的缩写,代表左
r 代表的是’right’的缩写,代表右
t 代表的是’top’的缩写,代表上
b 代表的是’bottom’的缩写,代表下
那么我们就可以将’lr’理解为从左到右,’tb’理解为从上到下,以此类推。 ‘lr-tb’可以看成先按行从左到右,当前窗口行满后再按列从上到下,那么’tb-lr’则可以看成先按列从上到下,当前窗口列满后,按从左到右排列。其他方式,以此类推。
接下来我们来写一个’lr-tb’排列,不同大小的布局,且按照’lr-tb’方式布局长度递增。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from kivy.uix.button import Buttonfrom kivy.uix.stacklayout import StackLayoutfrom kivy.app import Appfrom kivy.graphics import Rectangle, Colorclass StackLayoutWidget (StackLayout ): def __init__ (self, **kwargs ): self .orientation = 'lr-tb' super (StackLayoutWidget, self ).__init__(**kwargs) with self .canvas: Color(1 , 1 , 1 , 1 ) self .rect = Rectangle(size=self .size, pos=self .pos) self .bind(size=self .update_rect, pos=self .update_rect) for i in range (30 ): btn = Button(text=f'Btn_f{i} ' , width=50 + i * 8 , size_hint=(None , 0.20 )) self .add_widget(btn) def update_rect (self, *args ): self .rect.pos = self .pos self .rect.size = self .size class StackLayoutApp (App ): def build (self ): return StackLayoutWidget() if __name__ == '__main__' : StackLayoutApp().run()
前面已经写了很多从Python 文件改为Python和kv文件配合的情况了,这个我就不单独写了。 我们来看看StackLayout
有哪些属性。
orientation
: 前面我们说过,这个属性是用于决定布局内子部件的排列方式,也说明了具体有哪些属性。
padding
:这个属性有看前面的也不陌生,是用于控制布局和部件之间的间距的参数。接受的是一个list
,可以是四个和两个和一个。四个时分别是[padding_left, padding_top, padding_right, padding_bottom]
左、上、右、下 。两个时分别是[padding_horizontal,padding_vertical]
水平、垂直 两个方向。一个时代表控件四个方向的间距。他们的默认值都为0
spacing
: 这属性和前面的属性也是一样的用于控制部件和部件之间间距。他可以接受两个和一个参数,也是list。不同数量参数和padding
表示的一致。
以上三个参数StackLayout
常用的属性,还有几个就是布局会根据子部件自动设置一些属性。我们来看下分别是哪些(顾名思义即可):
minimum_height
:最低高度。
minimum_width
: 最小宽度
minimum_size
:最小尺寸(可同时设置最低高度和最小宽度)
以上就是StackLayout
参见的属性,就不做例子进行解释了。如果是按照前面一步步看过来的,也能很快理解意思。
例子:计时器 布局 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # !/usr/bin/env python3 # -*- coding: utf-8 -*- from kivy.app import App from kivy.uix.boxlayout import BoxLayout class ClockBoxLayout(BoxLayout): def __init__(self, **kwargs): super(ClockBoxLayout, self).__init__(**kwargs) class ClockApp(App): def build(self): return ClockBoxLayout() if __name__ == '__main__': # 设置页面背景 from kivy.core.window import Window Window.clearcolor = [.8, .8, .8, 1] ClockApp().run()
kv文件
<ClockBoxLayout>:
orientation: 'vertical'
Label:
id: time_label_id
text: '[b]00[/b]:00:00'
font_size: 60
markup: True
计时器 具有暂停和重置功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 from time import strftimefrom kivy.app import Appfrom kivy.uix.anchorlayout import AnchorLayoutfrom kivy.clock import Clockclass ClockBoxLayout (AnchorLayout ): def __init__ (self, **kwargs ): super (ClockBoxLayout, self ).__init__(**kwargs) self .timing_flag = False self .timing_seconds = 0 self .on_start() def on_start (self ): Clock.schedule_interval(self .update_time, 0 ) def update_time (self, nap ): if self .timing_flag: self .timing_seconds += nap self .ids.time_label_id.text = strftime('[b]%H[/b]:%M:%S' ) m, s = divmod (self .timing_seconds, 60 ) self .ids.stopwatch.text = ('%02d:%02d.[size=40]%02d[/size]' % (int (m), int (s), int (s * 100 % 100 ))) def start_on_stop (self ): self .ids.start_stop_button_id.text = 'Start' if self .timing_flag else 'Stop' self .timing_flag = not self .timing_flag def reset_clock (self ): if self .timing_flag: self .ids.start_stop_button_id.text = 'Start' self .timing_flag=False self .timing_seconds = 0 class ClockApp (App ): def build (self ): return ClockBoxLayout() if __name__ == '__main__' : from kivy.core.window import Window Window.clearcolor = [.8 , .8 , .8 , 1 ] ClockApp().run()
kv文件
<MyButton@Button>
font_size: 25
bold: True
border: (2,2,2,2)
<ClockBoxLayout>:
# 异步加载背景图像
AsyncImage:
source: 'https://blogcdn.sea-whales.cn/blog/typecho/11.jpg'
BoxLayout:
# 设置布局
orientation: 'vertical'
Label:
id: time_label_id # 添加ID属性,通过ID属性进行对控件的获取
text: '[b]00[/b]:00:00'
font_size: 60
markup: True #
BoxLayout:
orientation: 'horizontal'
padding: 20
spacing: 20
size_hint:(1, None)
height: 90
MyButton:
id: start_stop_button_id
text:'start'
on_press: root.start_on_stop()
MyButton:
text: 'Reset'
on_press: root.reset_clock() # 触发事件
Label:
id: stopwatch
text: '00:00.[size=40]00[/size]'
font_size: 60
markup: True
图形绘制 页面背景 看过布局的朋友们,一定发现了,在创建一个widget控件后,为布局设置背景色或者经常会有一个with self.canvas
并且加了几个属性和绑定了事件,这些其实是我们在生成一个控件后,Kivy自动生成的一个类似画布的。我们通过对画布的更改可以设置颜色、尺寸、背景图等。 这里说明一下,canvas 学过HTML5的同学肯定觉得眼熟,但是实际两个是不相同的。 HTML的Canvas的定义是:
HTML5 的 canvas 元素使用 JavaScript 在网页上绘制图像。 画布是一个矩形区域,您可以控制其每一像素。 canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。
而Kivy的Canvas本质是一组在坐标空间的指令容器,可以理解成坐标空间中一个无限的绘画板,通过添加指令来绘制图形。 我们先来使用纯色进行设置背景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from kivy.uix.boxlayout import BoxLayoutfrom kivy.app import Appfrom kivy.graphics import Rectangle, Colorclass BoxLayoutWidget (BoxLayout ): def __init__ (self, **kwargs ): super (BoxLayoutWidget, self ).__init__(**kwargs) with self .canvas: Color(1 , 1 , 1 , 1 ) Rectangle(pos=self .pos, size=self .size) class BoxLayoutApp (App ): def build (self ): return BoxLayoutWidget() if __name__ == '__main__' : BoxLayoutApp().run()
运行上面代码可以发现,在布局中,左下角有一块100*100的白色小方块,这时候我们就完成了基础的颜色设置,但是我们需要把白色填满整个方块。其实以上代码的操作是初始化一个BoxLayou然后,初始化后其实一个黑色的背景,然后在with里面新加一个画布,对画布初始化为白色背景。如果我们要填满整个窗口只需要在初始化好画布之后,对画布进行大小重定义即可实现覆盖。参考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from kivy.uix.boxlayout import BoxLayoutfrom kivy.app import Appfrom kivy.graphics import Rectangle, Colorclass BoxLayoutWidget (BoxLayout ): def __init__ (self, **kwargs ): super (BoxLayoutWidget, self ).__init__(**kwargs) with self .canvas: Color(127 / 255 , 255 / 255 , 212 / 255 , 1 ) self .rect = Rectangle(pos=self .pos, size=self .size) self .bind(pos=self .update_rect, size=self .update_rect) def update_rect (self, *args ): self .rect.pos = self .pos self .rect.size = self .size class BoxLayoutApp (App ): def build (self ): return BoxLayoutWidget() if __name__ == '__main__' : BoxLayoutApp().run()
老样子,我们看看KV文件配合的情况下如何实现以上样子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from kivy.uix.boxlayout import BoxLayoutfrom kivy.app import Appclass BoxLayoutWidget (BoxLayout ): def __init__ (self, **kwargs ): super (BoxLayoutWidget, self ).__init__(**kwargs) class ColorApp (App ): def build (self ): return BoxLayoutWidget() if __name__ == '__main__' : ColorApp().run()
kv文件
1 2 3 4 5 6 7 8 <BoxLayoutWidget> canvas: Color: rgba: [127 / 255, 255 / 255, 212 / 255, 1] Rectangle: size: self.size pos: self.pos
还可以在有底色的情况下,使用背景图片。 只需把前面Python代码中19行新增一个参数即可如下:
self.rect = Rectangle(pos=self.pos, size=self.size, source='canvas_image.jpg')
或者kv文件中Rectangle
下新增source: 'canvas_image.jpg'
,其中canvas_image.jpg
是图片名称和地址。
基本图形绘制 前面,我们使用纯色背景以及使用图片来将Canvas
这个画布进行上色和调整等,接下来我们试试在画好的画布中进行简单图形的绘制,比如。矩形、椭圆等
矩形 其实我们前面也一直在画这个矩形,只是有的部分把他填充了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # !/usr/bin/env python3 # -*- coding: utf-8 -*- from kivy.uix.relativelayout import RelativeLayout from kivy.app import App class RelativeWidget(RelativeLayout): def __init__(self, **kwargs): super(RelativeWidget, self).__init__(**kwargs) class DrawRectangleApp(App): def build(self): return RelativeWidget() if __name__ == '__main__': DrawRectangleApp().run()
drawrectangle.kv
<RelativeWidget>
canvas:
Color:
rgba: [1,1,1,1]
Rectangle:
size: self.width*0.2, self.height*.15
pos: self.x+10, self.y+10
椭圆 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # !/usr/bin/env python3 # -*- coding: utf-8 -*- from kivy.uix.relativelayout import RelativeLayout from kivy.app import App class RelativeWidget(RelativeLayout): def __init__(self, **kwargs): super(RelativeWidget, self).__init__(**kwargs) class DrawEllipseApp(App): def build(self): return RelativeWidget() if __name__ == '__main__': DrawEllipseApp().run()
drawellipse.kv
<RelativeWidget>
canvas:
Color:
rgba: [1,1,1,1]
Ellipse:
size: self.width*.35, self.height*.35
pos: self.x+250, self.top-400
半圆及多边形 其实半圆的画法和椭圆基本一致。只是增加了三个新属性。
angle_start
: 开始线角度,开始方向与y轴的角度。
angle_end
: 结束线角度,结束方向与y轴的角度。一般angle_end
的大小要大于angle_start
,若不大于则需要加 360° ,此时Kivy会顺时针画图形。否则则是逆时针画图形。
egments
: 多边形的边数,可以用来画三角形,六边形等形状。
例如,我们改下前面的椭圆,在它旁边加多一个六边形,一个三角形以及一个扇形还有正圆。Python代码我们继续使用椭圆的代码。改下Kv代码就好:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <RelativeWidget> canvas: Color: rgba: [1,1,1,1] Ellipse: # 正圆 size: self.width*0.20, self.width*0.20 pos: self.x+20, self.top-200 Ellipse: # 正六边形 size: self.height*0.25, self.height*0.25 pos: self.x+200, self.top-200 segments: 6 Ellipse: # 超过180°的顺时针扇形 size: self.width*0.25, self.height*.25 pos: self.x+400, self.top-200 angle_start: 120 angle_end: 420 Ellipse: # 正三角 size: self.height*.25, self.height*.25 pos: self.x+400, self.top-400 segments: 3 Ellipse: # 正半圆 size: self.height*0.25, self.height*.25 pos: self.x+200, self.top-400 angle_start: 0 angle_end: 180 Ellipse: # 椭圆 size: self.height*0.30, self.width*.15 pos: self.x+20, self.top-400
多边形 前面使用了画圆的方式使用参数segments
来画多边形,这些多变形画出来它的边长是随着圆的尺寸决定的,长度是不能自己决定的。 Kivy还提供了一种方式,用于指定各边的顶点坐标,可以绘制特殊边的多边形,但是值支持四个坐标点,可用于菱形、平行四边形、梯形等。
一样,我们值更改Kv文件如下:
<RelativeWidget>
canvas:
Color:
rgba: [1,1,1,1]
Quad:
points: 400,250, 640,280, 480,500, 380,520 # 设置顶点坐标。 按X轴,Y轴的顺序读取顶点坐标。
Quad
中的points
会按照x轴,y轴的的顺序,读取点的位置,并且在画布canvas
上绘制一个多边形。
点和线 kv文件
<RelativeWidget>
canvas:
Color:
rgba: [1,1,1,1]
Line:
points: 310,350, 310,280, 360,350, 510,350
Point:
points:300,200, 300,400
pointsize: 5
其实就像数学里面理解的,所有的线是由无数个点挨着排列形成的,同理面是由线挨着排列形成的。我们也可以用点和线画前面的图形。 例如:
<RelativeWidget>
canvas:
Color:
rgba: [1,1,1,1]
Line: # 线
points: 110,150, 310,80
Point: # 点
points:100,200, 180,260
pointsize: 5 # 点大小
Line: # 椭圆
ellipse: 210,320, 80,60, 120,420,180
width: 2 # 线宽
Line: # 圆
circle: 350,350, 40,0, 360,180
width: 1.5
Line: # 矩形
rectangle: 410,310, 80, 70
Line:
points: 510,310, 540,390, 590,320
close: True # 是否闭合
KV中Line对应参数说明:
ellipse:210, 320 表示椭圆的位置;80, 60 代表椭圆的宽和长;120, 420,180分别对应: angel_start, angel_end, segments.
circle: 350,350 表示圆心的位置;40表示圆的半径;0,360,180分别对应: angel_start, angel_end, segments.
rectangle: 420, 310 表示矩形位置左下角的顶点; 80,70,代表宽高
points: 510, 310 代表第一个点的位置,以此类推。
Cancas 属性 前面我们已经初步了解了Canvas
画布基本绘制功能。我们来深入了解下他有哪些属性。 在Kivy里,每个小部件和布局基本都有他的Canvas
、Canvas.before
和canvas.after
,其实我们可以将canvas
看作在坐标空间种,一个无限的绘图板,通过添加绘图指令来绘制想要的图形。Kivy的所有部件都是共享一个坐标空间的,且不限于窗口或者屏幕的大小。 我们前面学习的时候也已经试过给画布和小部件设置背景以及显示的颜色。而且我们还能添加不同Instructions
指令来达到不同的页面效果。
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.graphics import Rectangle, Color
from kivy.graphics.instructions import InstructionGroup
class RelativeWidget(RelativeLayout):
def __init__(self, **kwargs):
super(RelativeWidget, self).__init__(**kwargs)
blue = InstructionGroup()
blue.add(Color(1, 0, 0, .6))
blue.add(Rectangle(pos=self.pos, size=(300,300)))
self.canvas.add(blue)
green = InstructionGroup()
green.add(Color(0, 1, 0, 0.4))
green.add(Rectangle(pos=(300, 300), size=(300, 300)))
self.canvas.add(green)
class AttributeApp(App):
def build(self):
return RelativeWidget()
if __name__ == '__main__':
AttributeApp().run()
通常情况,我们在使用完canvas
画布后,还需使用clear()
方法清除所有加载在画布种的Instructions
指示类型。 我们可以把上面的代码改成with
语法,效果是一致的。
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.graphics import Rectangle, Color
from kivy.graphics.instructions import InstructionGroup
class RelativeWidgetWith(RelativeLayout):
def __init__(self, **kwargs):
super(RelativeWidgetWith, self).__init__(**kwargs)
with self.canvas:
Color(1, 0, 0, .6)
Rectangle(pos=self.pos, size=(300, 300))
Color(0, 1, 0, 0.4)
Rectangle(pos=(300, 300), size=(300, 300))
class AttributeApp(App):
def build(self):
return RelativeWidgetWith()
if __name__ == '__main__':
AttributeApp().run()
还有一些常用的属性:
Canvas属性
说明
add(Instructions c)
将Instructions类型的c添加到Canvas中
clear()
删除所有Instructions
get_group(str groupname)
返回特定组下所有Instructions
insert(int index, Instructions c)
指定位置插入c
indexof(Instructions c)
返回c下标
length()
返回canvas的长度
remove()
删除指定的c
remove_group(str groupname)
删除该组下所有的c
在Kivy每个小部件都有属性canvas
,除了这个包括有canvas.before
和canvas.after
属性,用法的话基本和canvas一致。只是在运行顺序上的优先级不同。 大概顺序是:
canvas.before > canvas > widget(canvas.befor,canvas,canvas.after) > canvas.after
旋转、平移和缩放空间坐标 在canvas 中是可以使用Rotate
指令来控制旋转操作,他与ScatterLayout
布局不同,它是对整个坐标空间,因此所有的子部件都是会受到影响。在使用整个参数时,需要指定三个参数;
axis: 设置用于旋转的轴,通常为z轴(0,0,1)
angle: 设置旋转度数
origin: 设置旋转参考点
来试试效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from kivy.uix.gridlayout import GridLayoutfrom kivy.app import Appclass RotateGridLayoutWidget (GridLayout ): def __init__ (self, **kwargs ): super (RotateGridLayoutWidget, self ).__init__(**kwargs) class RotateTranslateZoomApp (App ): def build (self ): return RotateGridLayoutWidget() if __name__ == '__main__' : RotateTranslateZoomApp().run()
rotatetranslatezoom.kv
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <MyImage@Image> source:'rotatetranslatezoom.jpg' pos: self.parent.pos size_hit: .5, .4 canvas: Color: rgba: 1,0,0,.5 Line: rectangle: self.x, self.y, self.width,self.height <RotateGridLayoutWidget> cols: 2 canvas: Color: rgba:(1,1,1,1) Rectangle: pos: self.pos size: self.size Button: text: 'col:1 row:1' FloatLayout: canvas: Rotate: axis:(0,0,1) angle: 60 origin: self.center Color: rgba:(1,1,1,1) Rectangle: pos: self.pos size: self.size MyImage: Button: text: 'col:1 row:2'
以上布局,会出现两个按钮和一张图片。其中按钮按列排列。但是由于我们在FloatLayout
中使用了Rotate
这样导致了画布进行了旋转。我们看到的效果是图像是沿着中点,逆时针旋转了60度的情况。并且由于旋转的是在Button前面加入的,所以导致后方的空间坐标系也收到了影响,也连带着旋转了。而且点击第二个按钮黑色部分发现无效。而是需要点击按钮本身的位置,也就是第一个按钮的垂直排列的列下方才有效。为了避免这个情况,我们需要引入两个新的命令。
PushMatrix : 保存上下文环境。
PopMatrix : 恢复上下文环境。 就像上面的栗子: 我们在对某个图像的画布或者其他进行操作的时候。就会导致代码排序在它之后的内容受到影响。这个时候我们就可以使用前面两个命令对环境进行保存和回复。 我们来改下KV文件:
<MyImage@Image>
source:'rotatetranslatezoom.jpg'
pos: self.parent.pos
size_hit: .5, .4
canvas:
PushMatrix
Rotate:
axis:(0,0,1)
angle: 60
origin: self.center
Color:
rgba: 1,0,0,.5
Line:
rectangle: self.x, self.y, self.width,self.height
PopMatrix
<RotateGridLayoutWidget>
cols: 2
canvas:
Color:
rgba:(1,1,1,1)
Rectangle:
pos: self.pos
size: self.size
Button:
text: 'col:1 row:1'
FloatLayout:
MyImage:
Button:
text: 'col:1 row:2'
以上文件运行后,会发现,红色矩形框旋转了60度。后面的按钮以及图像均不会受到影响,这是因为Rotate
指令我们是放在canvas
中的,是在屏幕上以及确定位置之后才开始执行的,并且在显示的时候,就使用前面的恢复环境命令恢复了。但是我们旋转的时候,就是为了让图像旋转。现在的情况却是图像并未旋转,这个时候,可以来试试前面我们学过的属性canvas.befor
和canvas.after
我们只需要在位置还没确定的时候,就旋转,这样图像就会在画布旋转之后进行图像的显示。这样就能达到指定效果了。我们在改动下KV文件:
<MyImage@Image>
source:'rotatetranslatezoom.jpg'
pos: self.parent.pos
size_hit: .5, .4
canvas.before: # 使用canvas.before 命令
PushMatrix # 保存当前上下文环境
Rotate:
axis:(0,0,1) # 旋转
angle: 60 # 角度
origin: self.center # 起点
canvas:
Color:
rgba: 1,0,0,.5
Line:
rectangle: self.x, self.y, self.width,self.height
canvas.after:
PopMatrix # 恢复上下文环境
<RotateGridLayoutWidget>
cols: 2
canvas:
Color:
rgba:(1,1,1,1)
Rectangle:
pos: self.pos
size: self.size
Button:
text: 'col:1 row:1'
FloatLayout:
MyImage:
Button:
text: 'col:1 row:2'
使用上面的kv文件,就会发现图像和矩形框都一起旋转了。这样就达到了,旋转的同时连小部件也旋转。当然会发现我们为什么不把PopMatrix
写在canvas
的末行,而是大费周章的写个canvas.after
,这里我就不解释了结合前面的知识点,大家可以尝试下,将PopMatrix
写在canvas末行,看看是什么效果。
试过了旋转,我们来看看平移:canvas
平移指令是Translate
除了属性以外,该指令和我们前面用的Rotate
指令基本是一致。 在使用Translate
时,是需要指定X轴,Y轴,Z轴上的移动距离,如果不指定,则默认不移动。我们再拿前面的旋转代码给他加上平移属性看看。
Translate:
x: -50
y: 100
z:0
就不贴完整代码了,大概就是在Rotate属性结束下方另起一行,加上以上四行代码。我们会发现,图片的位置会向左平移了50个像素,将按钮盖住了,并且也向上移动了100个像素。如果这里我们将z轴的值进行改变,会发现图像不见了,这是因为图像的位置已经不在画布上了。 同理缩放(Scale)也是和前面一致,我们只需要指定xyz轴上的缩放倍数即可:
Scale:
xyz:(1.25,1.30,0.5)
如上,则是x轴和y轴分别缩放1.25和1.30.倍,z轴缩放0.5倍。
实战–画板 我们先来做一个初始画板,先不添加其他复杂元素,来实现能写字的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 from kivy.app import Appfrom kivy.uix.widget import Widgetfrom kivy.graphics import Line, Colorclass DrawCanvasWidget (Widget ): def __init__ (self, **kwargs ): super (DrawCanvasWidget, self ).__init__(**kwargs) """设置画笔的默认颜色为黑色""" self .canvas.add(Color(rgb=[0 , 0 , 0 ])) self .line_width = 2 def on_touch_down (self, touch ): """触摸显示轨迹""" if Widget.on_touch_down(self , touch): return with self .canvas: touch.ud['current_line' ] = Line(points=(touch.x, touch.y), width=self .line_width) def on_touch_move (self, touch ): """连线""" if 'current_line' in touch.ud: touch.ud['current_line' ].points += (touch.x, touch.y) class PaintApp (App ): def build (self ): self .draw_canvas_widget = DrawCanvasWidget() return self .draw_canvas_widget if __name__ == '__main__' : PaintApp().run()
Kivy提供了on_touch_down
和on_touch_move
方法来实现监听屏幕点击和屏幕移动触发事件
1 2 3 4 5 6 7 <DrawCanvasWidget>: canvas.before: Color: rgba:[1,1,1,1] Rectangle: pos: self.pos size: self.size
以上能够实现使用一种黑色画笔在白色画板上进行写字。接下来,我们试试能否通过选择颜色去替换画笔颜色,然后进行绘画。 Kivy种设置颜色的方法一般是使用rgba(r红色,g绿色,b蓝色,alpha名度)一般我们就是使用颜色和255进行比值,得到百分比。我们也可以直接使用十六进制进行表示。当需要大量颜色的时候,Kivy在utils包内也提供了 get_color_from_hex()
方法进行16进制和百分比转换,我们使用当时时候传入十六进制字符串即可。 我们可以选择在python内使用,还是在kv文件内使用,使用方法如下: python文件内:
from kivy.utils import get_color_from_hex
get_color_from_hex('#98feab')
kv文件内使用:
#:import C kivy.utils.get_color_from_hex
Button:
background_color: C('#98feab')
我们可以在画板类里面写上一个方法来改变canvas画布颜色的方法。不是canvas.before背景:
def chang_color(self, new_color):
"""调色"""
self.canvas.add(Color(*new_color))
通过chang_color
方法在init中初始化一个颜色。在kv文件设置按钮,通过按钮单击调用chang_color
激活颜色的改变。 具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 from kivy.app import Appfrom kivy.uix.widget import Widgetfrom kivy.graphics import Line, Colorfrom kivy.utils import get_color_from_hexclass DrawCanvasWidget (Widget ): def __init__ (self, **kwargs ): super (DrawCanvasWidget, self ).__init__(**kwargs) """设置画笔的默认颜色为黑色""" self .change_color(get_color_from_hex('#12aced' )) self .line_width = 2 def on_touch_down (self, touch ): """触摸显示轨迹""" if Widget.on_touch_down(self , touch): return with self .canvas: touch.ud['current_line' ] = Line(points=(touch.x, touch.y), width=self .line_width) def on_touch_move (self, touch ): """连线""" if 'current_line' in touch.ud: touch.ud['current_line' ].points += (touch.x, touch.y) def change_color (self, new_color ): """调色""" self .canvas.add(Color(*new_color)) class PaintApp (App ): def build (self ): self .draw_canvas_widget = DrawCanvasWidget() return self .draw_canvas_widget if __name__ == '__main__' : PaintApp().run()
kv文件
#:import C kivy.utils.get_color_from_hex # 引入颜色转换方法
<BottomColorButton@ToggleButton>:
group: 'color'
background_normal: ''
background_down: ''
border: (3, 3, 3, 3)
on_release: app.draw_canvas_widget.change_color(self.background_color)
<DrawCanvasWidget>:
canvas.before:
Color:
rgba: [1, 1, 1, 1]
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
id: bottom_box
orientation: 'horizontal'
padding: 2
spacing: 2
size: root.width, 40
BottomColorButton:
background_color: C('#19caad')
state: 'down' # 按钮状态
BottomColorButton:
background_color: C('#8cc7b5')
BottomColorButton:
background_color: C('#a0eee1')
BottomColorButton:
background_color: C('#bee7e9')
BottomColorButton:
background_color: C('#beedc7')
BottomColorButton:
background_color: C('#d6d5b7')
BottomColorButton:
background_color: C('#d1ba74')
BottomColorButton:
background_color: C('#e6ceac')
BottomColorButton:
background_color: C('#ecad9e')
BottomColorButton:
background_color: C('#f4606c')
BottomColorButton:
background_color: C('#3498db')
BottomColorButton:
background_color: C('#1abc9c')
BottomColorButton:
background_color: C('#2ecc71')
BottomColorButton:
background_color: C('#f1c40f')
BottomColorButton:
background_color: C('#e67e22')
BottomColorButton:
background_color: C('#e74c3c')
BottomColorButton:
background_color: C('#9b59bc')
BottomColorButton:
background_color: C('#ecf0f1')
BottomColorButton:
background_color: C('#95a5a6')
BottomColorButton:
background_color: C('#000000')
有了画笔颜色的改变,我们接下来看看改变画笔的粗细。 同理,使用与改变颜色相同方法做一个画笔粗细:
def change_line_width(self, line_width='Normal'):
self.line_width = {'Thin': 1, 'Normal': 2, 'Thick': 4}[line_width]
1 2 3 4 5 6 7 8 9 <LineWidthButton@ToggleButton>: group: 'line_width' color: C('#2c3e50') background_color: C('#ecf0f1') background_normal: '' background_down: '' border: (3, 3, 3, 3) on_release: app.draw_canvas_widget.change_line_width(self.text)
画布内新增改变按钮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 BoxLayout: orientation: 'horizontal' padding: 2 spacing: 2 x: 0 top: root.top size_hint: None,None size: 280, 44 LineWidthButton: text: 'Thin' LineWidthButton: text: 'Normal' state: 'down' LineWidthButton: text: 'Thick'
新增清空画板功能;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 # !/usr/bin/env python3 # -*- coding: utf-8 -*- from kivy.app import App from kivy.uix.widget import Widget from kivy.graphics import Line, Color from kivy.utils import get_color_from_hex # 添加边框样式 from kivy.uix.behaviors import ToggleButtonBehavior from kivy.uix.togglebutton import ToggleButton # 因为已经在这边添加了ToggleButton的公共类,提取出公共属性的两个就不需要使用ToggleButton而是可以直接使用 # FrameToggleButton即可 class FrameToggleButton(ToggleButton): """当前按钮添加边框""" def do_press(self): """点击改变状态""" if self.state == 'normal': ToggleButtonBehavior.do_press(self) class DrawCanvasWidget(Widget): def __init__(self, **kwargs): super(DrawCanvasWidget, self).__init__(**kwargs) """设置画笔的默认颜色为黑色""" self.change_color(get_color_from_hex('#12aced')) self.change_line_width() def on_touch_down(self, touch): """触摸显示轨迹""" if Widget.on_touch_down(self, touch): return with self.canvas: touch.ud['current_line'] = Line(points=(touch.x, touch.y), width=self.line_width) def on_touch_move(self, touch): """连线""" if 'current_line' in touch.ud: touch.ud['current_line'].points += (touch.x, touch.y) def change_color(self, new_color): """调色""" self.last_color = new_color self.canvas.add(Color(*new_color)) def change_line_width(self, line_width='Normal'): self.line_width = {'Thin': 1, 'Normal': 2, 'Thick': 4}[line_width] def clear_canvas(self): saved = self.children[:] self.clear_widgets() self.canvas.clear() for widget in saved: self.add_widget(widget) self.change_color(self.last_color) class PaintApp(App): def build(self): self.draw_canvas_widget = DrawCanvasWidget() return self.draw_canvas_widget if __name__ == '__main__': PaintApp().run()
#:import C kivy.utils.get_color_from_hex
<BottomColorButton@FrameToggleButton>:
group: 'color'
background_normal: ''
background_down: ''
border: (1, 1, 1, 1)
on_release: app.draw_canvas_widget.change_color(self.background_color)
<LineWidthButton@FrameToggleButton>:
group: 'line_width'
color: C('#2c3e50')
background_color: C('#ecf0f1')
background_normal: ''
background_down: ''
border: (3, 3, 3, 3)
on_release: app.draw_canvas_widget.change_line_width(self.text)
<DrawCanvasWidget>:
canvas.before:
Color:
rgba: [1, 1, 1, 1]
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'horizontal'
padding: 2
spacing: 2
x: 0
top: root.top
size_hint: None,None
size: 280, 44
LineWidthButton:
text: 'Thin'
LineWidthButton:
text: 'Normal'
state: 'down'
LineWidthButton:
text: 'Thick'
Button:
text: 'Clear'
on_release: root.clear_canvas()
BoxLayout:
id: bottom_box
orientation: 'horizontal'
padding: 2
spacing: 2
size: root.width, 40
BottomColorButton:
background_color: C('#19caad')
state: 'down'
BottomColorButton:
background_color: C('#8cc7b5')
BottomColorButton:
background_color: C('#a0eee1')
BottomColorButton:
background_color: C('#bee7e9')
BottomColorButton:
background_color: C('#beedc7')
BottomColorButton:
background_color: C('#d6d5b7')
BottomColorButton:
background_color: C('#d1ba74')
BottomColorButton:
background_color: C('#e6ceac')
BottomColorButton:
background_color: C('#ecad9e')
BottomColorButton:
background_color: C('#f4606c')
BottomColorButton:
background_color: C('#3498db')
BottomColorButton:
background_color: C('#1abc9c')
BottomColorButton:
background_color: C('#2ecc71')
BottomColorButton:
background_color: C('#f1c40f')
BottomColorButton:
background_color: C('#e67e22')
BottomColorButton:
background_color: C('#e74c3c')
BottomColorButton:
background_color: C('#9b59bc')
BottomColorButton:
background_color: C('#ecf0f1')
BottomColorButton:
background_color: C('#95a5a6')
BottomColorButton:
background_color: C('#000000')
部分UX部件 前面我们已经对Layout布局,以及Canvas 画布进行了学习,我们接下来来看看,经常使用的一些部件,这些部件是图形界面设计的重要组成部分,合理使用可以更高效的开发程序。我们先从简单部件开始。
button常用属性
属性
说明
background_color
按钮的背景颜色,rgba格式,默认为灰色
text
按钮显示的文本
font_size
文本字体的大小,默认为15sp
color
文本字体颜色,rgba格式,默认为白色[1,1,1,1]
state
按钮状态,默认为“normal”,可设置成“down”
disables
如果为True时则禁用按钮,默认为False
background_disabled_normal
默认为“kivy\tools\theming\defaulttheme\button_disabled_pressed.png”
background_disabled_down
默认为“kivy\tools\theming\defaulttheme\button_disabled.png”
background_down
按下按钮时显示的图形,默认为“kivy\tools\theming\defaulttheme\button_pressed.png”
background_normal
未按下按钮时显示的图像,默认为“kivy\tools\theming\defaulttheme\button.png”
border
与background_normal 和background_down 属性一起使用,可用于自定义背景。
触发事件
on_press:按下按钮触发该事件
on_release: 按下按钮并释放时,触发该事件
Label标签 Kivy中的Label小部件用于显示文本,目前只支持ASCII和Unicode编码字符(暂不支持中文),可以通过属性设置文本内容,字体,大小,颜色,对齐方式,换行以及标记文字等内容。
常用属性
属性
说明
text
标签显示文本大小,默认为空字符串
text_size
标签文本大小、默认为(None,None),表示无限制
font_name
要使用字体的文件名,可以是绝对路径或者resource_find解析的相对路径
font_size
文本字体的大小,以像素为单位,默认为15sp
bold
字体使用粗体,默认为False
italic
字体使用斜体,默认为False
color
字体颜色,格式为rgba,默认为白色[1,1,1,1]
halign
文本的水平对齐方式,默认为‘auto’,可选参数为: buttom,middle 或者 Center 和 top
markup
是否分割所有标记文本,默认为False
refs
使用[ref=xxx]xxx[/ref]标记部分文本,使用时需要将markup属性设置为True
underline
文本上添加下划线,默认为False
padding_x
小部件框内文本的水平填充,默认为0
padding_y
小部件框内文本的垂直填充,默认为0
padding
以(padding_x,padding_y)格式填充文字
texture
文本的纹理对象,属性更改时会自动呈现文本,默认为None
texture_size
文本纹理大小,由字体大小和文本确定
unicode_error
如何处理Unicode解码错误,可选参数为:“strict”, “replace”(默认)和“ignore”
strikethrough
在文本中添加删除线
strip
与python 内置函数一致,是否删除空格以及换行符,默认为False
outline_color
文本轮廓颜色,格式为rgba, 默认为[0,0,0]
outline_width
文本周围轮廓的宽度,单位为像素,默认为None表示不会渲染轮廓
max_lines
要使用的最大行数,默认为0,表示无限制
shorten
是否应该尽可能缩短文本内容,默认为False
shorten_from
在哪一侧缩短文本,默认为center,可选 left,right 和 center
split_str
当shorten 为True 时,差分字符串,默认为空字符串
is_shortened
是否以缩短事件的方式进行渲染,默认为False
line_height
文本的行高,默认为1.0
base_direction
文本基本方向,当halign为auto 时,会影响水平对齐,可设置参数为:None、ltr (从左到右)、rtl、weak_lte、weak_rtl
disabled_color
禁用小部件时文本轮廓的颜色,格式为rgb,默认为[0,0,0]
ellipsis_options
使用’···’缩短文本,使用时要设置markuo和shorten为True,默认为空字典{}
font_blended
使用混合字体,默认为True
font_context
字体上下文,默认为None表示该字体单独使用
font_family
字体系列,仅在使用font_context时使用,
font_features
将CSS格式的OpenType字体传给Pango ,默认为空字符。
font_hinting
渲染字体的提示选项,可选参数为’normal‘,’light’,’mono’和None
font_kerning
是否为字形渲染,启用字距调整,默认为True
mipmap
是否将OpenGL mipmapping应用于纹理,默认为False
开启以下需要将markup打开
可用标记
解释
可用标记
解释
[b][/b]
加粗
[i][/i]
斜体
[u][/u]
下划线
[s][/s]
删除线
[sub][/sub]
下标
[sup][/sup]
上标
[font=][/font]
更改字体
[size=][/size]]
大小
[color=#][/color]
颜色
[anchor=]
锚点,(x,y)
[ref=][/ref]
添加一个交互式引用区,引用一个方法
[text_language=<>][/text_language]
文本语言,例: “<zh_CN>”
&bl
[
&br
]
&
&
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #:import C kivy.utils.get_color_from_hex <LabelBoxLayout>: orientation: 'vertical' Button: text: 'Button' on_press: root.on_press_btn() on_release: root.on_release_btn() Label: id: lable_btn text: 'The first step is one of awareness. It will be hard to make a change to positive thinking without being acutely intimate with the thoughts that run through your mind. Recently, I was amazed to discover deep buried emotions from negative thoughts that I had for fewer than 10 minutes. Without awareness, I would have carried the hurt and anger inside. Awareness helped me to bring them out to the open for me to deal with.' font_size: '20sp' color: C('#B0C4DE') italic: True text_size: cm(15), mm(200) halign: 'right' valign: 'middle' strikethrough: True Label: canvas.before: Color: rgba: C('#DCDCDC') Rectangle: pos: self.pos size: self.size text: 'There are moments in life when [i]you miss someone [color=#"4169E1"]so much that you just want[/color] to pick them from your[/i] dreams and hug them for real! Dream what you want to dream;go where you want to go;be what you want to be,because you have only one life and one chance to do all the things you want to do.' color: C('#FF0000') font_size: '15sp' bold: True text_size: cm(10), mm(100) halign: 'center' valign: 'middle' underline: True line_height: 1.0 markup: True
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from kivy.app import Appfrom kivy.uix.label import Labelfrom kivy.uix.boxlayout import BoxLayoutclass LabelBoxLayout (BoxLayout ): def __init__ (self, **kwargs ): super (LabelBoxLayout, self ).__init__(**kwargs) def on_press_btn (self ): self .children[1 ].strikethrough = not self .children[1 ].strikethrough def on_release_btn (self ): self .children[1 ].strikethrough = not self .children[1 ].strikethrough class LabelBoxApp (App ): def build (self ): return LabelBoxLayout() if __name__ == '__main__' : LabelBoxApp().run()
触发事件 Kivy中是使用ref 来标记触发事件的,点击ref包裹的文本,就可以触发“on_ref_press”事件。
设置Label 标签的触发事件 在使用ref之前,需要将markup设置为True
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.utils import get_color_from_hex
class LabelBoxLayout(BoxLayout):
def __init__(self, **kwargs):
super(LabelBoxLayout, self).__init__(**kwargs)
strtext = 'Awareness [color=#00FF00][ref=label][i]helped me[/i][/ref][/color] to [b]bring[/b] [s]them out[/s] to the open for me to deal with.'
label_ref = Label(font_size='25sp',text=strtext, markup=True,
color=get_color_from_hex('#F0F8FF'))
label_ref.bind(on_ref_press=self.on_press_btn_label_ref)
self.add_widget(label_ref)
def on_press_btn(self):
self.children[1].strikethrough = not self.children[1].strikethrough
def on_release_btn(self):
self.children[1].strikethrough = not self.children[1].strikethrough
def on_press_btn_label_ref(self, *args):
self.children[0].strikethrough = not self.children[0].strikethrough
class LabelBoxApp(App):
def build(self):
return LabelBoxLayout()
if __name__ == '__main__':
LabelBoxApp().run()
#:import C kivy.utils.get_color_from_hex
<LabelBoxLayout>:
orientation: 'vertical'
Button:
text: 'Button'
on_press: root.on_press_btn()
on_release: root.on_release_btn()
Label:
id: lable_btn
text: 'The [ref=lable]first step[/ref] is one of awareness. It will be hard to make a change to positive thinking without being acutely intimate with the thoughts that run through your mind. Recently, I was amazed to discover deep buried emotions from negative thoughts that I had for fewer than 10 minutes. Without awareness, I would have carried the hurt and anger inside. Awareness helped me to bring them out to the open for me to deal with.'
font_size: '20sp'
color: C('#B0C4DE')
italic: True
text_size: cm(15), mm(200)
halign: 'right'
valign: 'middle'
on_ref_press:
print('Click on_ref_press')
root.on_release_btn()
strikethrough: True
markup: True
Label:
canvas.before:
Color:
rgba: C('#DCDCDC')
Rectangle:
pos: self.pos
size: self.size
text: 'There are moments in life when [i]you miss someone [color=#"4169E1"]so much that you just want[/color] to pick them from your[/i] dreams and hug them for real! Dream what you want to dream;go where you want to go;be what you want to be,because you have only one life and one chance to do all the things you want to do.'
color: C('#FF0000')
font_size: '15sp'
bold: True
text_size: cm(10), mm(100)
halign: 'center'
valign: 'middle'
underline: True
line_height: 1.0
markup: True
单位介绍 px、dp和sp,这三个单位的区别在于,它们的定义各不相同:
px:指像素,是指基本原色素及其灰度的基本编码,是Pixel的缩写。像素是指基本原色素及其灰度的基本编码,由 Picture(图像)和 Element(元素)这两个单词的字母所组成的。当图片尺寸以像素为单位时,需要指定其固定的分辨率,才能将图片尺寸与现实中的实际尺寸相转换。 例如:大多数网页制作常用图片分辨率为72,即每英寸像素为72,1英寸等于2.54厘米。
dp:安卓开发时的长度单位,Density-independent pixel,是安卓开发用的长度单位,1dp表示在屏幕像素点密度为160ppi时1px长度。。
sp:与缩放无关的抽象像素,是字体单位.scale-independent pixel,安卓开发用的字体大小单位。一般情况下可认为sp=dp。
Image图片 使用Image控件来显示图片,可以通过size和pos来设置属性和设置大小和位置,通过source属性来指定图片的相对路径,此外,如果图片大小过大的时候,为了防止卡顿,可以使用异步的方式加载,这我们前面在学习的时候,咱们有试过,使用异步加载图片。
我们先来看看Image有哪些属性。
属性
属性
属性
source
图片的文件名和路径
texture
图像的纹理对象,默认为None
color
图像颜色,格式为rgba,默认为[1,1,1,1]
texture_size
图像的纹理大小
allow_stretch
是否放打图像到边框,默认为False
anim_delay
动画延迟,默认为0.25秒 (4fps),若设置为-1,则停止
anim_loop
循环播放的次数,默认为0
image_ratio
图片比例
keep_data
是否存储原始图像数据,经常用在基于像素的碰撞检测,默认为False
keep_ratio
是否以忽略图像纵横比的 方式放大图像以适合图像框,默认为True
mipmap
是否要将OpenGL mipmapping 应用于纹理,默认为False
nocache
是否不将图像添加到内部缓存种,默认为False 表示添加到缓存中
norm_image_size
以保留比例的方式标准化图像大小,只读属性,不可更改
reload()
重新加载图像
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class ImageBoxLayout(BoxLayout):
def __init__(self, **kwargs):
super(ImageBoxLayout, self).__init__(**kwargs)
class ImageBoxApp(App):
def build(self):
return ImageBoxLayout()
if __name__ == '__main__':
ImageBoxApp().run()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <ImageBoxLayout>: canvas: Color: rgba:[1,1,1,1] Rectangle: size:self.width+20, self.height+20 pos: self.x-10,self.y-10 source:'UxParts.jpg' AsyncImage: source:'https://blogcdn.sea-whales.cn/blog/typecho/17.jpg' size_hint_y: None width: 300 allow_stretch: True # 自适应 Image: source:'https://blogcdn.sea-whales.cn/blog/typecho/17.jpg' size_hint_y: None width: 300 allow_stretch: True
TextInput 输入框 先来看看有哪些属性,在Kivy中,为了方便输入值的样式,Kivy 提供了大量的属性。接下来我们来看看有哪些:
属性
说明
text
文本内容
text_language
文本语言,例如: zh_cn,en_us,fr 和 ja
halign
文本的水平对齐方式,可以设置为auto、left、center和right等属性
multiline
是否显示多行文本,默认属性为True
allow_copy
是否允许赋值文本,默认为True
auto_indent
自动缩进,多行文字,默认属性为False
font_size
文本的字体大小,以像素为单位。默认为15sp
foreground_color
前景色,格式是rgba 默认为[0,0,0,1]
minimum_height
文本框内容的最小高度
password
是否用password_mask 替换显示密码,默认为False
password_mask
当password为True时,默认以“*”替换显示密码
readonly
是否将文本框属性设为只读,默认为Fasle
background_color
背景颜色,格式为rgba,默认为白色[1,1,1,1]
padding_x
文本水平填充,格式为[padding_left, padding_right] 或 [padding_horizontal],默认为[0,0]
padding_y
文本垂直填充,格式为 [padding_top, padding_bottom] 或 [padding_vertical],默认为[0,0]
padding
文本填充,格式为[padding_left, padding_top, padding_right, padding_bottom]或者[padding_horizontal,padding_vertical],默认属性为[6,6,6,6]
hint_text
提示文本,默认为空字符串
hint_text_color
提示文本的当前颜色,格式是rgba,默认为[.5,.5,.5,1]
input_filter
过滤输入,默认为None
paste()
插入剪贴板内容到当前光标位置
write_tab
使用Tap键将移到下一个小部件,否则进入文本框,默认为True
background_active
默认属性为textinput_active.png
background_disabled_normal
禁用Textinput时的背景图像,默认为textinput_disabled.png
handle_image_left
待处理的文本框左侧显示的图像
background_normal
失去焦点时显示的图像
handle_image_middle
待处理文本框中间显示图像
handle_image_right
待处理文本框右侧显示图像
replace_crlf
用LF自动替换CRLF 换行
select_all()
选中文本框显示的所有文本
selection_color
所选内容的当前颜色,格式为rgba
selection_from
所选开始光标索引,为只读 默认为None
selection_to
所选结束光标,默认为None 为只读
selection_text
当前选择内容,默认为空字符串,为只读
suggestion_text
当前行的末尾显示建议文本
tab_widtg
将TAB键替换指定数量的空格,默认为4个空格。
seletc_text(start,end)
选中文本框显示部分的内容。
base_direction
文本的基本方向,默认为None ,可设置为ltr,rtl,weak_ltr,weak_rtl
border
边框,默认属性为(4,4,4,4)
cancel_selection()
取消当前的选择。
use_bubble
是否使用剪切,复制、黏贴气泡,在移动设备默认为True,其他默认为False
use_handles
是否显示选择指示,在移动设备默认为True,其他默认为false
copy(date=’’)
将提供的值复制到剪贴板中
cursor
当前光标位置
cursor_blink
光标是否闪烁,默认为True
cursor_col
光标的当前列 只读
cursor_row
光标当前行 只读
cursor_color
光标的当前颜色,rgba格式默认为[1,0,0,1]
cursor_index(cursor=None)
返回文本/值中的光标索引
cursor_offset()
获取当前行 光标偏移量
cursor_width
光标当前宽度,默认为”1 sp“
cursor_pos
光标当前位置,以(x,y)为单位只读
cut()
将当前选择剪切到剪贴板
keyboard_suggestions
在键盘上方是否提供建议输入,默认为True,设置input_type属性后才会生效
line_height
计算一行文本最小高度,为只读属性
line_spacing
线条之间的空间。默认为0
on_double_tap()
双击文本框触发该事件
on_triple_tap()
三击文本框触发该事件
on_quad_touch()
在文本框内部四指触摸触发事件
do_redo()
重新执行