You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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的方法,不用权限,最省心
  • 跨平台脚本里已经做了容错,就算某个方案用不了,也会自动尝试下一个

火山引擎 最新活动