豆包新出炉的四窗口鼠标同步点击器适合网游四开

2026-09-28 0 180

文章摘要

该文章介绍了一个基于Python的多窗口同步点击模拟器,核心功能是在主窗口点击时,向多个窗口后台同步发送点击,且不抢占焦点。程序使用tkinter构建界面,依赖pywin32和pynput实现窗口枚举与鼠标监听,通过PostMessage发送后台点击消息。用户可配置最多4个目标窗口及偏移量,启动监听后即可实现多窗口同步点击,适用于需要批量操作窗口的场景。

豆包新出炉的四窗口鼠标同步点击器适合网游四开

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. “””
  4. 多窗口同步点击模拟器
  5. 功能:在主窗口点击时,同时向多个窗口后台发送点击,不抢焦点
  6. 版本:v1.0
  7. “””
  8. import tkinter as tk
  9. from tkinter import ttk, messagebox, scrolledtext
  10. import threading
  11. import time
  12. import sys
  13. # 尝试导入Windows相关库
  14. try:
  15.     import win32gui
  16.     import win32con
  17.     import win32api
  18.     HAS_WIN32 = True
  19. except ImportError:
  20.     HAS_WIN32 = False
  21. try:
  22.     from pynput import mouse
  23.     HAS_PYNPUT = True
  24. except ImportError:
  25.     HAS_PYNPUT = False
  26. class ClickSimulatorApp:
  27.     def __init__(self, root):
  28.         self.root = root
  29.         self.root.title(“多窗口同步点击模拟器 v1.0”)
  30.         self.root.geometry(“750×650”)
  31.         self.root.resizable(False, False)
  32.         # 运行状态
  33.         self.running = False
  34.         self.mouse_listener = None
  35.         self.is_simulating = False
  36.         # 窗口配置:[(hwnd, 标题, dx, dy, 启用), …]
  37.         self.window_configs = []
  38.         # 初始化UI
  39.         self._setup_ui()
  40.         self._check_dependencies()
  41.         # 刷新窗口列表
  42.         self.refresh_windows()
  43.     def _check_dependencies(self):
  44.         “””检查依赖是否安装”””
  45.         missing = []
  46.         if not HAS_WIN32:
  47.             missing.append(“pywin32”)
  48.         if not HAS_PYNPUT:
  49.             missing.append(“pynput”)
  50.         if missing:
  51.             self.log(f”⚠ 缺少依赖库: {‘, ‘.join(missing)}”)
  52.             self.log(f”  请运行: pip install {‘ ‘.join(missing)}”)
  53.             messagebox.showwarning(
  54.                 “缺少依赖”,
  55.                 f”缺少以下依赖库:\n{‘, ‘.join(missing)}\n\n请运行:\npip install {‘ ‘.join(missing)}”
  56.             )
  57.     def _setup_ui(self):
  58.         “””构建界面”””
  59.         # 主框架
  60.         main_frame = ttk.Frame(self.root, padding=”10″)
  61.         main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
  62.         # ===== 标题 =====
  63.         title_label = ttk.Label(
  64.             main_frame,
  65.             text=”多窗口同步点击模拟器”,
  66.             font=(“微软雅黑”, 16, “bold”)
  67.         )
  68.         title_label.grid(row=0, column=0, columnspan=4, pady=(0, 10))
  69.         # ===== 窗口选择区 =====
  70.         window_frame = ttk.LabelFrame(main_frame, text=”窗口配置”, padding=”10″)
  71.         window_frame.grid(row=1, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=(0, 10))
  72.         # 表头
  73.         headers = [“启用”, “窗口标题”, “偏移X”, “偏移Y”]
  74.         col_widths = [60, 300, 80, 80]
  75.         for i, (header, width) in enumerate(zip(headers, col_widths)):
  76.             ttk.Label(window_frame, text=header, font=(“微软雅黑”, 9, “bold”)).grid(
  77.                 row=0, column=i, padx=5, pady=(0, 5)
  78.             )
  79.         # 创建4行窗口配置
  80.         self.window_vars = []  # 启用复选框变量
  81.         self.window_hwnd_vars = []  # 窗口选择下拉框
  82.         self.offset_x_vars = []  # X偏移
  83.         self.offset_y_vars = []  # Y偏移
  84.         self.all_windows = []  # 所有窗口列表 [(hwnd, title), …]
  85.         for row in range(4):
  86.             # 启用复选框
  87.             var_enabled = tk.BooleanVar(value=True if row == 0 else False)
  88.             self.window_vars.append(var_enabled)
  89.             ttk.Checkbutton(window_frame, variable=var_enabled).grid(
  90.                 row=row+1, column=0, padx=5, pady=2
  91.             )
  92.             # 窗口选择下拉框
  93.             var_hwnd = tk.StringVar()
  94.             self.window_hwnd_vars.append(var_hwnd)
  95.             combo = ttk.Combobox(
  96.                 window_frame,
  97.                 textvariable=var_hwnd,
  98.                 width=40,
  99.                 state=”readonly”
  100.             )
  101.             combo.grid(row=row+1, column=1, padx=5, pady=2)
  102.             # X偏移
  103.             var_x = tk.StringVar(value=”0″ if row == 0 else str(100 if row in [1, 3] else 0))
  104.             self.offset_x_vars.append(var_x)
  105.             ttk.Entry(window_frame, textvariable=var_x, width=8).grid(
  106.                 row=row+1, column=2, padx=5, pady=2
  107.             )
  108.             # Y偏移
  109.             var_y = tk.StringVar(value=”0″ if row in [0, 1] else “100”)
  110.             self.offset_y_vars.append(var_y)
  111.             ttk.Entry(window_frame, textvariable=var_y, width=8).grid(
  112.                 row=row+1, column=3, padx=5, pady=2
  113.             )
  114.         # 刷新窗口按钮
  115.         refresh_btn = ttk.Button(
  116.             window_frame,
  117.             text=”刷新窗口列表”,
  118.             command=self.refresh_windows
  119.         )
  120.         refresh_btn.grid(row=5, column=0, columnspan=4, pady=(8, 0))
  121.         # ===== 控制区 =====
  122.         control_frame = ttk.Frame(main_frame)
  123.         control_frame.grid(row=2, column=0, columnspan=4, pady=(0, 10))
  124.         self.start_btn = ttk.Button(
  125.             control_frame,
  126.             text=”▶ 启动监听”,
  127.             command=self.start_listening,
  128.             width=15
  129.         )
  130.         self.start_btn.grid(row=0, column=0, padx=5)
  131.         self.stop_btn = ttk.Button(
  132.             control_frame,
  133.             text=”■ 停止监听”,
  134.             command=self.stop_listening,
  135.             state=”disabled”,
  136.             width=15
  137.         )
  138.         self.stop_btn.grid(row=0, column=1, padx=5)
  139.         self.status_label = ttk.Label(
  140.             control_frame,
  141.             text=”● 未启动”,
  142.             foreground=”red”,
  143.             font=(“微软雅黑”, 10, “bold”)
  144.         )
  145.         self.status_label.grid(row=0, column=2, padx=20)
  146.         # ===== 说明区 =====
  147.         help_frame = ttk.LabelFrame(main_frame, text=”使用说明”, padding=”10″)
  148.         help_frame.grid(row=3, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=(0, 10))
  149.         help_text = (
  150.             “1. 点击「刷新窗口列表」获取当前所有打开的窗口\n”
  151.             “2. 在下拉框中选择要同步点击的窗口,设置X/Y偏移量\n”
  152.             “3. 勾选「启用」来决定该窗口是否参与同步\n”
  153.             “4. 点击「启动监听」后,在第一个勾选的窗口中点击即可同步到其他窗口\n”
  154.             “5. 偏移量表示:相对点击位置的横向/纵向偏移像素”
  155.         )
  156.         ttk.Label(help_frame, text=help_text, justify=tk.LEFT, font=(“微软雅黑”, 9)).grid(
  157.             row=0, column=0, sticky=tk.W
  158.         )
  159.         # ===== 日志区 =====
  160.         log_frame = ttk.LabelFrame(main_frame, text=”运行日志”, padding=”10″)
  161.         log_frame.grid(row=4, column=0, columnspan=4, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 5))
  162.         self.log_text = scrolledtext.ScrolledText(
  163.             log_frame,
  164.             width=85,
  165.             height=10,
  166.             font=(“Consolas”, 9),
  167.             state=”disabled”
  168.         )
  169.         self.log_text.grid(row=0, column=0)
  170.         # 底部说明
  171.         ttk.Label(
  172.             main_frame,
  173.             text=”提示:本工具通过 PostMessage 后台发送点击,不会激活其他窗口”,
  174.             foreground=”gray”,
  175.             font=(“微软雅黑”, 8)
  176.         ).grid(row=5, column=0, columnspan=4)
  177.     def log(self, message):
  178.         “””添加日志”””
  179.         timestamp = time.strftime(“%H:%M:%S”)
  180.         self.log_text.configure(state=”normal”)
  181.         self.log_text.insert(tk.END, f”[{timestamp}] {message}\n”)
  182.         self.log_text.see(tk.END)
  183.         self.log_text.configure(state=”disabled”)
  184.     def refresh_windows(self):
  185.         “””刷新窗口列表”””
  186.         if not HAS_WIN32:
  187.             self.log(“✗ 未安装 pywin32,无法获取窗口列表”)
  188.             return
  189.         self.all_windows = []
  190.         def callback(hwnd, _):
  191.             if win32gui.IsWindowVisible(hwnd):
  192.                 title = win32gui.GetWindowText(hwnd)
  193.                 if title.strip() and len(title.strip()) > 1:
  194.                     self.all_windows.append((hwnd, title.strip()))
  195.         win32gui.EnumWindows(callback, None)
  196.         # 更新下拉框选项
  197.         window_titles = [f”[{hwnd}] {title[:40]}” for hwnd, title in self.all_windows]
  198.         for combo_var in self.window_hwnd_vars:
  199.             # 找到对应的下拉框控件
  200.             combo_index = self.window_hwnd_vars.index(combo_var)
  201.             # 遍历找到对应的combobox
  202.             for child in self.root.winfo_children():
  203.                 self._find_and_update_combo(child, combo_var, window_titles)
  204.         self.log(f”✓ 刷新窗口列表,共找到 {len(self.all_windows)} 个可见窗口”)
  205.     def _find_and_update_combo(self, widget, var, values):
  206.         “””递归查找并更新下拉框”””
  207.         if isinstance(widget, ttk.Combobox) and str(widget.cget(“textvariable”)) == str(var):
  208.             widget[‘values’] = values
  209.             return True
  210.         for child in widget.winfo_children():
  211.             if self._find_and_update_combo(child, var, values):
  212.                 return True
  213.         return False
  214.     def _get_selected_hwnd(self, index):
  215.         “””获取选中的窗口句柄”””
  216.         selection = self.window_hwnd_vars[index].get()
  217.         if not selection:
  218.             return None
  219.         # 从 “[hwnd] title” 格式中提取 hwnd
  220.         try:
  221.             hwnd_str = selection.split(“]”)[0].strip(“[“)
  222.             return int(hwnd_str)
  223.         except:
  224.             return None
  225.     def start_listening(self):
  226.         “””启动鼠标监听”””
  227.         if not HAS_PYNPUT:
  228.             messagebox.showerror(“错误”, “未安装 pynput,请运行: pip install pynput”)
  229.             return
  230.         if not HAS_WIN32:
  231.             messagebox.showerror(“错误”, “未安装 pywin32,请运行: pip install pywin32”)
  232.             return
  233.         # 检查配置
  234.         enabled_count = sum(1 for v in self.window_vars if v.get())
  235.         if enabled_count == 0:
  236.             messagebox.showwarning(“提示”, “请至少启用一个窗口”)
  237.             return
  238.         # 检查是否都选择了窗口
  239.         for i, var in enumerate(self.window_vars):
  240.             if var.get() and not self._get_selected_hwnd(i):
  241.                 messagebox.showwarning(“提示”, f”第 {i+1} 行已启用但未选择窗口”)
  242.                 return
  243.         self.running = True
  244.         self.is_simulating = False
  245.         # 更新按钮状态
  246.         self.start_btn.configure(state=”disabled”)
  247.         self.stop_btn.configure(state=”normal”)
  248.         self.status_label.configure(text=”● 监听中”, foreground=”green”)
  249.         # 启动监听线程
  250.         self.mouse_listener = mouse.Listener(on_click=self._on_mouse_click)
  251.         self.mouse_listener.start()
  252.         self.log(“=” * 50)
  253.         self.log(“✓ 鼠标监听已启动”)
  254.         # 显示配置信息
  255.         for i, var in enumerate(self.window_vars):
  256.             if var.get():
  257.                 hwnd = self._get_selected_hwnd(i)
  258.                 title = win32gui.GetWindowText(hwnd)[:30] if hwnd else “未知”
  259.                 dx = self.offset_x_vars[i].get()
  260.                 dy = self.offset_y_vars[i].get()
  261.                 self.log(f”  窗口{i+1}: [{title}] 偏移({dx}, {dy})”)
  262.         self.log(“等待鼠标点击…”)
  263.     def stop_listening(self):
  264.         “””停止鼠标监听”””
  265.         self.running = False
  266.         if self.mouse_listener:
  267.             self.mouse_listener.stop()
  268.             self.mouse_listener = None
  269.         # 更新按钮状态
  270.         self.start_btn.configure(state=”normal”)
  271.         self.stop_btn.configure(state=”disabled”)
  272.         self.status_label.configure(text=”● 未启动”, foreground=”red”)
  273.         self.log(“✓ 鼠标监听已停止”)
  274.     def _on_mouse_click(self, x, y, button, pressed):
  275.         “””鼠标点击回调”””
  276.         # 跳过模拟事件
  277.         if self.is_simulating:
  278.             return
  279.         # 只响应左键按下
  280.         if not pressed or button != mouse.Button.left:
  281.             return
  282.         # 在后台线程执行
  283.         threading.Thread(
  284.             target=self._do_sync_click,
  285.             args=(x, y),
  286.             daemon=True
  287.         ).start()
  288.     def _do_sync_click(self, real_x, real_y):
  289.         “””执行同步点击”””
  290.         self.is_simulating = True
  291.         try:
  292.             self.log(f”\n[点击] 屏幕坐标: ({real_x}, {real_y})”)
  293.             for i, var in enumerate(self.window_vars):
  294.                 if not var.get():
  295.                     continue
  296.                 hwnd = self._get_selected_hwnd(i)
  297.                 if not hwnd:
  298.                     continue
  299.                 try:
  300.                     dx = int(self.offset_x_vars[i].get())
  301.                     dy = int(self.offset_y_vars[i].get())
  302.                 except ValueError:
  303.                     self.log(f”  ✗ 窗口{i+1} 偏移量格式错误”)
  304.                     continue
  305.                 click_x = real_x + dx
  306.                 click_y = real_y + dy
  307.                 # 发送后台点击
  308.                 self._post_click(hwnd, click_x, click_y, i+1)
  309.                 time.sleep(0.03)
  310.             self.log(”  ✓ 同步点击完成”)
  311.         finally:
  312.             time.sleep(0.1)
  313.             self.is_simulating = False
  314.     def _post_click(self, hwnd, screen_x, screen_y, window_index):
  315.         “””向窗口后台发送点击”””
  316.         try:
  317.             # 转换为客户区坐标
  318.             rect = win32gui.GetWindowRect(hwnd)
  319.             client_x = screen_x – rect[0]
  320.             client_y = screen_y – rect[1]
  321.             # 打包 lParam
  322.             lparam = win32api.MAKELONG(client_x, client_y)
  323.             # 发送按下消息
  324.             win32gui.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lparam)
  325.             time.sleep(0.02)
  326.             # 发送抬起消息
  327.             win32gui.PostMessage(hwnd, win32con.WM_LBUTTONUP, 0, lparam)
  328.             title = win32gui.GetWindowText(hwnd)[:20]
  329.             self.log(f”  → 窗口{window_index}: [{title}] 点击({client_x}, {client_y})”)
  330.         except Exception as e:
  331.             self.log(f”  ✗ 窗口{window_index} 发送失败: {e}”)
  332. def main():
  333.     root = tk.Tk()
  334.     app = ClickSimulatorApp(root)
  335.     # 窗口关闭时清理
  336.     def on_closing():
  337.         app.stop_listening()
  338.         root.destroy()
  339.     root.protocol(“WM_DELETE_WINDOW”, on_closing)
  340.     root.mainloop()
  341. if __name__ == “__main__”:
  342.     main()
  343. #(注:内容由AI生成)
复制代码

 

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

本站所有资源来源于网络,仅限用于学习研究;无任何技术支持!不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除内容。如果您喜欢,请支持正版。如有侵权请邮件与我们联系处理。

豆包新出炉的四窗口鼠标同步点击器适合网游四开
下一篇:

已经没有下一篇了!

常见问题
  • 网盘有时候会因为名字 关键词导致失效 大家可以给管理员提供失效信息,我们会给大家适当积分进行奖励 我们会第一时间进行补充修正 感谢大家的配合 让我们共同努力 打造良好的资源分享平台
查看详情

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务