基本上Kivy中的UI組件都是繼承Widget素邪,掌握好Widget就基本上掌握了UI組件的使用妻顶。
- Widget的設(shè)計(jì)理念捆等;
- Widget的基本使用氓皱;
一路召、Widget組件的默認(rèn)值與一些默認(rèn)行為
(1)Widget不是布局,Widget不會(huì)改變其包含的子組件的位置與大小波材。
(2)Widget的默認(rèn)大小是100*100股淡。
(3)Widget的size_hint默認(rèn)是1*1,如果其父組件是一個(gè)布局組件各聘,則其大小是父組件中的布局需要的大小揣非。
(4)on_touch_down(), on_touch_move(), on_touch_up() 等函數(shù)不進(jìn)行組件邊界范圍判定(碰撞判定),需要調(diào)用collide_point()
來(lái)判定躲因。
下面使用例子說(shuō)明:【 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() 不檢測(cè)Widget的觸發(fā)邊界
# 點(diǎn)擊Widget區(qū)域之外,事件照樣觸發(fā)忌傻。
def on_touch_up(self, touch):
print(touch)
class WidgetApp(App):
def build(self):
widget=MyWidget()
#1. size與size_hint的默認(rèn)值大脉。
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()
運(yùn)行控制臺(tái)輸出:
控制臺(tái)輸出說(shuō)明Widget的大小默認(rèn)值
運(yùn)行界面
說(shuō)明Widget不是布局镰矿,不會(huì)改變子組件大小位置(紫色為子組件Widget,父組件大小設(shè)置得與窗體一樣大蟹帧),點(diǎn)擊組件以外的區(qū)域也會(huì)觸發(fā)事件
二秤标、Widget的設(shè)計(jì)理念
1. 事件驅(qū)動(dòng)的屬性設(shè)計(jì)
??所謂事件驅(qū)動(dòng)的屬性設(shè)計(jì)是指每個(gè)屬性都可以綁定一個(gè)事件,當(dāng)屬性改變宙刘,可以觸發(fā)一個(gè)事件苍姜。
??屬性的事件是通過(guò)回調(diào)函數(shù)(on_屬性名)實(shí)現(xiàn)。
代碼如下:【 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_<屬性名>綁定一個(gè)事件方法悬包。
widget.on_size=self.changeSize()
#3.改變size會(huì)觸發(fā)change方法的調(diào)用衙猪。
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.實(shí)現(xiàn)一個(gè)事件方法
def changeSize(self):
print("Size Change")
app=WidgetApp()
app.run()
當(dāng)程序啟動(dòng),會(huì)改變size屬性布近,導(dǎo)致幫定的事件方法被調(diào)用垫释。
當(dāng)size屬性改變,屬性綁定的事件函數(shù)被調(diào)用的輸出
2. 組件與圖形分離的設(shè)計(jì)
??組件與圖形分離撑瞧,就是不按照傳統(tǒng)的圖形圖像繪制實(shí)現(xiàn)套路棵譬,提供一個(gè)draw或者paint類(lèi)似的方法,并提供一個(gè)圖像繪制對(duì)象來(lái)集中實(shí)現(xiàn)圖形圖像的繪制(這個(gè)在Java Swing或者JavaFX预伺,C++的Qt都是這個(gè)套路)订咸,傳統(tǒng)的套路下琅束,實(shí)現(xiàn)圖像繪制一般會(huì)override繪制函數(shù),并重新實(shí)現(xiàn)繪制過(guò)程算谈。這樣的缺點(diǎn)是必須要繼承Widget涩禀,Override繪制函數(shù)。
??組件與圖形分離的技術(shù)實(shí)際上C中慣用模式然眼,通過(guò)開(kāi)啟一個(gè)繪制環(huán)境艾船,在環(huán)境開(kāi)始后,任何獨(dú)立的繪制都影響繪制效果高每,繪制完畢后關(guān)閉繪制環(huán)境屿岂,之后的繪制就沒(méi)有效果。在Kivy中這個(gè)繪制環(huán)境就是Canvas鲸匿,在Widget中有這個(gè)成員爷怀。而繪制的函數(shù)與Canvas沒(méi)有任何代碼上的設(shè)計(jì)關(guān)聯(lián)關(guān)系,這樣實(shí)現(xiàn)繪制與Widget組件的寬?cǎi)詈显O(shè)計(jì)模式带欢。
??組件與圖形分離的編程模式在Python語(yǔ)言中特別容易實(shí)現(xiàn)运授,利用with語(yǔ)句塊自動(dòng)開(kāi)啟與自動(dòng)關(guān)閉一個(gè)環(huán)境,編程模式如下:
with widget組件.canvas: #繪制環(huán)境開(kāi)始
繪制的相關(guān)操作
pass #繪制環(huán)境結(jié)束
下面使用簡(jiǎn)單代碼說(shuō)明這種繪制模式(代碼繪制了一個(gè)白色矩形用來(lái)做背景乔煞,繪制了一個(gè)黃色線(xiàn)條):【 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不提供專(zhuān)門(mén)的圖形繪制函數(shù)吁朦,而是采用組件與繪制分離模式,這樣渡贾,可以在任何地方實(shí)現(xiàn)圖形繪制逗宜。
with widget.canvas:
#設(shè)置顏色
Color(255,255,255)
#繪制一個(gè)與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()
運(yùn)行結(jié)果如下:
在Widget中繪制圖形
3. 組件邊界檢測(cè)設(shè)計(jì)
??Widget的事件處理,需要檢測(cè)Widget的范圍空骚,否則在Widget組件外也會(huì)觸發(fā)事件纺讲。為了保障只有在點(diǎn)擊Widget材觸發(fā),需要做碰撞模式的邊界判定囤屹,Widget組件中邊界檢測(cè)函數(shù)聲明如下:
#判定(x熬甚,y)是否在Widget框內(nèi),在就返回True牺丙,否則返回False则涯。
Widget.collide_point(x, y)
下面是測(cè)試代碼:【 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區(qū)域點(diǎn)擊返回True,在區(qū)域外返回False冲簿。不管在區(qū)域內(nèi)外粟判,事件都會(huì)觸發(fā),需要自己處理點(diǎn)擊是否在Widget組件內(nèi)還是組件外峦剔。
??在上面例子中档礁,我們使用了兩個(gè)Widget,原因是直接放在Window中的Widget盡管設(shè)置了size屬性吝沫,但是在Widget添加在Window中的時(shí)候呻澜,Widget的size會(huì)設(shè)置成與Window的size一樣大小递礼。但是在Widget中的Widget不會(huì)。
運(yùn)行界面如下:
運(yùn)行界面
下面是在綠色Widget中點(diǎn)擊一次羹幸,在外面點(diǎn)擊一次的輸出:
三脊髓、Widget的基本使用
??有些屬性在后面使用說(shuō)明,比如id與ids栅受,cls(在kv語(yǔ)言的時(shí)候說(shuō)明)将硝,size_hint(在布局的時(shí)候說(shuō)明)屬性。
1. Widget構(gòu)造
class kivy.uix.widget.Widget(**kwargs)
其中**kwargs可是是Widget任何有效的屬性屏镊,參數(shù)設(shè)置方式:
屬性名=屬性值
下面是Widget常用的屬性:
基本屬性
屬性名 | 屬性說(shuō)明 |
---|---|
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. |
畫(huà)布依疼,透明與失效屬性
屬性名 | 屬性說(shuō)明 |
---|---|
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. |
集合屬性
屬性名 | 屬性說(shuō)明 |
---|---|
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. |
布局幾何屬性
屬性名 | 屬性說(shuō)明 |
---|---|
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. |
下面是通過(guò)構(gòu)造器使用屬性的例子:【 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:
#設(shè)置顏色
Color(255,255,255)
#繪制一個(gè)與Widget一樣大小的矩形
Rectangle(pos=widget.pos, size=widget.size)
return widget
app=WidgetApp()
app.run()
因?yàn)榇绑w背景是黑色律罢,Widget是白色,設(shè)置為半透明后棍丐,顯式效果是灰色误辑。
通過(guò)構(gòu)造器設(shè)置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:添加的索引位置,默認(rèn)是0骄酗,就是插入到組件列表的第一個(gè)稀余,但是最后繪制并顯式。
canvas:字符串參數(shù)趋翻,畫(huà)布處理。取值'befoe','after',或者None盒蟆。
remove_widget(widget)
4. 碰撞判定方法
方法名 | 方法說(shuō)明 |
---|---|
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把組件導(dǎo)出為圖像方法
export_to_png(filename, *args)
args參數(shù)沒(méi)有使用踏烙。
6. Widget樹(shù)訪(fǎng)問(wèn)
方法名 | 方法說(shuō)明 |
---|---|
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. 容器與組件坐標(biāo)變換
坐標(biāo)轉(zhuǎn)換函數(shù) | 函數(shù)說(shuō)明 |
---|---|
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("點(diǎn)擊位置:",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:
#設(shè)置顏色
Color(255,0,255)
#繪制一個(gè)與Widget一樣大小的矩形
Rectangle(pos=widget.pos, size=widget.size)
mywidget=MyWidget(size=(400,400),pos=(200,200))
with mywidget.canvas:
#設(shè)置顏色
Color(0,0,255)
#繪制一個(gè)與Widget一樣大小的矩形
Rectangle(pos=mywidget.pos, size=mywidget.size)
widget.add_widget(mywidget)
widget.export_to_png("img.png")
return widget
app=WidgetApp()
app.run()
其中運(yùn)行效果:
帶窗體的界面效果
控制臺(tái)輸出效果:
坐標(biāo)轉(zhuǎn)換的輸出效果
注意:其中relative參數(shù)設(shè)置為T(mén)rue,從中可以看出觸摸事件返回的觸摸點(diǎn)历等,實(shí)際是window坐標(biāo)系下的坐標(biāo)點(diǎn)讨惩。
export_to_png函數(shù)的保存的圖像效果:
export_to_png保存的圖像效果
資源
- 本文使用的資源同統(tǒng)一下載路徑: https://github.com/QiangAI/PythonSkill/tree/master/KivyUI
- Kivy的官方參考路徑:https://kivy.org/doc/stable/api-kivy.uix.widget.html