import tkinter as tk
import time
import pyautogui
import threading
from pynput import mouse, keyboard
class MacroRecorder:
def __init__(self, root):
self.root = root
self.root.title("Запись и воспроизведение действий")
self.recording = False
self.actions = []
self.last_position = None
self.min_distance = 10 # Минимальное расстояние между записываемыми движениями мыши
self.speed_multiplier = 2.0 # Множитель скорости (больше = быстрее)
# Создаем описание использования
description = """
Нажмите F9, чтобы начать запись действий мыши.
Нажмите F9 снова, чтобы остановить запись.
Нажмите F10, чтобы воспроизвести записанные действия.
"""
desc_label = tk.Label(root, text=description, justify=tk.LEFT)
desc_label.pack(pady=10, padx=10)
# Настройки скорости
speed_frame = tk.Frame(root)
speed_frame.pack(pady=5)
tk.Label(speed_frame, text="Скорость воспроизведения:").pack(side=tk.LEFT)
self.speed_var = tk.DoubleVar(value=self.speed_multiplier)
self.speed_scale = tk.Scale(speed_frame, from_=0.5, to=10.0, resolution=0.5,
orient=tk.HORIZONTAL, variable=self.speed_var,
command=self.update_speed)
self.speed_scale.pack(side=tk.LEFT, padx=5)
# Статус
self.status_label = tk.Label(root, text="Готов к записи")
self.status_label.pack(pady=10)
# Настройка мониторинга клавиатуры для горячих клавиш
self.key_listener = keyboard.Listener(on_press=self.on_key_press)
self.key_listener.start()
# Инициализация слушателя мыши (будет запущен при записи)
self.mouse_listener = None
def update_speed(self, val):
self.speed_multiplier = float(val)
def on_key_press(self, key):
try:
if key == keyboard.Key.f9:
self.toggle_recording()
elif key == keyboard.Key.f10:
self.playback()
except AttributeError:
pass
def on_mouse_move(self, x, y):
if self.recording:
# Пропускаем запись мелких движений для оптимизации
if self.last_position is None or self.calculate_distance(self.last_position, (x, y)) >= self.min_distance:
self.actions.append(('move', time.time(), (x, y), None))
self.last_position = (x, y)
def calculate_distance(self, pos1, pos2):
return ((pos1[0] - pos2[0]) ** 2 + (pos1[1] - pos2[1]) ** 2) ** 0.5
def on_mouse_click(self, x, y, button, pressed):
if self.recording:
state = 'down' if pressed else 'up'
btn = 'left' if button == mouse.Button.left else 'right'
self.actions.append(('click', time.time(), (x, y), (btn, state)))
self.last_position = (x, y) # Обновляем последнюю позицию при клике
def toggle_recording(self):
if not self.recording:
self.recording = True
self.actions = []
self.last_position = None
self.status_label.config(text="Запись... (F9 для остановки)")
# Запускаем слушателя мыши
self.mouse_listener = mouse.Listener(
on_move=self.on_mouse_move,
on_click=self.on_mouse_click
)
self.mouse_listener.start()
else:
self.recording = False
if self.mouse_listener:
self.mouse_listener.stop()
self.mouse_listener = None
self.status_label.config(text=f"Записано {len(self.actions)} действий")
def playback(self):
if not self.actions:
self.status_label.config(text="Нет записанных действий")
return
self.status_label.config(text="Воспроизведение...")
playback_thread = threading.Thread(target=self._playback)
playback_thread.daemon = True
playback_thread.start()
def _playback(self):
# Даем пользователю время переключиться из приложения
time.sleep(1)
# Если это первое действие, нет необходимости ждать
last_time = self.actions[0][1]
pyautogui.PAUSE = 0.01 # Установим минимальную паузу между действиями PyAutoGUI
for action, timestamp, position, extra in self.actions:
# Вычисляем, сколько времени нужно подождать, но с учетом скорости
time_to_wait = (timestamp - last_time) / self.speed_multiplier
# Ограничиваем максимальное время ожидания
time_to_wait = min(time_to_wait, 0.2)
if time_to_wait > 0:
time.sleep(time_to_wait)
x, y = position
if action == 'move':
pyautogui.moveTo(x, y, duration=0.01) # Короткая продолжительность для быстрых движений
elif action == 'click':
btn, state = extra
if state == 'down':
pyautogui.mouseDown(x=x, y=y, button=btn)
elif state == 'up':
pyautogui.mouseUp(x=x, y=y, button=btn)
last_time = timestamp
# Обновляем статус в главном потоке
self.root.after(0, lambda: self.status_label.config(text="Воспроизведение завершено"))
if __name__ == "__main__":
root = tk.Tk()
app = MacroRecorder(root)
root.geometry("400x250")
root.mainloop()