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

21

我在Kivy中尝试更改简单小部件的颜色时遇到了问题。我可以在创建小部件时设置颜色,但之后无法更改。

这是一个简单的布局定义文件circletest.kv。它定义了一个圆,其中颜色(实际上只是rgba中的r),位置和大小都与小部件类中的变量相关联。

#:kivy 1.4.1

<CircleWidget>:
    canvas:
        Color:
            rgba: self.r,1,1,1
        Ellipse:
            pos: self.pos
            size: self.size

这里是应用程序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()

有人能看出问题吗?

更新

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

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

Color:
    rgba: self.col

当我想从.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

我把所有东西都封装在一个属性中,这样使用起来更加简洁:

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

目前似乎可以工作。如果您知道更好的方法,请告诉我。 我相信一定有更简单的方法,而且我可能遗漏了一些明显的东西!


你尝试设置的浮点数可能有问题吗? - Difusio
嗨 @Difusio。你是在暗示这里可能存在类型冲突吗?我相当确定 r 应该是一个浮点数。我刚试了在构造函数中设置 s.r=0.0001,结果得到了类似的行为。我试过在构造函数中将 r 设置为列表 s.r=[0.1],然后出现了错误。但是当我在类的其他地方这样做时,它并不会导致错误,这表明在对象创建之后框架没有访问 s.r。也许我可以调用某个函数来强制框架更新值并重绘小部件? - Andy
我对我的颜色变量类型进行了更多实验。在 .kv 文件中,我将 rgba 值指向单个变量 rgba: self.c,然后在构造函数中初始化为列表 self.c = [1,1,1,1]。这展示了完全相同的行为:它在创建时设置颜色,但不允许我在之后设置它。我还尝试使用 kivy 的 Color 类:s.c = kivy.graphics.Color(1,1,1,1),但这给了我一个类型错误,因为它不支持迭代。 - Andy
2个回答

18
tshirtman的答案是正确的,这里是正在发生的事情的解释。
在您的kv文件中,当您设置时
<CircleWidget>:
    canvas:
        Color:
            rgba: self.r, 1, 1, 1
        Ellipse:
            pos: self.pos
            size: self.size

这一行代码:rgba: self.r, 1, 1, 1试图在r值发生变化时更新rgba的值。这是通过绑定隐式完成的,而kivy属性可以实现观察者模式。保留HTML标签。

您代码中的变量r已经被更新,但它只是一个变量,并没有提供任何指示它的值已经改变并且不能被绑定。如果您注意到对pos的更改,则是因为pos是一个ReferenceListProperty。保留HTML标签。

如果您想根据Widget/Object的属性更改代码,Kivy编程的一般规则是使用 Kivy Property。它为您提供了观察属性更改的选项,并通过绑定/on_property_name事件或如上所述的kv语言隐式地调整您的代码。


16
在你最初的版本中,你只是缺少了该属性的声明。
from kivy.properties import NumericProperty
在头部。
r = NumericProperty(0)

class CircleWidget(Widget):下方:

另外,你说你的kv文件名为circletest.kv,但是你的应用程序名为TestApp,所以你应该改变它们中的一个使它们一致,否则你的kv文件将无法找到。但由于你没有报告任何相关问题,我猜这只是一个问题描述上的笔误。编辑:刚看到Builder.load_file,没问题了。

祝好。


当椭圆用Builder.load_fileBuilder.load_string绘制时,这个方法有效,但是当顶点指令被动态添加时(即使用with self.canvas:),它会失败。我在这里创建了另一个问题。谢谢! - toto_tico
我添加了一个赏金... 我需要这个答案,因为我的当前方法似乎不合适。如果您能帮助我,我将非常感激。目前,我通过每次删除并创建顶点指令的新实例来解决它。我不明白为什么必须是一个新实例,所以我在这里问了另一个问题。 - toto_tico
对我来说,这适用于设置初始颜色,但是当我尝试在 on_touch_down() 下更改颜色时它不起作用。 - Tooniis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接