基本上Kivy中的UI组件都是继承Widget,掌握好Widget就基本上掌握了UI组件的使用。
- Widget的设计理念;
- Widget的基本使用;
一、Widget组件的默认值与一些默认行为
(1)Widget不是布局,Widget不会改变其包含的子组件的位置与大小。
(2)Widget的默认大小是100*100。
(3)Widget的size_hint默认是1*1,如果其父组件是一个布局组件,则其大小是父组件中的布局需要的大小。
(4)on_touch_down(), on_touch_move(), on_touch_up() 等函数不进行组件边界范围判定(碰撞判定),需要调用collide_point()
来判定。
下面使用例子说明:【 UI01_Widget_default.py 】
#coding=utf-8
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color
from kivy.graphics import Rectangle
from kivy.core.window import Window
class MyWidget(Widget):
# 3. on_touch_down(), on_touch_move(), on_touch_up() 不检测Widget的触发边界
# 点击Widget区域之外,事件照样触发。
def on_touch_up(self, touch):
print(touch)
class WidgetApp(App):
def build(self):
widget=MyWidget()
#1. size与size_hint的默认值。
print("大小:(%d,%d)"%(widget.width,widget.height)) #(100,100)
print("大小提示(%d,%d)"%(widget.size_hint[0],widget.size_hint[1])) #(1,1)
widget.size = (Window.size[0] / 2, Window.size[1] / 2,) #是窗体一般大小。
with widget.canvas:
Color(0,255,255)
Rectangle(pos=widget.pos, size=widget.size)
#2. 不改变子组件的位置与大小
widget2=Widget()
widget.add_widget(widget2)
with widget2.canvas:
Color(255,0,255)
Rectangle(pos=widget2.pos, size=widget2.size)
return widget
app=WidgetApp()
app.run()
运行控制台输出:
控制台输出说明Widget的大小默认值
运行界面
说明Widget不是布局,不会改变子组件大小位置(紫色为子组件Widget,父组件大小设置得与窗体一样大小),点击组件以外的区域也会触发事件
二、Widget的设计理念
1. 事件驱动的属性设计
所谓事件驱动的属性设计是指每个属性都可以绑定一个事件,当属性改变,可以触发一个事件。
属性的事件是通过回调函数(on_属性名)实现。
代码如下:【 UI01_Widget_eventdriven.py 】
#coding=utf-8
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color
from kivy.graphics import Rectangle
from kivy.core.window import Window
class WidgetApp(App):
def build(self):
widget=Widget()
#2. 使用on_<属性名>绑定一个事件方法。
widget.on_size=self.changeSize()
#3.改变size会触发change方法的调用。
widget.size=(Window.size[0]/2,Window.size[1]/2,)
with widget.canvas:
Color(0,255,255)
Rectangle(pos=widget.pos, size=widget.size)
return widget
#1.实现一个事件方法
def changeSize(self):
print("Size Change")
app=WidgetApp()
app.run()
当程序启动,会改变size属性,导致帮定的事件方法被调用。
当size属性改变,属性绑定的事件函数被调用的输出
2. 组件与图形分离的设计
组件与图形分离,就是不按照传统的图形图像绘制实现套路,提供一个draw或者paint类似的方法,并提供一个图像绘制对象来集中实现图形图像的绘制(这个在Java Swing或者JavaFX,C++的Qt都是这个套路),传统的套路下,实现图像绘制一般会override绘制函数,并重新实现绘制过程。这样的缺点是必须要继承Widget,Override绘制函数。
组件与图形分离的技术实际上C中惯用模式,通过开启一个绘制环境,在环境开始后,任何独立的绘制都影响绘制效果,绘制完毕后关闭绘制环境,之后的绘制就没有效果。在Kivy中这个绘制环境就是Canvas,在Widget中有这个成员。而绘制的函数与Canvas没有任何代码上的设计关联关系,这样实现绘制与Widget组件的宽耦合设计模式。
组件与图形分离的编程模式在Python语言中特别容易实现,利用with语句块自动开启与自动关闭一个环境,编程模式如下:
with widget组件.canvas: #绘制环境开始
绘制的相关操作
pass #绘制环境结束
下面使用简单代码说明这种绘制模式(代码绘制了一个白色矩形用来做背景,绘制了一个黄色线条):【 UI01_Widget_graphics.py 】
#coding=utf-8
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color
from kivy.graphics import Rectangle
from kivy.graphics import *
from kivy.core.window import Window
class WidgetApp(App):
def build(self):
widget=Widget()
widget.size=Window.size
#Kivy不提供专门的图形绘制函数,而是采用组件与绘制分离模式,这样,可以在任何地方实现图形绘制。
with widget.canvas:
#设置颜色
Color(255,255,255)
#绘制一个与Widget一样大小的矩形
Rectangle(pos=widget.pos, size=widget.size)
Color(255, 255, 0)
Line(points=[0,0,widget.size[0],widget.size[1]],width=5)
print(widget.size)
return widget
app=WidgetApp()
app.run()
运行结果如下:
在Widget中绘制图形
3. 组件边界检测设计
Widget的事件处理,需要检测Widget的范围,否则在Widget组件外也会触发事件。为了保障只有在点击Widget材触发,需要做碰撞模式的边界判定,Widget组件中边界检测函数声明如下:
#判定(x,y)是否在Widget框内,在就返回True,否则返回False。
Widget.collide_point(x, y)
下面是测试代码:【 UI01_Widget_collide.py 】
#coding=utf-8
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color
from kivy.graphics import Rectangle
from kivy.core.window import Window
class TheWidget(Widget):
def on_touch_down(self, touch):
print(self.size,self.pos)
print(self.collide_point(*touch.pos))
class TheApp(App):
def build(self):
parent_widget=Widget()
with parent_widget.canvas:
Color(255,0,0)
Rectangle(pos=parent_widget.pos,size=Window.size)
child_widget = TheWidget()
with child_widget.canvas:
Color(0,255,0)
Rectangle(pos=child_widget.pos,size=child_widget.size)
parent_widget.add_widget(child_widget)
return parent_widget
TheApp().run()
在绿色Widget区域点击返回True,在区域外返回False。不管在区域内外,事件都会触发,需要自己处理点击是否在Widget组件内还是组件外。
在上面例子中,我们使用了两个Widget,原因是直接放在Window中的Widget尽管设置了size属性,但是在Widget添加在Window中的时候,Widget的size会设置成与Window的size一样大小。但是在Widget中的Widget不会。
运行界面如下:
运行界面
下面是在绿色Widget中点击一次,在外面点击一次的输出:
三、Widget的基本使用
有些属性在后面使用说明,比如id与ids,cls(在kv语言的时候说明),size_hint(在布局的时候说明)属性。
1. Widget构造
class kivy.uix.widget.Widget(**kwargs)
其中**kwargs可是是Widget任何有效的属性,参数设置方式:
属性名=属性值
下面是Widget常用的属性:
基本属性
属性名 | 属性说明 |
---|---|
cls | Class of the widget, used for styling. |
id | Identifier of the widget in the tree. |
ids | This is a dictionary of ids defined in your kv language. |
画布,透明与失效属性
属性名 | 属性说明 |
---|---|
canvas = None | Canvas of the widget. |
opacity | Opacity of the widget and all its children. |
disabled | Indicates whether this widget can interact with input or not. |
集合属性
属性名 | 属性说明 |
---|---|
height | Height of the widget. |
width | Width of the widget. |
center,center_x,center_y | Center position of the widget. |
x | X position of the widget. |
y | Y position of the widget. |
size | Size of the widget |
pos | Position of the widget. |
right | Right position of the widget. |
top | Top position of the widget. |
布局几何属性
属性名 | 属性说明 |
---|---|
pos_hint | Position hint. This property allows you to set the position of the widget inside its parent layout, in percent (similar to size_hint) |
size_hint | Size hint.size_hint |
size_hint_max | Maximum size when using size_hint |
size_hint_max_x | When not None, the x-direction maximum size (in pixels, like width) when size_hint_x is also not None. |
size_hint_max_y | When not None, the y-direction maximum size (in pixels, like height) when size_hint_y is also not None. |
size_hint_min | Minimum size when using size_hint. |
size_hint_min_x | When not None, the x-direction minimum size (in pixels, like width) when size_hint_x is also not None. |
size_hint_min_y | When not None, the y-direction minimum size (in pixels, like height) when size_hint_y is also not None. |
size_hint_x | x size hint. Represents how much space the widget should use in the direction of the x axis relative to its parent’s width. Only the Layout and Window classes make use of the hint. |
size_hint_y | y size hint. |
下面是通过构造器使用属性的例子:【 UI01_Widget_property.py 】
#coding=utf-8
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color
from kivy.graphics import Rectangle
from kivy.graphics import *
from kivy.core.window import Window
class WidgetApp(App):
def build(self):
widget=Widget(opacity=0.5,size=Window.size)
with widget.canvas:
#设置颜色
Color(255,255,255)
#绘制一个与Widget一样大小的矩形
Rectangle(pos=widget.pos, size=widget.size)
return widget
app=WidgetApp()
app.run()
因为窗体背景是黑色,Widget是白色,设置为半透明后,显式效果是灰色。
通过构造器设置Widget的opacity属性
2. 事件使用
事件 | 事件描述 |
---|---|
on_touch_down : |
Fired when a new touch event occurs |
on_touch_move : |
Fired when an existing touch moves |
on_touch_up : |
Fired when an existing touch disappears |
3. addWidget与remove_widget方法
add_widget(widget, index=0, canvas=None)
widget:被添加的组件
index:添加的索引位置,默认是0,就是插入到组件列表的第一个,但是最后绘制并显式。
canvas:字符串参数,画布处理。取值'befoe','after',或者None。
remove_widget(widget)
4. 碰撞判定方法
方法名 | 方法说明 |
---|---|
collide_point(x, y) | Check if a point (x, y) is inside the widget’s axis aligned bounding box. |
collide_widget(wid) | Check if another widget collides with this widget. This function performs an axis-aligned bounding box intersection test by default. |
5. export_to_png把组件导出为图像方法
export_to_png(filename, *args)
args参数没有使用。
6. Widget树访问
方法名 | 方法说明 |
---|---|
get_parent_window() | Instance of the parent window. Can be a WindowBase or Widget. |
get_root_window() | Instance of the root window. Can be a WindowBase or Widget. |
7. 容器与组件坐标变换
坐标转换函数 | 函数说明 |
---|---|
to_local(x, y, relative=False) | Transform parent coordinates to local coordinates. |
to_parent(x, y, relative=False) | Transform local coordinates to parent coordinates. |
to_widget(x, y, relative=False) | Convert the given coordinate from window to local widget coordinates. |
to_window(x, y, initial=True, relative=False) | Transform local coordinates to window coordinates. |
get_window_matrix(x=0, y=0) | Calculate the transformation matrix to convert between window and widget coordinates. |
下面是方法使用的例子代码:【 UI01_Widget_method.py 】
#coding=utf-8
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import *
from kivy.core.window import Window
class MyWidget(Widget):
def on_touch_down(self, touch):
print("点击位置:",touch.pos)
print("local:",self.to_local(*touch.pos,relative=True))
print("parent:", self.to_parent(*touch.pos,relative=True))
print("widget:", self.to_widget(*touch.pos,relative=True))
print("window:", self.to_window(*touch.pos,relative=True))
class WidgetApp(App):
def build(self):
widget=Widget(size=Window.size)
with widget.canvas:
#设置颜色
Color(255,0,255)
#绘制一个与Widget一样大小的矩形
Rectangle(pos=widget.pos, size=widget.size)
mywidget=MyWidget(size=(400,400),pos=(200,200))
with mywidget.canvas:
#设置颜色
Color(0,0,255)
#绘制一个与Widget一样大小的矩形
Rectangle(pos=mywidget.pos, size=mywidget.size)
widget.add_widget(mywidget)
widget.export_to_png("img.png")
return widget
app=WidgetApp()
app.run()
其中运行效果:
带窗体的界面效果
控制台输出效果:
坐标转换的输出效果
注意:其中relative参数设置为True,从中可以看出触摸事件返回的触摸点,实际是window坐标系下的坐标点。
export_to_png函数的保存的图像效果:
export_to_png保存的图像效果
资源
- 本文使用的资源同统一下载路径: https://github.com/QiangAI/PythonSkill/tree/master/KivyUI
- Kivy的官方参考路径:https://kivy.org/doc/stable/api-kivy.uix.widget.html