|
| 1 | +"""Show all arcade.uicolors in a grid. |
| 2 | +
|
| 3 | +Click on a color to select |
| 4 | +it and copy the arcade reference to the clipboard. |
| 5 | +
|
| 6 | +If arcade and Python are properly installed, you can run this example with: |
| 7 | +python -m arcade.examples.gui.5_uicolor_picker |
| 8 | +
|
| 9 | +""" |
| 10 | + |
| 11 | +from dataclasses import dataclass |
| 12 | + |
| 13 | +import arcade |
| 14 | +from arcade.gui import ( |
| 15 | + UIAnchorLayout, |
| 16 | + UIEvent, |
| 17 | + UIInteractiveWidget, |
| 18 | + UITextWidget, |
| 19 | +) |
| 20 | + |
| 21 | + |
| 22 | +@dataclass |
| 23 | +class ChooseColorEvent(UIEvent): |
| 24 | + """Custom event, which is dispatched when a color button is clicked.""" |
| 25 | + |
| 26 | + color_name: str |
| 27 | + color: arcade.color.Color |
| 28 | + |
| 29 | + |
| 30 | +class Toast(arcade.gui.UILabel): |
| 31 | + """Label which disappears after a certain time.""" |
| 32 | + |
| 33 | + def __init__(self, text: str, duration: float = 2.0, **kwargs): |
| 34 | + super().__init__(**kwargs) |
| 35 | + self.text = text |
| 36 | + self.duration = duration |
| 37 | + self.time = 0 |
| 38 | + |
| 39 | + def on_update(self, dt): |
| 40 | + self.time += dt |
| 41 | + |
| 42 | + if self.time > self.duration: |
| 43 | + self.parent.remove(self) |
| 44 | + |
| 45 | + |
| 46 | +class ColorButton(UITextWidget, UIInteractiveWidget): |
| 47 | + """Button which shows a color and color name and |
| 48 | + emits a ChooseColorEvent event when clicked.""" |
| 49 | + |
| 50 | + def __init__( |
| 51 | + self, |
| 52 | + color_name: str, |
| 53 | + color: arcade.color.Color, |
| 54 | + **kwargs, |
| 55 | + ): |
| 56 | + super().__init__(text=color_name, **kwargs) |
| 57 | + # set color and place text on the bottom |
| 58 | + self.with_background(color=color) |
| 59 | + self.place_text(anchor_y="bottom") |
| 60 | + |
| 61 | + # set font color based on background color |
| 62 | + f = 2 if color_name.startswith("DARK") else 0.5 |
| 63 | + self.ui_label.update_font( |
| 64 | + font_color=arcade.color.Color(int(color[0] * f), int(color[1] * f), int(color[2] * f)) |
| 65 | + ) |
| 66 | + |
| 67 | + # store color name and color for later reference |
| 68 | + self._color_name = color_name |
| 69 | + self._color = color |
| 70 | + |
| 71 | + # register custom event |
| 72 | + self.register_event_type("on_choose_color") |
| 73 | + |
| 74 | + def on_update(self, dt): |
| 75 | + """Update the button state. |
| 76 | +
|
| 77 | + UIInteractiveWidget provides properties like hovered and pressed, |
| 78 | + which can be used to highlight the button.""" |
| 79 | + if self.pressed: |
| 80 | + self.with_border(color=arcade.uicolor.WHITE_CLOUDS, width=3) |
| 81 | + elif self.hovered: |
| 82 | + self.with_border(color=arcade.uicolor.WHITE_CLOUDS, width=2) |
| 83 | + else: |
| 84 | + self.with_border(color=arcade.color.BLACK, width=1) |
| 85 | + |
| 86 | + def on_click(self, event) -> bool: |
| 87 | + """Emit a ChooseColorEvent event when clicked.""" |
| 88 | + self.dispatch_event( |
| 89 | + "on_choose_color", ChooseColorEvent(self, self._color_name, self._color) |
| 90 | + ) |
| 91 | + return True |
| 92 | + |
| 93 | + def on_choose_color(self, event: ChooseColorEvent): |
| 94 | + """ChooseColorEvent event handler, which can be overridden.""" |
| 95 | + pass |
| 96 | + |
| 97 | + |
| 98 | +class ColorView(arcade.gui.UIView): |
| 99 | + """Uses the arcade.gui.UIView which takes care about the UIManager setup.""" |
| 100 | + |
| 101 | + def __init__(self): |
| 102 | + super().__init__() |
| 103 | + # Create an anchor layout, which can be used to position widgets on screen |
| 104 | + self.root = self.add_widget(UIAnchorLayout()) |
| 105 | + |
| 106 | + # Define colors in grid order |
| 107 | + self.colors = { |
| 108 | + # row 0 |
| 109 | + "GREEN_TURQUOISE": arcade.uicolor.GREEN_TURQUOISE, |
| 110 | + "GREEN_EMERALD": arcade.uicolor.GREEN_EMERALD, |
| 111 | + "BLUE_PETER_RIVER": arcade.uicolor.BLUE_PETER_RIVER, |
| 112 | + "PURPLE_AMETHYST": arcade.uicolor.PURPLE_AMETHYST, |
| 113 | + "DARK_BLUE_WET_ASPHALT": arcade.uicolor.DARK_BLUE_WET_ASPHALT, |
| 114 | + # row 1 |
| 115 | + "GREEN_GREEN_SEA": arcade.uicolor.GREEN_GREEN_SEA, |
| 116 | + "GREEN_NEPHRITIS": arcade.uicolor.GREEN_NEPHRITIS, |
| 117 | + "BLUE_BELIZE_HOLE": arcade.uicolor.BLUE_BELIZE_HOLE, |
| 118 | + "PURPLE_WISTERIA": arcade.uicolor.PURPLE_WISTERIA, |
| 119 | + "DARK_BLUE_MIDNIGHT_BLUE": arcade.uicolor.DARK_BLUE_MIDNIGHT_BLUE, |
| 120 | + # row 2 |
| 121 | + "YELLOW_SUN_FLOWER": arcade.uicolor.YELLOW_SUN_FLOWER, |
| 122 | + "ORANGE_CARROT": arcade.uicolor.ORANGE_CARROT, |
| 123 | + "RED_ALIZARIN": arcade.uicolor.RED_ALIZARIN, |
| 124 | + "WHITE_CLOUDS": arcade.uicolor.WHITE_CLOUDS, |
| 125 | + "GRAY_CONCRETE": arcade.uicolor.GRAY_CONCRETE, |
| 126 | + # row 3 |
| 127 | + "YELLOW_ORANGE": arcade.uicolor.YELLOW_ORANGE, |
| 128 | + "ORANGE_PUMPKIN": arcade.uicolor.ORANGE_PUMPKIN, |
| 129 | + "RED_POMEGRANATE": arcade.uicolor.RED_POMEGRANATE, |
| 130 | + "WHITE_SILVER": arcade.uicolor.WHITE_SILVER, |
| 131 | + "GRAY_ASBESTOS": arcade.uicolor.GRAY_ASBESTOS, |
| 132 | + } |
| 133 | + |
| 134 | + # setup grid with colors |
| 135 | + self.grid = self.root.add( |
| 136 | + arcade.gui.UIGridLayout( |
| 137 | + column_count=5, |
| 138 | + row_count=4, |
| 139 | + size_hint=(1, 1), |
| 140 | + ) |
| 141 | + ) |
| 142 | + for i, (name, color) in enumerate(self.colors.items()): |
| 143 | + button = self.root.add( |
| 144 | + ColorButton( |
| 145 | + color_name=name, |
| 146 | + color=color, |
| 147 | + # giving width and height is a workaround for a bug in the grid layout |
| 148 | + # we would want to set size_hint=(1, 1) and let |
| 149 | + # the grid layout handle the size |
| 150 | + width=self.window.width // 5, |
| 151 | + height=self.window.height // 4, |
| 152 | + size_hint=None, |
| 153 | + ) |
| 154 | + ) |
| 155 | + self.grid.add(button, row=i // 5, column=i % 5) |
| 156 | + |
| 157 | + # connect event handler |
| 158 | + button.on_choose_color = self.on_color_button_choose_color |
| 159 | + |
| 160 | + # setup toasts (temporary messages) |
| 161 | + self.toasts = self.root.add( |
| 162 | + arcade.gui.UIBoxLayout(space_between=2), anchor_x="right", anchor_y="top" |
| 163 | + ) |
| 164 | + self.toasts.with_padding(all=10) |
| 165 | + |
| 166 | + def on_color_button_choose_color(self, event: ChooseColorEvent) -> bool: |
| 167 | + """Color button click event handler, which copies the color name to the clipboard. |
| 168 | +
|
| 169 | + And shows a temporary message.""" |
| 170 | + self.window.set_clipboard_text(f"arcade.uicolor.{event.color_name}") |
| 171 | + |
| 172 | + # prepare and show toast |
| 173 | + toast = Toast(f"Copied {event.color_name}", width=250, size_hint=(None, 0)) |
| 174 | + toast.update_font( |
| 175 | + font_color=arcade.uicolor.DARK_BLUE_MIDNIGHT_BLUE, |
| 176 | + font_size=9, |
| 177 | + bold=True, |
| 178 | + ) |
| 179 | + toast.with_background(color=arcade.uicolor.GREEN_EMERALD) |
| 180 | + toast.with_padding(all=10) |
| 181 | + |
| 182 | + self.toasts.add(toast) |
| 183 | + |
| 184 | + return True |
| 185 | + |
| 186 | + def on_draw_before_ui(self): |
| 187 | + # Add draw commands that should be below the UI |
| 188 | + pass |
| 189 | + |
| 190 | + def on_draw_after_ui(self): |
| 191 | + # Add draw commands that should be on top of the UI (uncommon) |
| 192 | + pass |
| 193 | + |
| 194 | + |
| 195 | +if __name__ == "__main__": |
| 196 | + window = arcade.Window(title="GUI Example: Color Picker") |
| 197 | + window.show_view(ColorView()) |
| 198 | + window.run() |
0 commit comments