Quick search

Table Of Contents

Source code for kivy.uix.boxlayout

'''
Box Layout
==========

.. only:: html

    .. image:: images/boxlayout.gif
        :align: right

.. only:: latex

    .. image:: images/boxlayout.png
        :align: right

:class:`BoxLayout` arranges children in a vertical or horizontal box.

To position widgets above/below each other, use a vertical BoxLayout::

    layout = BoxLayout(orientation='vertical')
    btn1 = Button(text='Hello')
    btn2 = Button(text='World')
    layout.add_widget(btn1)
    layout.add_widget(btn2)

To position widgets next to each other, use a horizontal BoxLayout. In this
example, we use 10 pixel spacing between children; the first button covers
70% of the horizontal space, the second covers 30%::

    layout = BoxLayout(spacing=10)
    btn1 = Button(text='Hello', size_hint=(.7, 1))
    btn2 = Button(text='World', size_hint=(.3, 1))
    layout.add_widget(btn1)
    layout.add_widget(btn2)

Position hints are partially working, depending on the orientation:

* If the orientation is `vertical`: `x`, `right` and `center_x` will be used.
* If the orientation is `horizontal`: `y`, `top` and `center_y` will be used.

You can check the `examples/widgets/boxlayout_poshint.py` for a live example.

.. note::

    The `size_hint` uses the available space after subtracting all the
    fixed-size widgets. For example, if you have a layout that is 800px
    wide, and add three buttons like this:

    btn1 = Button(text='Hello', size=(200, 100), size_hint=(None, None))
    btn2 = Button(text='Kivy', size_hint=(.5, 1))
    btn3 = Button(text='World', size_hint=(.5, 1))

    The first button will be 200px wide as specified, the second and third
    will be 300px each, e.g. (800-200) * 0.5


.. versionchanged:: 1.4.1
    Added support for `pos_hint`.

'''

__all__ = ('BoxLayout', )

from kivy.uix.layout import Layout
from kivy.properties import (NumericProperty, OptionProperty,
                             VariableListProperty)


[docs]class BoxLayout(Layout): '''Box layout class. See module documentation for more information. ''' spacing = NumericProperty(0) '''Spacing between children, in pixels. :attr:`spacing` is a :class:`~kivy.properties.NumericProperty` and defaults to 0. ''' padding = VariableListProperty([0, 0, 0, 0]) '''Padding between layout box and children: [padding_left, padding_top, padding_right, padding_bottom]. padding also accepts a two argument form [padding_horizontal, padding_vertical] and a one argument form [padding]. .. versionchanged:: 1.7.0 Replaced NumericProperty with VariableListProperty. :attr:`padding` is a :class:`~kivy.properties.VariableListProperty` and defaults to [0, 0, 0, 0]. ''' orientation = OptionProperty('horizontal', options=( 'horizontal', 'vertical')) '''Orientation of the layout. :attr:`orientation` is an :class:`~kivy.properties.OptionProperty` and defaults to 'horizontal'. Can be 'vertical' or 'horizontal'. ''' def __init__(self, **kwargs): super(BoxLayout, self).__init__(**kwargs) update = self._trigger_layout fbind = self.fbind fbind('spacing', update) fbind('padding', update) fbind('children', update) fbind('orientation', update) fbind('parent', update) fbind('size', update) fbind('pos', update) def do_layout(self, *largs): # optimize layout by preventing looking at the same attribute in a loop len_children = len(self.children) if len_children == 0: return selfx = self.x selfy = self.y selfw = self.width selfh = self.height padding_left = self.padding[0] padding_top = self.padding[1] padding_right = self.padding[2] padding_bottom = self.padding[3] spacing = self.spacing orientation = self.orientation padding_x = padding_left + padding_right padding_y = padding_top + padding_bottom # calculate maximum space used by size_hint stretch_weight_x = 0. stretch_weight_y = 0. minimum_size_x = padding_x + spacing * (len_children - 1) minimum_size_y = padding_y + spacing * (len_children - 1) for w in self.children: shw = w.size_hint_x shh = w.size_hint_y if shw is None: minimum_size_x += w.width else: stretch_weight_x += shw if shh is None: minimum_size_y += w.height else: stretch_weight_y += shh if orientation == 'horizontal': x = padding_left stretch_space = max(0.0, selfw - minimum_size_x) for c in reversed(self.children): shw = c.size_hint_x shh = c.size_hint_y w = c.width h = c.height cx = selfx + x cy = selfy + padding_bottom if shw: w = stretch_space * shw / stretch_weight_x if shh: h = max(0, shh * (selfh - padding_y)) for key, value in c.pos_hint.items(): posy = value * (selfh - padding_y) if key == 'y': cy += posy elif key == 'top': cy += posy - h elif key == 'center_y': cy += posy - (h / 2.) c.x = cx c.y = cy c.width = w c.height = h x += w + spacing if orientation == 'vertical': y = padding_bottom stretch_space = max(0.0, selfh - minimum_size_y) for c in self.children: shw = c.size_hint_x shh = c.size_hint_y w = c.width h = c.height cx = selfx + padding_left cy = selfy + y if shh: h = stretch_space * shh / stretch_weight_y if shw: w = max(0, shw * (selfw - padding_x)) for key, value in c.pos_hint.items(): posx = value * (selfw - padding_x) if key == 'x': cx += posx elif key == 'right': cx += posx - w elif key == 'center_x': cx += posx - (w / 2.) c.x = cx c.y = cy c.width = w c.height = h y += h + spacing def add_widget(self, widget, index=0): widget.bind( pos_hint=self._trigger_layout) return super(BoxLayout, self).add_widget(widget, index) def remove_widget(self, widget): widget.unbind( pos_hint=self._trigger_layout) return super(BoxLayout, self).remove_widget(widget)