Thread dump result of region server

"IPC Server handler 26 on 60020" tid=72 daemon=true priority=5 state=BLOCKED at org.apache.hadoop.hbase.regionserver.HLog.append at org.apache.hadoop.hbase.regionserver.HRegion.put at org.apache.hadoop.hbase.regionserver.HRegion.put at org.apache.hadoop.hbase.regionserver.HRegion.put at org.apache.hadoop.hbase.regionserver.HRegionServer.put at sun.reflect.GeneratedMethodAccessor3.invoke at sun.reflect.DelegatingMethodAccessorImpl.invoke at java.lang.reflect.Method.invoke at org.apache.hadoop.hbase.ipc.HBaseRPC$Server.call at org.apache.hadoop.hbase.ipc.HBaseServer$Handler.run "IPC Server handler 64 on 60020" tid=110 daemon=true priority=5 state=BLOCKED at org.apache.hadoop.hbase.regionserver.HRegion.obtainRowLock at org.apache.hadoop.hbase.regionserver.HRegion.getLock at org.apache.hadoop.hbase.regionserver.HRegion.put at org.apache.hadoop.hbase.regionserver.HRegion.put at org.apache.hadoop.hbase.regionserver.HRegionServer.put at sun.reflect.GeneratedMethodAccessor3.invoke at sun.reflect.DelegatingMethodAccessorImpl.invoke at java.lang.reflect.Method.invoke at org.apache.hadoop.hbase.ipc.HBaseRPC$Server.call at org.apache.hadoop.hbase.ipc.HBaseServer$Handler.run "IPC Server handler 33 on 60020" tid=79 daemon=true priority=5 state=BLOCKED at org.apache.hadoop.hbase.regionserver.HRegion.releaseRowLock at org.apache.hadoop.hbase.regionserver.HRegion.put at org.apache.hadoop.hbase.regionserver.HRegion.put at org.apache.hadoop.hbase.regionserver.HRegionServer.put at sun.reflect.GeneratedMethodAccessor3.invoke at sun.reflect.DelegatingMethodAccessorImpl.invoke at java.lang.reflect.Method.invoke at org.apache.hadoop.hbase.ipc.HBaseRPC$Server.call at org.apache.hadoop.hbase.ipc.HBaseServer$Handler.run 这是HBase的client在做大量插入record的时候的log,可以发现很多时候线程阻塞在行锁上以及HLog获得写锁上。因为一个regionserver上面的所有region拥有同一个HLog,所以不可避免会有竞争。
优化以下代码“import cv2 import numpy as np import pyautogui import tkinter as tk from tkinter import filedialog, messagebox, ttk import threading import time import os from PIL import Image, ImageTk import json import random import gc import psutil import win32gui import win32con import win32api import subprocess import platform import math class OptimizedAutoClicker: def __init__(self, root): self.root = root self.root.title("星辰奇缘小助手") self.root.geometry("850x650") # 缩小窗口尺寸 # 变量初始化 self.templates = [] self.is_running = False self.click_history = [] self.storage_location = os.getcwd() self.config_file = "auto_clicker_config.json" self.selected_window = None self.window_rect = None self.cache_clean_interval = 100 # 增加缓存清理间隔 self.loop_count = 0 self.run_in_background = False self.process = psutil.Process() self.anti_detection_enabled = False self.human_behavior_enabled = False self.silent_mode_enabled = False # 性能优化变量 self.template_cache = {} # 缓存模板图像 self.last_screenshot = None # 缓存上一次截图 self.screenshot_count = 0 # 加载配置 self.load_config() # 创建界面 self.create_widgets() # 初始化窗口列表 self.update_window_list() def create_widgets(self): # 创建主框架 main_frame = ttk.Frame(self.root, padding="8") main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 创建左右分栏 left_frame = ttk.Frame(main_frame) left_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 5)) right_frame = ttk.Frame(main_frame) right_frame.grid(row=0, column=1, sticky=(tk.W, tk.E, tk.N, tk.S)) # 存储位置区域 - 放在左侧顶部 storage_frame = ttk.LabelFrame(left_frame, text="存储设置", padding="5") storage_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 5)) self.storage_location_label = ttk.Label(storage_frame, text=self.storage_location, wraplength=300, font=("Arial", 8)) self.storage_location_label.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=5) storage_buttons = ttk.Frame(storage_frame) storage_buttons.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=3) ttk.Button(storage_buttons, text="更改位置", command=self.change_storage_location, width=10).grid(row=0, column=0, padx=2) ttk.Button(storage_buttons, text="打开位置", command=self.open_storage_location, width=10).grid(row=0, column=1, padx=2) # 窗口选择区域 - 放在左侧 window_frame = ttk.LabelFrame(left_frame, text="窗口选择", padding="5") window_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(0, 5)) self.window_var = tk.StringVar() self.window_combo = ttk.Combobox(window_frame, textvariable=self.window_var, width=30) self.window_combo.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), padx=5, pady=2) window_buttons = ttk.Frame(window_frame) window_buttons.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=2) ttk.Button(window_buttons, text="刷新窗口", command=self.update_window_list, width=10).grid(row=0, column=0, padx=2) ttk.Button(window_buttons, text="当前窗口", command=self.select_current_window, width=10).grid(row=0, column=1, padx=2) self.window_info_label = ttk.Label(window_frame, text="未选择窗口", font=("Arial", 8)) self.window_info_label.grid(row=2, column=0, columnspan=2, sticky=tk.W, padx=5) # 模板管理区域 - 放在左侧 template_frame = ttk.LabelFrame(left_frame, text="模板管理", padding="5") template_frame.grid(row=2, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 5)) # 模板列表 self.template_listbox = tk.Listbox(template_frame, height=6, selectmode=tk.SINGLE, font=("Arial", 9)) self.template_listbox.grid(row=0, column=0, columnspan=4, sticky=(tk.W, tk.E, tk.N, tk.S), pady=2) self.template_listbox.bind('<<ListboxSelect>>', self.on_template_select) # 模板操作按钮 template_buttons = ttk.Frame(template_frame) template_buttons.grid(row=1, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=2) ttk.Button(template_buttons, text="添加", command=self.add_template, width=6).grid(row=0, column=0, padx=1) ttk.Button(template_buttons, text="删除", command=self.remove_template, width=6).grid(row=0, column=1, padx=1) ttk.Button(template_buttons, text="上移", command=self.move_template_up, width=6).grid(row=0, column=2, padx=1) ttk.Button(template_buttons, text="下移", command=self.move_template_down, width=6).grid(row=0, column=3, padx=1) # 模板预览 - 使用Frame包装Label以控制大小 preview_frame = ttk.Frame(template_frame) preview_frame.grid(row=2, column=0, columnspan=4, pady=2) self.template_preview_label = ttk.Label(preview_frame, text="模板预览") self.template_preview_label.grid(row=0, column=0) # 设置预览框架的最小尺寸 preview_frame.config(width=120, height=120) preview_frame.pack_propagate(False) # 防止子组件改变框架大小 # 参数设置区域 - 放在右侧顶部 param_frame = ttk.LabelFrame(right_frame, text="参数设置", padding="5") param_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 5)) # 第一行参数 param_row1 = ttk.Frame(param_frame) param_row1.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=2) ttk.Label(param_row1, text="置信度阈值:").grid(row=0, column=0, sticky=tk.W) self.confidence_var = tk.DoubleVar(value=0.7) confidence_scale = ttk.Scale(param_row1, from_=0.5, to=1.0, variable=self.confidence_var, orient=tk.HORIZONTAL, length=120) confidence_scale.grid(row=0, column=1, sticky=tk.W, padx=5) self.confidence_label = ttk.Label(param_row1, text="0.7", width=4) self.confidence_label.grid(row=0, column=2, sticky=tk.W) ttk.Label(param_row1, text="点击间隔:").grid(row=0, column=3, sticky=tk.W, padx=(10, 0)) self.interval_var = tk.DoubleVar(value=1.0) interval_spin = ttk.Spinbox(param_row1, from_=0.1, to=60.0, increment=0.1, textvariable=self.interval_var, width=6) interval_spin.grid(row=0, column=4, sticky=tk.W, padx=5) # 第二行参数 param_row2 = ttk.Frame(param_frame) param_row2.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=2) ttk.Label(param_row2, text="点击次数:").grid(row=0, column=0, sticky=tk.W) self.click_count_var = tk.IntVar(value=0) count_spin = ttk.Spinbox(param_row2, from_=0, to=1000, textvariable=self.click_count_var, width=6) count_spin.grid(row=0, column=1, sticky=tk.W, padx=5) ttk.Label(param_row2, text="匹配模式:").grid(row=0, column=2, sticky=tk.W, padx=(10, 0)) self.match_mode_var = tk.StringVar(value="顺序匹配") match_mode_combo = ttk.Combobox(param_row2, textvariable=self.match_mode_var, values=["顺序匹配", "随机匹配", "最高置信度"], width=10) match_mode_combo.grid(row=0, column=3, sticky=tk.W, padx=5) # 第三行参数 param_row3 = ttk.Frame(param_frame) param_row3.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=2) # 后台运行复选框 self.background_var = tk.BooleanVar(value=False) ttk.Checkbutton(param_row3, text="后台运行", variable=self.background_var).grid(row=0, column=0, sticky=tk.W) # 缓存清理间隔 ttk.Label(param_row3, text="缓存间隔:").grid(row=0, column=1, sticky=tk.W, padx=(10, 0)) self.cache_interval_var = tk.IntVar(value=100) cache_spin = ttk.Spinbox(param_row3, from_=50, to=500, textvariable=self.cache_interval_var, width=5) cache_spin.grid(row=0, column=2, sticky=tk.W, padx=5) # 内存使用显示 self.memory_label = ttk.Label(param_row3, text="内存: 0 MB", font=("Arial", 8)) self.memory_label.grid(row=0, column=3, sticky=tk.W, padx=(10, 0)) # 防检测设置区域 - 放在右侧 anti_detect_frame = ttk.LabelFrame(right_frame, text="防检测设置", padding="5") anti_detect_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(0, 5)) # 防检测选项 self.anti_detect_var = tk.BooleanVar(value=False) ttk.Checkbutton(anti_detect_frame, text="启用防检测", variable=self.anti_detect_var, command=self.toggle_anti_detection).grid(row=0, column=0, sticky=tk.W) self.human_behavior_var = tk.BooleanVar(value=False) ttk.Checkbutton(anti_detect_frame, text="模拟人类", variable=self.human_behavior_var).grid(row=0, column=1, sticky=tk.W) self.silent_mode_var = tk.BooleanVar(value=False) ttk.Checkbutton(anti_detect_frame, text="静默模式", variable=self.silent_mode_var).grid(row=0, column=2, sticky=tk.W) # 随机延迟 ttk.Label(anti_detect_frame, text="随机延迟:").grid(row=1, column=0, sticky=tk.W, pady=3) self.random_delay_min_var = tk.DoubleVar(value=0.1) self.random_delay_max_var = tk.DoubleVar(value=0.5) ttk.Entry(anti_detect_frame, textvariable=self.random_delay_min_var, width=4).grid(row=1, column=1, sticky=tk.W) ttk.Label(anti_detect_frame, text="-").grid(row=1, column=2, sticky=tk.W) ttk.Entry(anti_detect_frame, textvariable=self.random_delay_max_var, width=4).grid(row=1, column=3, sticky=tk.W) # 点击位置偏移 ttk.Label(anti_detect_frame, text="位置偏移:").grid(row=2, column=0, sticky=tk.W) self.click_offset_var = tk.IntVar(value=5) offset_scale = ttk.Scale(anti_detect_frame, from_=0, to=20, variable=self.click_offset_var, orient=tk.HORIZONTAL, length=120) offset_scale.grid(row=2, column=1, columnspan=3, sticky=tk.W, padx=5) self.offset_label = ttk.Label(anti_detect_frame, text="5", width=2) self.offset_label.grid(row=2, column=4, sticky=tk.W) # 操作按钮区域 - 放在右侧 button_frame = ttk.LabelFrame(right_frame, text="操作控制", padding="5") button_frame.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=(0, 5)) button_row1 = ttk.Frame(button_frame) button_row1.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=2) self.start_button = ttk.Button(button_row1, text="开始", command=self.start_clicking, width=10) self.start_button.grid(row=0, column=0, padx=2) self.stop_button = ttk.Button(button_row1, text="停止", command=self.stop_clicking, state=tk.DISABLED, width=10) self.stop_button.grid(row=0, column=1, padx=2) button_row2 = ttk.Frame(button_frame) button_row2.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=2) ttk.Button(button_row2, text="测试模板", command=self.test_all_templates, width=10).grid(row=0, column=0, padx=2) ttk.Button(button_row2, text="截图", command=self.capture_screen, width=10).grid(row=0, column=1, padx=2) ttk.Button(button_row2, text="清理缓存", command=self.force_clean_cache, width=10).grid(row=0, column=2, padx=2) # 日志区域 - 放在右侧底部 log_frame = ttk.LabelFrame(right_frame, text="操作日志", padding="5") log_frame.grid(row=3, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 添加滚动文本框 self.log_text = tk.Text(log_frame, height=10, width=50, font=("Arial", 9)) scrollbar = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=self.log_text.yview) self.log_text.configure(yscrollcommand=scrollbar.set) self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S)) # 清空日志按钮 ttk.Button(log_frame, text="清空日志", command=self.clear_log, width=10).grid(row=1, column=0, sticky=tk.W, pady=3) # 配置网格权重 main_frame.columnconfigure(0, weight=1) main_frame.columnconfigure(1, weight=1) main_frame.rowconfigure(0, weight=1) left_frame.columnconfigure(0, weight=1) left_frame.rowconfigure(2, weight=1) # 模板区域可扩展 right_frame.columnconfigure(0, weight=1) right_frame.rowconfigure(3, weight=1) # 日志区域可扩展 storage_frame.columnconfigure(0, weight=1) window_frame.columnconfigure(0, weight=1) template_frame.columnconfigure(0, weight=1) template_frame.rowconfigure(0, weight=1) # 模板列表可扩展 param_frame.columnconfigure(0, weight=1) anti_detect_frame.columnconfigure(0, weight=1) button_frame.columnconfigure(0, weight=1) log_frame.columnconfigure(0, weight=1) log_frame.rowconfigure(0, weight=1) # 绑定事件 confidence_scale.configure(command=self.update_confidence_label) offset_scale.configure(command=self.update_offset_label) self.window_combo.bind('<<ComboboxSelected>>', self.on_window_select) # 更新模板列表显示 self.update_template_list() # 启动内存监控 self.monitor_memory() def update_confidence_label(self, value): self.confidence_label.config(text=f"{float(value):.2f}") def update_offset_label(self, value): self.offset_label.config(text=f"{int(float(value))}") def toggle_anti_detection(self): if self.anti_detect_var.get(): self.log("防检测功能已启用") else: self.log("防检测功能已禁用") def change_storage_location(self): """更改存储位置""" new_location = filedialog.askdirectory(title="选择存储位置") if new_location: self.storage_location = new_location self.storage_location_label.config(text=self.storage_location) self.log(f"存储位置已更改为: {self.storage_location}") # 如果配置文件存在,移动到新位置 old_config_path = os.path.join(os.getcwd(), self.config_file) new_config_path = os.path.join(self.storage_location, self.config_file) if os.path.exists(old_config_path) and not os.path.exists(new_config_path): try: import shutil shutil.move(old_config_path, new_config_path) self.log("配置文件已移动到新位置") except Exception as e: self.log(f"移动配置文件时出错: {str(e)}") def update_window_list(self): """更新窗口列表""" windows = [] def enum_windows_proc(hwnd, windows): if win32gui.IsWindowVisible(hwnd) and win32gui.GetWindowText(hwnd): windows.append((hwnd, win32gui.GetWindowText(hwnd))) win32gui.EnumWindows(enum_windows_proc, windows) # 更新下拉框 self.window_combo['values'] = [f"{title} (0x{hwnd:X})" for hwnd, title in windows] self.log("窗口列表已更新") def on_window_select(self, event): """当选择窗口时""" selection = self.window_combo.get() if selection: # 提取窗口句柄 try: hwnd_str = selection.split("(0x")[1].rstrip(")") hwnd = int(hwnd_str, 16) self.selected_window = hwnd # 获取窗口位置和大小 rect = win32gui.GetWindowRect(hwnd) self.window_rect = rect # 更新窗口信息显示 self.window_info_label.config( text=f"位置: ({rect[0]}, {rect[1]}) 大小: {rect[2] - rect[0]}x{rect[3] - rect[1]}" ) self.log(f"已选择窗口: {selection}") except Exception as e: messagebox.showerror("错误", f"选择窗口时出错: {str(e)}") def select_current_window(self): """选择当前活动窗口""" hwnd = win32gui.GetForegroundWindow() if hwnd: title = win32gui.GetWindowText(hwnd) if title: self.window_combo.set(f"{title} (0x{hwnd:X})") self.on_window_select(None) else: messagebox.showwarning("警告", "当前窗口没有标题") else: messagebox.showwarning("警告", "无法获取当前窗口") def add_template(self): file_paths = filedialog.askopenfilenames( title="选择模板图像", filetypes=[("图像文件", "*.png;*.jpg;*.jpeg;*.bmp"), ("所有文件", "*.*")] ) for file_path in file_paths: template_name = os.path.splitext(os.path.basename(file_path))[0] self.templates.append({ "name": template_name, "path": file_path, "enabled": True }) self.update_template_list() self.log(f"添加了 {len(file_paths)} 个模板") def remove_template(self): selection = self.template_listbox.curselection() if selection: index = selection[0] template_name = self.templates[index]["name"] # 从缓存中删除模板 if template_name in self.template_cache: del self.template_cache[template_name] del self.templates[index] self.update_template_list() self.template_preview_label.config(image='', text="模板预览") self.log(f"删除了模板: {template_name}") def move_template_up(self): selection = self.template_listbox.curselection() if selection and selection[0] > 0: index = selection[0] self.templates[index], self.templates[index - 1] = self.templates[index - 1], self.templates[index] self.update_template_list() self.template_listbox.select_set(index - 1) def move_template_down(self): selection = self.template_listbox.curselection() if selection and selection[0] < len(self.templates) - 1: index = selection[0] self.templates[index], self.templates[index + 1] = self.templates[index + 1], self.templates[index] self.update_template_list() self.template_listbox.select_set(index + 1) def update_template_list(self): self.template_listbox.delete(0, tk.END) for template in self.templates: status = "✓" if template["enabled"] else "✗" self.template_listbox.insert(tk.END, f"{status} {template['name']}") def on_template_select(self, event): selection = self.template_listbox.curselection() if selection: index = selection[0] template = self.templates[index] # 显示模板预览 try: image = Image.open(template["path"]) image.thumbnail((120, 120)) # 缩小缩略图大小 photo = ImageTk.PhotoImage(image) self.template_preview_label.config(image=photo, text="") self.template_preview_label.image = photo # 保持引用 except Exception as e: self.template_preview_label.config(image='', text="无法加载预览") def get_screen_region(self): # 如果选择了特定窗口,使用窗口区域 if self.selected_window and self.window_rect: return self.window_rect # 否则使用全屏 return None def capture_screen(self): region = self.get_screen_region() if region: screenshot = pyautogui.screenshot(region=region) else: screenshot = pyautogui.screenshot() # 保存截图到指定存储位置 filename = f"screenshot_{time.strftime('%Y%m%d_%H%M%S')}.png" file_path = os.path.join(self.storage_location, filename) screenshot.save(file_path) self.log(f"截图已保存: {file_path}") def test_all_templates(self): if not self.templates: messagebox.showerror("错误", "请先添加模板图像") return region = self.get_screen_region() if region: screenshot = pyautogui.screenshot(region=region) else: screenshot = pyautogui.screenshot() screenshot_cv = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR) results = [] for template in self.templates: if not template["enabled"]: continue try: # 使用缓存的模板图像 template_img = self.get_cached_template(template["path"]) if template_img is None: self.log(f"错误: 无法读取模板 {template['name']}") continue # 执行模板匹配 result = cv2.matchTemplate(screenshot_cv, template_img, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) if max_val >= self.confidence_var.get(): h, w = template_img.shape[:2] top_left = max_loc bottom_right = (top_left[0] + w, top_left[1] + h) # 在截图上绘制矩形 cv2.rectangle(screenshot_cv, top_left, bottom_right, (0, 0, 255), 2) cv2.putText(screenshot_cv, template['name'], (top_left[0], top_left[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) results.append({ "name": template['name'], "confidence": max_val, "position": (top_left[0] + w // 2, top_left[1] + h // 2) }) except Exception as e: self.log(f"测试模板 {template['name']} 时出错: {str(e)}") # 显示结果图像 if results: self.show_result_image(screenshot_cv, f"找到 {len(results)} 个匹配项") for result in results: self.log(f"找到模板 '{result['name']}': 位置 {result['position']}, 置信度 {result['confidence']:.4f}") else: self.log("测试完成: 未找到任何匹配项") messagebox.showinfo("测试结果", "未找到任何匹配项") def show_result_image(self, image, title): # 创建新窗口显示结果 result_window = tk.Toplevel(self.root) result_window.title(title) result_window.geometry("600x500") # 限制结果窗口大小 # 调整图像大小以适应窗口 h, w = image.shape[:2] scale = min(500 / w, 400 / h) # 缩小显示尺寸 new_w, new_h = int(w * scale), int(h * scale) image_resized = cv2.resize(image, (new_w, new_h)) image_rgb = cv2.cvtColor(image_resized, cv2.COLOR_BGR2RGB) img = Image.fromarray(image_rgb) imgtk = ImageTk.PhotoImage(image=img) label = ttk.Label(result_window, image=imgtk) label.image = imgtk label.pack(padx=10, pady=10) ttk.Button(result_window, text="关闭", command=result_window.destroy).pack(pady=5) def start_clicking(self): if not any(template["enabled"] for template in self.templates): messagebox.showerror("错误", "请先添加并启用至少一个模板") return self.is_running = True self.start_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.NORMAL) self.run_in_background = self.background_var.get() self.anti_detection_enabled = self.anti_detect_var.get() self.human_behavior_enabled = self.human_behavior_var.get() self.silent_mode_enabled = self.silent_mode_var.get() # 清除截图缓存 self.last_screenshot = None self.screenshot_count = 0 # 在新线程中运行点击循环 self.click_thread = threading.Thread(target=self.click_loop) self.click_thread.daemon = True self.click_thread.start() self.log("开始自动点击") # 如果选择后台运行,最小化窗口 if self.run_in_background: self.root.iconify() self.log("程序已最小化到后台运行") def stop_clicking(self): self.is_running = False self.start_button.config(state=tk.NORMAL) self.stop_button.config(state=tk.DISABLED) self.log("停止自动点击") def get_cached_template(self, template_path): """获取缓存的模板图像,减少磁盘IO""" template_name = os.path.basename(template_path) if template_name not in self.template_cache: template_img = cv2.imread(template_path) if template_img is not None: self.template_cache[template_name] = template_img return template_img return self.template_cache[template_name] def human_like_mouse_move(self, start_x, start_y, end_x, end_y): """模拟人类鼠标移动轨迹""" # 使用贝塞尔曲线模拟自然移动 steps = random.randint(10, 20) control_x = random.randint(min(start_x, end_x), max(start_x, end_x)) control_y = random.randint(min(start_y, end_y), max(start_y, end_y)) for i in range(steps): if not self.is_running: return t = i / steps # 二次贝塞尔曲线 x = (1 - t) ** 2 * start_x + 2 * (1 - t) * t * control_x + t ** 2 * end_x y = (1 - t) ** 2 * start_y + 2 * (1 - t) * t * control_y + t ** 2 * end_y pyautogui.moveTo(int(x), int(y)) time.sleep(random.uniform(0.01, 0.05)) def silent_mouse_move(self, x, y): """静默移动鼠标(无动画效果)""" # 使用win32api直接设置鼠标位置,不显示移动动画 try: win32api.SetCursorPos((x, y)) except: # 如果win32api失败,使用pyautogui的瞬时移动 pyautogui.moveTo(x, y, duration=0) def random_delay(self): """随机延迟""" if self.anti_detection_enabled: delay = random.uniform(self.random_delay_min_var.get(), self.random_delay_max_var.get()) time.sleep(delay) def add_click_offset(self, x, y): """添加点击位置偏移""" if self.anti_detection_enabled and self.click_offset_var.get() > 0: offset = self.click_offset_var.get() x_offset = random.randint(-offset, offset) y_offset = random.randint(-offset, offset) return x + x_offset, y + y_offset return x, y def click_loop(self): click_count = 0 max_clicks = self.click_count_var.get() match_mode = self.match_mode_var.get() region = self.get_screen_region() self.loop_count = 0 # 获取当前鼠标位置 current_x, current_y = pyautogui.position() while self.is_running and (max_clicks == 0 or click_count < max_clicks): try: # 检查是否需要清理缓存 self.loop_count += 1 if self.loop_count % self.cache_interval_var.get() == 0: self.clean_cache() # 截取屏幕 - 使用缓存优化 self.screenshot_count += 1 if self.last_screenshot is None or self.screenshot_count % 5 == 0: # 每5次循环重新截图一次,减少截图频率 if region: screenshot = pyautogui.screenshot(region=region) else: screenshot = pyautogui.screenshot() self.last_screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR) del screenshot # 及时释放内存 screenshot_cv = self.last_screenshot # 根据匹配模式查找模板 matched_template = None matched_position = None matched_confidence = 0 enabled_templates = [t for t in self.templates if t["enabled"]] if match_mode == "顺序匹配": # 按顺序查找第一个匹配的模板 for template in enabled_templates: result = self.match_template(screenshot_cv, template) if result: matched_template = template matched_position = result["position"] matched_confidence = result["confidence"] break elif match_mode == "随机匹配": # 随机查找一个匹配的模板 random.shuffle(enabled_templates) for template in enabled_templates: result = self.match_template(screenshot_cv, template) if result: matched_template = template matched_position = result["position"] matched_confidence = result["confidence"] break elif match_mode == "最高置信度": # 查找置信度最高的模板 for template in enabled_templates: result = self.match_template(screenshot_cv, template) if result and result["confidence"] > matched_confidence: matched_template = template matched_position = result["position"] matched_confidence = result["confidence"] # 如果找到匹配的模板,执行点击 if matched_template and matched_position: # 调整点击位置(如果设置了区域限制) if region: click_x = matched_position[0] + region[0] click_y = matched_position[1] + region[1] else: click_x, click_y = matched_position # 应用点击位置偏移 click_x, click_y = self.add_click_offset(click_x, click_y) # 根据设置移动鼠标 if self.silent_mode_enabled: # 静默模式:直接设置鼠标位置,不显示移动 self.silent_mouse_move(click_x, click_y) elif self.human_behavior_enabled: # 模拟人类行为:使用曲线移动鼠标 self.human_like_mouse_move(current_x, current_y, click_x, click_y) else: # 普通模式:直接移动到目标位置 pyautogui.moveTo(click_x, click_y) # 点击目标 pyautogui.click(click_x, click_y) # 更新当前鼠标位置 current_x, current_y = click_x, click_y # 记录点击 click_count += 1 self.click_history.append({ 'time': time.strftime("%Y-%m-%d %H:%M:%S"), 'template': matched_template['name'], 'position': (click_x, click_y), 'confidence': matched_confidence }) self.log( f"点击 '{matched_template['name']}': ({click_x}, {click_y}), 置信度: {matched_confidence:.4f}, 总点击: {click_count}") else: if self.loop_count % 10 == 0: # 减少日志输出频率 self.log("未找到任何匹配的模板") except Exception as e: self.log(f"错误: {str(e)}") # 等待指定间隔 + 随机延迟 base_interval = self.interval_var.get() if self.anti_detection_enabled: base_interval += random.uniform(self.random_delay_min_var.get(), self.random_delay_max_var.get()) time.sleep(base_interval) if max_clicks > 0 and click_count >= max_clicks: self.log(f"已完成指定点击次数: {max_clicks}") # 停止运行 self.is_running = False self.root.after(0, self.stop_clicking) def match_template(self, screenshot, template): try: # 使用缓存的模板图像 template_img = self.get_cached_template(template["path"]) if template_img is None: return None # 执行模板匹配 result = cv2.matchTemplate(screenshot, template_img, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) # 检查是否找到目标 if max_val >= self.confidence_var.get(): h, w = template_img.shape[:2] center_x = max_loc[0] + w // 2 center_y = max_loc[1] + h // 2 return { "position": (center_x, center_y), "confidence": max_val } except Exception as e: self.log(f"匹配模板 '{template['name']}' 时出错: {str(e)}") return None def clean_cache(self): """清理内存缓存""" try: # 强制垃圾回收 gc.collect() # 记录清理前的内存使用 memory_before = self.process.memory_info().rss / 1024 / 1024 # 清理OpenCV内部缓存 cv2.destroyAllWindows() # 清理模板缓存(保留最近使用的) if len(self.template_cache) > 10: # 只保留前10个模板 keys_to_remove = list(self.template_cache.keys())[10:] for key in keys_to_remove: del self.template_cache[key] # 记录清理后的内存使用 memory_after = self.process.memory_info().rss / 1024 / 1024 memory_freed = memory_before - memory_after if memory_freed > 0: self.log(f"缓存清理完成,释放了 {memory_freed:.2f} MB 内存") except Exception as e: self.log(f"清理缓存时出错: {str(e)}") def force_clean_cache(self): """强制清理缓存""" self.clean_cache() messagebox.showinfo("缓存清理", "缓存清理完成") def open_storage_location(self): """打开存储位置""" try: # 根据操作系统打开文件管理器 if platform.system() == "Windows": os.startfile(self.storage_location) elif platform.system() == "Darwin": # macOS subprocess.Popen(["open", self.storage_location]) else: # Linux subprocess.Popen(["xdg-open", self.storage_location]) self.log(f"已打开存储位置: {self.storage_location}") except Exception as e: messagebox.showerror("错误", f"打开存储位置时出错: {str(e)}") def monitor_memory(self): """监控内存使用情况""" try: memory_usage = self.process.memory_info().rss / 1024 / 1024 self.memory_label.config(text=f"内存: {memory_usage:.1f} MB") except: pass # 每隔10秒更新一次内存显示(减少频率) self.root.after(10000, self.monitor_memory) def log(self, message): timestamp = time.strftime("%H:%M:%S") log_message = f"[{timestamp}] {message}\n" # 在主线程中更新UI def update_log(): self.log_text.insert(tk.END, log_message) self.log_text.see(tk.END) # 限制日志长度,防止内存占用过大 if int(self.log_text.index('end-1c').split('.')[0]) > 500: self.log_text.delete(1.0, 100.0) self.root.after(0, update_log) def clear_log(self): self.log_text.delete(1.0, tk.END) def load_config(self): try: # 首先尝试从存储位置加载配置 config_path = os.path.join(self.storage_location, self.config_file) if not os.path.exists(config_path): # 如果存储位置没有配置,尝试从当前目录加载 config_path = os.path.join(os.getcwd(), self.config_file) if os.path.exists(config_path): with open(config_path, 'r') as f: config = json.load(f) # 应用配置 if 'templates' in config: self.templates = config['templates'] if 'storage_location' in config: self.storage_location = config['storage_location'] self.storage_location_label.config(text=self.storage_location) except Exception as e: print(f"加载配置失败: {str(e)}") def save_config(self): try: config = { 'templates': self.templates, 'storage_location': self.storage_location } # 保存配置到存储位置 config_path = os.path.join(self.storage_location, self.config_file) with open(config_path, 'w') as f: json.dump(config, f) except Exception as e: print(f"保存配置失败: {str(e)}") def on_closing(self): self.save_config() self.is_running = False self.root.destroy() def main(): root = tk.Tk() app = OptimizedAutoClicker(root) root.protocol("WM_DELETE_WINDOW", app.on_closing) root.mainloop() if __name__ == "__main__": main()”
最新发布
11-18
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值