Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
298 views
in Technique[技术] by (71.8m points)

python - How do I change the color of my widget in Kivy at run time?

I'm having trouble changing the color of a simple widget in Kivy. I can set the color when I create the widget, but I can't change it afterwards.

Here is the simple layout definition file circletest.kv. It defines a circle where the color (actually just the r, from rgba), position and size are all linked to variables in the widget class.

#:kivy 1.4.1

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

Here's the application circletest.py. It creates and displays the simple widget. The color and position are successfully set when the object is created. When the widget is clicked the widget can change it's own position, but when I try to change the color nothing happens.

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()

Can anyone see the problem?

UPDATE

Still not sure what the answer to this question is, but I do have a work around:

In the .kv file I pointed the color to a variable in my object. Works for extracting the initial color:

Color:
    rgba: self.col

When I want to change the color from the .py file I loop through all the instructions in the canvas and modify the first one of type "Color". Obviously this is a hack, and won't work on widgets with more than one Color: property:

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

I wrapped that all up in a property so it's neat to use:

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

Seems to work for now. Please let me know if you know a better way of doing this. I'm sure there must be a simpler way, and that I'm missing something obvious!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The answer by tshirtman is correct, here is the explanation of what's going on.

In your kv file when you set

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

The line rgba: self.r, 1, 1, 1 tries to update the value of rgba whenever there is a change to the value of r. This is done implicitly in kv language by binding, which can be done on a kivy Property as it implements a Observer Pattern.

The variable r in your code was updated but it's just a variable that doesn't provide any indication that it's value has changed and can not be bound to. If you notice your changes to pos work because pos is a ReferenceListProperty.

General rule for programming in Kivy, if you want to change code depending on a property of a Widget/Object use a Kivy Property. It provides you the option to Observe Property changes and adjust your code accordingly either explicitly through bind/on_property_name events or implicitly through kv language as mentioned above.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...