I see it’s a common struggle for people to understand how to manage size and positions of the widgets in kivy.
There is not much to it, really, just a few principles to understand, but they are better explained by example, i believe.
dumb positionning
Let’s create a root widget, and put two buttons in it.
root = Widget()
b1 = Button()
b2 = Button()
root.add_widget(b1)
root.add_widget(b2)
What will happen when i return root to be used as my root widget? As a root widget, it will have all the space for itself, the two buttons, however, will only use their default size, which is (100, 100), and will use their default position, which is (0, 0), the bottom-left corner of the screen.
We can change b1 and b2 positions to absolute values, let’s change line 2 and 3 to:
b1 = Button(pos=(100, 100))
b2 = Button(pos=(200, 200))
something a little smarter
Now, that doesn’t make for a very flexible UI, if you ask me, it would be better to use the parent size to decide where to place the buttons, no?
b1 = Button(pos=(root.x, root.height / 2))
b2 = Button(pos=(root.width - 100, root.height / 2))
No, that’s not much smarter, it’s still quite dumb actually, it doesn’t even work, why? because we just instanciated root, it’s not even the root widget yet, it doesn’t have its final size, so our widgets will use the default value of (100, 100), for their calculations, no - no.
Let’s fix this with bindings.
b1 = Button()
root.bind(on_size=reposition_b1, on_pos=reposition_b1)
b2 = Button()
root.bind(on_size=reposition_b2, on_pos=reposition_b2)
Where these two functions will be along the lines of:
def reposition_b1(root, *args):
b1.pos = root.x, root.height / 2 - b1.height / 2
Also, widget have nice alias properties for things like height / 2
, x +
width
, x + width / 2
, here is a quick table:
right = x + width
top = y + height
center_x = x + width / 2
center_y = y + height / 2
center = (center_x, center_y)
and actually, pos
is just an alias property for (x, y)
.
Alias Properties works both ways, so we can use them to set our positions in a
simpler way.
def reposition_b1(root, *args):
b1.x = root.x
b1.center_y = root.center_y
A lot of work for not so much, right? That’s because we are not using the right tools!
Let’s jump on a layout, specifically FloatLayout.
FloatLayout to the rescue
Layouts are here to make our life easier when constructing an UI.
FloatLayout is very flexible, it lets you set rules for positions, or do things in the absolute way.
root = FloatLayout()
b1 = Button(pos_hint={'x': 0, 'center_y': .5})
b2 = Button(pos_hint={'right': 1, 'center_y': .5})
pos_hint will make the values relative to the size/position of the parent. So
here, for b1 x
will be the x of the parent, and center_y
will be at the
middle between y
and top
.
Now, if you run this, you may get a surprise, because FloatLayout also made b1
and b2 sizes relative to root.size, and the default value being (1, 1), they
both have the same size as the root widget, sometime you want to keep that
relative, maybe with a different value, sometime you want to have them keep the
value passed in size
(or default size
value), if so, you can pass (None,
None)
to size_hint.
b1 = Button(pos_hint={'x': 0, 'center_y': .5}, size_hint=(None, None))
Conclusion
I’ll probably do other posts on the subject, because there is much more to it, but the basic principles are here. I anyway really encourage you to look at this section of the documentation, which go further with the various kind of layouts you have access to.