day03 kivy简介二 kivy构建 & 事件和属性

一、Kivy构建

1.1 核心任务和输入支持

 
理解Kivy内部结构的一个关键思想是模块化和抽象。我们试图抽象出一些基本任务,比如打开窗口、显示图像和文本、播放音频、从相机获取图像、拼写更正等等。我们称之为核心任务。这使得API易于使用和扩展。最重要的是,它允许我们在运行应用程序的各个场景中使用特定的提供商,我们称之为特定提供商。例如,在OSX、Linux和Windows上,针对不同的核心任务有不同的本机API。我们称之为核心提供者的是一段代码,它使用这些特定的API中的一个与一方的操作系统通信,另一方与Kivy通信(充当中间通信层)。为每个平台使用专门的核心提供商的优势在于,我们可以充分利用操作系统公开的功能,并尽可能高效地执行操作。它还为用户提供了一个选择。此外,通过使用随任何一个平台提供的库,我们有效地减少了Kivy发行版的大小,并使打包更容易。这也使得将Kivy移植到其他平台更加容易。Android端口从中受益匪浅。
 
我们在输入处理方面遵循相同的概念。输入提供者是一段代码,它增加了对特定输入设备的支持,如苹果的轨迹板、TUIO或鼠标模拟器。如果需要添加对新输入设备的支持,只需提供一个新类,从设备读取输入数据并将其转换为Kivy基本事件。
 

1.2 绘图支持

Kivy的图形API是我们对OpenGL的抽象。在最低级别上,Kivy使用OpenGL发布硬件加速绘图命令。然而,编写OpenGL代码可能有点令人困惑,尤其是对新来说。这就是为什么我们提供了图形API,它可以让您使用OpenGL中不存在的简单隐喻(例如,画布、矩形等)来绘制东西。我们所有的小部件本身都使用这个图形API,出于性能原因,它是在C级上实现的。图形API的另一个优点是它能够自动优化代码出现问题的绘图命令。如果您不是在调优OpenGL方面的专家,这将特别有帮助。这使得您的绘图代码在许多情况下更有效。当然,如果您愿意,您仍然可以使用原始的OpenGL命令。我们的目标版本是所有设备上的OpenGL2.0ES(GLES2),所以如果您想保持跨平台兼容,我们建议您只使用GLES2函数。
 

1.3 核心

核心软件包中的代码提供了常用的特性,例如:
Clock(计时器) :您可以使用该时钟来调度计时器事件。同时支持一次性计时器和周期计时器。
Cache(缓存): 如果您需要缓存您经常使用的东西,您可以使用我们的类,而不是自己编写自己的类。
Gesture Detection(手势检测)我们提供了一个简单的手势识别器,你可以用来检测各种各样的笔画,如圆或矩形。你可以训练它来检测你自己的笔画。
Kivy Language(kv 语言)kivy语言被用来轻松和有效地描述用户界面。
Properties(特性) 这些不是python中常见的属性。它们是我们自己的属性类,将小部件代码与用户界面描述链接在一起。
 

1.4UIX(小部件和布局)

UIX模块包含常用的小部件和布局,您可以重写它们以快速创建用户界面。
 
Widgets:widgets小部件是将您添加到程序中以提供某种功能的用户界面元素。它们可能可见,也可能不可见。例如,会有一个文件浏览器、按钮、滑块、列表等等。widgets小部件接收运动事件。
 
Layouts: 您可以使用布局来排列小部件。当然,您可以自己计算小部件的位置,但通常使用我们现成的布局更方便。例如网格布局或长方体布局。也可以嵌套布局。
 

1.5 模块

如果您曾经使用过现代web浏览器,并使用一些附加组件对其进行了自定义,那么您已经了解了我们模块类背后的基本思想。模块可用于将功能注入Kivy程序,即使原始作者没有将其包括在内。
 
举个例子,一个模块,它总是显示当前应用程序的FPS,以及一些描述FPS的图表。
您也可以编写自己的模块。
 

1.6 输入事件(触摸)

Kivy抽象出不同的输入类型和来源,如触摸、鼠标、TUIO或类似输入。所有这些输入类型的共同点是,您可以将2D屏幕位置与任何单个输入事件相关联。(还有其他输入设备,如加速计,您无法轻松找到设备倾斜的2D位置。此类输入是单独处理的。在下文中,我们将介绍前几种类型。)
 
所有这些输入类型都由Touch() 类的实例表示。(请注意,这不仅指手指触摸,还指所有其他输入类型。为了简单起见,我们统称为触摸。请将其视为触摸用户界面或屏幕的东西。)触摸实例或对象可以处于三种状态之一。当触摸进入其中一种状态时,您的程序将被告知事件已发生。触摸屏可以处于以下三种状态:
Down: 一次触摸只下降了一次,就在它第一次出现的那一刻。
Move: 触摸可以在这种状态下持续无限时间。触摸在其生命周期内不必处于这种状态。只要触摸的2D位置发生变化,就会发生“移动”。
Up: 一个触摸最多会上升一次,或者永远不会。在实践中,你几乎总是会收到一个向上的事件,因为没有人会永远拿着一个手指在屏幕上,但这是不能保证的。如果您知道用户将使用的输入源,则您将知道是否可以依赖于所输入的此状态。
 

1.7小部件和事件调度

术语widget通常在GUI编程上下文中用于描述用户与之交互的程序的某些部分。在Kivy中,小部件是接收输入事件的对象。它不一定要在屏幕上有一个可见的表示。所有小部件都排列在一个小部件树中(这是计算机科学类中已知的树数据结构):一个小部件可以有任意数量的子小部件,也可以没有子小部件。在树的顶部正好有一个根小部件没有父小部件,而所有其他小部件都直接或间接地是这个小部件的子部件(这就是为什么它被称为根部件)。
当新的输入数据可用时,Kivy每次触摸都会发送一个事件。小部件树的根小部件首先接收该事件。根据触摸的状态,on_touch_down、on_touch_move或on_touch_up事件(以触摸作为参数)被发送到根小部件,这将导致根小部件对应的on_touch_down、on_touch_move或on_touch_up事件处理程序被调用。
树中的每个小部件(包括根小部件)都可以选择摘要或传递事件。如果事件处理程序返回True,则表示事件已被消化并正确处理。不会对该事件进行进一步处理。否则,事件处理程序通过调用相应事件处理程序的超类实现,将小部件传递给它自己的子部件。这一直延伸到基本小部件类,它在其触摸事件处理程序中只将触摸传递给其子级:
 
#这类似于移动/上升
#这类似于移动/上升
def on_touch_down(self, touch):
    for child in self.children[:]:
        if child.dispatch('on_touch_down', touch):
return True

 

这确实比最初看起来容易得多。下一节将给出一个示例,说明如何使用它快速创建好的应用程序。
通常情况下,您会希望限制屏幕上小部件监视触摸的区域。您可以使用小部件 collide_point() 方法来实现这一点。您只需将触摸的位置传递给它,如果触摸在“监视区域”内,则返回True,否则返回False。默认情况下,这将检查屏幕上的矩形区域,该区域由小部件的pos(位置;x&y)和大小(宽度和高度)描述,但您可以在自己的类中重写此行为。
 

二、事件和属性

事件是Kivy编程的重要组成部分。这对于那些有GUI开发经验的人来说可能并不奇怪,但对于新用户来说,这是一个重要的概念。一旦你了解了事件是如何工作的以及如何与它们绑定,你就会在Kivy中到处看到它们。它们让你可以很容易地建立你想要的任何行为。
下图显示了如何在Kivy框架中处理事件

2.1(Event Dispatcher)事件调度程序简介

该框架中最重要的基类之一是事件调度器类。此类允许您注册事件类型,并将它们分配给感兴趣的各方(通常是其他事件调度程序)。小部件、动画和时钟类是事件调度程序的示例。
事件调度程序对象依赖于主循环来生成和处理事件。

2.2 主循环

如上图所示,Kivy有一个主环。此循环在应用程序的所有生命周期内运行,仅在退出应用程序时才退出。
在循环中,在每次迭代中,从用户输入、硬件传感器或其他几个源生成事件,将帧呈现给显示器。
您的应用程序将指定回调(稍后将更多),由主循环调用。如果回调需要太长时间或根本没有退出,主循环就会中断,你的应用程序就不再正常工作。
在Kivy应用程序中,你必须避免长/无限循环或睡觉。例如,以下代码可以同时执行这两种操作:
while True:
    animate_something()
    time.sleep(.10)

 

当你运行这个程序时,程序将永远不会退出你的循环,阻止Kivy做所有其他需要做的事情。因此,您将看到一个黑色窗口,无法与之交互。相反,您需要“调度”反复调用animate_something() 函数
 

2.2.1 调度重复事件

您可以使用schedule_interval()每秒每X次调用一次函数或方法。下面是一个每秒调用一个名为my_callback的函数30次的示例:
 
def my_callback(dt):
    print('My callback is called', dt)
event = Clock.schedule_interval(my_callback, 1 / 30.)

 

有多种方式来调度以前计划的事件。第一,是使用cancel() 或unschedule() :
event.cancel()
or
Clock.unschedule(event)
 
例1:使用event.cancel() 或 Clock.unschedule(event)来调度事件
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.base import Clock
class TutorialApp(App):
    def __init__(self,**kwargs):
        super(TutorialApp, self).__init__(**kwargs)
        self.event = None
    def build(self):
        mylayout = BoxLayout(orientation='vertical')
        mybutton1 = Button(text = 'Click me!')
        mybutton1.bind(on_press = self.show)
        mylayout.add_widget(mybutton1)

        mybutton2 = Button(text = 'stop me!')
        mybutton2.bind(on_press = self.stop_me)
        mylayout.add_widget(mybutton2)
        return mylayout

    def my_callback(self,*args):
        print('My callback is called')

    def show(self,*args):
        self.event = Clock.schedule_interval(self.my_callback, 1 / 30.)
    def stop_me(self,*args):
        self.event.cancel()
        #Clock.unschedule(self.event)
        print('Last call of my callback, bye bye !')
TutorialApp().run()

 

例2: 您可以在回调中返回False,并且您的事件将自动意外调度
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.base import Clock
class TutorialApp(App):
    def __init__(self,**kwargs):
        super(TutorialApp, self).__init__(**kwargs)
        self.number = 0
    def build(self):
        mylayout = BoxLayout(orientation='vertical')
        mybutton = Button(text = 'Click me!')
        mybutton.bind(on_press = self.show)
        mylayout.add_widget(mybutton)

        return mylayout

    def my_callback(self,*args):
        self.number +=1
        print('My callback is called')
        if self.number == 10:
            print('Last call of my callback, bye bye !')
            self.number = 1
            return False
    def show(self,*args):
        self.event = Clock.schedule_interval(self.my_callback, 1 / 30.)
    def stop_me(self,*args):
        self.event.cancel()
        print('Last call of my callback, bye bye !')
TutorialApp().run()

 

 
 

2.2.2安排一次性事件

使用schedule_once(),您可以调用一个函数“稍后”,比如在下一帧中,或者在X秒内:
例:
from kivy.app import App
from kivy.base import Clock
class TutorialApp(App):
    def build(self):
        Clock.schedule_once(self.my_callback, 1)
    def my_callback(self,*args):
        print('My callback is called !')
TutorialApp().run()

 

这将在一秒钟内调用my_callback。第二个参数是在调用函数之前等待的时间,以秒为单位。但是,您可以使用第二个参数的特殊值来实现其他一些结果:
• 如果X大于0,则将在X秒内调用该回调
• 如果X为0,则将在下一帧之后调用回调
• 如果X为-1,则将在下一帧之前调用回调
-1主要在您已经进入计划事件时使用,如果您想在下一帧发生之前安排调用。
重复函数调用的第二种方法是首先用schedule_once()调度一次回调,然后在回调本身内部对该函数的第二次调用
 
例:
from kivy.app import App
from kivy.base import Clock
class TutorialApp(App):
    def build(self):
        self.my_callback()
        Clock.schedule_once(self.my_callback, 1)

    def my_callback(self, *args):
        print('My callback is called !')
        Clock.schedule_once(self.my_callback,1)

TutorialApp().run()

 

虽然主循环将尝试按照请求保持时间表,但对于何时调用计划回调存在一些不确定性。有时,应用程序中的另一个回调或其他任务将比预期的更长,因此时间可能有点短。在重复回调问题的后一种解决方案中,下一次迭代将在最后一次迭代结束后至少一秒钟被调用。但是,使用schedule_interval(),回调每秒钟就会被调用一次。
 

2.2.3 触发器事件

有时您可能希望为下一帧只调用一次函数,从而防止重复调用。你可能会想做到这一点:
例:下面操作将只调度my_callback()函数 一次
from kivy.app import App
from kivy.base import Clock

class TutorialApp(App):
    def build(self):
        #首先,调度一次。
        self.event = Clock.schedule_once(self.my_callback, 0)
        # 然后,在另一个地方,你将不得不取消计划第一个调度
        # 以避免重复调用。然后你就可以重新安排时间了。
        self.trigger_events()
    def my_callback(self, *args):
        print('My callback is called !')
    def trigger_events(self):
        Clock.unschedule(self.event)
        event = Clock.schedule_once(self.my_callback, 0)

TutorialApp().run()

 

这种编写触发器的方法成本很高,因为即使事件已经完成,您也总是调用unschedule。此外,每次都会创建一个新事件。
改用触发器:
from kivy.app import App
from kivy.base import Clock

class TutorialApp(App):
    def build(self):
        trigger = Clock.create_trigger(self.my_callback)
        trigger()
    def my_callback(self, *args):
        print('My callback is called !')
        
TutorialApp().run()

 

每次调用trigger(),它都会安排一次回调调用。如果已经安排调度,则不会重新安排调度。
 

2.3 小部件事件

一个小部件有两种默认类型的事件:
• Property event: 如果您的小部件更改了其位置或大小,则会触发一个事件。
• Widget-defifined event: 例如--按钮按下或释放时,将触发一个事件。
 

2.4 创建自定义事件

若要创建具有自定义事件的事件调度程序,您需要在类中注册事件的名称,然后创建同名的方法。
请参阅以下示例:
from kivy.base import EventDispatcher
from kivy.app import App
from kivy.base import Clock

class MyEventDispatcher(EventDispatcher):
    def __init__(self, **kwargs):
        self.register_event_type('on_test')
        super(MyEventDispatcher, self).__init__(**kwargs)
    def do_something(self, *args):
        #调用do_something时,“on_test”事件将与值一起调度
        self.dispatch('on_test', args)
    def on_test(self, *args):
        print("I am dispatched")

#调用自定义事件
class TutorialApp(App):
    def build(self):
        ev = MyEventDispatcher()
        ev.do_something('sss')

TutorialApp().run()

 

 

2.5 附加回调

要使用事件,必须将回调绑定到它们。调度事件时,将使用与该特定事件相关的参数调用回调。
回调可以是任何python可调用的,但您需要确保它接受事件发出的参数。为此,接受*args参数通常是最安全的,它将捕获args列表中的所有参数。
from kivy.base import EventDispatcher
from kivy.app import App
from kivy.base import Clock

class MyEventDispatcher(EventDispatcher):
    def __init__(self, **kwargs):
        self.register_event_type('on_test')
        super(MyEventDispatcher, self).__init__(**kwargs)
    def do_something(self, *args):
        #调用do_something时,“on_test”事件将与值一起调度
        self.dispatch('on_test',args)
    def on_test(self, *args):
        print("I am dispatched",args)

#调用自定义事件
class TutorialApp(App):
    def build(self):
        ev = MyEventDispatcher()
        #绑定方法
        ev.bind(on_test=self.my_callback)
        ev.do_something('test')
    def my_callback(value, *args):
        print("Hello, I got an event!")

TutorialApp().run()
请参考kivy.event.EventDispatcher.bind()方法文档的更多示例,关于如何附加回调。
 

2.6 属性介绍

属性是定义事件并与它们绑定的一种很棒的方法。本质上,它们产生的事件是,当对象的属性发生更改时,引用该属性的所有属性都会自动更新。
有不同类型的属性来描述您要处理的数据类型。
导入属性模块
from kivy.properties import 属性名称
• StringProperty 字符串属性
使用:
class Mywidget(Widget): 
    text = StringProperty('')
• NumericProperty: 浮点属性 ,它只接受int或浮点数字数据类型或可以转换为数字的字符串,对于其他数字类型,使用对象属性或使用错误处理程序将其转换为int/浮点。
例:
class Mywidget(Widget): 
    alp = NumericProperty()

 

它不支持numpy数字,因此它们必须手动转换为int/浮点。例如 .widget.alp=np.arange(4)[0] 将引发一个异常。Numpy数组根本不支持,即使是ObjectProperty类型,因为它们的比较不返回bool类型数据。但如果必须使用Kivy属性,请使用ObjectProperty类型 设置为np.array_equal的对象属性。
例:
>>> class A(EventDispatcher): 
    data = ObjectProperty(comparator=np.array_equal) 
>>> a = A() 
>>> a.bind(data=print) 
>>> a.data = np.arange(2) 
<__main__.A object at 0x000001C839B50208> [0 1] 
>>> a.data = np.arange(3)
 <__main__.A object at 0x000001C839B50208> [0 1 2]

• BoundedNumericProperty:默认值属性 设置最小和最大默认值

  方法:
      get_max(需要调度的对象) : 返回obj中BoundedNumericProperty的最大可接受值。 如果没有最大值,则返回无。 检查get_min以获取使用示例。
      get_min(需要调度的对象) :返回obj中BoundedNumericProperty的最低可接受值。 如果没有最小值,则返回无:
      class MyWidget(Widget): number = BoundedNumericProperty(0, min=-5, max=5) widget = MyWidget() print(widget.property('number').get_min(widget)) # 将会输出 -5
      set_max(需要调度的对象) :更改BoundedNumericProperty的最大可接受值,仅限对象实例。 如果您想禁用它,请不要设置。
      set_min(需要调度的对象) :更改BoundedNumericProperty可接受的最小值,仅限对象实例。 如果您想禁用它,请不要设置:
注意:改变边界不会重新验证当前价值。
class MyWidget(Widget): 
    number = BoundedNumericProperty(0, min=-5, max=5) 

widget = MyWidget() 
#将最小值更改为-10 
widget.property('number').set_min(widget, -10) 
#或取消最低值检查 
widget.property('number').set_min(widget, None)

 

• ObjectProperty:对象类型数据
class A(EventDispatcher): 
    data = ObjectProperty(comparator=np.array_equal) 
a = A() 
a.bind(data=print) 
a.data = np.arange(2) 
#将输出如下数据 
<__main__.A object at 0x000001C839B50208> [0 1]

 

• DictProperty:字典类型 与列表属性类似,当将字典分配给命令属性时,存储在属性中的字典是字典的浅层副本,而不是原始命令。
class MyWidget(Widget): 
    pressed = ListProperty({}}) 
    pressed['1'] = 2

 

• ListProperty:
例:
class MyWidget(Widget): 
    pressed = ListProperty([0, 0]) 
    pressed = [1,2]
• OptionProperty:设置默认选项 如果属性中设置的字符串不在有效选项列表中(在属性创建时传递),则将引发有效错误异常。
from kivy.app import App 
from kivy.properties import OptionProperty 
class TutorialApp(App): 
    def build(self): 
        state = OptionProperty("on", options=["On", "Off", "None"])     
        print(state.defaultvalue) 
TutorialApp().run() 
#后台输出 on    

 

• AliasProperty:自定义属性 如果您没有找到适合您需要的属性类,您可以通过创建自定义Python获取器和设置器方法来创建自己的属性类。
from kivy.app import App 
from kivy.properties import AliasProperty 
from kivy.uix.widget import Widget 
class TutorialApp(App): 
    def build(self): 
        obj = my_widget() 
        obj.set_right(200) 
        print(obj.get_right()) 
class my_widget(Widget): 
    def __init__(self,**kwargs): 
        super(my_widget, self).__init__(**kwargs) 
        right = AliasProperty(self.get_right, self.set_right, bind=['x', 'width']) 
    def get_right(self): 
        return self.x + self.width 
    def set_right(self,value): 
        self.x = value - self.width 

TutorialApp().run()
如果x是非一个Kivy属性,那么你必须从setter返回True来分派新的右值:
def set_right(self, value): 
    self.x = value - self.width 
    return True

 

• BooleanProperty 布尔值属性
• ReferenceListProperty :例如,如果x和y是数字属性类型,我们可以为pos创建一个引用列表属性。如果您更改了pos的值,它将自动相应地更改x和y的值。如果您读取pos的值,它将返回一个值为x和y的元组。
例:
class MyWidget(EventDispatcher): 
    x = NumericProperty(0) 
    y = NumericProperty(0) 
pos = ReferenceListProperty(x, y)
  方法:
      get(EventDispatcher obj)
      link(EventDispatcher obj, unicode name) → PropertyStorage
      link_deps(EventDispatcher obj, unicode name)
      set(EventDispatcher obj, _value) 
      setitem(EventDispatcher obj, key, value)
      trigger_change(EventDispatcher obj, value)
 

2.7财产申报

要声明属性,必须在类级声明它们。然后,该类将在创建对象时实例化实际属性。这些属性不是属性:它们是基于属性创建事件的机制:
class MyWidget(Widget):
    text = StringProperty('')

 

重写__init__时,始终接受**kwargs 并使用super() 调用父类的__init__方法,传入类实例:
def __init__(self, **kwargs):
    super(MyWidget, self).__init__(**kwargs)

 

 

2.8 调度属性事件

默认情况下,Kivy属性会提供一个on_<property_name>事件。当属性的值被更改时,将调用此事件。
注意:如果属性的新值等于当前值,则将不调用on_<property_name>事件
例如,如下代码:
from kivy.app import Widget
class CustomBtn(Widget):
    pressed = ListProperty([0, 0])
    
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            self.pressed = touch.pos
            return True
        return super(CustomBtn, self).on_touch_down(touch)
        
    def on_pressed(self, instance, pos):
        print('pressed at {pos}'.format(pos=pos))

 

在上面第3行的代码中:
 pressed = ListProperty([0, 0])

 

我们定义ListProperty类型的pressed属性,并将其默认值设置为[0,0]。从这一点开始,每当更改此属性的值时,将调用on_pressed事件。
第5行:
def on_touch_down(self, touch):
    if self.collide_point(*touch.pos):
        self.pressed = touch.pos
        return True
    return super(CustomBtn, self).on_touch_down(touch)
我们重写Widget类的on_touch_down()方法。在这里,我们检查触摸和小部件的冲突。
如果触摸在我们的小部件中,我们将pressed的值更改为touch.pos并返回True,这表示我们已经使用了触摸,不希望它进一步传播。
最后,如果触摸超出了我们的小部件,我们使用super(…) 调用原始事件并返回结果。这允许触摸事件继续传播,因为它通常会发生。
 
最后在第11行:
def on_pressed(self, instance, pos):
    print('pressed at {pos}'.format(pos=pos))
我们定义了一个on_pressed函数,当属性值改变时,属性将被调用。
注意:此on_<prop_name>事件在定义该属性的类中被调用。要监视/观察对所定义的类之外的属性的任何更改,您应该绑定到该属性,如下所示。
 
绑定到属性
当您只能访问一个小部件实例时,如何监视对属性的更改?您可以绑定到该属性:
your_widget_instance.bind(property_name=function_name)

 

例如,请看下代码:
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class RootWidget(BoxLayout): def __init__(self, **kwargs): super(RootWidget, self).__init__(**kwargs) self.add_widget(Button(text='btn 1')) cb = CustomBtn() cb.bind(pressed=self.btn_pressed) self.add_widget(cb) self.add_widget(Button(text='btn 2')) def btn_pressed(self, instance, pos): print('pos: printed from root widget: {pos}'.format(pos=.pos))

 

 
如果按原样运行代码,您将注意到控制台中有两条print语句。一个来自CustomBtn类内部调用的on_pressed事件,另一个来自绑定到属性更改的btn_pressed函数。
调用这两个函数的原因很简单。绑定并不意味着重写。拥有这两种功能是冗余的,通常只应使用其中一种方法来侦听/响应属性更改。
您还应该注意传递给on_<property_name>事件或绑定到属性的函数的参数。
def btn_pressed(self, instance, pos):
第一个参数是函数本身,它是定义此函数的类的实例。您可以按如下方式使用内嵌函数:
cb = CustomBtn()
def _local_func(instance, pos):
    print('pos: printed from root widget: {pos}'.format(pos=pos))
    cb.bind(pressed=_local_func)
    self.add_widget(cb)
第一个参数将是属性被定义的类的实例。
第二个参数将是值,这是属性的新值
下面是一个完整的示例,源于上面的代码段,您可以使用它复制并粘贴到编辑器中进行实验。
 
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ListProperty
class RootWidget(BoxLayout):
    def __init__(self, **kwargs):
        super(RootWidget, self).__init__(**kwargs)
        self.add_widget(Button(text='btn 1'))
        cb = CustomBtn()
        cb.bind(pressed=self.btn_pressed)
        self.add_widget(cb)
        self.add_widget(Button(text='btn 2'))

    def btn_pressed(self, instance, pos):
        print('pos: printed from root widget: {pos}'.format(pos=pos))
class CustomBtn(Widget):
    pressed = ListProperty([0, 0])
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            self.pressed = touch.pos
            # 我们消耗了触觉。在这里返回False,以便将触摸进一步传播给子类们。.
            return True

        return super(CustomBtn, self).on_touch_down(touch)

    def on_pressed(self, instance, pos):
        print('pressed at {pos}'.format(pos=pos))

class TestApp(App):
    def build(self):
        return RootWidget()

if __name__ == '__main__':
    TestApp().run()

 

 
运行上面的代码将提供以下输出:
 
我们的CustomBtn没有视觉表现,因此显示为黑色。您可以触摸/单击黑色区域以查看控制台上的输出。
 

2.9 复合特性

定义AliasProperty时,通常您自己定义一个getter和setter函数。在这里,您需要定义何时使用bind参数调用getter和setter函数。
请看以下代码:
f
from kivy.properties import AliasProperty

cursor_pos = AliasProperty( _get_cursor_pos, None,bind=('cursor', 'padding', 'pos', 'size', 'focus', 'scroll_x', 'scroll_y', 'line_height', 'line_spacing'), cache=True)
'''光标的当前位置,在(x、y)中.
 :attr:`cursor_pos`是一个:class:`~kivy.properties。Alias属性`,只读。
 '''

 

这里cursor_pos是一个别名属性,它使用getter_get_cursor_pos,setter部分设置为None,这意味着这是一个只读属性
末尾的bind参数定义了当bind=参数中使用的任何属性更改时,将调度on_cursor_pos事件。

posted @ 2021-09-14 11:26  编程届的小学生  阅读(1083)  评论(0编辑  收藏  举报