pythonkivykivy-language

How do I use self.size in Kivy without using a kv file?


I'm trying to create a simple application using Kivy where an image fills up the entire widget. I wrote code for this using a kv file and it worked just fine. But, if I try to make it entirely in the python file, it doesn't work and self.size gives the value (100, 100), which results in a 100 x 100 image on the bottom left corner of the window.

Here's my code for the kv file approach :

from kivy.app import App
from kivy.uix.widget import Widget


class MainWidget(Widget):
    pass


class MainApp(App):
    def build(self):
        return MainWidget()


MainApp().run()

kv file :

<MainWidget>:
    canvas.before:
        Rectangle:
            source: "background.jpg"
            size: self.size

Here's my attempt at making it entirely in the python file :

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import *


class MainWidget(Widget):
    def __init__(self, **kwargs):
        super(MainWidget, self).__init__(**kwargs)
        with self.canvas.before:
            Rectangle(size = self.size, source = "background.jpg")


class MainApp(App):
    def build(self):
        return MainWidget()


MainApp().run()

Aren't the two approaches equivalent? What's causing the unexpected output?


Solution

  • The two methods that you show are not equivalent. One of the major advantages of using kv is that it automatically sets up bindings for you. So the kv code:

    size: self.size
    

    sets up a binding so that whenever the size of the MainWidget changes, the size of the Recrangle will be adjusted to the same.

    In the code where you do not use kv no such binding is created. In that code the size of the Rectangle is set when the code:

    Rectangle(size = self.size, source = "background.jpg")
    

    is executed. At that point, the size of MainWidget is still the default of (100 100) and the position is the default of (0, 0). If you want the size of the Rectangle to be updated, you must provide the code to do so. Here is an example of one way to do that:

    from kivy.app import App
    from kivy.uix.widget import Widget
    from kivy.graphics import *
    
    
    class MainWidget(Widget):
        def __init__(self, **kwargs):
            super(MainWidget, self).__init__(**kwargs)
            with self.canvas.before:
                self.rect = Rectangle(size = self.size, source = "background.jpg")
    
        def on_size(self, *args):
            self.rect.size = self.size
    
    
    class MainApp(App):
        def build(self):
            return MainWidget()
    
    
    MainApp().run()