Paul Manta
Paul Manta

Reputation: 31597

How can the parent widget position its child widgets?

I have a Kivy Widget called MainPage that has a child widget. These child widget needs to have access to the same message dispatcher (has nothing to do with Kivy's EventDispatcher) as its parent:

class MainPage(Widget):
    def __init__(self, dispatcher):
        self.child = ChildWidget(dispatcher)
        self.add_widget(child1)

Inside the .kv file I defined the visual properties of <ChildWidget>, but I have not specified its position inside <MainPage>. I would like <MainPage> to specify how its children are supposed to be positioned. Is something like this possible?

<ChildWidget>:
    ...

<MainPage>:
    self.child:
        pos: (10, 10)

Upvotes: 1

Views: 3004

Answers (2)

toto_tico
toto_tico

Reputation: 19037

If your goal of positioning is not too complicated, a RelativeLayout might be enough.

class MainPage(RelativeLayout):
    def __init__(self, dispatcher, **kwargs):
        super(MainPage, self).__init__(**kwargs)
        self.child = ChildWidget(dispatcher, pos=(10,10))
        self.add_widget(self.child)

The RelativeLayout (and in general any layout - except FloatLayout which keeps the absolute positioning) keeps the children update to the position of the parent. All the layouts are also widgets so you should be safe.

<ChildWidget>:
    size_hint: .1, .05
    text: "press me"
    on_press: self.parent.pos = (200,200)
<MainPage>:
    pos: 400,400

Here is the complete code I tried:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.button import Button

Builder.load_string("""
<ChildWidget>:
    size_hint: .1, .05
    text: "press me"
    on_press: self.parent.pos = (200,200)
<MainPage>:
    pos: 400,400
""")

class Dispatcher():
    pass

class ChildWidget(Button):
    def __init__(self, dispatcher, **kwargs):
        super(ChildWidget, self).__init__(**kwargs)
        self.dispatcher = dispatcher

class MainPage(RelativeLayout):
    def __init__(self, dispatcher, **kwargs):
        super(MainPage, self).__init__(**kwargs)
        self.child = ChildWidget(dispatcher, pos=(10,10))
        self.add_widget(self.child)

class TestApp(App):
    def build(self):
        dispatcher = Widget()
        return MainPage(dispatcher)

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

Upvotes: 1

qua-non
qua-non

Reputation: 4182

Assuming ChildWidget is still a Kivy Widget (inherited from Widget or a subclass of it).

[simple way using kv]

if you wanted the child to control where it is positioned inside it's parent you could do::

<ChildWidget>
    pos:  (self.parent.x + 10, self.parent.y + 10) if self.parent else (100, 100)

for controlling the children's position one could do::

<MainPage>
    on_children:
        # reposition children
        root.reposition_children(args[1])
    on_size:
        # reposition children when parents size changes
        root.reposition_children(self.children)
    on_pos:
        # reposition children when widgets size changes
        root.reposition_children(self.children)

in your .py

class MainPage(Widget):
    ...
    ...
    def reposition_children(self, children):
        for child in children:
            child.pos = x, y
            child.size = d, e

[More elaborate and complete way]

What you essentially seem to want is your Main Widget to behave like a Layout. This link is to the code which also includes the inline docs. The code itself is a abstract class that gives you a guide-line on how to structure/fashion your layout.

You should inherit from the Layout class and do your widget positioning in do_layout, call it through self.trigger_layout(this way positioning of the widgets is delayed till just before they need to be displayed on screen and trigger makes sure that the function is not called multiple times per frame).

You should bind trigger_layout with properties like children, size, pos... so that do_layout is re-triggered whenever these properties change.

Upvotes: 1

Related Questions