如何在Python中不使用PyAutoGUI获取Windows及Wayland/Fedora系统下的全局鼠标坐标
如何在Python中不使用PyAutoGUI获取Windows及Wayland/Fedora系统下的全局鼠标坐标
嘿,我刚好折腾过这个需求——不想被PyAutoGUI连带的Pillow这些依赖拖后腿,只想轻量地获取全局鼠标坐标,针对Windows和Fedora的Wayland环境,我整理了两个靠谱的方案,完全不用PyAutoGUI:
Windows平台:纯ctypes调用系统API,零第三方依赖
Windows系统本身就自带了直接获取鼠标位置的API,咱们用Python自带的ctypes就能直接调用,连额外的库都不用装,超轻量!
原理很简单:调用user32.dll里的GetCursorPos函数,这个函数会把鼠标的屏幕坐标写入一个POINT结构体,咱们用ctypes把这个结构体在Python里定义出来就行。
直接上可运行的代码:
import ctypes import time # 定义和Windows API对应的POINT结构体,用来存坐标 class POINT(ctypes.Structure): _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_long)] def get_mouse_pos_windows(): pt = POINT() # 调用系统API获取当前鼠标位置 ctypes.windll.user32.GetCursorPos(ctypes.byref(pt)) return (pt.x, pt.y) if __name__ == "__main__": try: while True: x, y = get_mouse_pos_windows() print(f"X={x}, Y={y}") time.sleep(2) except KeyboardInterrupt: print("\n程序已终止")
小说明:
- 这个代码完全不用装任何额外包,Python自带的库就能跑
- 按
Ctrl+C就能终止循环,和你原来用PyAutoGUI的逻辑完全一致
Wayland/Fedora平台:轻量依赖方案,无冗余包袱
Wayland和老的X11不一样,没有直接的全局坐标API,但在Fedora常用的Wayland桌面(比如GNOME)里,有两个省心的方案:
方案1:GNOME Wayland专属,用DBus接口(几乎零依赖)
GNOME的Wayland桌面会通过DBus暴露鼠标位置的接口,咱们用dbus-python就能读取。这个包在Fedora里一般是预装的,要是没有的话,一条命令就能装:sudo dnf install python3-dbus,非常轻量。
代码示例:
import dbus import time def get_mouse_pos_gnome_wayland(): try: bus = dbus.SessionBus() # 连接GNOME的窗口管理服务 gnome_shell = bus.get_object( 'org.gnome.Shell', '/org/gnome/Shell' ) # 调用获取鼠标位置的方法 mouse_pos = gnome_shell.GetCursorPosition( dbus_interface='org.gnome.Shell' ) # 返回的是浮点数,转成整数更直观 return (int(mouse_pos[0]), int(mouse_pos[1])) except dbus.exceptions.DBusException: print("当前不是GNOME Wayland环境,这个方案用不了哦") return (0, 0) if __name__ == "__main__": try: while True: x, y = get_mouse_pos_gnome_wayland() print(f"X={x}, Y={y}") time.sleep(2) except KeyboardInterrupt: print("\n程序已终止")
方案2:通用Wayland方案,用libinput(支持所有Wayland桌面)
如果你的Fedora用的不是GNOME,而是Sway这类其他Wayland桌面,可以用python-libinput,安装命令是sudo dnf install python3-libinput。不过这个方法需要读取输入设备的权限,要么运行的时候加sudo,要么把你的用户加入input组(sudo usermod -aG input $USER,加完后重启会话生效)。
简化版代码示例:
import libinput import time from evdev import InputDevice, ecodes def get_mouse_pos_general_wayland(): # 先找鼠标设备 mouse_dev = None for path in InputDevice.list_devices(): dev = InputDevice(path) # 筛选支持绝对坐标的鼠标设备 if ecodes.EV_ABS in dev.capabilities() and ecodes.ABS_X in dev.capabilities()[ecodes.EV_ABS]: mouse_dev = dev break if not mouse_dev: print("没找到能读取坐标的鼠标设备") return (0, 0) # 读取当前鼠标的绝对坐标,再转成屏幕坐标 x, y = 0, 0 for event in mouse_dev.read_one() or []: if event.type == ecodes.EV_ABS: if event.code == ecodes.ABS_X: x = event.value elif event.code == ecodes.ABS_Y: y = event.value # 动态获取屏幕分辨率(用xrandr命令) import subprocess screen_width, screen_height = 1920, 1080 # 默认值,防止获取失败 try: output = subprocess.check_output(["xrandr"]).decode() for line in output.splitlines(): if "*" in line: res = line.split()[0].split("x") screen_width, screen_height = int(res[0]), int(res[1]) break except: pass # 把设备的绝对坐标转成屏幕实际坐标 x_max = mouse_dev.absinfo(ecodes.ABS_X).max y_max = mouse_dev.absinfo(ecodes.ABS_Y).max x = int(x * screen_width / x_max) y = int(y * screen_height / y_max) return (x, y) if __name__ == "__main__": try: while True: x, y = get_mouse_pos_general_wayland() print(f"X={x}, Y={y}") time.sleep(2) except KeyboardInterrupt: print("\n程序已终止")
小提示:
- 优先用GNOME的DBus方案,不用权限,最省心
- 其他Wayland桌面用libinput方案的话,记得给用户加
input组,避免每次都输sudo
跨平台整合方案:一个脚本跑通两个系统
把上面的代码整合起来,自动判断当前系统,这样一个脚本就能在Windows和Fedora/Wayland下用:
import ctypes import time import platform # 定义Windows用的POINT结构体 class POINT(ctypes.Structure): _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_long)] def get_mouse_pos(): os_name = platform.system() if os_name == "Windows": pt = POINT() ctypes.windll.user32.GetCursorPos(ctypes.byref(pt)) return (pt.x, pt.y) elif os_name == "Linux": # 先尝试GNOME Wayland的DBus方案 try: import dbus bus = dbus.SessionBus() gnome_shell = bus.get_object('org.gnome.Shell', '/org/gnome/Shell') mouse_pos = gnome_shell.GetCursorPosition(dbus_interface='org.gnome.Shell') return (int(mouse_pos[0]), int(mouse_pos[1])) except: # fallback到通用Wayland方案 try: from evdev import InputDevice, ecodes mouse_dev = None for path in InputDevice.list_devices(): dev = InputDevice(path) if ecodes.EV_ABS in dev.capabilities() and ecodes.ABS_X in dev.capabilities()[ecodes.EV_ABS]: mouse_dev = dev break if not mouse_dev: return (0, 0) x, y = 0, 0 for event in mouse_dev.read_one() or []: if event.type == ecodes.EV_ABS: if event.code == ecodes.ABS_X: x = event.value elif event.code == ecodes.ABS_Y: y = event.value # 获取屏幕分辨率 screen_width, screen_height = 1920, 1080 try: import subprocess output = subprocess.check_output(["xrandr"]).decode() for line in output.splitlines(): if "*" in line: res = line.split()[0].split("x") screen_width, screen_height = int(res[0]), int(res[1]) break except: pass x_max = mouse_dev.absinfo(ecodes.ABS_X).max y_max = mouse_dev.absinfo(ecodes.ABS_Y).max x = int(x * screen_width / x_max) y = int(y * screen_height / y_max) return (x, y) except: print("当前Linux环境不支持该方法,请检查是否为Wayland,或安装了必要依赖") return (0, 0) else: print("当前系统暂时不支持") return (0, 0) if __name__ == "__main__": try: while True: x, y = get_mouse_pos() print(f"X={x}, Y={y}") time.sleep(2) except KeyboardInterrupt: print("\n程序已终止")
最后提个醒:
- Windows的方案完全零依赖,直接运行就行
- Linux下如果是GNOME Wayland,优先用DBus的方法,不用权限,最省心
- 跨平台脚本里已经做了容错,就算某个方案用不了,也会自动尝试下一个




