|
@@ -0,0 +1,292 @@
|
|
|
|
|
+import tkinter as tk
|
|
|
|
|
+from tkinter import ttk, scrolledtext
|
|
|
|
|
+import pyautogui
|
|
|
|
|
+import pyperclip
|
|
|
|
|
+import time
|
|
|
|
|
+import threading
|
|
|
|
|
+import csv
|
|
|
|
|
+import os
|
|
|
|
|
+import re
|
|
|
|
|
+from datetime import datetime
|
|
|
|
|
+
|
|
|
|
|
+class AutoClickerApp:
|
|
|
|
|
+ def __init__(self, root):
|
|
|
|
|
+ self.root = root
|
|
|
|
|
+ self.root.title("自动点击采集工具")
|
|
|
|
|
+ self.root.geometry("900x700")
|
|
|
|
|
+
|
|
|
|
|
+ # 状态变量
|
|
|
|
|
+ self.is_running = False
|
|
|
|
|
+ self.thread = None
|
|
|
|
|
+ self.stop_event = threading.Event()
|
|
|
|
|
+
|
|
|
|
|
+ # 数据存储
|
|
|
|
|
+ self.coord_data = [] # 存储CSV数据
|
|
|
|
|
+ self.last_coords = [] # 存储每次采集到的坐标 (用于去重)
|
|
|
|
|
+
|
|
|
|
|
+ self.setup_ui()
|
|
|
|
|
+ self.load_csv_data()
|
|
|
|
|
+
|
|
|
|
|
+ def setup_ui(self):
|
|
|
|
|
+ # 控制按钮框架
|
|
|
|
|
+ control_frame = tk.Frame(self.root)
|
|
|
|
|
+ control_frame.pack(pady=10)
|
|
|
|
|
+
|
|
|
|
|
+ self.start_btn = tk.Button(control_frame, text="开始", command=self.start_action,
|
|
|
|
|
+ bg="green", fg="white", font=("Arial", 12), width=10)
|
|
|
|
|
+ self.start_btn.pack(side=tk.LEFT, padx=5)
|
|
|
|
|
+
|
|
|
|
|
+ self.stop_btn = tk.Button(control_frame, text="停止", command=self.stop_action,
|
|
|
|
|
+ bg="red", fg="white", font=("Arial", 12), width=10)
|
|
|
|
|
+ self.stop_btn.pack(side=tk.LEFT, padx=5)
|
|
|
|
|
+
|
|
|
|
|
+ # 表格框架
|
|
|
|
|
+ table_frame = tk.Frame(self.root)
|
|
|
|
|
+ table_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
|
|
|
|
|
+
|
|
|
|
|
+ # 创建表格
|
|
|
|
|
+ self.tree = ttk.Treeview(table_frame, columns=('x_range', 'y_range', 'collect_time'), show='headings', height=15)
|
|
|
|
|
+
|
|
|
|
|
+ # 设置列
|
|
|
|
|
+ self.tree.heading('x_range', text='X坐标范围')
|
|
|
|
|
+ self.tree.heading('y_range', text='Y坐标范围')
|
|
|
|
|
+ self.tree.heading('collect_time', text='采集时间')
|
|
|
|
|
+
|
|
|
|
|
+ self.tree.column('x_range', width=150, anchor='center')
|
|
|
|
|
+ self.tree.column('y_range', width=150, anchor='center')
|
|
|
|
|
+ self.tree.column('collect_time', width=250, anchor='center')
|
|
|
|
|
+
|
|
|
|
|
+ # 添加滚动条
|
|
|
|
|
+ scrollbar = ttk.Scrollbar(table_frame, orient=tk.VERTICAL, command=self.tree.yview)
|
|
|
|
|
+ self.tree.configure(yscrollcommand=scrollbar.set)
|
|
|
|
|
+
|
|
|
|
|
+ self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
|
|
|
+ scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
|
|
|
|
+
|
|
|
|
|
+ # 日志框架
|
|
|
|
|
+ log_frame = tk.Frame(self.root)
|
|
|
|
|
+ log_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
|
|
|
|
|
+
|
|
|
|
|
+ tk.Label(log_frame, text="日志:", font=("Arial", 10, "bold")).pack(anchor='w')
|
|
|
|
|
+
|
|
|
|
|
+ self.log_text = scrolledtext.ScrolledText(log_frame, height=10, wrap=tk.WORD)
|
|
|
|
|
+ self.log_text.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
+
|
|
|
|
|
+ def load_csv_data(self):
|
|
|
|
|
+ """加载CSV文件数据"""
|
|
|
|
|
+ csv_file = "coordinates.csv"
|
|
|
|
|
+
|
|
|
|
|
+ if not os.path.exists(csv_file):
|
|
|
|
|
+ self.log_message(f"未找到 {csv_file} 文件,请创建示例文件")
|
|
|
|
|
+ self.create_sample_csv()
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ with open(csv_file, 'r', encoding='utf-8') as file:
|
|
|
|
|
+ csv_reader = csv.reader(file)
|
|
|
|
|
+ self.coord_data = list(csv_reader)
|
|
|
|
|
+
|
|
|
|
|
+ # 清空表格
|
|
|
|
|
+ for item in self.tree.get_children():
|
|
|
|
|
+ self.tree.delete(item)
|
|
|
|
|
+
|
|
|
|
|
+ # 填充表格
|
|
|
|
|
+ for row in self.coord_data:
|
|
|
|
|
+ if len(row) >= 3:
|
|
|
|
|
+ self.tree.insert('', 'end', values=(row[0], row[1], row[2]))
|
|
|
|
|
+
|
|
|
|
|
+ self.log_message(f"成功加载 {len(self.coord_data)} 条坐标数据")
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ self.log_message(f"加载CSV文件失败: {str(e)}")
|
|
|
|
|
+
|
|
|
|
|
+ def create_sample_csv(self):
|
|
|
|
|
+ """创建示例CSV文件"""
|
|
|
|
|
+ sample_data = [
|
|
|
|
|
+ ['62-42', '225-73', ''],
|
|
|
|
|
+ ['240-300', '100-150', ''],
|
|
|
|
|
+ ['180-220', '50-90', '']
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ with open('coordinates.csv', 'w', newline='', encoding='utf-8') as file:
|
|
|
|
|
+ writer = csv.writer(file)
|
|
|
|
|
+ writer.writerows(sample_data)
|
|
|
|
|
+ self.log_message("已创建示例CSV文件: coordinates.csv")
|
|
|
|
|
+ self.load_csv_data()
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ self.log_message(f"创建示例CSV文件失败: {str(e)}")
|
|
|
|
|
+
|
|
|
|
|
+ def log_message(self, message):
|
|
|
|
|
+ """添加日志消息"""
|
|
|
|
|
+ timestamp = datetime.now().strftime("%H:%M:%S")
|
|
|
|
|
+ self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
|
|
|
|
|
+ self.log_text.see(tk.END)
|
|
|
|
|
+
|
|
|
|
|
+ def update_table_time(self, x, y):
|
|
|
|
|
+ """根据坐标更新表格中的时间"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ x_float = float(x)
|
|
|
|
|
+ y_float = float(y)
|
|
|
|
|
+
|
|
|
|
|
+ # 从最后一行开始向前匹配(只匹配最新的)
|
|
|
|
|
+ for i in range(len(self.coord_data) - 1, -1, -1):
|
|
|
|
|
+ row = self.coord_data[i]
|
|
|
|
|
+ if len(row) >= 2:
|
|
|
|
|
+ # 解析X范围
|
|
|
|
|
+ x_range = row[0].strip()
|
|
|
|
|
+ if '-' in x_range:
|
|
|
|
|
+ x_start, x_end = x_range.split('-')
|
|
|
|
|
+ try:
|
|
|
|
|
+ x_start = float(x_start.strip())
|
|
|
|
|
+ x_end = float(x_end.strip())
|
|
|
|
|
+
|
|
|
|
|
+ if x_start <= x_float <= x_end:
|
|
|
|
|
+ # 解析Y范围
|
|
|
|
|
+ y_range = row[1].strip()
|
|
|
|
|
+ if '-' in y_range:
|
|
|
|
|
+ y_start, y_end = y_range.split('-')
|
|
|
|
|
+ y_start = float(y_start.strip())
|
|
|
|
|
+ y_end = float(y_end.strip())
|
|
|
|
|
+
|
|
|
|
|
+ if y_start <= y_float <= y_end:
|
|
|
|
|
+ # 匹配成功,更新时间
|
|
|
|
|
+ current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
+ row[2] = current_time
|
|
|
|
|
+
|
|
|
|
|
+ # 更新表格显示
|
|
|
|
|
+ children = self.tree.get_children()
|
|
|
|
|
+ if i < len(children):
|
|
|
|
|
+ self.tree.delete(children[i])
|
|
|
|
|
+ self.tree.insert('', i, values=(row[0], row[1], row[2]))
|
|
|
|
|
+
|
|
|
|
|
+ self.log_message(f"坐标 ({x_float}, {y_float}) 匹配成功,时间已更新为 {current_time}")
|
|
|
|
|
+ return True
|
|
|
|
|
+ except ValueError:
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ self.log_message(f"坐标 ({x_float}, {y_float}) 未匹配到任何范围")
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ except ValueError:
|
|
|
|
|
+ self.log_message(f"坐标格式错误: {x}, {y}")
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ def parse_clipboard_content(self, content):
|
|
|
|
|
+ """解析剪贴板内容,提取坐标"""
|
|
|
|
|
+ lines = content.strip().split('\n')
|
|
|
|
|
+ found_coords = None
|
|
|
|
|
+
|
|
|
|
|
+ # 查找"发现真武矿采集物坐标"的行
|
|
|
|
|
+ for i, line in enumerate(lines):
|
|
|
|
|
+ if '发现真武矿采集物坐标' in line:
|
|
|
|
|
+ # 检查前一行是否包含"挖着了"
|
|
|
|
|
+ if i > 0 and '挖着了' in lines[i-1]:
|
|
|
|
|
+ # 提取坐标
|
|
|
|
|
+ match = re.search(r'发现真武矿采集物坐标:([\d.]+),([\d.]+)', line)
|
|
|
|
|
+ if match:
|
|
|
|
|
+ x = match.group(1)
|
|
|
|
|
+ y = match.group(2)
|
|
|
|
|
+ found_coords = (x, y)
|
|
|
|
|
+ self.log_message(f"发现有效坐标: {x}, {y}")
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ if found_coords:
|
|
|
|
|
+ x, y = found_coords
|
|
|
|
|
+ # 检查是否与上次采集的坐标相同(去重)
|
|
|
|
|
+ if (x, y) not in self.last_coords:
|
|
|
|
|
+ self.last_coords.append((x, y))
|
|
|
|
|
+ # 只保留最近10条记录
|
|
|
|
|
+ if len(self.last_coords) > 10:
|
|
|
|
|
+ self.last_coords.pop(0)
|
|
|
|
|
+ self.update_table_time(x, y)
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.log_message(f"坐标 ({x}, {y}) 已存在,跳过重复更新")
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.log_message("未找到有效的采集坐标")
|
|
|
|
|
+
|
|
|
|
|
+ def perform_actions(self):
|
|
|
|
|
+ """执行主要操作"""
|
|
|
|
|
+ self.log_message("开始执行操作序列...")
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ # 1. 右键点击指定位置
|
|
|
|
|
+ pyautogui.click(1730, 300, button='right')
|
|
|
|
|
+ self.log_message("已右键点击 (1730, 300)")
|
|
|
|
|
+ time.sleep(0.5)
|
|
|
|
|
+
|
|
|
|
|
+ # 2. 按键 A
|
|
|
|
|
+ pyautogui.press('a')
|
|
|
|
|
+ self.log_message("已按键 A")
|
|
|
|
|
+ time.sleep(0.5)
|
|
|
|
|
+
|
|
|
|
|
+ # 3. 按键 Ctrl+C
|
|
|
|
|
+ pyautogui.hotkey('ctrl', 'c')
|
|
|
|
|
+ self.log_message("已执行 Ctrl+C")
|
|
|
|
|
+ time.sleep(0.5)
|
|
|
|
|
+
|
|
|
|
|
+ # 4. 获取剪贴板内容
|
|
|
|
|
+ clipboard_content = pyperclip.paste()
|
|
|
|
|
+ if clipboard_content:
|
|
|
|
|
+ self.log_message("成功获取剪贴板内容")
|
|
|
|
|
+ self.parse_clipboard_content(clipboard_content)
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.log_message("剪贴板内容为空")
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ self.log_message(f"操作执行失败: {str(e)}")
|
|
|
|
|
+
|
|
|
|
|
+ def worker(self):
|
|
|
|
|
+ """工作线程函数"""
|
|
|
|
|
+ self.log_message("程序已启动,每10秒执行一次操作")
|
|
|
|
|
+
|
|
|
|
|
+ while not self.stop_event.is_set():
|
|
|
|
|
+ if not self.is_running:
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ self.perform_actions()
|
|
|
|
|
+
|
|
|
|
|
+ # 等待10秒,但每秒检查一次停止信号
|
|
|
|
|
+ for _ in range(10):
|
|
|
|
|
+ if self.stop_event.is_set() or not self.is_running:
|
|
|
|
|
+ break
|
|
|
|
|
+ time.sleep(1)
|
|
|
|
|
+
|
|
|
|
|
+ self.log_message("操作线程已停止")
|
|
|
|
|
+
|
|
|
|
|
+ def start_action(self):
|
|
|
|
|
+ """启动按钮回调"""
|
|
|
|
|
+ if self.is_running:
|
|
|
|
|
+ self.log_message("程序已在运行中")
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ # 清空上次的坐标记录
|
|
|
|
|
+ self.last_coords = []
|
|
|
|
|
+ self.stop_event.clear()
|
|
|
|
|
+ self.is_running = True
|
|
|
|
|
+
|
|
|
|
|
+ self.start_btn.config(state=tk.DISABLED)
|
|
|
|
|
+ self.stop_btn.config(state=tk.NORMAL)
|
|
|
|
|
+
|
|
|
|
|
+ self.thread = threading.Thread(target=self.worker, daemon=True)
|
|
|
|
|
+ self.thread.start()
|
|
|
|
|
+
|
|
|
|
|
+ def stop_action(self):
|
|
|
|
|
+ """停止按钮回调"""
|
|
|
|
|
+ if not self.is_running:
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ self.log_message("正在停止程序...")
|
|
|
|
|
+ self.is_running = False
|
|
|
|
|
+ self.stop_event.set()
|
|
|
|
|
+
|
|
|
|
|
+ self.start_btn.config(state=tk.NORMAL)
|
|
|
|
|
+ self.stop_btn.config(state=tk.DISABLED)
|
|
|
|
|
+
|
|
|
|
|
+def main():
|
|
|
|
|
+ root = tk.Tk()
|
|
|
|
|
+ app = AutoClickerApp(root)
|
|
|
|
|
+ root.mainloop()
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ main()
|