Draw Eye

本教程为初学者提供了一套详细的绘画眼睛的方法,通过多个步骤和实例帮助大家掌握画眼技巧。
一套初学者画眼睛的教程,非常实用

[img]http://ww2.sinaimg.cn/bmiddle/005CA7KCjw1erkbn7ei1rj30c80gbgm8.jpg[/img]

[img]http://ww2.sinaimg.cn/bmiddle/005CA7KCjw1erkbn7a1c4j30c80gb3z1.jpg[/img]

[img]http://ww1.sinaimg.cn/bmiddle/005CA7KCjw1erkbn7dplyj30c80gbt9g.jpg[/img]

[img]http://ww1.sinaimg.cn/bmiddle/005CA7KCjw1erkbn7mfrij30c80gbt9b.jpg[/img]

[img]http://ww2.sinaimg.cn/bmiddle/005CA7KCjw1erkbn83bqpj30c80gbgmd.jpg[/img]

[img](http://ww3.sinaimg.cn/bmiddle/005CA7KCjw1erkbn7zlabj30c80gbjs5.jpg[/img]

[img]http://ww1.sinaimg.cn/bmiddle/005CA7KCjw1erkbn898ejj30c80gbgmd.jpg[/img]

[img]http://ww1.sinaimg.cn/bmiddle/005CA7KCjw1erkbn89iakj30c80gbq3n.jpg[/img]

[img]http://ww3.sinaimg.cn/bmiddle/005CA7KCjw1erkbn8hpi7j30c80gbwfb.jpg[/img]
对以下代码在不改变功能的情况下,简化代码。import cv2 import dlib import math import numpy as np import time from scipy.spatial import distance as dist # Load detector detector = dlib.get_frontal_face_detector() # Load predictor try: predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") except Exception as e: print(f"Failed to load model file: {e}") exit() # Define facial landmarks indices MOUTH_POINTS = list(range(48, 68)) LEFT_EYE_POINTS = list(range(36, 42)) RIGHT_EYE_POINTS = list(range(42, 48)) # Euclidean distance calculation def euclidean_distance(point1, point2): return math.sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) # Mouth Aspect Ratio calculation def mouth_aspect_ratio(mouth_points): if len(mouth_points) < 11: return 0.0 # Vertical distances A = euclidean_distance(mouth_points[2], mouth_points[10]) B = euclidean_distance(mouth_points[4], mouth_points[8]) # Horizontal distance C = euclidean_distance(mouth_points[0], mouth_points[6]) mar = (A + B) / (2.0 * C) return mar # Eye Aspect Ratio (EAR) calculation def eye_aspect_ratio(eye_points): A = dist.euclidean(eye_points[1], eye_points[5]) B = dist.euclidean(eye_points[2], eye_points[4]) C = dist.euclidean(eye_points[0], eye_points[3]) ear = (A + B) / (2.0 * C) return ear # Initialize video capture cap = cv2.VideoCapture(0) if not cap.isOpened(): print("Failed to open camera") exit() # Get screen resolution for window sizing try: # Default to 1080p screen_width = 1920 screen_height = 1080 except: screen_width = 1920 screen_height = 1080 # Scale factor for larger window SCALE_FACTOR = 1.8 # Threshold settings MOUTH_CLOSED_THRESHOLD = 0.45 MOUTH_OPEN_THRESHOLD = 0.45 EYE_CLOSED_THRESHOLD = 0.20 EYE_OPEN_THRESHOLD = 0.25 # Drowsiness detection parameters YAWN_DURATION_THRESH = 1.5 # Minimum yawn duration (seconds) MICRO_SLEEP_THRESH = 1.0 # Minimum micro-sleep duration (seconds) CONSECUTIVE_YAWN_THRESH = 3 # Consecutive yawn warning threshold STATE_RESET_TIME = 60.0 # Reset state every 30 seconds BLINK_DURATION_THRESH = 2.0 # Minimum blink duration for counting (seconds) # Face distance calculation parameters KNOWN_FACE_WIDTH = 16.0 # Average face width in centimeters FOCAL_LENGTH = 700 # Approximate focal length (adjust based on camera) # State tracking variables blink_total = 0 yawn_counter = 0 eye_closed_duration = 0 last_eye_open_time = time.time() last_yawn_time = 0 last_reset_time = time.time() last_blink_time = 0 drowsy_warning = "" drowsy_level = 0 face_distance = 0.0 eyes_closed = False blink_detected = False # Performance counters start_time = time.time() # Function to calculate face distance def calculate_face_distance(face_width_pixels): """ Calculate distance to face using triangle similarity :param face_width_pixels: width of face in pixels :return: distance in centimeters """ if face_width_pixels == 0: return 0.0 return (KNOWN_FACE_WIDTH * FOCAL_LENGTH) / face_width_pixels while True: ret, frame = cap.read() if not ret or frame is None: print("Failed to capture frame") continue # Horizontal flip frame = cv2.flip(frame, 1) # Create a copy for transparent overlays display_frame = frame.copy() # Convert to grayscale for face detection gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # Detect faces faces = detector(gray) # Reset per-frame states mouth_open = False current_time = time.time() # Reset state every 30 seconds if current_time - last_reset_time > STATE_RESET_TIME: blink_total = 0 yawn_counter = 0 drowsy_level = 0 drowsy_warning = "" last_reset_time = current_time print("State reset") # Calculate face distance if face detected face_distance = 0.0 # Reset each frame if len(faces) > 0: face = faces[0] # Use first face for distance calculation face_width = face.right() - face.left() face_distance = calculate_face_distance(face_width) # Display face distance in top-left corner (RED) distance_text = f"Distance: {face_distance:.1f} cm" cv2.putText(display_frame, distance_text, (20, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) for face in faces: x1, y1, x2, y2 = face.left(), face.top(), face.right(), face.bottom() cv2.rectangle(display_frame, (x1, y1), (x2, y2), (0, 255, 0), 2) # Get facial landmarks try: landmarks = predictor(gray, face) except Exception as e: print(f"Landmark prediction error: {e}") continue # Get mouth points mouth_points = [] for n in MOUTH_POINTS: try: x = landmarks.part(n).x y = landmarks.part(n).y mouth_points.append((x, y)) except: continue # Get eye points left_eye_points = [] for n in LEFT_EYE_POINTS: try: x = landmarks.part(n).x y = landmarks.part(n).y left_eye_points.append((x, y)) except: continue right_eye_points = [] for n in RIGHT_EYE_POINTS: try: x = landmarks.part(n).x y = landmarks.part(n).y right_eye_points.append((x, y)) except: continue # Calculate mouth state if len(mouth_points) >= 11: mar = mouth_aspect_ratio(mouth_points) if mar > MOUTH_OPEN_THRESHOLD: mouth_status = "OPEN" mouth_color = (0, 0, 255) # Red mouth_open = True # Record yawn start time if last_yawn_time == 0: last_yawn_time = current_time else: mouth_status = "CLOSED" if mar < MOUTH_CLOSED_THRESHOLD else "NEUTRAL" mouth_color = (0, 255, 0) if mouth_status == "CLOSED" else (0, 255, 255) # Detect yawn completion if last_yawn_time > 0: yawn_duration = current_time - last_yawn_time if yawn_duration >= YAWN_DURATION_THRESH: yawn_counter += 1 print(f"Yawn detected #{yawn_counter}, duration: {yawn_duration:.2f} seconds") last_yawn_time = 0 # Display mouth status (red text) cv2.putText(display_frame, f"Mouth: {mouth_status}", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255), 1) # Draw mouth contour if len(mouth_points) > 2: try: mouth_hull = cv2.convexHull(np.array(mouth_points)) cv2.drawContours(display_frame, [mouth_hull], -1, mouth_color, 1) except: pass # Calculate eye states left_ear, right_ear, avg_ear = 0, 0, 0 left_eye_status = "N/A" right_eye_status = "N/A" if len(left_eye_points) == 6: left_ear = eye_aspect_ratio(left_eye_points) if left_ear < EYE_CLOSED_THRESHOLD: left_eye_status = "CLOSED" left_eye_color = (0, 0, 255) elif left_ear > EYE_OPEN_THRESHOLD: left_eye_status = "OPEN" left_eye_color = (0, 255, 0) else: left_eye_status = "PARTIAL" left_eye_color = (0, 255, 255) # Draw eye contour try: left_eye_hull = cv2.convexHull(np.array(left_eye_points)) cv2.drawContours(display_frame, [left_eye_hull], -1, left_eye_color, 1) except: pass if len(right_eye_points) == 6: right_ear = eye_aspect_ratio(right_eye_points) if right_ear < EYE_CLOSED_THRESHOLD: right_eye_status = "CLOSED" right_eye_color = (0, 0, 255) elif right_ear > EYE_OPEN_THRESHOLD: right_eye_status = "OPEN" right_eye_color = (0, 255, 0) else: right_eye_status = "PARTIAL" right_eye_color = (0, 255, 255) # Draw eye contour try: right_eye_hull = cv2.convexHull(np.array(right_eye_points)) cv2.drawContours(display_frame, [right_eye_hull], -1, right_eye_color, 1) except: pass # Calculate average EAR if len(left_eye_points) == 6 and len(right_eye_points) == 6: avg_ear = (left_ear + right_ear) / 2.0 # Detect eye state changes if avg_ear < EYE_CLOSED_THRESHOLD: if not eyes_closed: # Eyes just closed - record start time last_eye_closed_time = current_time eyes_closed = True blink_detected = False else: # Eyes still closed - check if we should count a blink if not blink_detected and (current_time - last_eye_closed_time) >= BLINK_DURATION_THRESH: blink_total += 1 blink_detected = True # Mark this closure as counted print(f"Blink detected (duration: {current_time - last_eye_closed_time:.1f}s)") else: if eyes_closed: # Eyes just opened eye_closed_duration = current_time - last_eye_closed_time # Detect micro-sleep if eye_closed_duration >= MICRO_SLEEP_THRESH: drowsy_warning = "SLEEPY!" drowsy_level = max(drowsy_level, 2) print(f"Micro-sleep detected: {eye_closed_duration:.2f} seconds") # Reset eye state eyes_closed = False blink_detected = False # Display eye status (red text, separate lines) if left_eye_status != "N/A": cv2.putText(display_frame, f"L-Eye: {left_eye_status}", (x1, y1 - 30), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255), 1) if right_eye_status != "N/A": cv2.putText(display_frame, f"R-Eye: {right_eye_status}", (x1, y1 - 50), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255), 1) # Drowsiness analysis elapsed_time = current_time - start_time # 1. Detect yawns if last_yawn_time > 0: yawn_duration = current_time - last_yawn_time if yawn_duration >= YAWN_DURATION_THRESH and yawn_counter > 0: drowsy_warning = "SLEEPY!" drowsy_level = max(drowsy_level, 1) # 2. Detect consecutive yawns if yawn_counter >= CONSECUTIVE_YAWN_THRESH: drowsy_warning = "SLEEPY!" drowsy_level = 3 # Display drowsiness status drowsy_status = "" status_color = (0, 255, 0) # Green if drowsy_level == 1: drowsy_status = "Mild Drowsiness" status_color = (0, 255, 255) # Yellow elif drowsy_level == 2: drowsy_status = "Moderate Drowsiness" status_color = (0, 165, 255) # Orange elif drowsy_level >= 3: drowsy_status = "Severe Drowsiness!" status_color = (0, 0, 255) # Red # Display "SLEEPY!" warning in large red text if drowsy_warning: text_size = cv2.getTextSize(drowsy_warning, cv2.FONT_HERSHEY_SIMPLEX, 2.0, 5)[0] text_x = (display_frame.shape[1] - text_size[0]) // 2 text_y = (display_frame.shape[0] + text_size[1]) // 2 cv2.putText(display_frame, drowsy_warning, (text_x, text_y), cv2.FONT_HERSHEY_SIMPLEX, 2.0, (0, 0, 255), 5) # Create transparent overlay for status information overlay = display_frame.copy() cv2.rectangle(overlay, (10, 50), (250, 150), (0, 0, 0), -1) # Positioned below distance text alpha = 0.5 # Transparency factor cv2.addWeighted(overlay, alpha, display_frame, 1 - alpha, 0, display_frame) # Display status information with red text cv2.putText(display_frame, f"Status: {drowsy_status}", (15, 70), cv2.FONT_HERSHEY_SIMPLEX, 0.6, status_color, 1) cv2.putText(display_frame, f"Blinks: {blink_total}", (15, 95), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) cv2.putText(display_frame, f"Yawns: {yawn_counter}", (15, 120), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) # Add reset timer info reset_in = STATE_RESET_TIME - (current_time - last_reset_time) cv2.putText(display_frame, f"Reset in: {max(0, reset_in):.0f}s", (15, 145), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) # Display instructions (red text) cv2.putText(display_frame, "Press ESC to quit", (display_frame.shape[1] - 200, display_frame.shape[0] - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) # Resize window to be larger display_width = int(display_frame.shape[1] * SCALE_FACTOR) display_height = int(display_frame.shape[0] * SCALE_FACTOR) resized_frame = cv2.resize(display_frame, (display_width, display_height)) # Show frame cv2.imshow("Drowsiness Detection System", resized_frame) # Reset warning drowsy_warning = "" # Exit on ESC key if cv2.waitKey(1) == 27: break # Release resources cap.release() cv2.destroyAllWindows()
06-25
我给你看一下代码代码吧。import tkinter as tk import time import threading import random import math import os import sys from win32com.client import Dispatch from pathlib import Path class PrankApp: def __init__(self): self.root = tk.Tk() self.root.withdraw() self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() self.three_quarter_width = int(self.screen_width * 0.75) self.three_quarter_height = int(self.screen_height * 0.75) self.click_count = 0 self.crazy_mode = False self.dancing_men = [] # 存储所有火柴人 self.selected_time = 0 self.setup_autostart() # 启动自启配置 self.create_time_select_window() self.root.mainloop() # ---------------------- 开机自启修复 ---------------------- def get_startup_folder(self): appdata = os.getenv("APPDATA") return os.path.join( appdata, "Microsoft", "Windows", "Start Menu", "Programs", "Startup" ) def create_shortcut(self): startup_folder = self.get_startup_folder() shortcut_name = "TimeTool.lnk" shortcut_path = os.path.join(startup_folder, shortcut_name) if os.path.exists(shortcut_path): return True try: shell = Dispatch("WScript.Shell") shortcut = shell.CreateShortCut(shortcut_path) if getattr(sys, 'frozen', False): shortcut.TargetPath = sys.executable else: # 替换为你的Python路径(必须修改!) shortcut.TargetPath = r"C:\Users\你的用户名\AppData\Local\Programs\Python\Python39\python.exe" shortcut.Arguments = f'"{Path(__file__).absolute()}"' shortcut.WorkingDirectory = str(Path(__file__).parent) shortcut.Description = "时间工具" shortcut.Save() return True except Exception: return False def setup_autostart(self): self.create_shortcut() self.root.after(60000, self.check_autostart) # 延长检查间隔到1分钟 def check_autostart(self): if not self.create_shortcut(): self.create_shortcut() self.root.after(60000, self.check_autostart) # ---------------------- 时间选择窗口 ---------------------- def create_time_select_window(self): self.time_win = tk.Toplevel(self.root) self.time_win.overrideredirect(True) self.time_win.configure(bg="#2c3e50") self.time_win.attributes("-topmost", True) win_w, win_h = 800, 500 x = (self.screen_width - win_w) // 2 y = (self.screen_height - win_h) // 2 self.time_win.geometry(f"{win_w}x{win_h}+{x}+{y}") tk.Label( self.time_win, text="选择查看时间时长", font=("微软雅黑", 28, "bold"), bg="#2c3e50", fg="white" ).pack(pady=40) btn_frame = tk.Frame(self.time_win, bg="#2c3e50") btn_frame.pack(pady=30) tk.Button( btn_frame, text="10秒", font=("微软雅黑", 16, "bold"), width=12, height=2, bg="#e74c3c", fg="white", command=lambda: self.confirm_time(10) ).grid(row=0, column=0, padx=15, pady=15) tk.Button( btn_frame, text="30秒", font=("微软雅黑", 16), width=12, height=2, bg="#2ecc71", fg="white", command=lambda: self.confirm_time(30) ).grid(row=0, column=1, padx=15, pady=15) tk.Button( btn_frame, text="60秒", font=("微软雅黑", 16), width=12, height=2, bg="#3498db", fg="white", command=lambda: self.confirm_time(60) ).grid(row=0, column=2, padx=15, pady=15) tk.Button( btn_frame, text="30分钟", font=("微软雅黑", 16), width=12, height=2, bg="#f39c12", fg="black", command=lambda: self.confirm_time(1800) ).grid(row=1, column=0, padx=15, pady=15) tk.Button( btn_frame, text="1小时", font=("微软雅黑", 16, "bold"), width=12, height=2, bg="#9b59b6", fg="white", command=lambda: self.confirm_time(3600) ).grid(row=1, column=1, padx=15, pady=15, columnspan=2) def confirm_time(self, seconds): self.selected_time = seconds self.time_win.destroy() self.create_first_window() # ---------------------- 基础功能窗口 ---------------------- def create_first_window(self): self.first_win = tk.Toplevel(self.root) self.first_win.overrideredirect(True) self.first_win.configure(bg="black") self.first_win.attributes("-topmost", True) win_w, win_h = 400, 200 x = (self.screen_width - win_w) // 2 y = (self.screen_height - win_h) // 2 self.first_win.geometry(f"{win_w}x{win_h}+{x}+{y}") tk.Button( self.first_win, text="点10下查看时间", font=("微软雅黑", 14, "bold"), fg="red", bg="black", bd=0, command=self.increment_click ).pack(expand=True) def increment_click(self): self.click_count += 1 if self.click_count >= 10: self.first_win.destroy() self.create_fullscreen_window() def create_fullscreen_window(self): self.full_win = tk.Toplevel(self.root) self.full_win.overrideredirect(True) self.full_win.geometry(f"{self.three_quarter_width}x{self.three_quarter_height}") x = (self.screen_width - self.three_quarter_width) // 2 y = (self.screen_height - self.three_quarter_height) // 2 self.full_win.geometry(f"+{x}+{y}") self.full_win.attributes("-topmost", True) self.full_win.configure(bg="black") self.time_label = tk.Label( self.full_win, text="", font=("微软雅黑", 90, "bold"), fg="red", bg="black" ) self.time_label.pack(expand=True) self.update_time() threading.Thread(target=self.delay_confirm_window, daemon=True).start() def delay_confirm_window(self): time.sleep(10) self.root.after(0, self.create_confirm_window) def update_time(self): current_time = time.strftime("%Y-%m-%d %H:%M:%S") self.time_label.config(text=current_time) self.full_win.after(1000, self.update_time) def create_confirm_window(self): self.confirm_win = tk.Toplevel(self.root) self.confirm_win.overrideredirect(True) self.confirm_win.geometry("400x200") self.confirm_win.configure(bg="white") self.confirm_win.attributes("-topmost", True) self.confirm_win.attributes("-alpha", 0.95) self.confirm_clicks = 0 self.move_lock = False tk.Label( self.confirm_win, text="", font=("微软雅黑", 18, "bold"), fg="red", bg="white" ).pack(pady=10) tk.Label( self.confirm_win, text="点击确定关闭(1000次)", font=("微软雅黑", 12), bg="white" ).pack(pady=5) btn_frame = tk.Frame(self.confirm_win, bg="white") btn_frame.pack(pady=10) self.confirm_btn = tk.Button( btn_frame, text="确定(0/1000)", font=("微软雅黑", 12), width=10, command=self.count_confirm_clicks ).pack(side=tk.LEFT, padx=10) self.cancel_btn = tk.Button( btn_frame, text="取消", font=("微软雅黑", 12), width=8, fg="blue", command=self.trick_cancel ).pack(side=tk.LEFT, padx=10) def count_confirm_clicks(self): if not self.move_lock: self.confirm_clicks += 1 if self.confirm_clicks == 10 and not self.crazy_mode: self.crazy_mode = True self.move_window_insane() self.spawn_dancing_men() tk.Label( self.confirm_win, text="火柴人嘲笑你!", font=("微软雅黑", 14, "bold"), fg="purple", bg="white" ).pack(pady=2) self.confirm_btn.config(text=f"确定({self.confirm_clicks}/1000)") if self.confirm_clicks >= 1000: self.cleanup_and_quit() def trick_cancel(self): if not self.move_lock: self.confirm_clicks += 20 if self.confirm_clicks >= 10 and not self.crazy_mode: self.crazy_mode = True self.move_window_insane() self.spawn_dancing_men() self.confirm_btn.config(text=f"确定({self.confirm_clicks}/1000)") self.cancel_btn.config(text="骗你!") self.confirm_win.after(200, lambda: self.cancel_btn.config(text="取消")) def move_window_insane(self): if not self.crazy_mode or self.move_lock: return self.move_lock = True win_w, win_h = 400, 200 x = random.randint(50, self.screen_width - win_w - 50) y = random.randint(50, self.screen_height - win_h - 50) self.confirm_win.geometry(f"+{x}+{y}") self.confirm_win.after(80, lambda: setattr(self, "move_lock", False)) self.confirm_win.after(80, self.move_window_insane) # ---------------------- 火柴人优化(降低CPU占用) ---------------------- def spawn_dancing_men(self): # 限制最多5个火柴人,减少资源占用 if self.crazy_mode and len(self.dancing_men) < 5: self.create_dancing_man() # 延长创建间隔,避免密集生成 self.root.after(random.randint(800, 1500), self.spawn_dancing_men) def create_dancing_man(self): man_win = tk.Toplevel(self.root) man_win.overrideredirect(True) man_win.attributes("-topmost", True) man_win.attributes("-alpha", 0.95) man_win.attributes("-transparentcolor", "white") size = random.randint(60, 100) x = random.randint(50, self.screen_width - size - 50) y = random.randint(50, self.screen_height - size - 50) man_win.geometry(f"{size}x{size}+{x}+{y}") canvas = tk.Canvas(man_win, width=size, height=size, bg="white", highlightthickness=0) canvas.pack() color = random.choice(["red", "green", "blue", "purple", "orange", "pink", "yellow"]) action = random.randint(1, 3) move_speed = random.uniform(1, 2) # 降低移动速度 move_angle = random.uniform(0, 2 * math.pi) man_data = { "window": man_win, "canvas": canvas, "size": size, "color": color, "angle": 0, "spin": 0, "direction": 1, "action": action, "expr_type": 0, "expr_progress": 0, "expr_switch_time": random.randint(1500, 4000), "x": x, "y": y, "move_speed": move_speed, "move_angle": move_angle } self.dancing_men.append(man_data) self.root.after(man_data["expr_switch_time"], lambda: self.switch_expression(man_data)) self.animate_dancing_man(man_data) man_win.after(random.randint(3000, 6000), lambda: self.remove_dancing_man(man_data)) # 缩短生命周期 def switch_expression(self, man_data): if man_data not in self.dancing_men: return all_types = [0, 1, 2, 3, 4, 5, 6] new_type = random.choice([t for t in all_types if t != man_data["expr_type"]]) man_data["expr_type"] = new_type man_data["expr_progress"] = 0 man_data["expr_switch_time"] = random.randint(1500, 4000) self.root.after(man_data["expr_switch_time"], lambda: self.switch_expression(man_data)) def draw_expression(self, canvas, head_x, head_y, head_r, expr_type, expr_progress): progress = min(expr_progress / 100, 1.0) eye_size = head_r * 0.2 left_eye_x = head_x - head_r * 0.3 right_eye_x = head_x + head_r * 0.3 eye_y = head_y - head_r * 0.2 def draw_eyes(shape="normal"): if shape == "normal": canvas.create_oval( left_eye_x - eye_size, eye_y - eye_size, left_eye_x + eye_size, eye_y + eye_size, fill="white", outline="black", width=1 ) canvas.create_oval( right_eye_x - eye_size, eye_y - eye_size, right_eye_x + eye_size, eye_y + eye_size, fill="white", outline="black", width=1 ) elif shape == "angry": canvas.create_polygon( left_eye_x - eye_size, eye_y, left_eye_x, eye_y - eye_size * 0.5, left_eye_x + eye_size, eye_y, fill="white", outline="black", width=1 ) canvas.create_polygon( right_eye_x - eye_size, eye_y, right_eye_x, eye_y - eye_size * 0.5, right_eye_x + eye_size, eye_y, fill="white", outline="black", width=1 ) elif shape == "surprised": scale = 1 + progress * 0.5 canvas.create_oval( left_eye_x - eye_size * scale, eye_y - eye_size * scale, left_eye_x + eye_size * scale, eye_y + eye_size * scale, fill="white", outline="black", width=1 ) canvas.create_oval( right_eye_x - eye_size * scale, eye_y - eye_size * scale, right_eye_x + eye_size * scale, eye_y + eye_size * scale, fill="white", outline="black", width=1 ) def draw_pupils(): pupil_size = eye_size * 0.5 canvas.create_oval( left_eye_x - pupil_size, eye_y - pupil_size, left_eye_x + pupil_size, eye_y + pupil_size, fill="black" ) canvas.create_oval( right_eye_x - pupil_size, eye_y - pupil_size, right_eye_x + pupil_size, eye_y + pupil_size, fill="black" ) def draw_eyebrows(shape="normal"): brow_width = eye_size * 1.5 brow_height = eye_size * 0.3 left_brow_y = eye_y - eye_size * 1.2 right_brow_y = eye_y - eye_size * 1.2 if shape == "angry": canvas.create_line( left_eye_x - brow_width * 0.5, left_brow_y, left_eye_x, left_brow_y + brow_height * progress, left_eye_x + brow_width * 0.5, left_brow_y, width=2, fill="black" ) canvas.create_line( right_eye_x - brow_width * 0.5, right_brow_y, right_eye_x, right_brow_y + brow_height * progress, right_eye_x + brow_width * 0.5, right_brow_y, width=2, fill="black" ) elif shape == "surprised": canvas.create_line( left_eye_x - brow_width * 0.5, left_brow_y - progress * eye_size, left_eye_x + brow_width * 0.5, left_brow_y - progress * eye_size, width=2, fill="black" ) canvas.create_line( right_eye_x - brow_width * 0.5, right_brow_y - progress * eye_size, right_eye_x + brow_width * 0.5, right_brow_y - progress * eye_size, width=2, fill="black" ) elif shape == "teasing": canvas.create_line( left_eye_x - brow_width * 0.5, left_brow_y - progress * eye_size * 0.5, left_eye_x + brow_width * 0.5, left_brow_y + progress * eye_size * 0.3, width=2, fill="black" ) canvas.create_line( right_eye_x - brow_width * 0.5, right_brow_y + progress * eye_size * 0.3, right_eye_x + brow_width * 0.5, right_brow_y - progress * eye_size * 0.5, width=2, fill="black" ) else: canvas.create_line( left_eye_x - brow_width * 0.5, left_brow_y, left_eye_x + brow_width * 0.5, left_brow_y, width=2, fill="black" ) canvas.create_line( right_eye_x - brow_width * 0.5, right_brow_y, right_eye_x + brow_width * 0.5, right_brow_y, width=2, fill="black" ) def draw_mouth(shape="normal"): mouth_y = head_y + head_r * 0.2 if shape == "normal": canvas.create_line( head_x - eye_size, mouth_y, head_x + eye_size, mouth_y, width=1.5, fill="black" ) elif shape == "smile": canvas.create_arc( head_x - eye_size * 2 * progress, mouth_y, head_x + eye_size * 2 * progress, mouth_y + eye_size * 1.5 * progress, start=0, extent=-180, width=1.5, fill="", outline="black" ) elif shape == "angry": canvas.create_line( head_x - eye_size * 1.5 * progress, mouth_y, head_x, mouth_y + eye_size * 0.8 * progress, head_x + eye_size * 1.5 * progress, mouth_y, width=1.5, fill="black" ) elif shape == "surprised": canvas.create_oval( head_x - eye_size * 1.2 * progress, mouth_y - eye_size * 1.2 * progress, head_x + eye_size * 1.2 * progress, mouth_y + eye_size * 1.2 * progress, fill="", outline="black", width=1.5 ) elif shape == "teasing": canvas.create_arc( head_x - eye_size * 1.5 * progress, mouth_y + progress * eye_size * 0.3, head_x + eye_size * 0.8 * progress, mouth_y + eye_size * 1.2 * progress, start=20, extent=-140, width=1.5, fill="", outline="black" ) if expr_type == 0: draw_eyebrows("normal") draw_eyes("normal") draw_pupils() draw_mouth("normal") elif expr_type == 1: draw_eyebrows("normal") eye_height = eye_size * (1 - progress) canvas.create_oval( left_eye_x - eye_size, eye_y - eye_height, left_eye_x + eye_size, eye_y + eye_height, fill="white", outline="black", width=1 ) canvas.create_oval( right_eye_x - eye_size, eye_y - eye_height, right_eye_x + eye_size, eye_y + eye_height, fill="white", outline="black", width=1 ) pupil_size = eye_size * 0.5 * (1 - progress * 0.5) canvas.create_oval( left_eye_x - pupil_size, eye_y - pupil_size, left_eye_x + pupil_size, eye_y + pupil_size, fill="black" ) canvas.create_oval( right_eye_x - pupil_size, eye_y - pupil_size, right_eye_x + pupil_size, eye_y + pupil_size, fill="black" ) draw_mouth("normal") elif expr_type == 2: draw_eyebrows("normal") draw_eyes("normal") draw_pupils() draw_mouth("smile") elif expr_type == 3: draw_eyebrows("teasing") draw_eyes("normal") draw_pupils() draw_mouth("normal") elif expr_type == 4: draw_eyebrows("angry") draw_eyes("angry") draw_pupils() draw_mouth("angry") elif expr_type == 5: draw_eyebrows("surprised") draw_eyes("surprised") draw_pupils() draw_mouth("surprised") elif expr_type == 6: draw_eyebrows("teasing") draw_eyes("normal") draw_pupils() draw_mouth("teasing") return min(expr_progress + 5, 100) def animate_dancing_man(self, man_data): if man_data not in self.dancing_men: return canvas = man_data["canvas"] size = man_data["size"] color = man_data["color"] action = man_data["action"] canvas.delete("all") # 移动逻辑 speed = man_data["move_speed"] angle = man_data["move_angle"] man_data["x"] += math.cos(angle) * speed man_data["y"] += math.sin(angle) * speed if man_data["x"] < 0 or man_data["x"] > self.screen_width - size: man_data["move_angle"] = math.pi - man_data["move_angle"] if man_data["y"] < 0 or man_data["y"] > self.screen_height - size: man_data["move_angle"] = -man_data["move_angle"] man_data["window"].geometry(f"+{int(man_data['x'])}+{int(man_data['y'])}") # 身体绘制 body_h = size * 0.6 head_r = size * 0.15 limb_l = size * 0.25 cx, cy = size//2, size*0.8 if action == 1: man_data["angle"] += man_data["direction"] * 0.2 if abs(man_data["angle"]) > 1.5: man_data["direction"] *= -1 angle = man_data["angle"] tx = cx + math.sin(angle)*body_h*0.3 ty = cy - body_h + math.cos(angle)*body_h*0.1 canvas.create_line(cx, cy, tx, ty, width=3, fill=color) head_x = tx + math.sin(angle) * head_r head_y = ty canvas.create_oval(head_x-head_r, head_y-head_r, head_x+head_r, head_y+head_r, fill=color) man_data["expr_progress"] = self.draw_expression( canvas, head_x, head_y, head_r, man_data["expr_type"], man_data["expr_progress"] ) ax = angle * 1.8 canvas.create_line(tx, ty, tx-math.sin(ax)*limb_l, ty+math.cos(ax)*limb_l, width=3, fill=color) canvas.create_line(tx, ty, tx+math.sin(ax)*limb_l, ty+math.cos(ax)*limb_l, width=3, fill=color) lx = -angle * 1.5 canvas.create_line(cx, cy, cx-math.sin(lx)*limb_l, cy+math.cos(lx)*limb_l, width=3, fill=color) canvas.create_line(cx, cy, cx+math.sin(lx)*limb_l, cy+math.cos(lx)*limb_l, width=3, fill=color) elif action == 2: man_data["angle"] += man_data["direction"] * 0.25 if abs(man_data["angle"]) > 1.2: man_data["direction"] *= -1 angle = man_data["angle"] jump = math.sin(angle)*body_h*0.15 tx, ty = cx, cy - body_h - jump canvas.create_line(cx, cy-jump, tx, ty, width=3, fill=color) head_x = tx head_y = ty canvas.create_oval(head_x-head_r, head_y-head_r, head_x+head_r, head_y+head_r, fill=color) man_data["expr_progress"] = self.draw_expression( canvas, head_x, head_y, head_r, man_data["expr_type"], man_data["expr_progress"] ) ax = math.sin(angle*3)*1.5 canvas.create_line(tx, ty, tx-math.cos(ax)*limb_l, ty+math.sin(ax)*limb_l, width=3, fill=color) canvas.create_line(tx, ty, tx+math.cos(ax)*limb_l, ty+math.sin(ax)*limb_l, width=3, fill=color) lx = abs(angle) canvas.create_line(cx, cy-jump, cx-math.sin(lx)*limb_l*0.7, (cy-jump)+math.cos(lx)*limb_l*0.7, width=3, fill=color) canvas.create_line(cx, cy-jump, cx+math.sin(lx)*limb_l*0.7, (cy-jump)+math.cos(lx)*limb_l*0.7, width=3, fill=color) else: man_data["spin"] += 0.15 man_data["angle"] += man_data["direction"] * 0.15 if abs(man_data["angle"]) > 0.8: man_data["direction"] *= -1 spin, angle = man_data["spin"], man_data["angle"] tx = cx + math.sin(spin)*body_h*0.1 + math.sin(angle)*body_h*0.1 ty = cy - body_h + math.cos(spin)*body_h*0.05 canvas.create_line(cx, cy, tx, ty, width=3, fill=color) head_x = tx + math.sin(spin*1.2)*head_r head_y = ty + math.cos(spin*1.2)*head_r canvas.create_oval(head_x-head_r, head_y-head_r, head_x+head_r, head_y+head_r, fill=color) man_data["expr_progress"] = self.draw_expression( canvas, head_x, head_y, head_r, man_data["expr_type"], man_data["expr_progress"] ) ax = spin + angle*2 canvas.create_line(tx, ty, tx-math.sin(ax)*limb_l, ty+math.cos(ax)*limb_l, width=3, fill=color) canvas.create_line(tx, ty, tx+math.sin(ax+math.pi)*limb_l, ty+math.cos(ax+math.pi)*limb_l, width=3, fill=color) lx = spin + math.pi/2 canvas.create_line(cx, cy, cx-math.sin(lx)*limb_l, cy+math.cos(lx)*limb_l, width=3, fill=color) canvas.create_line(cx, cy, cx+math.sin(lx)*limb_l, cy+math.cos(lx)*limb_l, width=3, fill=color) # 降低帧率到80ms/帧 canvas.after(80, lambda: self.animate_dancing_man(man_data)) def remove_dancing_man(self, man_data): if man_data in self.dancing_men: self.dancing_men.remove(man_data) man_data["window"].destroy() def cleanup_and_quit(self): for man in self.dancing_men: man["window"].destroy() for win in ["first_win", "full_win", "confirm_win"]: if hasattr(self, win) and getattr(self, win).winfo_exists(): getattr(self, win).destroy() self.root.quit() if __name__ == "__main__": app = PrankApp()
10-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值