Python程序是一个基于Tkinter的GUI应用程序,用于录制和回放用户的鼠标和键盘操作

总结

这个Python程序是一个基于Tkinter的GUI应用程序,用于录制和回放用户的鼠标和键盘操作。主要功能包括:

  1. 录制功能

    • 用户可以选择录制哪些类型的动作(如单击、双击、拖动、滚动、按键、移动)。
    • 通过按 F1 键可以开始或停止录制。
    • 录制的动作会显示在一个列表框中。
  2. 回放功能

    • 用户可以通过输入重复次数、延迟时间和间隔时间来配置回放参数。
    • 通过按 F2 键可以开始回放录制的动作。
    • 通过按 F3 键可以停止正在进行的回放。
    • 回放的状态会在状态面板中显示。
  3. 保存和加载功能

    • 用户可以将录制的动作保存到文件中,默认文件名为"录制自动点击脚本.txt"。
    • 用户可以从文件中加载之前保存的动作。
    • 通过按 F4 键可以保存录制的动作。
    • 通过按 F5 键可以加载录制的动作。
  4. 其他功能

    • 用户可以通过按 F6 键启动循环点击模式。
    • 用户可以通过按 F7 键清除当前所有的录制动作。

主要组件

  • ActionManager: 负责管理录制的动作,包括添加、获取、清除动作以及保存和加载动作到文件。
  • RecordingController: 控制录制过程,监听鼠标的移动、点击、滚动事件以及键盘的按键事件,并记录相应的动作。
  • Controller: 控制回放过程,负责执行录制的动作并处理相关的状态更新。
  • RecordingPlaybackTab: 构建GUI界面,包含录制、回放、保存、加载等功能的控件,并绑定相应的事件处理函数。

这个程序适合需要自动化测试、演示或其他需要模拟用户操作的场景。

python 复制代码
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from pynput import mouse, keyboard
from pynput.mouse import Listener as MouseListener
from pynput.keyboard import Listener as KeyboardListener
from threading import Thread
import time
import random  # 导入 random 模块


class ActionManager:
    def __init__(self):
        self.actions = []

    def add_action(self, action):
        self.actions.append(action)

    def get_actions(self):
        return self.actions

    def clear_actions(self):
        self.actions.clear()

    def save_to_file(self, file_path):
        with open(file_path, 'w') as file:
            for action in self.actions:
                file.write(str(action) + '\n')

    def load_from_file(self, file_path):
        self.actions.clear()
        with open(file_path, 'r') as file:
            for line in file:
                action = eval(line.strip())
                self.actions.append(action)


class RecordingController:
    def __init__(self, action_manager, update_action_listbox, cycle_clicks, toggle_recording,
                 record_single_click, record_double_click, record_dragging, record_scroll, record_key_press, record_mouse_move):
        self.action_manager = action_manager
        self.update_action_listbox = update_action_listbox
        self.cycle_clicks = cycle_clicks
        self.toggle_recording = toggle_recording
        self.recording = False
        self.mouse_listener = None
        self.keyboard_listener = None
        self.mouse = mouse.Controller()
        self.keyboard = keyboard.Controller()
        self.drag_start_pos = None
        self.dragging = False
        self.record_single_click = record_single_click
        self.record_double_click = record_double_click
        self.record_dragging = record_dragging
        self.record_scroll = record_scroll
        self.record_key_press = record_key_press
        self.record_mouse_move = record_mouse_move

    def on_move(self, x, y):
        if self.recording and self.dragging and self.record_dragging.get():
            action = {"type": "mouse_move", "position": (x, y)}
            self.action_manager.add_action(action)
            self.update_action_listbox(action)
        elif self.recording and self.record_mouse_move.get():
            action = {"type": "mouse_move", "position": (x, y)}
            self.action_manager.add_action(action)
            self.update_action_listbox(action)

    def on_click(self, x, y, button, pressed):
        if self.recording:
            if not pressed and self.dragging and self.record_dragging.get():
                self.dragging = False
                action = {"type": "mouse_release", "position": (x, y), "button": str(button)}
                self.action_manager.add_action(action)
                self.update_action_listbox(action)
            elif pressed and not self.dragging and self.record_dragging.get():
                self.dragging = True
                self.drag_start_pos = (x, y)
                action = {"type": "mouse_press", "position": (x, y), "button": str(button)}
                self.action_manager.add_action(action)
                self.update_action_listbox(action)
            else:
                action = {"type": "mouse_click", "position": (x, y), "button": str(button), "pressed": pressed}
                if self.record_single_click.get() or (not pressed and self.record_double_click.get()):
                    self.action_manager.add_action(action)
                    self.update_action_listbox(action)

    def on_scroll(self, x, y, dx, dy):
        if self.recording and self.record_scroll.get():
            action = {"type": "mouse_scroll", "position": (x, y), "dx": dx, "dy": dy}
            self.action_manager.add_action(action)
            self.update_action_listbox(action)

    def on_press(self, key):
        try:
            k = key.char
        except AttributeError:
            k = f"{key}"

        if self.recording and self.record_key_press.get():
            action = {"type": "key_press", "key": k}
            self.action_manager.add_action(action)
            self.update_action_listbox(action)

        if key == keyboard.Key.f1:
            self.toggle_recording()
        elif key == keyboard.Key.f2:
            self.start_playing()
        elif key == keyboard.Key.f3:
            self.stop_playing()
        elif key == keyboard.Key.f4:
            self.save_recorded_actions()
        elif key == keyboard.Key.f5:
            self.load_recorded_actions()
        elif key == keyboard.Key.f6:
            self.cycle_clicks()
        elif key == keyboard.Key.f7:
            self.clear_recorded_actions()

    def start_recording(self):
        if self.recording:
            return

        self.recording = True
        self.mouse_listener = MouseListener(on_move=self.on_move, on_click=self.on_click, on_scroll=self.on_scroll)
        self.keyboard_listener = KeyboardListener(on_press=self.on_press)
        self.mouse_listener.start()
        self.keyboard_listener.start()

    def stop_recording(self):
        if not self.recording:
            return

        self.recording = False
        self.mouse_listener.stop()
        self.keyboard_listener.stop()


class Controller:
    def __init__(self, action_manager, recording_controller, update_action_listbox, update_status_panel):
        self.action_manager = action_manager
        self.recording_controller = recording_controller
        self.update_action_listbox = update_action_listbox
        self.update_status_panel = update_status_panel
        self.playing = False
        self.thread = None

    def start_playing(self, repeat_times, min_interval, max_interval, delay):
        if self.playing:
            return

        self.playing = True
        self.thread = Thread(target=self._play_actions, args=(repeat_times, min_interval, max_interval, delay))
        self.thread.start()

    def _play_actions(self, repeat_times, min_interval, max_interval, delay):
        actions = self.action_manager.get_actions()
        if not actions:
            self.update_status_panel("没有可用的动作进行播放。")
            self.playing = False
            return

        time.sleep(delay / 1000)  # Delay before starting playback

        for _ in range(repeat_times):
            if not self.playing:
                break
            for action in actions:
                if not self.playing:
                    break
                interval = random.uniform(min_interval, max_interval)  # Generate a random interval within the specified range
                if action["type"] == "mouse_click":
                    button = getattr(mouse.Button, action["button"].split('.')[1]) if '.' in action["button"] else mouse.Button.left
                    self.recording_controller.mouse.position = action["position"]
                    if action["pressed"]:
                        self.recording_controller.mouse.press(button)
                    else:
                        self.recording_controller.mouse.release(button)
                elif action["type"] == "mouse_scroll":
                    self.recording_controller.mouse.position = action["position"]
                    self.recording_controller.mouse.scroll(action["dx"], action["dy"])
                elif action["type"] == "key_press":
                    key = action["key"]
                    if key.startswith('Key'):
                        key = getattr(keyboard.Key, key.split('.')[1])
                    self.recording_controller.keyboard.press(key)
                    self.recording_controller.keyboard.release(key)
                elif action["type"] == "mouse_move":
                    self.recording_controller.mouse.position = action["position"]
                elif action["type"] == "mouse_press":
                    button = getattr(mouse.Button, action["button"].split('.')[1]) if '.' in action["button"] else mouse.Button.left
                    self.recording_controller.mouse.position = action["position"]
                    self.recording_controller.mouse.press(button)
                elif action["type"] == "mouse_release":
                    button = getattr(mouse.Button, action["button"].split('.')[1]) if '.' in action["button"] else mouse.Button.left
                    self.recording_controller.mouse.position = action["position"]
                    self.recording_controller.mouse.release(button)
                time.sleep(interval)

        self.playing = False
        self.update_status_panel("回放已完成")

    def stop_playing(self):
        self.playing = False
        if self.thread and self.thread.is_alive():
            self.thread.join()


class RecordingPlaybackTab(ttk.Frame):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent

        self.repeat_times = 1  # 默认重复次数1次
        self.delay = 0  # 默认延迟为0ms
        self.min_interval = 0.01  # 默认最小间隔时间为0.1秒
        self.max_interval = 0.1  # 默认最大间隔时间为1秒

        self.action_manager = ActionManager()
        self.record_single_click = tk.BooleanVar(value=False)
        self.record_double_click = tk.BooleanVar(value=False)
        self.record_dragging = tk.BooleanVar(value=False)
        self.record_scroll = tk.BooleanVar(value=False)
        self.record_key_press = tk.BooleanVar(value=False)
        self.record_mouse_move = tk.BooleanVar(value=False)

        self.recording_controller = RecordingController(
            self.action_manager,
            self.update_action_listbox,
            self.cycle_clicks,
            self.toggle_recording,
            self.record_single_click,
            self.record_double_click,
            self.record_dragging,
            self.record_scroll,
            self.record_key_press,
            self.record_mouse_move
        )
        self.controller = Controller(self.action_manager, self.recording_controller, self.update_action_listbox, self.update_status_panel)

        self.create_widgets()

    def create_widgets(self):
        # 设置主框架
        main_frame = ttk.Frame(self)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 录制和回放控制区
        control_frame = ttk.Frame(main_frame)
        control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=10)

        # 重复次数
        repeat_times_frame = ttk.Frame(control_frame)
        repeat_times_label = ttk.Label(repeat_times_frame, text="重复次数:")
        self.repeat_times_entry = ttk.Entry(repeat_times_frame, width=5)
        self.repeat_times_entry.insert(0, str(self.repeat_times))  # 默认1次
        repeat_times_label.grid(row=0, column=0, padx=(10, 0), pady=(10, 0))
        self.repeat_times_entry.grid(row=0, column=1, padx=(0, 10), pady=(10, 0))
        repeat_times_frame.pack(padx=10, fill=tk.X)

        # 延迟
        delay_frame = ttk.Frame(control_frame)
        delay_label = ttk.Label(delay_frame, text="延迟(ms):")
        self.delay_entry = ttk.Entry(delay_frame, width=5)
        self.delay_entry.insert(0, str(self.delay))  # 默认0ms
        delay_label.grid(row=0, column=0, padx=(10, 0), pady=(10, 0))
        self.delay_entry.grid(row=0, column=1, padx=(0, 10), pady=(10, 0))
        delay_frame.pack(padx=10, fill=tk.X)

        # 最小间隔时间
        min_interval_frame = ttk.Frame(control_frame)
        min_interval_label = ttk.Label(min_interval_frame, text="最小间隔时间(秒):")
        self.min_interval_entry = ttk.Entry(min_interval_frame, width=5)
        self.min_interval_entry.insert(0, str(self.min_interval))  # 默认值0.1秒
        min_interval_label.grid(row=0, column=0, padx=(10, 0), pady=(10, 0))
        self.min_interval_entry.grid(row=0, column=1, padx=(0, 10), pady=(10, 0))
        min_interval_frame.pack(padx=10, fill=tk.X)

        # 最大间隔时间
        max_interval_frame = ttk.Frame(control_frame)
        max_interval_label = ttk.Label(max_interval_frame, text="最大间隔时间(秒):")
        self.max_interval_entry = ttk.Entry(max_interval_frame, width=5)
        self.max_interval_entry.insert(0, str(self.max_interval))  # 默认值1秒
        max_interval_label.grid(row=0, column=0, padx=(10, 0), pady=(10, 0))
        self.max_interval_entry.grid(row=0, column=1, padx=(0, 10), pady=(10, 0))
        max_interval_frame.pack(padx=10, fill=tk.X)

        # 录制选项
        record_options_frame = ttk.LabelFrame(control_frame, text="录制选项")
        record_options_frame.pack(padx=10, pady=10, fill=tk.X)

        single_click_checkbox = ttk.Checkbutton(record_options_frame, text="单击", variable=self.record_single_click)
        double_click_checkbox = ttk.Checkbutton(record_options_frame, text="双击", variable=self.record_double_click)
        dragging_checkbox = ttk.Checkbutton(record_options_frame, text="拖动", variable=self.record_dragging)
        scroll_checkbox = ttk.Checkbutton(record_options_frame, text="滚动", variable=self.record_scroll)
        key_press_checkbox = ttk.Checkbutton(record_options_frame, text="按键", variable=self.record_key_press)
        mouse_move_checkbox = ttk.Checkbutton(record_options_frame, text="移动", variable=self.record_mouse_move)

        single_click_checkbox.grid(row=0, column=0, sticky=tk.W, padx=10, pady=5)
        double_click_checkbox.grid(row=1, column=0, sticky=tk.W, padx=10, pady=5)
        dragging_checkbox.grid(row=2, column=0, sticky=tk.W, padx=10, pady=5)

        scroll_checkbox.grid(row=0, column=1, sticky=tk.W, padx=10, pady=5)
        key_press_checkbox.grid(row=1, column=1, sticky=tk.W, padx=10, pady=5)
        mouse_move_checkbox.grid(row=2, column=1, sticky=tk.W, padx=10, pady=5)

        # 录制按钮
        record_button = ttk.Button(
            control_frame, text="开始/停止录制 (F7)", command=self.toggle_recording
        )
        record_button.pack(pady=(10, 0))

        # 清除录制按钮
        clear_button = ttk.Button(
            control_frame, text="清除录制 (F1)", command=self.clear_recorded_actions
        )
        clear_button.pack(pady=(10, 0))

        # 回放按钮
        play_button = ttk.Button(
            control_frame, text="开始回放 (F2)", command=self.start_playing
        )
        play_button.pack(pady=(10, 0))

        # 停止回放按钮
        self.stop_button = ttk.Button(
            control_frame, text="停止回放 (F3)", command=self.stop_playing, state=tk.DISABLED
        )
        self.stop_button.pack(pady=(10, 0))

        # 保存录制的动作到文件
        save_button = ttk.Button(
            control_frame, text="保存录制的动作 (F4)", command=self.save_recorded_actions
        )
        save_button.pack(pady=(10, 0))

        # 加载录制的动作从文件
        load_button = ttk.Button(
            control_frame, text="加载录制的动作 (F5)", command=self.load_recorded_actions
        )
        load_button.pack(pady=(10, 0))

        # 记录的动作列表
        self.action_listbox = tk.Listbox(main_frame, height=30, width=100)
        self.action_listbox.pack(pady=(10, 0), fill=tk.BOTH, expand=True)

        # 状态面板
        status_frame = ttk.Frame(main_frame)
        status_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=10)

        self.status_text = tk.Text(status_frame, wrap=tk.WORD, height=10, width=100)
        self.status_text.pack(expand=True, fill=tk.BOTH, padx=10, pady=10)

    def toggle_recording(self):
        if self.recording_controller.recording:
            self.recording_controller.stop_recording()
            self.update_action_listbox()
            self.update_status_panel("录制已停止")
        else:
            self.recording_controller.start_recording()
            self.update_status_panel("正在录制... 按 F1 结束录制")

    def update_action_listbox(self, action=None):
        self.action_listbox.delete(0, tk.END)
        for action in self.action_manager.get_actions():
            formatted_action = self.format_action(action)
            self.action_listbox.insert(tk.END, formatted_action)

    def format_action(self, action):
        if action["type"] == "mouse_click":
            press_state = "按下" if action["pressed"] else "释放"
            return f"鼠标点击 ({action['position'][0]}, {action['position'][1]}), 按钮: {action['button']}, 状态: {press_state}"
        elif action["type"] == "mouse_scroll":
            return f"鼠标滚轮 ({action['position'][0]}, {action['position'][1]}), 变量: ({action['dx']}, {action['dy']})"
        elif action["type"] == "key_press":
            return f"按键: {action['key']}"
        elif action["type"] == "mouse_move":
            return f"鼠标移动到 ({action['position'][0]}, {action['position'][1]})"
        elif action["type"] == "mouse_press":
            return f"鼠标按住 ({action['position'][0]}, {action['position'][1]}), 按钮: {action['button']}"
        elif action["type"] == "mouse_release":
            return f"鼠标释放 ({action['position'][0]}, {action['position'][1]}), 按钮: {action['button']}"
        return ""

    def validate_interval(self, value_if_allowed):
        try:
            if float(value_if_allowed) <= 0 or float(value_if_allowed) > 10:
                raise ValueError
            return True
        except ValueError:
            return False

    def start_playing(self):
        try:
            repeat_times = int(self.repeat_times_entry.get())
            delay = int(self.delay_entry.get())
            min_interval = float(self.min_interval_entry.get())
            max_interval = float(self.max_interval_entry.get())
        except ValueError:
            self.update_status_panel("请输入有效的数值。")
            return

        if min_interval < 0 or min_interval > 60 or max_interval < 0 or max_interval > 60 or min_interval >= max_interval:
            messagebox.showwarning("无效的间隔时间", "请将最小和最大间隔时间设置在0.1秒到10秒之间,并且最小间隔时间应小于最大间隔时间。")
            return

        self.controller.start_playing(repeat_times, min_interval, max_interval, delay)
        self.stop_button["state"] = tk.NORMAL

    def stop_playing(self):
        self.controller.stop_playing()
        self.stop_button["state"] = tk.DISABLED

    def save_recorded_actions(self):
        default_filename = "录制自动点击脚本.txt"
        file_path = filedialog.asksaveasfilename(defaultextension=".txt",
                                                initialfile=default_filename,
                                                filetypes=[("文本文件", "*.txt"),
                                                           ("所有文件", "*.*")])
        if file_path:
            self.action_manager.save_to_file(file_path)
            self.update_status_panel(f"动作已保存到 {file_path}")

    def load_recorded_actions(self):
        file_path = filedialog.askopenfilename(filetypes=[("文本文件", "*.txt"),
                                                         ("所有文件", "*.*")])
        if file_path:
            self.action_manager.load_from_file(file_path)
            self.update_action_listbox()
            self.update_status_panel(f"动作已从 {file_path} 加载")

    def cycle_clicks(self):
        if self.action_manager.get_actions():
            self.controller.start_playing(999, 0, 0, 0)  # 循环点击,无间隔,无延迟
            self.update_status_panel("循环点击启动")

    def clear_recorded_actions(self):
        self.action_manager.clear_actions()
        self.update_action_listbox()
        self.update_status_panel("录制已清除")

    def update_status_panel(self, message):
        self.status_text.insert(tk.END, message + "\n")
        self.status_text.see(tk.END)


if __name__ == "__main__":
    root = tk.Tk()
    root.title("动作录制与回放工具")
    tab = RecordingPlaybackTab(root)
    tab.pack(fill=tk.BOTH, expand=True)

    # 监听全局键盘事件
    global_keyboard_listener = KeyboardListener(on_press=lambda key: tab.recording_controller.on_press(key))
    global_keyboard_listener.start()

    root.mainloop()
相关推荐
图灵追慕者几秒前
基于turtle库的圣诞树的绘制
python·turtle·圣诞树
天天要nx几秒前
D93【python 接口自动化学习】- pytest基础用法
python·pytest
sagima_sdu1 小时前
Python 程序与 Java 系统集成:通过 FastAPI 实现 HTTP 接口
java·python·fastapi
液态不合群1 小时前
[Java] Stream流使用最多的方式
java·windows·python
weixin_307779131 小时前
大数据、云计算和容器技术软件开发技能笔试题
大数据·python·spark·云计算·aws
A Runner for leave2 小时前
146.组合总和
java·数据结构·python·算法·leetcode
魏+Mtiao15_2 小时前
矩阵源代码部署与功能简介
人工智能·python·线性代数·矩阵·php·音视频
VIT199801062 小时前
AI实现葡萄叶片识别(基于深度学习的葡萄叶片识别)
人工智能·python·深度学习
人生の三重奏2 小时前
django项目2——django版本为3.xx或者4.xx的创建哈
python·django·sqlite