小庚资源网 · 免费提供绿色软件、活动线报以及其他网络资源,好货不私藏!
当前位置:网站首页 > 网站源码 > 只有源码 >正文

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

作者:小编日期:2026-09-27浏览:5029分类:只有源码

image.png

  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("750x650")

  31.         self.root.resizable(False, False)

  32.          

  33.         # 运行状态

  34.         self.running = False

  35.         self.mouse_listener = None

  36.         self.is_simulating = False

  37.          

  38.         # 窗口配置:[(hwnd, 标题, dx, dy, 启用), ...]

  39.         self.window_configs = []

  40.          

  41.         # 初始化UI

  42.         self._setup_ui()

  43.         self._check_dependencies()

  44.          

  45.         # 刷新窗口列表

  46.         self.refresh_windows()

  47.      

  48.     def _check_dependencies(self):

  49.         """检查依赖是否安装"""

  50.         missing = []

  51.         if not HAS_WIN32:

  52.             missing.append("pywin32")

  53.         if not HAS_PYNPUT:

  54.             missing.append("pynput")

  55.          

  56.         if missing:

  57.             self.log(f"⚠ 缺少依赖库: {', '.join(missing)}")

  58.             self.log(f"  请运行: pip install {' '.join(missing)}")

  59.             messagebox.showwarning(

  60.                 "缺少依赖",

  61.                 f"缺少以下依赖库:\n{', '.join(missing)}\n\n请运行:\npip install {' '.join(missing)}"

  62.             )

  63.      

  64.     def _setup_ui(self):

  65.         """构建界面"""

  66.         # 主框架

  67.         main_frame = ttk.Frame(self.root, padding="10")

  68.         main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

  69.          

  70.         # ===== 标题 =====

  71.         title_label = ttk.Label(

  72.             main_frame,

  73.             text="多窗口同步点击模拟器",

  74.             font=("微软雅黑", 16, "bold")

  75.         )

  76.         title_label.grid(row=0, column=0, columnspan=4, pady=(0, 10))

  77.          

  78.         # ===== 窗口选择区 =====

  79.         window_frame = ttk.LabelFrame(main_frame, text="窗口配置", padding="10")

  80.         window_frame.grid(row=1, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=(0, 10))

  81.          

  82.         # 表头

  83.         headers = ["启用", "窗口标题", "偏移X", "偏移Y"]

  84.         col_widths = [60, 300, 80, 80]

  85.          

  86.         for i, (header, width) in enumerate(zip(headers, col_widths)):

  87.             ttk.Label(window_frame, text=header, font=("微软雅黑", 9, "bold")).grid(

  88.                 row=0, column=i, padx=5, pady=(0, 5)

  89.             )

  90.          

  91.         # 创建4行窗口配置

  92.         self.window_vars = []  # 启用复选框变量

  93.         self.window_hwnd_vars = []  # 窗口选择下拉框

  94.         self.offset_x_vars = []  # X偏移

  95.         self.offset_y_vars = []  # Y偏移

  96.          

  97.         self.all_windows = []  # 所有窗口列表 [(hwnd, title), ...]

  98.          

  99.         for row in range(4):

  100.             # 启用复选框

  101.             var_enabled = tk.BooleanVar(value=True if row == 0 else False)

  102.             self.window_vars.append(var_enabled)

  103.             ttk.Checkbutton(window_frame, variable=var_enabled).grid(

  104.                 row=row+1, column=0, padx=5, pady=2

  105.             )

  106.             

  107.             # 窗口选择下拉框

  108.             var_hwnd = tk.StringVar()

  109.             self.window_hwnd_vars.append(var_hwnd)

  110.             combo = ttk.Combobox(

  111.                 window_frame,

  112.                 textvariable=var_hwnd,

  113.                 width=40,

  114.                 state="readonly"

  115.             )

  116.             combo.grid(row=row+1, column=1, padx=5, pady=2)

  117.             

  118.             # X偏移

  119.             var_x = tk.StringVar(value="0" if row == 0 else str(100 if row in [1, 3] else 0))

  120.             self.offset_x_vars.append(var_x)

  121.             ttk.Entry(window_frame, textvariable=var_x, width=8).grid(

  122.                 row=row+1, column=2, padx=5, pady=2

  123.             )

  124.             

  125.             # Y偏移

  126.             var_y = tk.StringVar(value="0" if row in [0, 1] else "100")

  127.             self.offset_y_vars.append(var_y)

  128.             ttk.Entry(window_frame, textvariable=var_y, width=8).grid(

  129.                 row=row+1, column=3, padx=5, pady=2

  130.             )

  131.          

  132.         # 刷新窗口按钮

  133.         refresh_btn = ttk.Button(

  134.             window_frame,

  135.             text="刷新窗口列表",

  136.             command=self.refresh_windows

  137.         )

  138.         refresh_btn.grid(row=5, column=0, columnspan=4, pady=(8, 0))

  139.          

  140.         # ===== 控制区 =====

  141.         control_frame = ttk.Frame(main_frame)

  142.         control_frame.grid(row=2, column=0, columnspan=4, pady=(0, 10))

  143.          

  144.         self.start_btn = ttk.Button(

  145.             control_frame,

  146.             text="▶ 启动监听",

  147.             command=self.start_listening,

  148.             width=15

  149.         )

  150.         self.start_btn.grid(row=0, column=0, padx=5)

  151.          

  152.         self.stop_btn = ttk.Button(

  153.             control_frame,

  154.             text="■ 停止监听",

  155.             command=self.stop_listening,

  156.             state="disabled",

  157.             width=15

  158.         )

  159.         self.stop_btn.grid(row=0, column=1, padx=5)

  160.          

  161.         self.status_label = ttk.Label(

  162.             control_frame,

  163.             text="● 未启动",

  164.             foreground="red",

  165.             font=("微软雅黑", 10, "bold")

  166.         )

  167.         self.status_label.grid(row=0, column=2, padx=20)

  168.          

  169.         # ===== 说明区 =====

  170.         help_frame = ttk.LabelFrame(main_frame, text="使用说明", padding="10")

  171.         help_frame.grid(row=3, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=(0, 10))

  172.          

  173.         help_text = (

  174.             "1. 点击「刷新窗口列表」获取当前所有打开的窗口\n"

  175.             "2. 在下拉框中选择要同步点击的窗口,设置X/Y偏移量\n"

  176.             "3. 勾选「启用」来决定该窗口是否参与同步\n"

  177.             "4. 点击「启动监听」后,在第一个勾选的窗口中点击即可同步到其他窗口\n"

  178.             "5. 偏移量表示:相对点击位置的横向/纵向偏移像素"

  179.         )

  180.         ttk.Label(help_frame, text=help_text, justify=tk.LEFT, font=("微软雅黑", 9)).grid(

  181.             row=0, column=0, sticky=tk.W

  182.         )

  183.          

  184.         # ===== 日志区 =====

  185.         log_frame = ttk.LabelFrame(main_frame, text="运行日志", padding="10")

  186.         log_frame.grid(row=4, column=0, columnspan=4, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 5))

  187.          

  188.         self.log_text = scrolledtext.ScrolledText(

  189.             log_frame,

  190.             width=85,

  191.             height=10,

  192.             font=("Consolas", 9),

  193.             state="disabled"

  194.         )

  195.         self.log_text.grid(row=0, column=0)

  196.          

  197.         # 底部说明

  198.         ttk.Label(

  199.             main_frame,

  200.             text="提示:本工具通过 PostMessage 后台发送点击,不会激活其他窗口",

  201.             foreground="gray",

  202.             font=("微软雅黑", 8)

  203.         ).grid(row=5, column=0, columnspan=4)

  204.      

  205.     def log(self, message):

  206.         """添加日志"""

  207.         timestamp = time.strftime("%H:%M:%S")

  208.         self.log_text.configure(state="normal")

  209.         self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")

  210.         self.log_text.see(tk.END)

  211.         self.log_text.configure(state="disabled")

  212.      

  213.     def refresh_windows(self):

  214.         """刷新窗口列表"""

  215.         if not HAS_WIN32:

  216.             self.log("✗ 未安装 pywin32,无法获取窗口列表")

  217.             return

  218.          

  219.         self.all_windows = []

  220.          

  221.         def callback(hwnd, _):

  222.             if win32gui.IsWindowVisible(hwnd):

  223.                 title = win32gui.GetWindowText(hwnd)

  224.                 if title.strip() and len(title.strip()) > 1:

  225.                     self.all_windows.append((hwnd, title.strip()))

  226.          

  227.         win32gui.EnumWindows(callback, None)

  228.          

  229.         # 更新下拉框选项

  230.         window_titles = [f"[{hwnd}] {title[:40]}" for hwnd, title in self.all_windows]

  231.          

  232.         for combo_var in self.window_hwnd_vars:

  233.             # 找到对应的下拉框控件

  234.             combo_index = self.window_hwnd_vars.index(combo_var)

  235.             # 遍历找到对应的combobox

  236.             for child in self.root.winfo_children():

  237.                 self._find_and_update_combo(child, combo_var, window_titles)

  238.          

  239.         self.log(f"✓ 刷新窗口列表,共找到 {len(self.all_windows)} 个可见窗口")

  240.      

  241.     def _find_and_update_combo(self, widget, var, values):

  242.         """递归查找并更新下拉框"""

  243.         if isinstance(widget, ttk.Combobox) and str(widget.cget("textvariable")) == str(var):

  244.             widget['values'] = values

  245.             return True

  246.         for child in widget.winfo_children():

  247.             if self._find_and_update_combo(child, var, values):

  248.                 return True

  249.         return False

  250.      

  251.     def _get_selected_hwnd(self, index):

  252.         """获取选中的窗口句柄"""

  253.         selection = self.window_hwnd_vars[index].get()

  254.         if not selection:

  255.             return None

  256.          

  257.         # 从 "[hwnd] title" 格式中提取 hwnd

  258.         try:

  259.             hwnd_str = selection.split("]")[0].strip("[")

  260.             return int(hwnd_str)

  261.         except:

  262.             return None

  263.      

  264.     def start_listening(self):

  265.         """启动鼠标监听"""

  266.         if not HAS_PYNPUT:

  267.             messagebox.showerror("错误", "未安装 pynput,请运行: pip install pynput")

  268.             return

  269.          

  270.         if not HAS_WIN32:

  271.             messagebox.showerror("错误", "未安装 pywin32,请运行: pip install pywin32")

  272.             return

  273.          

  274.         # 检查配置

  275.         enabled_count = sum(1 for v in self.window_vars if v.get())

  276.         if enabled_count == 0:

  277.             messagebox.showwarning("提示", "请至少启用一个窗口")

  278.             return

  279.          

  280.         # 检查是否都选择了窗口

  281.         for i, var in enumerate(self.window_vars):

  282.             if var.get() and not self._get_selected_hwnd(i):

  283.                 messagebox.showwarning("提示", f"第 {i+1} 行已启用但未选择窗口")

  284.                 return

  285.          

  286.         self.running = True

  287.         self.is_simulating = False

  288.          

  289.         # 更新按钮状态

  290.         self.start_btn.configure(state="disabled")

  291.         self.stop_btn.configure(state="normal")

  292.         self.status_label.configure(text="● 监听中", foreground="green")

  293.          

  294.         # 启动监听线程

  295.         self.mouse_listener = mouse.Listener(on_click=self._on_mouse_click)

  296.         self.mouse_listener.start()

  297.          

  298.         self.log("=" * 50)

  299.         self.log("✓ 鼠标监听已启动")

  300.          

  301.         # 显示配置信息

  302.         for i, var in enumerate(self.window_vars):

  303.             if var.get():

  304.                 hwnd = self._get_selected_hwnd(i)

  305.                 title = win32gui.GetWindowText(hwnd)[:30] if hwnd else "未知"

  306.                 dx = self.offset_x_vars[i].get()

  307.                 dy = self.offset_y_vars[i].get()

  308.                 self.log(f"  窗口{i+1}: [{title}] 偏移({dx}, {dy})")

  309.          

  310.         self.log("等待鼠标点击...")

  311.      

  312.     def stop_listening(self):

  313.         """停止鼠标监听"""

  314.         self.running = False

  315.          

  316.         if self.mouse_listener:

  317.             self.mouse_listener.stop()

  318.             self.mouse_listener = None

  319.          

  320.         # 更新按钮状态

  321.         self.start_btn.configure(state="normal")

  322.         self.stop_btn.configure(state="disabled")

  323.         self.status_label.configure(text="● 未启动", foreground="red")

  324.          

  325.         self.log("✓ 鼠标监听已停止")

  326.      

  327.     def _on_mouse_click(self, x, y, button, pressed):

  328.         """鼠标点击回调"""

  329.         # 跳过模拟事件

  330.         if self.is_simulating:

  331.             return

  332.          

  333.         # 只响应左键按下

  334.         if not pressed or button != mouse.Button.left:

  335.             return

  336.          

  337.         # 在后台线程执行

  338.         threading.Thread(

  339.             target=self._do_sync_click,

  340.             args=(x, y),

  341.             daemon=True

  342.         ).start()

  343.      

  344.     def _do_sync_click(self, real_x, real_y):

  345.         """执行同步点击"""

  346.         self.is_simulating = True

  347.          

  348.         try:

  349.             self.log(f"\n[点击] 屏幕坐标: ({real_x}, {real_y})")

  350.             

  351.             for i, var in enumerate(self.window_vars):

  352.                 if not var.get():

  353.                     continue

  354.                  

  355.                 hwnd = self._get_selected_hwnd(i)

  356.                 if not hwnd:

  357.                     continue

  358.                  

  359.                 try:

  360.                     dx = int(self.offset_x_vars[i].get())

  361.                     dy = int(self.offset_y_vars[i].get())

  362.                 except ValueError:

  363.                     self.log(f"  ✗ 窗口{i+1} 偏移量格式错误")

  364.                     continue

  365.                  

  366.                 click_x = real_x + dx

  367.                 click_y = real_y + dy

  368.                  

  369.                 # 发送后台点击

  370.                 self._post_click(hwnd, click_x, click_y, i+1)

  371.                  

  372.                 time.sleep(0.03)

  373.             

  374.             self.log("  ✓ 同步点击完成")

  375.          

  376.         finally:

  377.             time.sleep(0.1)

  378.             self.is_simulating = False

  379.      

  380.     def _post_click(self, hwnd, screen_x, screen_y, window_index):

  381.         """向窗口后台发送点击"""

  382.         try:

  383.             # 转换为客户区坐标

  384.             rect = win32gui.GetWindowRect(hwnd)

  385.             client_x = screen_x - rect[0]

  386.             client_y = screen_y - rect[1]

  387.             

  388.             # 打包 lParam

  389.             lparam = win32api.MAKELONG(client_x, client_y)

  390.             

  391.             # 发送按下消息

  392.             win32gui.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lparam)

  393.             time.sleep(0.02)

  394.             # 发送抬起消息

  395.             win32gui.PostMessage(hwnd, win32con.WM_LBUTTONUP, 0, lparam)

  396.             

  397.             title = win32gui.GetWindowText(hwnd)[:20]

  398.             self.log(f"  → 窗口{window_index}: [{title}] 点击({client_x}, {client_y})")

  399.             

  400.         except Exception as e:

  401.             self.log(f"  ✗ 窗口{window_index} 发送失败: {e}")



  402. def main():

  403.     root = tk.Tk()

  404.     app = ClickSimulatorApp(root)

  405.      

  406.     # 窗口关闭时清理

  407.     def on_closing():

  408.         app.stop_listening()

  409.         root.destroy()

  410.      

  411.     root.protocol("WM_DELETE_WINDOW", on_closing)

  412.     root.mainloop()



  413. if __name__ == "__main__":

  414.     main()

  415. #(注:内容由AI生成)


暂无评论,来添加一个吧。

取消回复欢迎你发表评论:

Copyright© XGW9.COM版权所有〖小庚资源网〗
〖恒创科技〗为本站提供专业云计算服务  
本站发布的内容来源于互联网,如果有侵权内容,请联系我们删除!E-mail:xgzyw6@outlook.com
关于我们|我要投稿|免责声明|XML地图