GUI Layouts

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()