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()