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

news/2024/12/13 23:41:38/

总结

这个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.actionsdef 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_managerself.update_action_listbox = update_action_listboxself.cycle_clicks = cycle_clicksself.toggle_recording = toggle_recordingself.recording = Falseself.mouse_listener = Noneself.keyboard_listener = Noneself.mouse = mouse.Controller()self.keyboard = keyboard.Controller()self.drag_start_pos = Noneself.dragging = Falseself.record_single_click = record_single_clickself.record_double_click = record_double_clickself.record_dragging = record_draggingself.record_scroll = record_scrollself.record_key_press = record_key_pressself.record_mouse_move = record_mouse_movedef 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 = Falseaction = {"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 = Trueself.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.charexcept 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:returnself.recording = Trueself.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:returnself.recording = Falseself.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_managerself.recording_controller = recording_controllerself.update_action_listbox = update_action_listboxself.update_status_panel = update_status_panelself.playing = Falseself.thread = Nonedef start_playing(self, repeat_times, min_interval, max_interval, delay):if self.playing:returnself.playing = Trueself.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 = Falsereturntime.sleep(delay / 1000)  # Delay before starting playbackfor _ in range(repeat_times):if not self.playing:breakfor action in actions:if not self.playing:breakinterval = random.uniform(min_interval, max_interval)  # Generate a random interval within the specified rangeif action["type"] == "mouse_click":button = getattr(mouse.Button, action["button"].split('.')[1]) if '.' in action["button"] else mouse.Button.leftself.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.leftself.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.leftself.recording_controller.mouse.position = action["position"]self.recording_controller.mouse.release(button)time.sleep(interval)self.playing = Falseself.update_status_panel("回放已完成")def stop_playing(self):self.playing = Falseif self.thread and self.thread.is_alive():self.thread.join()class RecordingPlaybackTab(ttk.Frame):def __init__(self, parent):super().__init__(parent)self.parent = parentself.repeat_times = 1  # 默认重复次数1次self.delay = 0  # 默认延迟为0msself.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))  # 默认0msdelay_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 ValueErrorreturn Trueexcept ValueError:return Falsedef 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("请输入有效的数值。")returnif 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秒之间,并且最小间隔时间应小于最大间隔时间。")returnself.controller.start_playing(repeat_times, min_interval, max_interval, delay)self.stop_button["state"] = tk.NORMALdef stop_playing(self):self.controller.stop_playing()self.stop_button["state"] = tk.DISABLEDdef 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()


http://www.ppmy.cn/news/1554878.html

相关文章

ThinkPHP框架审计--基础

基础入门 搭建好thinkphp 查看版本方法&#xff0c;全局搜version 根据开发手册可以大致了解该框架的路由 例如访问url http://127.0.0.1:8094/index.php/index/index/index 对应代码位置 例如在代码下面添加新方法 那么访问这个方法的url就是 http://127.0.0.1:8094/index.…

视频自定义全屏功能——兼容安卓和ios

视频窗口 <div class"relative bg-black" style"width: 100%; height: 30vh"><div id"currentVideo" class"w100 h100 absolute"><div class"absolute flex_r w100" style"bottom: 0"><divv…

【OpenCV】视频录制

介绍 在 OpenCV 中&#xff0c;视频录制涉及到读取摄像头或视频文件的帧&#xff0c;并将这些帧保存到新的视频文件中。为了实现这个过程&#xff0c;你需要创建一个 VideoCapture 对象来获取视频帧&#xff0c;以及一个 VideoWriter 对象来保存视频帧。以下是使用 OpenCV 进行…

四、vue多事件处理器

<template> <view> <!-- 这两个 one() 和 two() 将执行按钮点击事件 --> <button click"one($event); two($event)"> Submit </button> </view> </t…

经纬度解析到省市区【开源】

现在业务中有需要解析经纬度到省市区。 按理说可以直接使用高德&#xff0c;百度之类的。 但是老板太抠。于是去找开源项目。找了一圈&#xff0c;数据都太老了&#xff0c;而且有时候编码还不匹配。 所以诞生了这个项目&#xff0c;提供完整的一套省市区编码和定位反解析。…

软考中级-软件设计师通过心路经验分享

执念&#xff0c;第四次终于通过了 没买书&#xff0c;下班后每天2小时&#xff0c;四个2个月终于过了 学习经验&#xff1a; 1.下班后学习真的靠毅力&#xff0c;和上学的时候考证不是一个状态&#xff0c;大家要及时调整&#xff0c;否则过程很痛苦 2.失败三次的经验&#xf…

自定义生成小游戏提现截图工具的设计与实现

摘要 在游戏开发和测试过程中&#xff0c;开发者经常需要生成游戏截图以展示游戏界面或用于测试。本文将介绍一个自定义生成小游戏截图的工具&#xff0c;该工具可以帮助开发者快速生成游戏界面截图&#xff0c;以满足不同场景下的需求。 视频演示 引言 随着移动游戏市场的迅…

iview date-picker options只可选当前日期之前的

iview date-picker options只可选当前日期之前的 如果日期对象date的时间戳大于当前时间的时间戳&#xff0c;则该日期在当前日期之后&#xff0c;会被禁用。 <template><Row><Col span"12"><DatePickertype"datetime":options&qu…