【问题标题】:Find out mouse button state via Xlib in Python在 Python 中通过 Xlib 找出鼠标按钮状态
【发布时间】:2019-03-04 23:03:52
【问题描述】:

我可以通过以下方式确定当前鼠标指针位置:

from Xlib.display import Display
display = Display()
qp = display.screen().root.query_pointer()
print(qp.root_x, qp.root_y)

如何通过Xlib 获取当前鼠标按钮状态,例如按下/释放左/右按钮? (或者如果这不可能 - 为什么不呢?)

【问题讨论】:

    标签: python linux xlib mouse-cursor


    【解决方案1】:

    您的 X 窗口必须支持 XInput 扩展。 Real X 可以工作,但如果 X 服务器不支持 VNC 服务器等扩展,则无法使用鼠标按钮。

    如果X服务器支持,则可以如下进入鼠标状态:

    from Xlib.display import Display
    from Xlib.ext import xinput
    
    display = Display()
    
    import time
    
    while True:
        buttons = []
    
        for device_info in display.xinput_query_device(xinput.AllDevices).devices:
            if not device_info.enabled:
                continue
            if xinput.ButtonClass not in [ device_class.type for device_class in device_info.classes ]:
                continue
            buttons.append(device_info)
    
        for button in buttons:
            for device_class in button.classes:
                if xinput.ButtonClass == device_class.type:
                    if device_class.state[0]:
                        print('Device {name} - Primary button down'.format(name=button.name))
        time.sleep(1)
    

    我不能 100% 确定,因为在任何地方都找不到文档,但我很确定 device_class.state[0] 是主要的(左按钮),1 是中间按钮,2 是右按钮。

    你大概可以查到the button number assignment spec here

    编辑:

    为什么有两个 for 循环 - 首先我在永远循环之外编写了“按钮”部分。但是,“嘿,你可以随时插入鼠标。”

    您会发现,有很多设备,包括“虚拟”设备。在笔记本电脑上,触摸板也可以用作按钮,因此在您的应用程序中,如果您想知道真正的鼠标按钮,您可能必须从名称中选择一个设备。同样,没有好的文档,因此您可能必须破译设备类对象。您可以在 /usr/lib/python3/dist-packages/Xlib/ext/xinput.py 中找到 xinput。 (如果您使用的是 Python2,请相应调整。)祝您好运。

    【讨论】:

    • 您可以获得按钮向上/向下事件,这意味着您必须自己保留记分牌。这不是不可能的,但很容易出错。这可能取决于您的应用程序 - 如果您不需要在没有精确时间的情况下跟踪鼠标按钮,那没关系。 X的本质是基于事件的,扫描按钮的静态需要ext,以及x服务器和客户端(你的python)之间的快速连接。如果它在同一主机上,则可以。远程连接 - 你很可能。必须处理穿梭请求/答案的延迟。
    • 嘿,感谢您回复我 :) 最好通过其他答案的 RECORD 扩展来访问这些事件?
    • 另外,我想我同意你的观点,X 的大部分似乎都是基于事件的。这也适用于鼠标指针位置,不是吗?至少,有鼠标指针运动事件。但是,使用问题文本中示例中的命令可以很容易地获取鼠标指针位置。所以这就是为什么我想,可能还有另一组简单的命令来获取鼠标按钮状态。我希望我没有把这弄得太混乱。 ;)
    【解决方案2】:

    如果 X 服务器启用了 RECORD 扩展,它可以用来找出当前的鼠标按钮状态。但是,它不会直接提供信息:必须跟踪单个按钮的向上和向下事件。

    这也意味着只有在程序收到至少一个鼠标按钮向上或向下的第一个事件后,才能知道当前状态。

    python-xlibhttps://github.com/python-xlib/python-xlib/blob/master/examples/record_demo.py 提供以下示例脚本

    from __future__ import print_function
    
    import sys
    import os
    
    # Change path so we find Xlib
    sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
    
    from Xlib import X, XK, display
    from Xlib.ext import record
    from Xlib.protocol import rq
    
    local_dpy = display.Display()
    record_dpy = display.Display()
    
    
    def lookup_keysym(keysym):
        for name in dir(XK):
            if name[:3] == "XK_" and getattr(XK, name) == keysym:
                return name[3:]
        return "[%d]" % keysym
    
    
    def record_callback(reply):
        if reply.category != record.FromServer:
            return
        if reply.client_swapped:
            print("* received swapped protocol data, cowardly ignored")
            return
        if not len(reply.data) or reply.data[0] < 2:
            # not an event
            return
    
        data = reply.data
        while len(data):
            event, data = rq.EventField(None).parse_binary_value(
                data, record_dpy.display, None, None)
    
            if event.type in [X.KeyPress, X.KeyRelease]:
                pr = event.type == X.KeyPress and "Press" or "Release"
    
                keysym = local_dpy.keycode_to_keysym(event.detail, 0)
                if not keysym:
                    print("KeyCode%s" % pr, event.detail)
                else:
                    print("KeyStr%s" % pr, lookup_keysym(keysym))
    
                if event.type == X.KeyPress and keysym == XK.XK_Escape:
                    local_dpy.record_disable_context(ctx)
                    local_dpy.flush()
                    return
            elif event.type == X.ButtonPress:
                print("ButtonPress", event.detail)
            elif event.type == X.ButtonRelease:
                print("ButtonRelease", event.detail)
            elif event.type == X.MotionNotify:
                print("MotionNotify", event.root_x, event.root_y)
    
    
    # Check if the extension is present
    if not record_dpy.has_extension("RECORD"):
        print("RECORD extension not found")
        sys.exit(1)
    r = record_dpy.record_get_version(0, 0)
    print("RECORD extension version %d.%d" % (r.major_version, r.minor_version))
    
    # Create a recording context; we only want key and mouse events
    ctx = record_dpy.record_create_context(
        0,
        [record.AllClients],
        [{
            'core_requests': (0, 0),
            'core_replies': (0, 0),
            'ext_requests': (0, 0, 0, 0),
            'ext_replies': (0, 0, 0, 0),
            'delivered_events': (0, 0),
            'device_events': (X.KeyPress, X.MotionNotify),
            'errors': (0, 0),
            'client_started': False,
            'client_died': False,
        }])
    
    # Enable the context; this only returns after a call to record_disable_context,
    # while calling the callback function in the meantime
    record_dpy.record_enable_context(ctx, record_callback)
    
    # Finally free the context
    record_dpy.record_free_context(ctx)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多