4

I have the following code:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout


class TestGUI(BoxLayout):
    pass


class TestApp(App):
    def build(self):
        return TestGUI()


if __name__ == '__main__':
    TestApp().run()

and the corresponding .kv file:

#:kivy 1.9.1

<TestGUI>:
    temp_size: (0.5 * x for x in self.size)
    canvas.before:
        Color:
            rgba: (1, 0, 0, 1)
        Rectangle:
            size: self.temp_size

The code does not run, throwing:

TypeError: 'NoneType' object is not iterable

due to the last line of code in the kv file. It seems that self.size is not initialized when temp_size is declared, but this raises a few questions.

  • Why am I allowed to iterate through self.size when declaring temp_size if it is a NoneType object?

  • Also, why does the code work perfectly fine when I replaces the last line with size: (0.5 * x for x in self.size) instead of using a variable?

  • Also, how can I circumvent this issue and assign variables based on the value of self.size?

Peter Badida
  • 11,310
  • 10
  • 44
  • 90
null
  • 2,060
  • 3
  • 23
  • 42
  • is it 'root' instead of 'self'? https://kivy.org/docs/api-kivy.lang.html – Skinner Feb 08 '17 at 03:57
  • In this case, they're interchangeable, but I'm asking about a general case. For example, if the `TestGUI` was nested in a `BoxLayout`, then it would be `self.size`. – null Feb 08 '17 at 04:23

1 Answers1

2

What happens here is basically this:

[x for x in None]

and why is it like that you can read in this bug report. It's because the canvas isn't available yet and the properties seem to be calculated after it's created, therefore - if no canvas, then value == garbage.

To fix it you have to do it in the canvas itself like this and not outside in a property, because the canvas is built first:

<TestGUI>:
    canvas.before:
        Color:
            rgba: (1, 0, 0, 1)
        Rectangle:
            size: (0.5 * x for x in self.size)

I'm not sure what do you mean by the last question, though I think it might be either using properties after the canvas is already available, or using values from Config if you intend to use Window and not Widget size, or set the property in __init__ and use it later in kv.

The latter one might be the best thing for you, but don't forget to:

  • initialize with some values (ListProperty([0, 0])) otherwise the Rectangle will just kill the app
  • use super(), for inheritance reasons

Example:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ListProperty
Builder.load_string('''
<TestGUI>:
    canvas.before:
        Color:
            rgba: (1, 0, 0, 1)
        Rectangle:
            size: self.temp_size
''')


class TestGUI(BoxLayout):
    temp_size = ListProperty([0, 0])

    def __init__(self, **kwargs):
        super(TestGUI, self).__init__(**kwargs)
        print(self.size)
        self.temp_size = [0.5 * x for x in self.size]


class TestApp(App):
    def build(self):
        return TestGUI()


if __name__ == '__main__':
    TestApp().run()
Peter Badida
  • 11,310
  • 10
  • 44
  • 90
  • But if the canvas is built before the properties are initialized, then why does `self.size` not return `None` when setting the rectangle's size? – null Feb 09 '17 at 04:01
  • @Kootling I believe I missed a step or two that preceed the canvas creation. Most likely righr before the canvas is created, the size/pos/... properties are available to the canvas itself, but I believe the custom ones are created a moment after that. However, when used in the canvas, the custom property is calculated a moment *before* the canvas creation also, but even before canvas' size/pos/... are available (thus size/pos/... are None). – Peter Badida Feb 09 '17 at 08:07
  • That makes more sense, but why do I get no error when setting `temp_size` to `[x for x in None]`? I only get the error when setting the rectangle's size to `temp_size`. – null Feb 11 '17 at 07:34
  • @Kootling Because of the order the property is evaluated. Try to print the value of the variable with on_* event. – Peter Badida Feb 11 '17 at 09:56