|
| 1 | +"""Example of creating a custom progress bar. |
| 2 | +
|
| 3 | +This example shows how to create a custom layout. |
| 4 | +
|
| 5 | +The layout will place the widgets in a rotating circle around the center of the screen. |
| 6 | +
|
| 7 | +If arcade and Python are properly installed, you can run this example with: |
| 8 | +python -m arcade.examples.gui.own_layout |
| 9 | +""" |
| 10 | + |
| 11 | +from __future__ import annotations |
| 12 | + |
| 13 | +from math import cos, sin |
| 14 | + |
| 15 | +import arcade |
| 16 | +from arcade.gui import UIAnchorLayout, UIFlatButton, UILayout, UIView, UIWidget |
| 17 | + |
| 18 | + |
| 19 | +class CircleLayout(UILayout): |
| 20 | + """A custom progress bar widget. |
| 21 | +
|
| 22 | + A UIAnchorLayout is a layout that arranges its children in a specific way. |
| 23 | + The actual bar is a UISpace that fills the parent widget from left to right. |
| 24 | + """ |
| 25 | + |
| 26 | + def __init__(self, size_hint=(1, 1), **kwargs): |
| 27 | + super().__init__(size_hint=size_hint, **kwargs) |
| 28 | + self._time = 0 # used for rotation |
| 29 | + |
| 30 | + def add(self, child: UIWidget, **kwargs) -> UIWidget: |
| 31 | + """Add a widget to the layout. |
| 32 | +
|
| 33 | + The widget is placed in a circle around the center of the screen. |
| 34 | + """ |
| 35 | + return super().add(child, **kwargs) |
| 36 | + |
| 37 | + def do_layout(self): |
| 38 | + """Layout the children in a circle around the center of the screen.""" |
| 39 | + if not self._children: |
| 40 | + return |
| 41 | + |
| 42 | + # handle the size hints of the children |
| 43 | + for child in self.children: |
| 44 | + self._resize_child(child) |
| 45 | + |
| 46 | + # calculate the radius, so that the children are placed inside the parent widget |
| 47 | + max_child_width = max(child.content_width for child in self.children) |
| 48 | + max_child_height = max(child.content_height for child in self.children) |
| 49 | + radius = ( |
| 50 | + min(self.content_width - max_child_width, self.content_height - max_child_height) / 2 |
| 51 | + ) |
| 52 | + |
| 53 | + for i, child in enumerate(self.children): |
| 54 | + angle = i / len(self.children) * 2 * 3.1415 |
| 55 | + # add rotation based on time |
| 56 | + angle += self._time * 0.08 |
| 57 | + center_x = self.center_x + radius * cos(angle) |
| 58 | + center_y = self.center_y + radius * sin(angle) |
| 59 | + |
| 60 | + new_rect = child.rect.align_center((center_x, center_y)) |
| 61 | + child.rect = new_rect |
| 62 | + |
| 63 | + def _resize_child(self, child: UIWidget): |
| 64 | + """Resizes the child based on the size_hint, size_hint_min, and size_hint_max.""" |
| 65 | + new_child_rect = child.rect |
| 66 | + |
| 67 | + sh_w, sh_h = child.size_hint or (None, None) |
| 68 | + shmn_w, shmn_h = child.size_hint_min or (None, None) |
| 69 | + shmx_w, shmx_h = child.size_hint_max or (None, None) |
| 70 | + |
| 71 | + if sh_w is not None: |
| 72 | + new_child_rect = new_child_rect.resize(width=self.content_width * sh_w) |
| 73 | + |
| 74 | + if shmn_w: |
| 75 | + new_child_rect = new_child_rect.min_size(width=shmn_w) |
| 76 | + if shmx_w: |
| 77 | + new_child_rect = new_child_rect.max_size(width=shmx_w) |
| 78 | + |
| 79 | + if sh_h is not None: |
| 80 | + new_child_rect = new_child_rect.resize(height=self.content_height * sh_h) |
| 81 | + |
| 82 | + if shmn_h: |
| 83 | + new_child_rect = new_child_rect.min_size(height=shmn_h) |
| 84 | + if shmx_h: |
| 85 | + new_child_rect = new_child_rect.max_size(height=shmx_h) |
| 86 | + |
| 87 | + child.rect = new_child_rect |
| 88 | + |
| 89 | + def on_update(self, dt): |
| 90 | + self._time += dt |
| 91 | + |
| 92 | + |
| 93 | +class MyView(UIView): |
| 94 | + def __init__(self): |
| 95 | + super().__init__() |
| 96 | + self.background_color = arcade.uicolor.BLUE_BELIZE_HOLE |
| 97 | + |
| 98 | + root = self.ui.add(UIAnchorLayout()) |
| 99 | + |
| 100 | + # Create a custom layout |
| 101 | + self.circle_layout = root.add(CircleLayout(size_hint=(0.8, 0.8))) |
| 102 | + |
| 103 | + # Add buttons to the layout |
| 104 | + for i in range(8): |
| 105 | + self.circle_layout.add( |
| 106 | + UIFlatButton( |
| 107 | + text=f"Button {i}", |
| 108 | + size_hint=(0.1, 0.1), |
| 109 | + ) |
| 110 | + ) |
| 111 | + |
| 112 | + |
| 113 | +if __name__ == "__main__": |
| 114 | + window = arcade.Window(title="GUI Example: CircleLayout") |
| 115 | + window.show_view(MyView()) |
| 116 | + arcade.run() |
0 commit comments