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