GUI Layouts

Screen shot of GUI Basic Setup
1_layouts.py
  1"""Demonstrates the use of layouts.
  2
  3If Arcade and Python are properly installed, you can run this example with:
  4python -m arcade.examples.gui.1_layouts
  5
  6Content:
  7- Create a view with a description of layouts
  8- Use UIAnchorLayout to position widgets relative to the screen edges
  9- Use UIBoxLayout to position widgets in a horizontal or vertical line
 10- Use UIGridLayout to position widgets in a grid
 11
 12"""
 13
 14from datetime import datetime
 15
 16import arcade
 17from arcade.gui import UIAnchorLayout, UIImage, UITextArea
 18
 19arcade.resources.load_kenney_fonts()
 20
 21DESCRIPTION = """How to place widgets on the screen?
 22
 23Widgets can be manually placed on the screen by using the `add` method of the UIManager
 24and setting the rect of a widget. This is useful for simple ui setups.
 25
 26This approach requires to manually calculate the position of the
 27widgets and handling screen resizes.
 28
 29Another approach is to use layouts. Layouts are containers that automatically
 30position widgets based on the layout rules. This example shows how to use
 31the UIAnchorLayout, UIBoxLayout, and UIGridLayout.
 32
 33UIAnchorLayout:
 34- UIAnchorLayout is a layout that positions widgets relative to the screen edges.
 35- Widgets can be anchored to the top, bottom, left, right, center, or any combination of these.
 36- Widgets can be aligned to the anchor position with a pixel offset.
 37
 38UIBoxLayout:
 39- UIBoxLayout is a layout that positions widgets in a horizontal or vertical line.
 40- Widgets can be aligned on the orthogonal axis.
 41- Widgets can have a space between them.
 42
 43UIGridLayout:
 44- UIGridLayout is a layout that positions widgets in a grid.
 45- Widgets can be placed in rows and columns.
 46- Widgets can have a vertical and horizontal spacing.
 47
 48The layouts calculate the size of the widgets based on the size_hint,
 49size_hint_min, and size_hint_max.
 50
 51- size_hint: A tuple of two floats that represent the relative size of the widget.
 52- size_hint_min: A tuple of two floats that represent the minimum size of the widget.
 53- size_hint_max: A tuple of two floats that represent the maximum size of the widget.
 54
 55Layouts only resize widgets if the size_hint is set for the resprecitve axis.
 56If the size_hint is not set, the widget will use its current size.
 57
 58Some widgets calculate their minimum size based on their content like UILabel
 59and layouts in general.
 60"""
 61
 62TEX_SCROLL_DOWN = arcade.load_texture(":resources:gui_basic_assets/scroll/indicator_down.png")
 63TEX_SCROLL_UP = arcade.load_texture(":resources:gui_basic_assets/scroll/indicator_up.png")
 64
 65
 66class ScrollableTextArea(UITextArea, UIAnchorLayout):
 67    """This widget is a text area that can be scrolled, like a UITextLayout, but shows indicator,
 68    that the text can be scrolled."""
 69
 70    def __init__(self, **kwargs):
 71        super().__init__(**kwargs)
 72
 73        indicator_size = 22
 74        self._down_indicator = UIImage(
 75            texture=TEX_SCROLL_DOWN, size_hint=None, width=indicator_size, height=indicator_size
 76        )
 77        self._down_indicator.visible = False
 78        self.add(self._down_indicator, anchor_x="right", anchor_y="bottom", align_x=-3)
 79
 80        self._up_indicator = UIImage(
 81            texture=TEX_SCROLL_UP, size_hint=None, width=indicator_size, height=indicator_size
 82        )
 83        self._up_indicator.visible = False
 84        self.add(self._up_indicator, anchor_x="right", anchor_y="top", align_x=-3)
 85
 86    def on_update(self, dt):
 87        self._up_indicator.visible = self.layout.view_y < 0
 88        self._down_indicator.visible = (
 89            abs(self.layout.view_y) < self.layout.content_height - self.layout.height
 90        )
 91
 92
 93class LayoutView(arcade.gui.UIView):
 94    """This view demonstrates the use of layouts."""
 95
 96    def __init__(self):
 97        super().__init__()
 98        self.background_color = arcade.uicolor.BLUE_PETER_RIVER
 99
100        # Create a anchor layout, which can be used to position widgets on screen
101        self.anchor = self.add_widget(UIAnchorLayout())
102
103        # Add describing text in center
104        text_area = ScrollableTextArea(
105            text=DESCRIPTION,
106            text_color=arcade.uicolor.WHITE_CLOUDS,
107            font_name=("Lato", "proxima-nova", "Helvetica Neue", "Arial", "sans-serif"),
108            font_size=12,
109            size_hint=(0.5, 0.8),
110        )
111        self.anchor.add(text_area, anchor_x="center_x", anchor_y="center_y")
112        text_area.with_border(color=arcade.uicolor.DARK_BLUE_MIDNIGHT_BLUE)
113        text_area.with_background(color=arcade.uicolor.DARK_BLUE_MIDNIGHT_BLUE.replace(a=125))
114        text_area.with_padding(left=5)
115
116        # add a grid layout with the window and grid size and grid position
117        self.grid = arcade.gui.UIGridLayout(
118            column_count=2,
119            row_count=2,
120            align_horizontal="left",
121        )
122        self.grid.with_background(color=arcade.uicolor.GRAY_ASBESTOS)
123        self.grid.with_border(color=arcade.uicolor.GRAY_CONCRETE)
124        self.grid.with_padding(all=10)
125        self.anchor.add(self.grid, anchor_x="left", anchor_y="top", align_x=10, align_y=-10)
126        self.grid.add(
127            arcade.gui.UILabel(text="Arcade: "),
128            row=0,
129            column=0,
130        )
131        self.grid.add(
132            arcade.gui.UILabel(text=arcade.VERSION),
133            row=0,
134            column=1,
135        )
136        self.grid.add(
137            arcade.gui.UILabel(text="Today: "),
138            row=1,
139            column=0,
140        )
141        self.grid.add(
142            arcade.gui.UILabel(text=datetime.utcnow().isoformat()[:10]),
143            row=1,
144            column=1,
145        )
146
147        # add a horizontal boxlayout with buttons
148        h_box = arcade.gui.UIBoxLayout(space_between=20, vertical=False)
149        self.anchor.add(h_box, anchor_x="center_x", anchor_y="bottom", align_y=5)
150
151        # Add a button to move the grid layout to top_right
152        # top left
153        move_left_button = arcade.gui.UIFlatButton(text="Top Left", width=150)
154        move_left_button.disabled = True
155        h_box.add(move_left_button)
156
157        @move_left_button.event("on_click")
158        def _(event):
159            self.anchor.remove(self.grid)
160            self.anchor.add(self.grid, anchor_x="left", anchor_y="top", align_x=10, align_y=-10)
161            move_left_button.disabled = True
162            move_right_button.disabled = False
163
164        move_right_button = arcade.gui.UIFlatButton(text="Top Right", width=150)
165        h_box.add(move_right_button)
166
167        @move_right_button.event("on_click")
168        def _(event):
169            self.anchor.remove(self.grid)
170            self.anchor.add(self.grid, anchor_x="right", anchor_y="top", align_x=-10, align_y=-10)
171            move_right_button.disabled = True
172            move_left_button.disabled = False
173
174    def on_draw_before_ui(self):
175        # Add draw commands that should be below the UI
176        pass
177
178    def on_draw_after_ui(self):
179        # Add draw commands that should be on top of the UI (uncommon)
180        pass
181
182
183def main():
184    window = arcade.Window(title="GUI Example: Layouts")
185    window.show_view(LayoutView())
186    window.run()
187
188
189if __name__ == '__main__':
190    main()