鱼C论坛

 找回密码
 立即注册
查看: 530|回复: 8

Python实现免息屏脚本功能

[复制链接]
发表于 2025-6-15 12:57:32 | 显示全部楼层
  1. # -*- coding: utf-8 -*-
  2. import sys
  3. import threading
  4. import time
  5. import queue

  6. import tkinter as tk
  7. import tkinter.messagebox as messagebox

  8. import win32api
  9. import win32con
  10. import win32gui
  11. import win32gui_struct

  12. # DPI感知(高DPI屏幕支持)
  13. try:
  14.     import ctypes
  15.     ctypes.windll.shcore.SetProcessDpiAwareness(1)  # 1=系统DPI感知, 2=每监视器DPI感知
  16. except Exception:
  17.     pass

  18. APP_NAME = "息屏脚本"
  19. TRAY_TOOLTIP = APP_NAME
  20. TRAY_ICON = win32con.IDI_APPLICATION  # 使用默认应用程序图标

  21. # 鼠标移动范围
  22. X_MIN = 100
  23. X_MAX = 1000
  24. Y_FIXED = 200  # 固定Y坐标,可以根据实际需要修改

  25. # ==========================
  26. # 鼠标移动线程
  27. # ==========================
  28. class MouseMover(threading.Thread):
  29.     def __init__(self, coord_queue, lock, stop_event):
  30.         super().__init__(daemon=True)
  31.         self.coord_queue = coord_queue
  32.         self.lock = lock
  33.         self.stop_event = stop_event
  34.         self.direction = 1  # 1:右, -1:左
  35.         self.x = X_MIN
  36.         self.y = Y_FIXED

  37.     def run(self):
  38.         try:
  39.             while not self.stop_event.is_set():
  40.                 with self.lock:
  41.                     # 计算新X坐标
  42.                     self.x += self.direction
  43.                     if self.x >= X_MAX:
  44.                         self.x = X_MAX
  45.                         self.direction = -1
  46.                     elif self.x <= X_MIN:
  47.                         self.x = X_MIN
  48.                         self.direction = 1
  49.                     # 移动鼠标
  50.                     win32api.SetCursorPos((self.x, self.y))
  51.                     # 通知主线程当前坐标
  52.                     self.coord_queue.queue.clear()  # 保证只保留最新
  53.                     self.coord_queue.put_nowait((self.x, self.y))
  54.                 time.sleep(60)  # 每60秒移动一次
  55.         except Exception as e:
  56.             # 线程内部异常打印,可扩展为日志
  57.             print(f"[MouseMover] {e}")

  58. # ==========================
  59. # 系统托盘线程
  60. # ==========================
  61. class SysTrayIcon(threading.Thread):
  62.     def __init__(self, on_restore, on_quit):
  63.         super().__init__(daemon=True)
  64.         self.on_restore = on_restore
  65.         self.on_quit = on_quit
  66.         self.hwnd = None
  67.         self.notify_id = None
  68.         self.running = threading.Event()

  69.     def show_icon(self):
  70.         # 创建窗口
  71.         message_map = {
  72.             win32con.WM_DESTROY: self.on_destroy,
  73.             win32con.WM_COMMAND: self.on_command,
  74.             win32con.WM_USER+20: self.on_tray_notify,
  75.         }
  76.         wc = win32gui.WNDCLASS()
  77.         hinst = wc.hInstance = win32api.GetModuleHandle(None)
  78.         wc.lpszClassName = "SysTrayApp"
  79.         wc.lpfnWndProc = message_map
  80.         class_atom = win32gui.RegisterClass(wc)
  81.         self.hwnd = win32gui.CreateWindow(
  82.             class_atom, APP_NAME, 0,
  83.             0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
  84.             0, 0, hinst, None
  85.         )
  86.         self.notify_id = (self.hwnd, 0, win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
  87.                           win32con.WM_USER+20, win32gui.LoadIcon(0, TRAY_ICON), TRAY_TOOLTIP)
  88.         win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, self.notify_id)
  89.         self.running.set()
  90.         win32gui.PumpMessages()

  91.     def on_destroy(self, hwnd, msg, wparam, lparam):
  92.         if self.notify_id:
  93.             win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, self.notify_id)
  94.         win32gui.PostQuitMessage(0)
  95.         return 0

  96.     def on_tray_notify(self, hwnd, msg, wparam, lparam):
  97.         if lparam == win32con.WM_RBUTTONUP:
  98.             self.show_menu()
  99.         elif lparam == win32con.WM_LBUTTONDBLCLK:
  100.             self.on_restore()
  101.         return 0

  102.     def show_menu(self):
  103.         menu = win32gui.CreatePopupMenu()
  104.         win32gui.AppendMenu(menu, win32con.MF_STRING, 1023, "显示窗口")
  105.         win32gui.AppendMenu(menu, win32con.MF_STRING, 1024, "退出程序")
  106.         pos = win32gui.GetCursorPos()
  107.         win32gui.SetForegroundWindow(self.hwnd)
  108.         win32gui.TrackPopupMenu(menu, win32con.TPM_LEFTALIGN, pos[0], pos[1], 0, self.hwnd, None)
  109.         win32gui.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)

  110.     def on_command(self, hwnd, msg, wparam, lparam):
  111.         id = win32api.LOWORD(wparam)
  112.         if id == 1023:  # 显示窗口
  113.             self.on_restore()
  114.         elif id == 1024:  # 退出
  115.             self.on_quit()
  116.         return 0

  117.     def run(self):
  118.         try:
  119.             self.show_icon()
  120.         except Exception as e:
  121.             print(f"[SysTrayIcon] {e}")

  122.     def close(self):
  123.         # 线程安全关闭托盘
  124.         if self.hwnd:
  125.             try:
  126.                 win32gui.PostMessage(self.hwnd, win32con.WM_DESTROY, 0, 0)
  127.             except Exception:
  128.                 pass

  129. # ==========================
  130. # 主GUI窗口
  131. # ==========================
  132. class MainApp:
  133.     def __init__(self, root):
  134.         self.root = root
  135.         self.root.title(APP_NAME)
  136.         self.root.protocol("WM_DELETE_WINDOW", self.on_close)
  137.         self.root.geometry("300x150")
  138.         self.root.resizable(False, False)

  139.         # 共享资源
  140.         self.coord_queue = queue.Queue()
  141.         self.coord_lock = threading.Lock()
  142.         self.mover_stop = threading.Event()
  143.         self.mover_thread = None

  144.         # 托盘
  145.         self.tray_thread = None

  146.         # UI
  147.         self.coord_label = tk.Label(root, text="当前鼠标坐标:", font=("微软雅黑", 12))
  148.         self.coord_label.pack(pady=10)

  149.         self.start_btn = tk.Button(root, text="开始", width=10, command=self.start_move)
  150.         self.start_btn.pack(pady=5)
  151.         self.stop_btn = tk.Button(root, text="结束", width=10, command=self.stop_move, state=tk.DISABLED)
  152.         self.stop_btn.pack()

  153.         # 每秒刷新鼠标坐标
  154.         self.update_coord_label()
  155.         self.root.after(1000, self.refresh_coord_from_sys)

  156.     def start_move(self):
  157.         if self.mover_thread and self.mover_thread.is_alive():
  158.             return
  159.         self.mover_stop.clear()
  160.         self.mover_thread = MouseMover(self.coord_queue, self.coord_lock, self.mover_stop)
  161.         self.mover_thread.start()
  162.         self.start_btn.config(state=tk.DISABLED)
  163.         self.stop_btn.config(state=tk.NORMAL)

  164.     def stop_move(self):
  165.         self.mover_stop.set()
  166.         self.start_btn.config(state=tk.NORMAL)
  167.         self.stop_btn.config(state=tk.DISABLED)

  168.     def update_coord_label(self):
  169.         try:
  170.             pos = win32api.GetCursorPos()
  171.             self.coord_label.config(text=f"当前鼠标坐标:({pos[0]}, {pos[1]})")
  172.         except Exception:
  173.             self.coord_label.config(text="无法获取坐标")
  174.         self.root.after(1000, self.update_coord_label)

  175.     def refresh_coord_from_sys(self):
  176.         # 从队列获取由后台线程移动后的坐标(如果有)
  177.         try:
  178.             while not self.coord_queue.empty():
  179.                 x, y = self.coord_queue.get_nowait()
  180.                 self.coord_label.config(text=f"当前鼠标坐标:({x}, {y})")
  181.         except Exception:
  182.             pass
  183.         self.root.after(1000, self.refresh_coord_from_sys)

  184.     def on_close(self):
  185.         # 最小化到托盘
  186.         self.hide_to_tray()

  187.     def hide_to_tray(self):
  188.         self.root.withdraw()
  189.         if not self.tray_thread or not self.tray_thread.is_alive():
  190.             self.tray_thread = SysTrayIcon(self.show_from_tray, self.quit_app)
  191.             self.tray_thread.start()

  192.     def show_from_tray(self):
  193.         def _show():
  194.             self.root.deiconify()
  195.             self.root.after(100, self.root.lift)
  196.         self.root.after(0, _show)
  197.         if self.tray_thread:
  198.             self.tray_thread.close()
  199.             self.tray_thread = None

  200.     def quit_app(self):
  201.         # 主动退出、资源清理
  202.         try:
  203.             self.stop_move()
  204.             if self.tray_thread:
  205.                 self.tray_thread.close()
  206.                 self.tray_thread = None
  207.             self.root.after(200, self.root.destroy)
  208.         except Exception:
  209.             sys.exit(0)

  210. # ==========================
  211. # 程序入口
  212. # ==========================
  213. def main():
  214.     root = tk.Tk()
  215.     app = MainApp(root)
  216.     try:
  217.         root.mainloop()
  218.     except KeyboardInterrupt:
  219.         pass

  220. if __name__ == '__main__':
  221.     main()
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-9-22 05:26

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表