Joel Cross
Joel Cross

Reputation: 1460

Kivy: Getting a fixed-size widget to display in the centre of the window

I've been pulling my hair out about this for hours. I have a custom widget that subclasses RelativeLayout, which needs to be of a fixed size. I want this widget to display in the centre of the window. I will also want other, normal widgets (ie. buttons) to display around it. After spending an entire day getting the thing to centre in the first place, I now find that upon adding a button it resists all my attempts to get it to behave. Please could someone take a look at my .kv file and tell me where I'm going wrong?

#:kivy 1.8.0

# This is my fixed-size widget
<DrawableGrid>:
    # Note: this is just an example size to keep things simple
    size: 500, 500
    canvas:
        Color:
            rgba: 1, 0, 0, 1
        Rectangle:
            pos: self.pos
            size: self.size

# This subclasses BoxLayout    
CustomLayout:
    orientation: "vertical"

    # I put my DrawableGrid inside an AnchorLayout in the hope that it might
    # behave in some way like the documentation says it does. No such luck.
    # This should be taking up the entire top half of the window.
    AnchorLayout:
        size_hint: 1, 1
        # This anchor_x/anchor_y stuff seemingly does nothing.
        anchor_x: "center"
        anchor_y: "center"

        # This should be appearing as a red rectangle of size 500x500
        DrawableGrid:
            # This next line was needed to set a custom size in the first place
            size_hint: 0, 0

    # This appears exactly as expected, filling the bottom half of the window.
    Button:
        id: next_button
        text: "Next"

So, to reiterate, what I currently want is:

And what I actually get is this: Screenshot (as you can see, the red square is conspicuously absent)

I have tried a whole number of different ways (some more hacky than others) to get this work, but to no avail. Please can someone advise how I can fix this?

Upvotes: 1

Views: 4925

Answers (2)

kitti
kitti

Reputation: 14814

RelativeLayout positions all children relative to the layout's position. This includes all drawing instructions - because that's all children really are. All of a layout's children are drawn on the canvas, so the positions used within the canvas must be relative to the layout itself! So when you say in canvas to draw the rectangle at self.pos, that position is translated just like any other child (in other words, the rectangle is drawn at self.pos + self.pos). If you change the rectangle pos to 0, 0 then it will draw as you expect.

To help explain, here is the default style for RelativeLayout (from kivy/data/style.kv):

<RelativeLayout>:
    canvas.before:
        PushMatrix
        Translate:
            xy: self.pos
    canvas.after:
        PopMatrix

Anything you put in canvas happens between canvas.before and canvas.after.

Upvotes: 0

brousch
brousch

Reputation: 1072

I think you want a vertical BoxLayout as your root widget. The first widget in root will be a RelativeLayout and the second will be your button. This will give you the 50/50 split screen. The RelativeLayout will contain your 500x500 widget with a position of (self.parent.width-self.width)/2, (self.parent.height-self.height)/2

Note: in the following code example, I use 100x100 as the widget since 500x500 would not fit on my screen.

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


kv = """
<Test>:
    orientation: "vertical"
    RelativeLayout:
        GridLayout:
            pos: (self.parent.width-self.width)/2, (self.parent.height-self.height)/2
            size_hint: None, None
            size: 100, 100
            canvas:
                Color:
                    rgba: 1, 0, 0, 1
                Rectangle:
                    pos: self.pos
                    size: self.size

    Button:
        text: "Next"
"""

Builder.load_string(kv)


class Test(BoxLayout):
    def __init__(self, **kwargs):
        super(Test, self).__init__(**kwargs)


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

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

Upvotes: 2

Related Questions