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