如何根据 Widget Kivy 属性更新**动态添加的**椭圆(不使用 Builder)的颜色?

Posted

技术标签:

【中文标题】如何根据 Widget Kivy 属性更新**动态添加的**椭圆(不使用 Builder)的颜色?【英文标题】:How do I update the Color of a **dynamically added** Ellipse (not using Builder) according to Widget Kivy properties? 【发布时间】:2013-06-08 03:05:08 【问题描述】:

这与this other question 非常相关。唯一的区别是我使用with self.canvas 动态添加椭圆,而不是使用生成器(Builder.load_stringBuilder.load_file)。所以这里是可以工作的代码。当您单击椭圆时,它会移动并更改颜色:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty
from kivy.graphics import Color, Ellipse

Builder.load_string("""
<CircleWidget>:
    canvas:
        Color:
            rgba: self.r,1,1,1
        Ellipse:
            pos: self.pos
            size: self.size
""")

class CircleWidget(Widget):
    r = NumericProperty(0)
    def __init__(s, **kwargs):
        s.size= [50,50]
        s.pos = [100,50]
        super(CircleWidget, s).__init__(**kwargs)

    def on_touch_down(s, touch):
        if s.collide_point(touch.x,touch.y):    
            s.pos = [s.pos[1],s.pos[0]]     # this works
            s.r = 1.0                       # this also works

class TestApp(App):
    def build(s):
        parent = Widget()
        parent.add_widget(CircleWidget())
        return parent

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

如果我尝试在不使用 Builder 的情况下做同样的事情,它就不再起作用了:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty
from kivy.graphics import Color, Ellipse

class CircleWidget(Widget):
    r = NumericProperty(0)
    def __init__(s, **kwargs):
        s.size= [50,50]
        s.pos = [100,50]
        super(CircleWidget, s).__init__(**kwargs)
        with s.canvas:
            Color(s.r,1,1,1)
            Ellipse(pos = s.pos, size = s.size)

    def on_touch_down(s, touch):
        if s.collide_point(touch.x,touch.y):    
            s.pos = [s.pos[1],s.pos[0]]     # This doesn't work anymore
            s.r = 1.0                       # Neither do this

class TestApp(App):
    def build(s):
        parent = Widget()
        parent.add_widget(CircleWidget())
        return parent

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

代码运行,事件被实际调用。此外,Widget 被移动(即使它在视觉上不清晰)但画布的 Instructions 没有更新。

有什么想法吗?

【问题讨论】:

我也尝试过ask_update,但没有成功。 我喜欢在 *** 上几乎逐字回答我需要回答的问题 :) 【参考方案1】:

第一个版本有效,因为 kv lang 自动将画布重新绑定到表达式中的属性,所以这些行:

    Color:
        rgba: self.r,1,1,1

做更多的事情:

    Color(s.r,1,1,1)

你可以做的就是绑定 self.r 来自动重建你的画布指令。

__init__

self.bind(r=self.redraw)
self.bind(pos=self.redraw)
self.bind(size=self.redraw)

然后移动

    with s.canvas:
        Color(s.r,1,1,1)
        Ellipse(pos = s.pos, size = s.size)

属于名为redraw 的方法,之前调用了self.canvas.clear()。

完整结果:

from kivy.app import App
from kivy.uix.widget import Widget

from kivy.properties import NumericProperty
from kivy.graphics import Color, Ellipse

class CircleWidget(Widget):
    r = NumericProperty(0)

    def __init__(s, **kwargs):
        super(CircleWidget, s).__init__(**kwargs)
        s.bind(r=s.redraw)
        s.bind(pos=s.redraw)
        s.bind(size=s.redraw)
        s.size = [50, 50]
        s.pos = [100, 50]

    def redraw(s, *args):
        s.canvas.clear()
        with s.canvas:
            Color(s.r, 1, 1, 1)
            Ellipse(pos = s.pos, size = s.size)

    def on_touch_down(s, touch):
        if s.collide_point(touch.x, touch.y):
            print "gotcha"
            s.pos = [s.pos[1], s.pos[0]]
            s.r = 1.0

class TestApp(App):
    def build(s):
        parent = Widget()
        parent.add_widget(CircleWidget())
        return parent

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

【讨论】:

非常感谢!我开始怀疑 Builder 所做的不仅仅是解析 Kivy 语言。 嗯,从技术上讲,它是解析的一部分,只是 kv 语言的构建是为了让这些事情比普通的 python 更容易。谢谢你的奖金:)。

以上是关于如何根据 Widget Kivy 属性更新**动态添加的**椭圆(不使用 Builder)的颜色?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用绑定动态更新 Kivy Label 的文本属性

运行代码时更新 kivy 小部件的属性

如果未动态添加,如何正确删除Kivy中的小部件

Kivy - AttributeError:“NoneType”对象没有属性“add_widget”

将 Kivy Widget 传递给另一个类

KIVY:如何清除所有子项的网格布局