Kivy tutorial 008: More kv?language – Kivy Blog
Central themes:?Event binding and canvas instructions in kv?language
中心主題: 事件綁定 和 kv語言里的畫布結構
This tutorial directly follows on from the previous, so start by retrieving the previous code, as?below:
這節導師課直接地跟隨上節的,因此從上次的代碼找了些代碼開始。
main.py:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Sliderfrom kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Sliderfrom kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Linefrom random import randomclass DrawingWidget(Widget):def __init__(self):super(DrawingWidget, self).__init__()with self.canvas:Color(1, 1, 1, 1)self.rect = Rectangle(size=self.size,pos=self.pos)self.bind(pos=self.update_rectangle,size=self.update_rectangle)def update_rectangle(self, instance, value):self.rect.pos = self.posself.rect.size = self.sizedef on_touch_down(self, touch):super(DrawingWidget, self).on_touch_down(touch)if not self.collide_point(*touch.pos):returnwith self.canvas:Color(random(), random(), random())self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)def on_touch_move(self, touch):if not self.collide_point(*touch.pos):returnself.line.points = self.line.points + [touch.pos[0], touch.pos[1]]class Interface(BoxLayout):passclass DrawingApp(App):def build(self):root_widget = Interface()return root_widgetDrawingApp().run()
drawing.kv:
<Interface>:orientation: 'vertical'DrawingWidget:Slider:min: 0max: 1value: 0.5size_hint_y: Noneheight: 80Slider:min: 0max: 1value: 0.5size_hint_y: Noneheight: 80Slider:min: 0max: 1value: 0.5size_hint_y: Noneheight: 80BoxLayout:orientation: 'horizontal'size_hint_y: Noneheight: 80Label:text: 'output colour:'Widget:
The first thing to do is draw the coloured Rectangle that the final Widget uses to display an output colour, and for this we need to know how to draw canvas instructions in kv language. The syntax is as?below:
第一件事 是?畫多彩的矩形, 這矩形是最后的組件 用來演示一個output顏色, 對這個顏色,我們需要知道kv語言中畫圖結構如何。
Widget:canvas:Color:rgb: 0, 1, 0 # using a fixed colour for nowRectangle:size: self.sizepos: self.pos
Run the code, and you’ll see another of kv language’s most important features;?automatic event binding. In the original Python code of tutorial 7 we needed an extra?.bind(...)?call to make the be updated to always be placed within its Widget. In kv language this is not necessary, the dependency on?self.size?and?self.pos?is automatically detected, and a binding automatically?created!
運行代碼, 并且你將看到kv語言另一個最重要的特征;自動地事件綁定。 在導師課7中的Python源始代碼,我們需要一個巨大的? .extra() 召喚, 讓更新 總是在它組件內。 在kv語言中是不必要的, self.size 和self.pos獨自自動地執行并且綁定了自動地創建!
This is also the generic syntax for canvas instructions; first add?canvas:?(or?canvas.before?or?canvas.after), then, indent by 4 spaces, and add canvas instructions much like you would Widgets. However, note that canvas instructions are?not?widgets.
這也是通用的語法給canvas結構;第一 添加canvas: (或者canvas.before 或者 canvas.after )然后, 4個空格縮進, 并且增加盡可能你想要的組件到canvas結構。 然而, 注意canvas 結構不是組件。
The only thing now missing from the original Python interface implementation in tutorial 7 is having the Sliders automatically update the output colour rectangle. Change the?<Interface>:?rule to the?following:
在導師課第7節中從源碼界面執行中唯一缺少的事情是 有sliders 自動地更新到 輸出顏色矩形。 改變<interface>: 規則在下面:
<Interface>:orientation: 'vertical'DrawingWidget:Slider:id: red_slidermin: 0max: 1value: 0.5size_hint_y: Noneheight: 80Slider:id: green_slidermin: 0max: 1value: 0.5size_hint_y: Noneheight: 80Slider:id: blue_slidermin: 0max: 1value: 0.5size_hint_y: Noneheight: 80BoxLayout:orientation: 'horizontal'size_hint_y: Noneheight: 80Label:text: 'output colour:'Widget:canvas:Color:rgb: red_slider.value, green_slider.value, blue_slider.valueRectangle:size: self.sizepos: self.pos
There are actually only two changes here; we gave each Slider an?id?declaration, and in the canvas Color referred to the sliders with this name. Giving a widget an id is just like naming it in Python so that you can refer to it?elsewhere.
這只有2個改變: 我們給每個slider 一個id聲明, 并且在canvas 顏色中提及了sliders 和這個名字。 給與一個widget組件一個id 就像在python中命名它一樣,因此你可以在其他的地方提及它。
Thanks to kv’s automatic binding, this is all we need to do to have the Color update automatically whenever a slider value changes. Run the code, and you should see that things work exactly as they did in the original Python?interface.
感謝kv自動地綁定, 這是所有我們需要做的是 讓顏色更新自動地當一個slider值改變時。 運行代碼,并且你應該看到事兒們嚴謹地像在源碼python界面中做的一樣。
We can finish this tutorial with a couple of extra kv conveniences. First, just as we added an automatically updating Rectangle in the Widget kv, we can do the same for the background of the DrawingWidget. Delete the?__init__?and?update_rectangle?methods in the Python DrawingWidget code, and add a new rule in the kv?file:
我們可以完成這節導師課同一些kv的巨大方便性。 首先,像我們添加的一個自動地更新的矩形在widget kv中一樣, 我們可以在DrawingWidget的背景中做相同的事情。 刪除__init__ 和? update_rectangle 方法在python 的DrawingWidget代碼中, 并且添加一個新的規則在kv文件中:
<DrawingWidget>:canvas:Color:rgba: 1, 1, 1, 1Rectangle:pos: self.possize: self.size
Second, you might have noticed that there’s a lot of code duplication in each of the Slider rules - we set the same?min,?max, initial?value`, ``size_hint_y`?and?height?for every one. As is normal in Python, it would be natural to abstract this in a new class, so as to set each value only once. You can probably already see how to do this with what we’ve learned so far (make a new?class YourSlider(Slider):?in the Python and add a new?<YourSlider>:?rule in the kv), but I’ll note that you can even do this entirely in?kv:
其次, 你可能已經注意到了那有一些復制代碼在每個slider規則中,我們設置了相同的 min, initial value 'size_hint_y'? 和高度給每一個Slider。 像平常在Python中一樣, 它總是本能的使分離在一個新的類中,因此像每次設置每個值。你大概可以已經看到如何同我們學到的來這么做, (制作一個新的類: YourSlider(Slider): 在python 代碼中,并且增加一個新的<YourSlider>: 規則在kv中, 但是我將意識到 你可以在kv中全部這么干:
<ColourSlider@Slider>:min: 0max: 1value: 0.5size_hint_y: Noneheight: 80<Interface>:orientation: 'vertical'DrawingWidget:ColourSlider:id: red_sliderColourSlider:id: green_sliderColourSlider:id: blue_sliderBoxLayout:orientation: 'horizontal'size_hint_y: Noneheight: 80Label:text: 'output colour:'Widget:canvas:Color:rgb: red_slider.value, green_slider.value, blue_slider.valueRectangle:size: self.sizepos: self.pos
The new?<ColourSlider@Slider>:?rule defines a?dynamic class, a Python class kv rule without a corresponding Python code definition. This is convenient if you want to do something repeatedly only in kv, and never access it from?Python.
新的 <ColourSlider@Slider>: 規則定義了一個動力的類, 一個python 類kv規則沒有一個相關的python編碼定義。 這是方便的如果你只想做些重復的事在kv中, 并且絕不從python中訪問它。
At this point, we’ve reached feature parity with the original Python code, and seen all the basics of kv language. In the next tutorial we’ll finish off the original purpose of all these sliders; letting the user set the colour of line that is drawn by the?DrawingWidget.
這時候,我們已經達到了和源碼python代碼相同的效果, 并且看到了所有的kv的基礎元素。 在下面的導師課中,我們將完成這些sliders 原始的目的,讓用戶設置DrawingWidget畫的線的顏色。
Full?code
main.py:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Sliderfrom kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Sliderfrom kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Linefrom random import randomclass DrawingWidget(Widget):def on_touch_down(self, touch):super(DrawingWidget, self).on_touch_down(touch)if not self.collide_point(*touch.pos):returnwith self.canvas:Color(random(), random(), random())self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)def on_touch_move(self, touch):if not self.collide_point(*touch.pos):returnself.line.points = self.line.points + [touch.pos[0], touch.pos[1]]class Interface(BoxLayout):passclass DrawingApp(App):def build(self):root_widget = Interface()return root_widgetDrawingApp().run()
drawing.kv:
<DrawingWidget>:canvas:Color:rgba: 1, 1, 1, 1Rectangle:pos: self.possize: self.size<ColourSlider@Slider>:min: 0max: 1value: 0.5size_hint_y: Noneheight: 80<Interface>:orientation: 'vertical'DrawingWidget:ColourSlider:id: red_sliderColourSlider:id: green_sliderColourSlider:id: blue_sliderBoxLayout:orientation: 'horizontal'size_hint_y: Noneheight: 80Label:text: 'output colour:'Widget:canvas:Color:rgb: red_slider.value, green_slider.value, blue_slider.valueRectangle:size: self.sizepos: self.pos
這段代碼是使用Kivy框架(一個用于開發多觸摸應用的Python庫)來創建一個可以繪制線條的DrawingWidget
類。讓我們逐步分析這段代碼:
-
類定義:
class DrawingWidget(Widget):
這里定義了一個名為DrawingWidget
的類,它繼承自Kivy的Widget
類。
??????2.?on_touch_down方法:
當觸摸屏幕并按下時,這個方法會被調用
* 首先,它調用了`super(DrawingWidget, self).on_touch_down(touch)`,這是為了確保父類(即`Widget`類)的`on_touch_down`方法也能得到執行(如果有需要的話)。
* 接著,它檢查觸摸點是否在`DrawingWidget`的區域內(使用`self.collide_point(*touch.pos)`)。如果不在,方法就返回,不執行后續的繪制代碼。
* 如果在區域內,它會進入`with self.canvas:`塊,這意味著它將對`canvas`(即繪圖表面)執行一些操作。 + 它首先使用`Color(random(), random(), random())`隨機選擇一個顏色。 + 然后,它創建了一個`Line`對象,該對象從觸摸點開始,但只有一個點(即觸摸點的位置)。注意這里有個小問題:`Line`對象的`points`應該是一個二維列表,即列表的每個元素都是一個坐標(x, y)。但在這里,它只是一個包含兩個元素的列表,這可能會導致問題。
小問題:如果觸摸點不在DrawingWidget區域內,return 返回的是什么?
在給出的代碼片段中,如果觸摸點不在DrawingWidget
的區域內,return
語句將直接返回None
,因為它沒有指定要返回的具體值。在Python中,如果函數或方法中沒有明確的return
語句或return
后面沒有跟任何值,那么該函數或方法將默認返回None
。
在on_touch_down
和on_touch_move
方法中,當self.collide_point(*touch.pos)
返回False
(即觸摸點不在DrawingWidget
區域內)時,方法會立即通過return
退出,不執行后續的代碼。由于沒有指定返回值,所以返回的是None
。
這種設計是為了避免在不必要的情況下執行繪圖或更新操作,從而提高代碼效率和響應速度。
????????3.?on_touch_move方法:
當觸摸點在屏幕上移動時,這個方法會被調用。
* 同樣,它首先檢查觸摸點是否在`DrawingWidget`的區域內。
* 如果在區域內,它會更新`line`對象的`points`屬性,將新的觸摸點坐標添加到列表的末尾。但這里也有一個小問題:由于`line.points`是一個二維列表(至少它應該是一個),但之前我們設置它時似乎沒有將其作為一個二維列表。所以,這里應該是`self.line.points += [[touch.pos[0], touch.pos[1]]]`,以確保我們添加的是一個坐標點。