[Asm] 纯文本查看 复制代码
import tkinter as tk
from tkinter import filedialog, simpledialog, ttk, messagebox
from PIL import Image, ImageTk, ImageGrab, ImageEnhance
import cv2
import numpy as np
import pyautogui
import time
import random
import threading
import logging
import os
import json
class ImageRecognitionApp:
def __init__(self, root):
self.root = root
self.root.title("图像识别与点击")
self.image_list = []
self.init_variables()
self.init_ui()
self.init_logging()
self.load_config()
def init_variables(self):
self.screenshot_area = None
self.rect = None
self.start_x = self.start_y = None
self.canvas = None
self.running = False
self.thread = None
self.hotkey = '<F1>'
self.similarity_threshold = 0.8
self.delay_time = 0.1
self.loop_count = 1
self.selected_image = None
self.preview_photo = None # Added to store the reference to the preview image
def init_ui(self):
self.main_frame = tk.Frame(self.root)
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
self.create_left_frame()
self.create_right_frame()
self.root.bind(self.hotkey, self.toggle_script)
def create_left_frame(self):
self.left_frame = tk.Frame(self.main_frame)
self.left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=10)
buttons = [
("上传图像", self.batch_upload_images),
("框选截图", self.prepare_capture_screenshot),
("删除图片", self.delete_selected_image),
("开始运行", self.toggle_script),
("保存配置", self.save_config),
("设置热键", self.set_hotkey),
("加载配置", self.load_config_manually)
]
for text, command in buttons:
tk.Button(self.left_frame, text=text, command=command).pack(pady=5)
self.create_loop_count_input()
def create_loop_count_input(self):
tk.Label(self.left_frame, text="循环次数:").pack(pady=5)
self.loop_count_entry = tk.Entry(self.left_frame)
self.loop_count_entry.insert(0, str(self.loop_count))
self.loop_count_entry.pack(pady=5)
def create_right_frame(self):
self.right_frame = tk.Frame(self.main_frame)
self.right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10, pady=10)
self.create_tree_view()
self.create_preview_area()
def create_tree_view(self):
self.tree_frame = tk.Frame(self.right_frame)
self.tree_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
columns = ("图片", "步骤名称", "相似度", "键盘输入")
self.tree = ttk.Treeview(self.tree_frame, columns=columns, show='headings')
for col in columns:
self.tree.heading(col, text=col)
self.tree.column(col, width=100)
self.tree.column("图片", width=200)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, pady=5)
self.tree.bind('<<TreeviewSelect>>', self.on_treeview_select)
self.tree.bind('<Double-1>', self.edit_image_name)
self.tree.bind('<Double-3>', self.edit_similarity_threshold)
self.tree.bind('<Double-2>', self.edit_keyboard_input)
self.scrollbar = ttk.Scrollbar(self.tree_frame, orient="vertical", command=self.tree.yview)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.tree.configure(yscrollcommand=self.scrollbar.set)
def create_preview_area(self):
self.preview_frame = tk.Frame(self.right_frame)
self.preview_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True, pady=10)
tk.Label(self.preview_frame, text="图片预览").pack()
self.preview_image = tk.Label(self.preview_frame)
self.preview_image.pack()
def init_logging(self):
logging.basicConfig(filename='app.log', level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
def batch_upload_images(self):
file_paths = filedialog.askopenfilenames(filetypes=[("Image files", "*.jpg *.png")])
if file_paths:
for file_path in file_paths:
self.image_list.append((file_path, os.path.basename(file_path), 0.8, ""))
self.update_image_listbox()
def prepare_capture_screenshot(self):
self.root.withdraw()
time.sleep(0.5)
self.create_screenshot_window()
def create_screenshot_window(self):
self.top = tk.Toplevel(self.root)
self.top.attributes('-fullscreen', True, '-alpha', 0.3)
self.canvas = tk.Canvas(self.top, cursor="cross", bg='grey')
self.canvas.pack(fill=tk.BOTH, expand=True)
self.canvas.bind("<ButtonPress-1>", self.on_button_press)
self.canvas.bind("<B1-Motion>", self.on_mouse_drag)
self.canvas.bind("<ButtonRelease-1>", self.on_button_release)
def on_button_press(self, event):
self.start_x = event.x
self.start_y = event.y
if self.rect:
self.canvas.delete(self.rect)
self.rect = self.canvas.create_rectangle(self.start_x, self.start_y, self.start_x, self.start_y, outline='red', width=2)
def on_mouse_drag(self, event):
cur_x, cur_y = (event.x, event.y)
self.canvas.coords(self.rect, self.start_x, self.start_y, cur_x, cur_y)
def on_button_release(self, event):
end_x = event.x
end_y = event.y
bbox = (min(self.start_x, end_x), min(self.start_y, end_y), max(self.start_x, end_x), max(self.start_y, end_y))
timestamp = f"JT{random.randint(100000, 999999)}.png"
screenshot_path = timestamp
screenshot = ImageGrab.grab(bbox)
screenshot.save(screenshot_path)
self.image_list.append((screenshot_path, timestamp, 0.8, ""))
self.update_image_listbox()
self.top.destroy()
self.root.deiconify()
def update_image_listbox(self):
for row in self.tree.get_children():
self.tree.delete(row)
self.tree.image_refs = [] # Reset the image reference list
for img_path, img_name, similarity_threshold, keyboard_input in self.image_list:
image = Image.open(img_path)
image.thumbnail((50, 50))
photo = ImageTk.PhotoImage(image)
self.tree.insert("", tk.END, values=(img_path, img_name, similarity_threshold, keyboard_input), image=photo)
self.tree.image_refs.append(photo) # Save the image reference
if self.tree.selection():
self.on_treeview_select(None)
def on_treeview_select(self, event):
selected_items = self.tree.selection()
if selected_items:
item = selected_items[0]
img_path = self.tree.item(item, "values")[0]
self.display_preview(img_path)
def display_preview(self, img_path):
try:
image = Image.open(img_path)
image.thumbnail((300, 300))
self.preview_photo = ImageTk.PhotoImage(image) # Use instance variable to store reference
self.preview_image.config(image=self.preview_photo)
except Exception as e:
print(f"无法加载图片: {e}")
self.preview_image.config(image=None)
self.preview_photo = None
def edit_image_name(self, event):
selected_item = self.tree.selection()[0]
selected_index = self.tree.index(selected_item)
selected_image = self.image_list[selected_index]
new_image_name = simpledialog.askstring("修改步骤名称", "请输入新的步骤名称:", initialvalue=selected_image[1])
if new_image_name is not None:
self.image_list[selected_index] = (selected_image[0], new_image_name, selected_image[2], selected_image[3])
self.update_image_listbox()
def edit_similarity_threshold(self, event):
selected_item = self.tree.selection()[0]
selected_index = self.tree.index(selected_item)
selected_image = self.image_list[selected_index]
new_similarity_threshold = simpledialog.askfloat("修改相似度", "请输入新的相似度(0.1 - 1.0):", initialvalue=selected_image[2], minvalue=0.1, maxvalue=1.0)
if new_similarity_threshold is not None:
self.image_list[selected_index] = (selected_image[0], selected_image[1], new_similarity_threshold, selected_image[3])
self.update_image_listbox()
def edit_keyboard_input(self, event):
selected_item = self.tree.selection()[0]
selected_index = self.tree.index(selected_item)
selected_image = self.image_list[selected_index]
new_keyboard_input = simpledialog.askstring("修改键盘输入", "请输入新的键盘输入:", initialvalue=selected_image[3])
if new_keyboard_input is not None:
self.image_list[selected_index] = (selected_image[0], selected_image[1], selected_image[2], new_keyboard_input)
self.update_image_listbox()
def delete_selected_image(self):
selected_item = self.tree.selection()
if selected_item:
selected_index = self.tree.index(selected_item[0])
del self.image_list[selected_index]
self.update_image_listbox()
def toggle_script(self, event=None):
if not self.running:
self.start_script_thread()
self.toggle_run_button.config(text="停止运行")
else:
self.stop_script()
self.toggle_run_button.config(text="开始运行")
def start_script_thread(self):
if not self.running:
self.running = True
self.thread = threading.Thread(target=self.run_script, daemon=True)
self.thread.start()
def run_script(self):
self.loop_count = int(self.loop_count_entry.get())
current_loop = 0
while self.running and current_loop < self.loop_count:
for img_path, img_name, similarity_threshold, keyboard_input in self.image_list:
if not self.running:
break
while self.running:
if self.match_and_click(img_path, similarity_threshold):
if keyboard_input:
pyautogui.write(keyboard_input)
break
time.sleep(self.delay_time)
current_loop += 1
def stop_script(self):
self.running = False
if self.thread is not None:
self.thread.join()
print("脚本已停止")
logging.info("脚本已停止")
def match_and_click(self, template_path, similarity_threshold):
screenshot = pyautogui.screenshot()
screenshot = np.array(screenshot)
screenshot = cv2.cvtColor(screenshot, cv2.COLOR_RGB2BGR)
template = cv2.imread(template_path, 0)
if template is None:
print(f"无法读取图像: {template_path}")
logging.error(f"无法读取图像: {template_path}")
return False
gray_screenshot = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
result = cv2.matchTemplate(gray_screenshot, template, cv2.TM_CCOEFF_NORMED)
loc = np.where(result >= similarity_threshold)
found = False
for pt in zip(*loc[::-1]):
click_x = pt[0] + template.shape[1] // 2
click_y = pt[1] + template.shape[0] // 2
pyautogui.click(click_x, click_y)
found = True
break
if not found:
print(f"未找到匹配区域: {template_path}")
logging.warning(f"未找到匹配区域: {template_path}")
return False
return True
def set_hotkey(self):
new_hotkey = simpledialog.askstring("设置热键", "请输入新的热键(例如:<F1>):", initialvalue=self.hotkey)
if new_hotkey is not None:
self.hotkey = new_hotkey
self.root.bind(self.hotkey, self.toggle_script)
print(f"热键已设置为: {self.hotkey}")
logging.info(f"热键已设置为: {self.hotkey}")
def save_config(self):
config = {
"image_list": self.image_list,
"hotkey": self.hotkey,
"screenshot_area": self.screenshot_area,
"similarity_threshold": self.similarity_threshold,
"delay_time": self.delay_time,
"loop_count": self.loop_count
}
with open('config.json', 'w') as f:
json.dump(config, f)
print("配置已保存")
logging.info("配置已保存")
def load_config(self):
if os.path.exists('config.json'):
with open('config.json', 'r') as f:
config = json.load(f)
self.image_list = config.get("image_list", [])
self.hotkey = config.get("hotkey", '<F1>')
self.screenshot_area = config.get("screenshot_area", None)
self.similarity_threshold = config.get("similarity_threshold", 0.8)
self.delay_time = config.get("delay_time", 0.1)
self.loop_count = config.get("loop_count", 1)
self.loop_count_entry.delete(0, tk.END)
self.loop_count_entry.insert(0, str(self.loop_count))
self.root.bind(self.hotkey, self.toggle_script)
self.update_image_listbox()
print("配置已加载")
logging.info("配置已加载")
def load_config_manually(self):
file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
if file_path:
with open(file_path, 'r') as f:
config = json.load(f)
self.image_list = config.get("image_list", [])
self.hotkey = config.get("hotkey", '<F1>')
self.screenshot_area = config.get("screenshot_area", None)
self.similarity_threshold = config.get("similarity_threshold", 0.8)
self.delay_time = config.get("delay_time", 0.1)
self.loop_count = config.get("loop_count", 1)
self.loop_count_entry.delete(0, tk.END)
self.loop_count_entry.insert(0, str(self.loop_count))
self.root.bind(self.hotkey, self.toggle_script)
self.update_image_listbox()
print("配置已加载")
logging.info("配置已加载")
if __name__ == "__main__":
root = tk.Tk()
app = ImageRecognitionApp(root)
root.mainloop()