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