-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathradial_menu.py
More file actions
178 lines (143 loc) · 7.54 KB
/
radial_menu.py
File metadata and controls
178 lines (143 loc) · 7.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import tkinter as tk
import math
import win32api
import os
from PIL import Image, ImageTk
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame
class RadialMenu:
def __init__(self, parent_root, on_select_callback, accent_color="#3498db", hover_color="#2980b9", center_icon_path=None):
self.window = tk.Toplevel(parent_root)
self.window.overrideredirect(True)
self.window.attributes("-topmost", True)
self.window.attributes("-transparentcolor", "magenta")
self.size = 400
self.center = self.size / 2
self.radius_outer = 175
self.radius_inner = 35
self.accent_color = accent_color
self.hover_color = hover_color
self.canvas = tk.Canvas(self.window, width=self.size, height=self.size, bg="magenta", highlightthickness=0)
self.canvas.pack()
self.on_select_callback = on_select_callback
self.items = []
self.arcs = []
self.hovered_index = -1
self.is_open = False
self.image_cache = {}
self.base_volume = 0.5
self.current_name = None # NOUVEAU : Mémorise le nom du perso actif
try:
pygame.mixer.init()
self.mixer_active = True
hover_path = os.path.abspath("sounds/hover.wav")
click_path = os.path.abspath("sounds/click.wav")
self.sound_hover = pygame.mixer.Sound(hover_path) if os.path.exists(hover_path) else None
self.sound_click = pygame.mixer.Sound(click_path) if os.path.exists(click_path) else None
self.set_base_volume(0.5)
except Exception as e:
print(f"Erreur d'initialisation du son : {e}")
self.mixer_active = False
self.center_image = None
if center_icon_path and os.path.exists(center_icon_path):
try:
img = Image.open(center_icon_path).resize((40, 40), Image.Resampling.LANCZOS)
self.center_image = ImageTk.PhotoImage(img)
except Exception as e:
pass
self.window.withdraw()
def set_base_volume(self, volume):
self.base_volume = volume
if self.mixer_active:
if self.sound_hover: self.sound_hover.set_volume(0.3 * self.base_volume)
if self.sound_click: self.sound_click.set_volume(0.8 * self.base_volume)
def load_image(self, class_name):
# NOUVEAU : Cherche "Classe_retro.png" si le mode Rétro est activé
filename = f"{class_name}_retro" if getattr(self, 'is_retro', False) and class_name != "Inconnu" else class_name
if filename in self.image_cache: return self.image_cache[filename]
path = f"skin/{filename}.png"
if not os.path.exists(path): return None
try:
img = Image.open(path).resize((36, 36), Image.Resampling.LANCZOS)
photo = ImageTk.PhotoImage(img)
self.image_cache[filename] = photo
return photo
except Exception as e:
return None
def show(self, x, y, items, current_name=None, is_retro=False):
if not items: return
self.items = items
self.current_name = current_name
self.is_retro = is_retro # NOUVEAU : On mémorise si on est en mode Rétro
self.build_wheel()
pos_x = int(x - self.center)
pos_y = int(y - self.center)
self.window.geometry(f"+{pos_x}+{pos_y}")
self.window.deiconify()
self.is_open = True
self.hovered_index = -1
self.update_hover()
def hide(self):
if not self.is_open: return
self.is_open = False
self.window.withdraw()
if self.hovered_index != -1 and self.hovered_index < len(self.items):
if self.mixer_active and self.sound_click:
self.sound_click.play()
selected_name = self.items[self.hovered_index]['name']
self.on_select_callback(selected_name)
def build_wheel(self):
self.canvas.delete("all")
self.arcs.clear()
nb_items = len(self.items)
angle_per_item = 360 / nb_items
bbox = (self.center - self.radius_outer, self.center - self.radius_outer, self.center + self.radius_outer, self.center + self.radius_outer)
for i, item in enumerate(self.items):
# NOUVEAU : Dessin dans le SENS HORAIRE en partant de 12h (90 degrés)
start = 90 - (i + 1) * angle_per_item
# NOUVEAU : Application du bleu foncé si c'est la fenêtre active
is_active = (item['name'] == self.current_name)
base_color = "#1c456d" if is_active else "#2b2b2b"
arc = self.canvas.create_arc(bbox, start=start, extent=angle_per_item, fill=base_color, outline="#3d3d3d", width=2)
self.arcs.append(arc)
mid_angle = math.radians(start + (angle_per_item / 2))
base_x = self.center + math.cos(mid_angle) * (self.radius_outer * 0.65)
base_y = self.center - math.sin(mid_angle) * (self.radius_outer * 0.65)
img_y = base_y - 12
txt_y = base_y + 18
skin_img = self.load_image(item['classe'])
if skin_img: self.canvas.create_image(base_x, img_y, image=skin_img)
display_text = item['name'][:10]
self.canvas.create_text(base_x, txt_y, text=display_text, fill="#ecf0f1", font=("Segoe UI", 9, "bold"))
self.canvas.create_oval(self.center - self.radius_inner, self.center - self.radius_inner, self.center + self.radius_inner, self.center + self.radius_inner, fill="#1a1a1a", outline="#3d3d3d", width=2)
if self.center_image: self.canvas.create_image(self.center, self.center, image=self.center_image)
else: self.canvas.create_oval(self.center - 4, self.center - 4, self.center + 4, self.center + 4, fill=self.accent_color, outline="")
def update_hover(self):
if not self.is_open: return
x_mouse, y_mouse = win32api.GetCursorPos()
x_center = self.window.winfo_rootx() + self.center
y_center = self.window.winfo_rooty() + self.center
distance = math.hypot(x_mouse - x_center, y_mouse - y_center)
if distance < self.radius_inner or distance > self.radius_outer:
self.highlight_slice(-1)
else:
dx = x_mouse - x_center
dy = y_mouse - y_center # Modifié pour le calcul Horaire
# NOUVEAU : Calcul Trigonométrique Horaire à partir de Midi
cw_angle = (math.degrees(math.atan2(dx, -dy)) + 360) % 360
angle_per_item = 360 / len(self.items)
index = int(cw_angle // angle_per_item)
self.highlight_slice(index)
self.window.after(15, self.update_hover)
def highlight_slice(self, index):
if index == self.hovered_index: return
self.hovered_index = index
if index != -1 and self.mixer_active and self.sound_hover:
self.sound_hover.play()
for i, arc in enumerate(self.arcs):
# Conserve le bleu foncé si la souris s'en va de la case active
is_active = (getattr(self, 'current_name', None) == self.items[i]['name'])
base_color = "#1c456d" if is_active else "#2b2b2b"
color = self.hover_color if i == index else base_color
outline_color = self.accent_color if i == index else "#3d3d3d"
self.canvas.itemconfig(arc, fill=color, outline=outline_color)