0

To the best of my knowledge, to set a background image in a Kivy application, you should define a Rectangle widget as a child of canvas.before and set its source. This works for a static value.

However, I would like to change the background from time to time. I expected this MRE to do just that by invoking canvas.ask_update() but that doesn't work. The debug statement Got background X is printed only once.

How can I dynamically update the background? I would prefer to define widgets in kv rather than programmatically, if at possible.

sample2.kv

#:kivy 1.0.9

<Sample2Gui>:
    canvas.before:
        Rectangle:
            pos: self.pos
            size: self.size
            source: app.get_background_source()

    Button:
        font_size: sp(50)
        pos_hint: {"center_x": 0.5, "center_y": 0.5}
        size_hint: 0.2, 0.1
        text: "Cycle background"
        on_press: app.update_background_source()

sample2.py

from kivy.app import App
from kivy.config import Config
from kivy.uix.floatlayout import FloatLayout


class Sample2Gui(FloatLayout):
    pass


class Sample2App(App):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.index = 0
        print("Initialised index to 0.")

    def build(self):
        return Sample2Gui()

    def get_background_source(self):
        source = f"background{self.index}.png"
        print(f"Got background {source}.")
        return source

    def update_background_source(self):
        self.index = (self.index + 1) % 3  # 0, 1, 2
        print(f"Set index to {self.index}.")
        self.root.canvas.ask_update()


if __name__ == '__main__':
    Config.set('graphics', 'window_state', 'maximized')
    Sample2App().run()
lofidevops
  • 15,528
  • 14
  • 79
  • 119

1 Answers1

0

From a comment buried underneath this helpful blog post about the Kivy canvas:

The short answer is that you need to create a property in your python code that you will reference in the kv language. You will use that property to modify the string of the source.

This is not a Python property (defined with the @property decorator), but a type of Kivy class. From a related answer:

General rule for programming in Kivy, if you want to change code depending on a property of a Widget/Object use a Kivy Property. [A Kivy property] provides you the option to Observe Property changes... implicitly through kv language as mentioned above.

In this case we can define the source as a StringProperty:

sample2.kv

#:kivy 1.0.9

<Sample2Gui>:
    canvas.before:
        Rectangle:
            pos: self.pos
            size: self.size
            source: app.background_source
            # NOTE: no longer a method, see Python
            # code for matching definition

    Button:
        font_size: sp(50)
        pos_hint: {"center_x": 0.5, "center_y": 0.5}
        size_hint: 0.2, 0.1
        text: "Cycle background"
        on_press: app.update_background_source()

sample2.py

from kivy.app import App
from kivy.config import Config
from kivy.properties import StringProperty
from kivy.uix.floatlayout import FloatLayout


class Sample2Gui(FloatLayout):
    pass


class Sample2App(App):
    background_source = StringProperty()  # default is ""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.index = -1
        self.update_background_source()

    def build(self):
        return Sample2Gui()

    def update_background_source(self):
        self.index = (self.index + 1) % 3  # 0, 1, 2
        self.background_source = f"background{self.index}.png"


if __name__ == '__main__':
    Config.set('graphics', 'window_state', 'maximized')
    Sample2App().run()
lofidevops
  • 15,528
  • 14
  • 79
  • 119