感谢您提供的详细日志信息。根据您的反馈,问题可能出在 `pyautogui` 没有正确获取鼠标焦点或无法在预期的位置执行点击操作。我们将采取以下步骤来进一步诊断和解决这个问题:
1. **确保 `pyautogui` 正确执行点击操作**:
- 我们将添加更多的调试信息来确认 `pyautogui` 是否真的尝试执行点击操作。
- 使用 `pyautogui.position()` 来验证当前鼠标位置是否符合预期。
2. **检查权限和环境限制**:
- 确保脚本有足够的权限来控制鼠标。
- 检查是否有任何安全软件阻止了自动化操作。
3. **使用绝对坐标进行测试**:
- 直接使用绝对坐标调用 `pyautogui.click()` 来确认是否可以执行点击操作。
4. **简化回放逻辑**:
- 创建一个简单的回放函数,只包含基本的鼠标点击操作,以排除其他因素的影响。
### 第一步:确保 `pyautogui` 正确执行点击操作
我们将在 `_async_play_actions` 方法中添加更多调试信息,并使用 `pyautogui.position()` 来验证当前鼠标位置。
### 第二步:检查权限和环境限制
确保脚本有足够的权限来控制鼠标。您可以尝试以管理员身份运行脚本,或者检查系统设置中的安全性选项。
### 第三步:使用绝对坐标进行测试
直接使用绝对坐标调用 `pyautogui.click()` 来确认是否可以执行点击操作。
### 第四步:简化回放逻辑
创建一个简单的回放函数,只包含基本的鼠标点击操作,以排除其他因素的影响。
以下是更新后的代码,包含了上述改进措施:
### 主要修改点
1. **增加调试信息**:
- 在 `_async_play_actions` 方法中,增加了对当前位置的验证,即在每次鼠标点击后打印当前鼠标的实际位置。
- 例如,`current_position = pyautogui.position()` 和 `self.status_callback(f"当前位置: ({current_position.x}, {current_position.y})")`。
2. **使用绝对坐标进行点击**:
- 在 `pyautogui.mouseDown` 和 `pyautogui.mouseUp` 中指定了具体的坐标位置,确保点击操作在正确的坐标上执行。
3. **其他改进**:
- 添加了一些注释以便更好地理解代码逻辑。
- 确保所有输入字段都有合理的默认值和提示。
### 运行代码
确保您的虚拟环境已经激活,然后运行脚本:
```sh
python 666.py
```
### 调试步骤
1. **录制和回放**:
- 使用 `AutomaticClickerApp` 进行录制。
- 查看状态面板中的输出,确认所有动作都被正确记录。
- 开始回放,并再次查看状态面板中的输出,确认每个动作都被正确执行。
- 特别注意每次鼠标点击后的“当前位置”信息,确认它与预期的点击位置一致。
2. **手动测试点击**:
- 手动在终端或命令行中运行以下命令,确保 `pyautogui` 可以正确执行点击操作:
```sh
python -c "import pyautogui; pyautogui.click(x=100, y=100)"
```
- 观察屏幕上的鼠标点击行为,确认点击操作是否在预期的位置发生。
通过这些步骤,我们应该能够确定问题的具体原因。如果仍然存在问题,请提供更多的详细信息,例如录制的动作列表和回放时的状态面板输出,特别是“当前位置”的信息,以便进一步诊断问题。
import tkinter as tk
from tkinter import ttk, filedialog
import pyautogui
import time
import threading
from pynput import mouse, keyboard
class ActionManager:
def __init__(self):
self.actions = []
def add_action(self, action):
current_time = time.time()
if self.actions:
action["time"] = current_time - self.actions[-1]["timestamp"]
else:
action["time"] = 0
action["timestamp"] = current_time
self.actions.append(action)
def clear_actions(self):
self.actions.clear()
def get_actions(self):
return self.actions
def save_to_file(self, filepath):
with open(filepath, 'w') as file:
for action in self.actions:
file.write(str(action) + '\n')
def load_from_file(self, filepath):
self.actions.clear()
with open(filepath, 'r') as file:
for line in file:
action = eval(line.strip())
self.actions.append(action)
class EventListener:
def __init__(self, action_manager, update_callback, f6_callback):
self.action_manager = action_manager
self.recording = False
self.mouse_listener = None
self.keyboard_listener = None
self.update_callback = update_callback
self.f6_callback = f6_callback
def start_recording(self):
self.recording = True
self.mouse_listener = mouse.Listener(
on_click=self.on_mouse_click,
on_scroll=self.on_mouse_scroll
)
self.keyboard_listener = keyboard.Listener(on_press=self.on_key_press)
self.mouse_listener.start()
self.keyboard_listener.start()
def stop_recording(self):
self.recording = False
if self.mouse_listener:
self.mouse_listener.stop()
if self.keyboard_listener:
self.keyboard_listener.stop()
def on_mouse_click(self, x, y, button, pressed):
if self.recording:
action = self.action_manager.add_action({
"type": "mouse_click",
"position": (x, y),
"button": str(button).split('.')[1],
"pressed": pressed
})
self.update_callback(action)
def on_mouse_scroll(self, x, y, dx, dy):
if self.recording:
action = self.action_manager.add_action({
"type": "mouse_scroll",
"position": (x, y),
"dx": dx,
"dy": dy
})
self.update_callback(action)
def on_key_press(self, key):
if self.recording:
try:
if hasattr(key, 'char'):
action = self.action_manager.add_action({"type": "key_press", "key": key.char})
else:
action = self.action_manager.add_action({"type": "key_press", "key": f"{key}"})
self.update_callback(action)
except AttributeError:
pass
elif key == keyboard.Key.f6:
self.f6_callback()
class Controller:
def __init__(self, action_manager, event_listener, status_callback):
self.action_manager = action_manager
self.event_listener = event_listener
self.is_running = False
self.play_thread = None
self.status_callback = status_callback
def start_playing(self, repeat_times, interval, delay):
if not self.is_running and self.action_manager.get_actions():
self.is_running = True
self.status_callback("开始回放...")
self.play_thread = threading.Thread(target=self._async_play_actions, args=(repeat_times, interval, delay))
self.play_thread.start()
def stop_playing(self):
self.is_running = False
self.status_callback("停止回放")
if self.play_thread and self.play_thread is not threading.current_thread() and self.play_thread.is_alive():
self.play_thread.join()
def _async_play_actions(self, repeat_times, interval, delay):
actions = self.action_manager.get_actions()
time.sleep(delay / 1000) # Convert ms to seconds
while self.is_running:
start_time = time.time()
for i, action in enumerate(actions):
if not self.is_running:
break
time.sleep(max(0, action["time"]))
if action["type"] == "key_press":
pyautogui.press(action["key"])
self.status_callback(f"按键: {action['key']} (索引: {i})")
elif action["type"] == "mouse_click":
if action["pressed"]:
pyautogui.mouseDown(button=action["button"], x=action["position"][0], y=action["position"][1])
else:
pyautogui.mouseUp(button=action["button"], x=action["position"][0], y=action["position"][1])
self.status_callback(f"鼠标点击 ({action['position'][0]}, {action['position'][1]}), 按钮: {action['button']}, 状态: {'按下' if action['pressed'] else '释放'} (索引: {i})")
# 验证当前位置
current_position = pyautogui.position()
self.status_callback(f"当前位置: ({current_position.x}, {current_position.y})")
elif action["type"] == "mouse_scroll":
pyautogui.scroll(action["dy"], *action["position"])
self.status_callback(f"鼠标滚轮 ({action['position'][0]}, {action['position'][1]}), 变量: ({action['dx']}, {action['dy']}) (索引: {i})")
if repeat_times != 999:
repeat_times -= 1
if repeat_times <= 0:
self.stop_playing()
else:
time.sleep(interval)
class AutomaticClickerApp(tk.Tk):
def __init__(self):
super().__init__()
self.title("增强版自动点击器")
self.geometry("1200x800")
self.interval = 1.0 # 默认间隔时间1秒
self.repeat_times = 1 # 默认重复次数1次
self.delay = 0 # 默认延迟为0ms
self.action_manager = ActionManager()
self.event_listener = EventListener(self.action_manager, self.update_action_listbox, self.cycle_clicks)
self.controller = Controller(self.action_manager, self.event_listener, 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)
# 每次鼠标点击的间隔时间
interval_frame = ttk.Frame(control_frame)
interval_label = ttk.Label(interval_frame, text="每次操作的间隔时间(秒):")
self.interval_entry = ttk.Entry(interval_frame, width=5)
self.interval_entry.insert(0, str(self.interval)) # 默认值1秒
interval_label.grid(row=0, column=0, padx=(10, 0), pady=(10, 0))
self.interval_entry.grid(row=0, column=1, padx=(0, 10), pady=(10, 0))
interval_frame.pack(padx=10, fill=tk.X)
# 重复次数
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)
# 录制按钮
record_button = ttk.Button(
control_frame, text="开始/停止录制", command=self.toggle_recording
)
record_button.pack(pady=(10, 0))
# 清除录制按钮
clear_button = ttk.Button(
control_frame, text="清除录制", command=self.clear_recorded_actions
)
clear_button.pack(pady=(10, 0))
# 回放按钮
play_button = ttk.Button(
control_frame, text="开始回放", command=self.start_playing
)
play_button.pack(pady=(10, 0))
# 保存录制的动作到文件
save_button = ttk.Button(
control_frame, text="保存录制的动作", command=self.save_recorded_actions
)
save_button.pack(pady=(10, 0))
# 加载录制的动作从文件
load_button = ttk.Button(
control_frame, text="加载录制的动作", command=self.load_recorded_actions
)
load_button.pack(pady=(10, 0))
# 停止按钮
self.stop_button = ttk.Button(control_frame, text="停止", command=self.stop_playing, state=tk.DISABLED)
self.stop_button.pack(padx=10, pady=(10, 0))
# 状态面板
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)
# 记录的动作列表
self.action_listbox = tk.Listbox(main_frame, height=30, width=100)
self.action_listbox.pack(pady=(10, 0), fill=tk.BOTH, expand=True)
def toggle_recording(self):
if self.event_listener.recording:
self.event_listener.stop_recording()
self.update_action_listbox()
self.update_status_panel("录制已停止")
else:
self.event_listener.start_recording()
self.update_status_panel("正在录制... 按 F7 结束录制")
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']}"
return ""
def start_playing(self):
try:
interval = float(self.interval_entry.get())
repeat_times = int(self.repeat_times_entry.get())
delay = int(self.delay_entry.get())
except ValueError:
self.update_status_panel("请输入有效的数值。")
return
self.controller.start_playing(repeat_times, 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):
file_path = filedialog.asksaveasfilename(defaultextension=".txt",
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) # 循环点击,无间隔,无延迟
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):
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
self.status_text.insert(tk.END, f"[{current_time}] {message}\n")
self.status_text.see(tk.END)
if __name__ == "__main__":
app = AutomaticClickerApp()
app.mainloop()