用python脚本检测热插拔事件

在 Python 中,检测热插拔事件(例如 USB 设备插入或移除)可以通过以下几种方法实现。最常用的方式是使用 pyudev 库(适用于 Linux 系统)来监听系统设备的变化。

用python脚本检测热插拔事件_第1张图片

1、问题背景

用户正在尝试使用 Python 来检测鼠标和键盘事件,并在检测过程中容忍热插拔操作。用户编写的脚本可以自动检测运行时环境中的键盘和鼠标插件,并输出所有键盘和鼠标事件。用户使用了 evdev 和 pyudev 包来实现此功能。脚本大部分都可以正常工作,包括键盘和鼠标事件检测以及插件检测。然而,每当用户拔出鼠标时,都会发生许多奇怪的事情,导致脚本无法正常工作。

(1)每当将鼠标插入系统时,/dev/input/ 文件夹中都会生成两个文件,包括 ./mouseX 和 ./eventX。用户尝试通过 cat 命令查看两个源的输出,发现确实有差异,但我不理解为什么 Linux 会在 ./eventX 已经存在的情况下仍然会有 ./mouseX。

(2)每当用户拔下鼠标时,./mouseX 拔下事件会首先发生,但我在 evdev 中没有使用它,这会导致脚本失败,因为 ./eventX(脚本中读取数据的位置)也会同时拔下,但我只能在下一轮中检测到 ./eventX。我使用了一个技巧(脚本中的变量 i)来绕过这个问题,但即使我能够成功删除鼠标设备,select.select() 也会开始无限期地读取输入,即使我没有对键盘输入任何内容。

以下是脚本的内容(根据先前帖子的答案进行了修改),非常感谢您事先的关注!

#!/usr/bin/env python

import pyudev
from evdev import InputDevice, list_devices, categorize
from select import select

context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by(subsystem='input')
monitor.start()

devices = map(InputDevice, list_devices())
dev_paths = []
finalizers = []

for dev in devices:
    if "keyboard" in dev.name.lower():
        dev_paths.append(dev.fn)
    elif "mouse" in dev.name.lower():
        dev_paths.append(dev.fn)

devices = map(InputDevice, dev_paths)
devices = {dev.fd : dev for dev in devices}
devices[monitor.fileno()] = monitor
count = 1

while True:
    r, w, x = select(devices, [], [])

    if monitor.fileno() in r:
        r.remove(monitor.fileno())

        for udev in iter(functools.partial(monitor.poll, 0), None):
            # we're only interested in devices that have a device node
            # (e.g. /dev/input/eventX)
            if not udev.device_node:
                break

            # find the device we're interested in and add it to fds
            for name in (i['NAME'] for i in udev.ancestors if 'NAME' in i):
                # I used a virtual input device for this test - you
                # should adapt this to your needs
                if 'mouse' in name.lower() and 'event' in udev.device_node:
                    if udev.action == 'add':
                        print('Device added: %s' % udev)
                        dev = InputDevice(udev.device_node)
                        devices[dev.fd] = dev
                        break
                    if udev.action == 'remove':
                        print('Device removed: %s' % udev)
                        finalizers.append(udev.device_node)
                        break


    for path in finalizers:
        for dev in devices.keys():
            if dev != monitor.fileno() and devices[dev].fn == path:
                print "delete the device from list"
                del devices[dev]

    for i in r:
        if i in devices.keys() and count != 0:
            count = -1
            for event in devices[i].read():
                count = count + 1
                print(categorize(event))

2、解决方案

答案1:

mouseX 和 eventX 之间的区别在于,一般来说,eventX 是 evdev 设备,而 mouseX 是“传统”设备(例如,不支持各种 evdev ioctl)。

我不知道你发布的代码出了什么问题,但这里有一个代码片段可以解决这个问题。

#!/usr/bin/env python

import pyudev
import evdev
import select
import sys
import functools
import errno

context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by(subsystem='input')
# NB: Start monitoring BEFORE we query evdev initially, so that if
# there is a plugin after we evdev.list_devices() we'll pick it up
monitor.start()

# Modify this predicate function for whatever you want to match against
def pred(d):
    return "keyboard" in d.name.lower() or "mouse" in d.name.lower()

# Populate the "active devices" map, mapping from /dev/input/eventXX to
# InputDevice
devices = {}
for d in map(evdev.InputDevice, evdev.list_devices()):
    if pred(d):
        print d
        devices[d.fn] = d

# "Special" monitor device
devices['monitor'] = monitor

while True:
    rs, _, _ = select.select(devices.values(), [], [])
    # Unconditionally ping monitor; if this is spurious this
    # will no-op because we pass a zero timeout.  Note that
    # it takes some time for udev events to get to us.
    for udev in iter(functools.partial(monitor.poll, 0), None):
        if not udev.device_node: break
        if udev.action == 'add':
            if udev.device_node not in devices:
                print "Device added: %s" % udev
                try:
                    devices[udev.device_node] = evdev.InputDevice(udev.device_node)
                except IOError, e:
                    # udev reports MORE devices than are accessible from
                    # evdev; a simple way to check is see if the devinfo
                    # ioctl fails
                    if e.errno != errno.ENOTTY: raise
                    pass
        elif udev.action == 'remove':
            # NB: This code path isn't exercised very frequently,
            # because select() will trigger a read immediately when file
            # descriptor goes away, whereas the udev event takes some
            # time to propagate to us.
            if udev.device_node in devices:
                print "Device removed (udev): %s" % devices[udev.device_node]
                del devices[udev.device_node]
    for r in rs:
        # You can't read from a monitor
        if r.fileno() == monitor.fileno(): continue
        if r.fn not in devices: continue
        # Select will immediately return an fd for read if it will
        # ENODEV.  So be sure to handle that.
        try:
            for event in r.read():
                pass
                print evdev.categorize(event)
        except IOError, e:
            if e.errno != errno.ENODEV: raise
            print "Device removed: %s" % r
            del devices[r.fn]

然后你可以用类似的方式来监听事件。

你可能感兴趣的:(python,windows,开发语言,java,android)