Skip to content

SplitContainer

Usage

import toga

left_container = toga.Box()
right_container = toga.ScrollContainer()

split = toga.SplitContainer(content=[left_container, right_container])

Content can be specified when creating the widget, or after creation by assigning the content attribute. The direction of the split can also be configured, either at time of creation, or by setting the direction attribute:

import toga
from toga.constants import Direction

split = toga.SplitContainer(direction=Direction.HORIZONTAL)

left_container = toga.Box()
right_container = toga.ScrollContainer()

split.content = [left_container, right_container]

By default, the space of the SplitContainer will be evenly divided between the two panels. To specify an uneven split, you can provide a flex value when specifying content. In the following example, there will be a 60/40 split between the left and right panels.

import toga

split = toga.SplitContainer()
left_container = toga.Box()
right_container = toga.ScrollContainer()

split.content = [(left_container, 3), (right_container, 2)]

This only specifies the initial split; the split can be modified by the user once it is displayed.

Reference

Bases: Widget

Source code in core/src/toga/widgets/splitcontainer.py
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
class SplitContainer(Widget):
    HORIZONTAL = Direction.HORIZONTAL
    VERTICAL = Direction.VERTICAL
    _USE_DEBUG_BACKGROUND = True

    def __init__(
        self,
        id: str | None = None,
        style: StyleT | None = None,
        direction: Direction = Direction.VERTICAL,
        content: Sequence[SplitContainerContentT] | None = None,
        **kwargs,
    ):
        """Create a new SplitContainer.

        :param id: The ID for the widget.
        :param style: A style object. If no style is provided, a default style will be
            applied to the widget.
        :param direction: The direction in which the divider will be drawn. Either
            [`Direction.HORIZONTAL`][toga.constants.Direction.HORIZONTAL] or
            [`Direction.VERTICAL`][toga.constants.Direction.VERTICAL]; defaults to
            [`Direction.VERTICAL`][toga.constants.Direction.VERTICAL]
        :param content: Initial
            [SplitContainer content][toga.widgets.splitcontainer.SplitContainerContentT]
            of the container. Defaults to both panels being empty.
        :param kwargs: Initial style properties.
        """
        self._content: list[SplitContainerContentT] = [None, None]
        super().__init__(id, style, **kwargs)

        if content:
            self.content = content
        self.direction = direction

    def _create(self) -> Any:
        return self.factory.SplitContainer(interface=self)

    @property
    def enabled(self) -> bool:
        """Is the widget currently enabled? i.e., can the user interact with the widget?

        SplitContainer widgets cannot be disabled; this property will always return
        True; any attempt to modify it will be ignored.
        """
        return True

    @enabled.setter
    def enabled(self, value: object) -> None:
        pass

    def focus(self) -> None:
        """No-op; SplitContainer cannot accept input focus."""
        pass

    @property
    def content(self) -> list[SplitContainerContentT]:
        """The widgets displayed in the SplitContainer.

        This property accepts a sequence of exactly 2 elements, each of which can be
        either:

        * A [`Widget`][toga.Widget] to display in the panel.
        * `None`, to make the panel empty.
        * A tuple consisting of a Widget (or `None`) and the initial flex value to
          apply to that panel in the split, which must be greater than 0.

        If a flex value isn't specified, a value of 1 is assumed.

        When reading this property, only the widgets are returned, not the flex values.
        """
        return self._content

    @content.setter
    def content(self, content: Sequence[SplitContainerContentT]) -> None:
        for old_content in self._content:
            if old_content is not None:
                old_content.app = None
                old_content.window = None

        try:
            if len(content) != 2:
                raise TypeError()
        except TypeError as exc:
            raise ValueError(
                "SplitContainer content must be a sequence with exactly 2 elements"
            ) from exc

        _content = []
        flex = []
        for item in content:
            match item:
                case toga.Widget() | None as widget:
                    flex_value = 1
                case toga.Widget() | None as widget, int() as flex_value:
                    if flex_value <= 0:  # no-cover-if-lt-py311
                        raise ValueError(
                            "The flex value for an item in a SplitContainer must be >0"
                        )
                case _:
                    raise ValueError(
                        "An item in SplitContainer content must be a 2-tuple "
                        "containing the widget, and the flex weight to assign to that "
                        "widget."
                    )

            _content.append(widget)
            flex.append(flex_value)

            if widget:
                widget.app = self.app
                widget.window = self.window

        self._impl.set_content(
            [w._impl if w is not None else None for w in _content],
            flex,
        )
        self._content = list(_content)
        self.refresh()

    @Widget.app.setter
    def app(self, app: App | None) -> None:
        # Invoke the superclass property setter
        Widget.app.fset(self, app)

        # Also assign the app to the content in the container
        for content in self.content:
            if content:
                content.app = app

    @Widget.window.setter
    def window(self, window: Window | None) -> None:
        # Invoke the superclass property setter
        Widget.window.fset(self, window)

        # Also assign the window to the content in the container
        for content in self._content:
            if content:
                content.window = window

    @property
    def direction(self) -> Direction:
        """The direction of the split."""
        return self._impl.get_direction()

    @direction.setter
    def direction(self, value: object) -> None:
        self._impl.set_direction(value)
        self.refresh()

content property writable

The widgets displayed in the SplitContainer.

This property accepts a sequence of exactly 2 elements, each of which can be either:

  • A Widget to display in the panel.
  • None, to make the panel empty.
  • A tuple consisting of a Widget (or None) and the initial flex value to apply to that panel in the split, which must be greater than 0.

If a flex value isn't specified, a value of 1 is assumed.

When reading this property, only the widgets are returned, not the flex values.

direction property writable

The direction of the split.

enabled property writable

Is the widget currently enabled? i.e., can the user interact with the widget?

SplitContainer widgets cannot be disabled; this property will always return True; any attempt to modify it will be ignored.

__init__(id=None, style=None, direction=Direction.VERTICAL, content=None, **kwargs)

Create a new SplitContainer.

:param id: The ID for the widget. :param style: A style object. If no style is provided, a default style will be applied to the widget. :param direction: The direction in which the divider will be drawn. Either Direction.HORIZONTAL or Direction.VERTICAL; defaults to Direction.VERTICAL :param content: Initial SplitContainer content of the container. Defaults to both panels being empty. :param kwargs: Initial style properties.

Source code in core/src/toga/widgets/splitcontainer.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def __init__(
    self,
    id: str | None = None,
    style: StyleT | None = None,
    direction: Direction = Direction.VERTICAL,
    content: Sequence[SplitContainerContentT] | None = None,
    **kwargs,
):
    """Create a new SplitContainer.

    :param id: The ID for the widget.
    :param style: A style object. If no style is provided, a default style will be
        applied to the widget.
    :param direction: The direction in which the divider will be drawn. Either
        [`Direction.HORIZONTAL`][toga.constants.Direction.HORIZONTAL] or
        [`Direction.VERTICAL`][toga.constants.Direction.VERTICAL]; defaults to
        [`Direction.VERTICAL`][toga.constants.Direction.VERTICAL]
    :param content: Initial
        [SplitContainer content][toga.widgets.splitcontainer.SplitContainerContentT]
        of the container. Defaults to both panels being empty.
    :param kwargs: Initial style properties.
    """
    self._content: list[SplitContainerContentT] = [None, None]
    super().__init__(id, style, **kwargs)

    if content:
        self.content = content
    self.direction = direction

focus()

No-op; SplitContainer cannot accept input focus.

Source code in core/src/toga/widgets/splitcontainer.py
77
78
79
def focus(self) -> None:
    """No-op; SplitContainer cannot accept input focus."""
    pass

An item of content that can be added to a SplitContainer. This content can be:

  • a Widget; or
  • a 2-tuple, containing a Widget, and a 0-1 [float][] value describing the percentage of the available area should be taken up by the widget.