Ouroboros
Ouroboros

Reputation: 79

Add Widget to child of inherited class in kv dynamic class

If I have a dynamically defined class such as this:

<Foo@BoxLayout>:
    orientation: "vertical"

    ...some other child widgets...

    BoxLayout:
        id: target
        orientation: "horizontal"

        ...other children...

How do I create a class that inherits from this, with the only change being an additional widget added to the BoxLayout with id: target?

I attempted to change the dynamic classes into rules and define the classes in the python:

class Foo(BoxLayout):
    pass

class EditedFoo(Foo):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.ids["target"].add_widget(<the widget I want to add>, index=0)

however ids was empty from the __init__ function (and from the on_parent function).

Is there any way to do this without redefining the whole class?

Edit:

from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout

kv = """
BoxLayout:
    EditedFoo:

<Foo>:
    orientation: "vertical"

    BoxLayout:
        id: target
        orientation: "horizontal"
"""

class TestApp(App):
    def build(self):
        return Builder.load_string(kv)

class Foo(BoxLayout):
    pass

class EditedFoo(Foo):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.ids["target"].add_widget(Button(text="hello"), index=0)

TestApp().run()

Here is a complete verified example of it not working

Upvotes: 2

Views: 1332

Answers (2)

eyllanesc
eyllanesc

Reputation: 243887

The .kv code implements the class created in Python so it is understood that it will be added after the constructor finishes executing, and consequently the ids will be empty in the constructor, a trick is to use Clock that will call a function a moment after rendering the whole window since at that moment the ids will not be empty:

# ...
from kivy.clock import Clock
# ...

class EditedFoo(Foo):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Clock.schedule_once(self.callback)

    def callback(self, *args):
        self.ids["target"].add_widget(Button(text="hello"), index=0)

Upvotes: 1

HumbleCoder
HumbleCoder

Reputation: 596

The codes in .kv file get initialized after the Python codes are executed.

Therefore, your EditedFoo(Foo) will inherit the Foo(BoxLayout) from the Python codes first, then Foo in .kv file will be re-declared.

Best way is to put initial attributes of Foo(BoxLayout) in Python codes, then inherit Foo in .kv such as <EditedFoo@Foo>

For example,

In .py:

class Foo(BoxLayout):
    greeting = "hi"

In .kv:

<EditedFoo@Foo>
    greeting: "Goodbye World"

Foo:
    id: root_foo
    Button:
        text: root_foo.greeting
    EditedFoo:
        id: foo1
        Label:
            text: foo1.greeting
    EditedFoo:
        id: foo2
        greeting: "Hello Another World"
        Button:
            text: foo2.greeting

In this way, you can use EditedFoo class in .kv inherited from Foo.

enter image description here

Upvotes: 2

Related Questions