前言

最近重复工作比较多,想着写个自动化脚本来替我打工!废话不多说,上源码:

需要先安装第三方库:

#使用国内源提速
pip install pyautogui keyboard -i https://pypi.tuna.tsinghua.edu.cn/simple/
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import pyautogui
import time
import threading
import keyboard
from functools import partial



class Action:
    def __init__(self, action_type, x=0, y=0, key=None, description=None):
        self.action_type = action_type  # "click", "key", "paste", "enter"
        self.x = x
        self.y = y
        self.key = key
        self.description = description or self.get_default_description()

    def get_default_description(self):
        if self.action_type == "click":
            return f"点击位置 ({self.x}, {self.y})"
        elif self.action_type == "key":
            return f"按键 {self.key}"
        elif self.action_type == "paste":
            return "粘贴 (Ctrl+V)"
        elif self.action_type == "enter":
            return "按回车键"
        return "未知操作"

    def execute(self, delay):
        if self.action_type == "click":
            pyautogui.click(self.x, self.y)
        elif self.action_type == "key":
            pyautogui.press(self.key)
        elif self.action_type == "paste":
            pyautogui.hotkey('ctrl', 'v')
        elif self.action_type == "enter":
            pyautogui.press('enter')
        time.sleep(delay)


class EnhancedAutoClickerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("自动化点击工具")
        self.root.geometry("600x600")

        # 存储动作列表
        self.actions = []

        # 设置循环次数和延迟时间
        self.loop_count = tk.StringVar(value="10")
        self.delay_time = tk.StringVar(value="0.5")

        # 运行状态标志
        self.is_running = False
        self.is_listening = False

        # 创建界面
        self.create_widgets()

        # 设置F2热键监听
        keyboard.add_hotkey('f2', self.on_f2_pressed)

    def create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        title_label = ttk.Label(main_frame, text="增强版自动化点击工具", font=("Arial", 16, "bold"))
        title_label.pack(pady=10)

        # 设置框架
        settings_frame = ttk.LabelFrame(main_frame, text="设置")
        settings_frame.pack(fill=tk.X, padx=5, pady=5)

        # 循环设置
        loop_frame = ttk.Frame(settings_frame)
        loop_frame.pack(fill=tk.X, padx=5, pady=5)

        ttk.Label(loop_frame, text="循环次数:").pack(side=tk.LEFT, padx=5)
        ttk.Entry(loop_frame, textvariable=self.loop_count, width=10).pack(side=tk.LEFT, padx=5)

        ttk.Label(loop_frame, text="操作间隔(秒):").pack(side=tk.LEFT, padx=5)
        ttk.Entry(loop_frame, textvariable=self.delay_time, width=10).pack(side=tk.LEFT, padx=5)

        # 操作按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, padx=5, pady=5)

        # 添加鼠标点击按钮
        self.add_click_btn = ttk.Button(
            button_frame,
            text="添加鼠标点击 (F2)",
            command=self.start_mouse_capture
        )
        self.add_click_btn.pack(side=tk.LEFT, padx=5)

        # 添加其他操作按钮
        ttk.Button(
            button_frame,
            text="添加粘贴操作",
            command=lambda: self.add_action(Action("paste"))
        ).pack(side=tk.LEFT, padx=5)

        ttk.Button(
            button_frame,
            text="添加回车键",
            command=lambda: self.add_action(Action("enter"))
        ).pack(side=tk.LEFT, padx=5)

        ttk.Button(
            button_frame,
            text="添加按键",
            command=self.add_key_action
        ).pack(side=tk.LEFT, padx=5)

        # 动作列表框架
        actions_frame = ttk.LabelFrame(main_frame, text="动作列表 (可拖动排序)")
        actions_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 创建Treeview用于显示和排序动作
        self.actions_tree = ttk.Treeview(
            actions_frame,
            columns=("序号", "动作"),
            show="headings",
            selectmode="browse"
        )
        self.actions_tree.heading("序号", text="序号")
        self.actions_tree.heading("动作", text="动作")
        self.actions_tree.column("序号", width=50, anchor=tk.CENTER)
        self.actions_tree.column("动作", width=500, anchor=tk.W)
        self.actions_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 绑定拖放事件
        self.actions_tree.bind("<ButtonPress-1>", self.on_tree_press)
        self.actions_tree.bind("<B1-Motion>", self.on_tree_motion)
        self.actions_tree.bind("<ButtonRelease-1>", self.on_tree_release)

        # 右键菜单
        self.context_menu = tk.Menu(self.root, tearoff=0)
        self.context_menu.add_command(label="编辑", command=self.edit_action)
        self.context_menu.add_command(label="删除", command=self.delete_action)
        self.actions_tree.bind("<Button-3>", self.show_context_menu)

        # 控制按钮框架
        control_frame = ttk.Frame(main_frame)
        control_frame.pack(fill=tk.X, padx=5, pady=10)

        self.start_btn = ttk.Button(
            control_frame,
            text="开始执行",
            command=self.start_automation
        )
        self.start_btn.pack(side=tk.LEFT, padx=5)

        self.stop_btn = ttk.Button(
            control_frame,
            text="停止执行",
            command=self.stop_automation,
            state="disabled"
        )
        self.stop_btn.pack(side=tk.LEFT, padx=5)

        ttk.Button(
            control_frame,
            text="清空列表",
            command=self.clear_actions
        ).pack(side=tk.LEFT, padx=5)

        # 状态栏
        self.status_var = tk.StringVar(value="就绪")
        status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.pack(fill=tk.X, padx=5, pady=5)

        # 提示信息
        ttk.Label(
            main_frame,
            text="提示: 按下F2可以捕获鼠标位置,按ESC键可以紧急停止执行",
            foreground="gray"
        ).pack(pady=5)

        # 设置拖放变量
        self.drag_data = {"item": None, "x": 0, "y": 0}

    def on_tree_press(self, event):
        """开始拖动项目"""
        item = self.actions_tree.identify_row(event.y)
        if item:
            self.drag_data["item"] = item
            self.drag_data["x"] = event.x
            self.drag_data["y"] = event.y

    def on_tree_motion(self, event):
        """拖动项目时显示移动线"""
        if self.drag_data["item"]:
            # 显示拖动线
            self.actions_tree.selection_set(self.drag_data["item"])

    def on_tree_release(self, event):
        """释放拖动项目,重新排序"""
        if self.drag_data["item"]:
            target_item = self.actions_tree.identify_row(event.y)
            if target_item and target_item != self.drag_data["item"]:
                # 获取源和目标的索引
                source_idx = int(self.actions_tree.item(self.drag_data["item"], "values")[0]) - 1
                target_idx = int(self.actions_tree.item(target_item, "values")[0]) - 1

                # 移动动作
                action = self.actions.pop(source_idx)
                self.actions.insert(target_idx, action)

                # 更新显示
                self.update_actions_tree()

            self.drag_data["item"] = None

    def show_context_menu(self, event):
        """显示右键菜单"""
        item = self.actions_tree.identify_row(event.y)
        if item:
            self.actions_tree.selection_set(item)
            self.context_menu.post(event.x_root, event.y_root)

    def edit_action(self):
        """编辑选中的动作"""
        selection = self.actions_tree.selection()
        if not selection:
            return

        item = selection[0]
        idx = int(self.actions_tree.item(item, "values")[0]) - 1
        action = self.actions[idx]

        if action.action_type == "click":
            # 编辑点击位置
            result = simpledialog.askstring(
                "编辑点击位置",
                "输入新的坐标 (x,y):",
                initialvalue=f"{action.x},{action.y}"
            )
            if result:
                try:
                    x, y = map(int, result.split(","))
                    action.x = x
                    action.y = y
                    action.description = f"点击位置 ({x}, {y})"
                    self.update_actions_tree()
                except:
                    messagebox.showerror("错误", "坐标格式无效,请使用 x,y 格式")
        elif action.action_type == "key":
            # 编辑按键
            key = simpledialog.askstring(
                "编辑按键",
                "输入新的按键:",
                initialvalue=action.key
            )
            if key:
                action.key = key
                action.description = f"按键 {key}"
                self.update_actions_tree()

    def delete_action(self):
        """删除选中的动作"""
        selection = self.actions_tree.selection()
        if not selection:
            return

        item = selection[0]
        idx = int(self.actions_tree.item(item, "values")[0]) - 1
        self.actions.pop(idx)
        self.update_actions_tree()

    def start_mouse_capture(self):
        """开始捕获鼠标位置"""
        if not self.is_listening:
            self.is_listening = True
            self.add_click_btn.configure(text="正在捕获... (按F2记录位置)")
            self.status_var.set("按F2记录当前鼠标位置")

    def on_f2_pressed(self):
        """当按下F2时,捕获鼠标位置"""
        if self.is_listening:
            x, y = pyautogui.position()
            self.add_action(Action("click", x, y))
            self.status_var.set(f"已添加点击位置: ({x}, {y})")

    def add_key_action(self):
        """添加按键动作"""
        key = simpledialog.askstring("添加按键", "输入要按下的键:")
        if key:
            self.add_action(Action("key", key=key))

    def add_action(self, action):
        """添加动作到列表"""
        self.actions.append(action)
        self.update_actions_tree()

        # 如果是在捕获模式下,继续保持捕获状态
        if not self.is_listening and action.action_type == "click":
            self.add_click_btn.configure(text="添加鼠标点击 (F2)")

    def update_actions_tree(self):
        """更新动作列表显示"""
        # 清空树
        for item in self.actions_tree.get_children():
            self.actions_tree.delete(item)

        # 添加动作
        for i, action in enumerate(self.actions):
            self.actions_tree.insert("", "end", values=(i + 1, action.description))

    def start_automation(self):
        """开始执行自动化任务"""
        if not self.actions:
            messagebox.showwarning("警告", "请先添加动作")
            return

        try:
            loops = int(self.loop_count.get())
            delay = float(self.delay_time.get())

            if loops <= 0 or delay < 0:
                messagebox.showwarning("警告", "循环次数和延迟时间必须大于0")
                return

            # 停止捕获模式
            self.is_listening = False
            self.add_click_btn.configure(text="添加鼠标点击 (F2)")

            # 开始执行
            self.is_running = True
            self.start_btn.configure(state="disabled")
            self.stop_btn.configure(state="normal")

            # 创建并启动执行线程
            self.automation_thread = threading.Thread(
                target=self.run_automation,
                args=(loops, delay),
                daemon=True
            )
            self.automation_thread.start()

        except ValueError:
            messagebox.showerror("错误", "请输入有效的循环次数和延迟时间")

    def run_automation(self, loops, delay):
        """在线程中执行自动化任务"""
        try:
            self.status_var.set("准备执行,3秒后开始...")
            time.sleep(3)  # 给用户时间切换窗口

            for loop in range(loops):
                if not self.is_running:
                    break

                self.status_var.set(f"执行中: 第 {loop + 1}/{loops} 次循环")

                for i, action in enumerate(self.actions):
                    if not self.is_running:
                        break
                    self.root.after(0, lambda idx=i: self.highlight_action(idx))
                    action.execute(delay)

            self.root.after(0, self.reset_ui)
            self.status_var.set("执行完成")

        except Exception as e:
            self.root.after(0, lambda: messagebox.showerror("执行错误", str(e)))
            self.root.after(0, self.reset_ui)

    def highlight_action(self, index):
        """高亮显示当前执行的动作"""
        for item in self.actions_tree.get_children():
            idx = int(self.actions_tree.item(item, "values")[0]) - 1
            if idx == index:
                self.actions_tree.selection_set(item)
                self.actions_tree.see(item)
                break

    def stop_automation(self):
        """停止执行"""
        self.is_running = False
        self.status_var.set("已停止执行")
        self.reset_ui()

    def reset_ui(self):
        """重置UI状态"""
        self.is_running = False
        self.start_btn.configure(state="normal")
        self.stop_btn.configure(state="disabled")

    def clear_actions(self):
        """清空动作列表"""
        if messagebox.askyesno("确认", "确定要清空所有动作吗?"):
            self.actions = []
            self.update_actions_tree()
            self.status_var.set("动作列表已清空")

def main():
    root = tk.Tk()
    app = EnhancedAutoClickerApp(root)

    # 设置ESC键为紧急停止
    keyboard.add_hotkey('esc', app.stop_automation)

    root.mainloop()

if __name__ == "__main__":
        main()

实现一个自动化点击工具,使用Python的tkinter构建GUI界面,结合pyautogui和keyboard库实现自动化操作。

核心功能概述

  1. 自动化操作执行:可以按设定的循环次数和时间间隔自动执行一系列预定义的操作
  2. 多种操作类型支持

    • 鼠标点击(记录坐标)
    • 键盘按键
    • 粘贴操作(Ctrl+V)
    • 回车键操作
  3. 动作管理功能

    • 添加/编辑/删除动作
    • 拖拽排序动作顺序
    • 清空动作列表
  4. 实时捕获鼠标位置:通过F2热键记录当前鼠标位置
  5. 紧急停止机制:通过ESC键可随时中断执行

代码结构分析

1. Action类

这是一个表示自动化操作的基础类,具有以下特点:

class Action:
    def __init__(self, action_type, x=0, y=0, key=None, description=None):
        # 初始化操作类型和参数
  • 操作类型:支持"click"、"key"、"paste"、"enter"四种类型
  • 参数存储:存储坐标(x,y)、按键名称等
  • 描述生成:自动生成操作的文字描述
  • 执行方法execute()方法根据操作类型调用pyautogui执行相应动作

2. EnhancedAutoClickerApp类

主应用类,实现了GUI界面和核心功能:

初始化和界面构建

def __init__(self, root):
    # 初始化应用
    # 设置F2热键监听

def create_widgets(self):
    # 构建复杂的GUI界面元素
  • 创建主窗口和各种控件
  • 设置布局和事件绑定
  • 初始化数据结构和状态变量

动作管理功能

def add_action(self, action):
    # 添加动作到列表

def edit_action(self):
    # 编辑选中的动作

def delete_action(self):
    # 删除选中的动作
  • 支持添加、编辑和删除动作
  • 使用对话框收集用户输入
  • 实时更新界面显示

拖放排序功能

def on_tree_press(self, event):
    # 开始拖动

def on_tree_motion(self, event):
    # 拖动过程

def on_tree_release(self, event):
    # 完成拖动并重排序
  • 实现了Treeview控件中的项目拖放
  • 支持动作顺序的直观调整
  • 拖放后自动更新数据和界面

鼠标位置捕获

def start_mouse_capture(self):
    # 开始捕获鼠标位置

def on_f2_pressed(self):
    # 当按下F2时,捕获鼠标位置
  • 通过F2热键实时捕获鼠标坐标
  • 自动添加为点击操作

自动化执行

def start_automation(self):
    # 开始执行自动化任务

def run_automation(self, loops, delay):
    # 在线程中执行自动化任务
  • 使用单独线程执行操作,避免界面卡死
  • 支持设定循环次数和操作间隔
  • 执行过程中高亮显示当前操作
  • 提供紧急停止机制

技术亮点

  1. 多线程设计:使用threading模块创建后台线程执行自动化任务,保持GUI响应性
  2. 热键集成:通过keyboard库实现全局热键(F2捕获位置,ESC紧急停止)
  3. 拖放排序:实现了自定义的拖放排序功能,增强用户体验
  4. 上下文菜单:右键菜单提供快捷操作
  5. 状态管理:清晰的状态变量和UI状态同步机制
  6. 异常处理:完善的错误处理和用户反馈

使用场景

这个工具适用于多种自动化场景:

  1. 重复性表单填写:自动点击表单字段并输入数据
  2. 游戏自动化:执行重复性游戏操作(如自动点击、自动钓鱼等)
  3. 软件测试:自动执行UI测试步骤
  4. 批量处理:自动执行多步骤的文件处理操作
  5. 数据录入:简化重复性数据录入工作

可能的改进点

  1. 配置保存/加载:添加保存和加载操作序列的功能
  2. 条件分支:增加条件判断和分支执行能力
  3. 图像识别:整合图像识别功能,基于屏幕内容执行操作
  4. 延时随机化:添加随机延时选项,使自动化操作更像人工操作
  5. 日志记录:添加详细的执行日志功能

这改进有空再做哈哈哈哈哈,

使用截图:

image-20250403172254335.png

关注@运维躬行录,获取【按键精灵】。转发本文至三个技术群,可免费领取《python自动化办公教程》祝你效率翻倍

标签: none