如何在运行时更改Kivy中我的小部件的颜色?

And*_*ndy 18 python kivy

我在Kivy中更改简单小部件的颜色时遇到了麻烦.我可以在创建窗口小部件时设置颜色,但之后我无法更改它.

这是简单的布局定义文件circletest.kv.它定义了一个圆,其中颜色(实际上只是r,来自rgba),位置和大小都链接到widget类中的变量.

#:kivy 1.4.1

<CircleWidget>:
    canvas:
        Color:
            rgba: self.r,1,1,1
        Ellipse:
            pos: self.pos
            size: self.size
Run Code Online (Sandbox Code Playgroud)

这是应用程序circletest.py.它创建并显示简单的小部件.创建对象时成功设置颜色和位置.单击小部件时,小部件可以更改它自己的位置,但是当我尝试更改颜色时,没有任何反应.

import kivy
kivy.require('1.4.1')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget

Builder.load_file('circletest.kv')

class CircleWidget(Widget):

    def __init__(s, **kwargs):
        s.size= [50,50]
        s.pos = [100,50]
        s.r = 0
        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 does nothing!

class TestApp(App):

    def build(s):
        parent = Widget()
        w = CircleWidget()
        parent.add_widget(w)
        return parent

if __name__ == '__main__':
    TestApp().run()
Run Code Online (Sandbox Code Playgroud)

有谁能看到这个问题?

UPDATE

仍然不确定这个问题的答案是什么,但我确实有一个解决方法:

在.kv文件中,我将颜色指向对象中的变量.用于提取初始颜色:

Color:
    rgba: self.col
Run Code Online (Sandbox Code Playgroud)

当我想从.py文件中更改颜色时,我遍历画布中的所有指令并修改第一个类型为"Color"的指令.显然这是一个hack,并且不适用于具有多个Color:属性的小部件:

for i in s.canvas.get_group(None):
    if type(i) is Color:
        i.r, i.g, i.b, i.a = v
        break
Run Code Online (Sandbox Code Playgroud)

我把它全部包裹在一个属性中所以它很好用:

class CircleWidget(Widget):

    def get_col(s):
        return s._col

    def set_col(s,v):
        for i in s.canvas.get_group(None):
            if type(i) is Color:
                i.r, i.g, i.b, i.a = v
                break
        s._col = v

    col = property(get_col, set_col)

    def __init__(s, **kwargs):
        s.size= [50,50]
        s.pos = [100,50]
        s._col = (1,1,0,1)
        super(CircleWidget, s).__init__(**kwargs)

    def on_touch_down(s, touch):
        if s.collide_point(touch.x,touch.y):    
            s.col = (s.col[::-1]) # Set to some other color
Run Code Online (Sandbox Code Playgroud)

似乎现在工作.如果您知道更好的方法,请告诉我.我敢肯定必须有一个更简单的方法,而且我错过了一些明显的东西!

qua*_*non 17

tshirtman的答案是正确的,这里是对正在发生的事情的解释.

在您设置的kv文件中

<CircleWidget>:
    canvas:
        Color:
            rgba: self.r, 1, 1, 1
        Ellipse:
            pos: self.pos
            size: self.size
Run Code Online (Sandbox Code Playgroud)

该行rgba: self.r, 1, 1, 1尝试更新值的rgba更改时的值 r.这是通过绑定在kv语言中隐式完成的,可以在kivy属性上完成,因为它实现了一个Observer模式.

r代码中的变量已更新,但它只是一个变量,它不会提供任何值已更改且无法绑定的变量.如果您注意到您的更改pos有效,因为pos它是ReferenceListProperty.

在Kivy中编程的一般规则,如果你想根据Widget/Object的属性更改代码,请使用Kivy属性.它为您提供了观察属性更改的选项,并相应地通过bind/on_property_name事件或通过上面提到的kv语言隐式调整您的代码.


Tsh*_*man 14

在您的初始版本中,您只是错过了该属性的声明

from kivy.properties import NumericProperty
Run Code Online (Sandbox Code Playgroud)

在标题和

r = NumericProperty(0)
Run Code Online (Sandbox Code Playgroud)

就在 class CircleWidget(Widget):

另外,您声明您的kv文件名为circletest.kv,但您的应用程序名为TestApp,因此您应该更改其中一个以使其一致,否则您的kv文件将无法找到,但因为您没有报告任何问题,我想这只是问题中的一个错字.编辑:刚刚看到了Builder.load_file,

干杯.