Python Linux:在PyQt5 GUI上显示当前连接的所有USB记忆棒的名称

Python Linux:在PyQt5 GUI上显示当前连接的所有USB记忆棒的名称,python,pyqt5,pyudev,Python,Pyqt5,Pyudev,下面的代码显示了Linux上控制台中新插入的U盘的名称(作为PyQt5 GUI的替代品) 不幸的是,在未正确弹出U盘的情况下拔下U盘时,控制台中会立即出现pyudev.device.\u errors.DeviceNotFoundAtPathError 需要更改什么来修复此错误 main.py: from functools import partial import os import sys import pyudev from PyQt5.QtWidgets import QAppli

下面的代码显示了Linux上控制台中新插入的U盘的名称(作为PyQt5 GUI的替代品)

不幸的是,在未正确弹出U盘的情况下拔下U盘时,控制台中会立即出现pyudev.device.\u errors.DeviceNotFoundAtPathError

需要更改什么来修复此错误

main.py

from functools import partial
import os
import sys

import pyudev

from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QSocketNotifier, QObject, pyqtSignal


class MainWindow():

    def __init__(self, parent=None):
        super().__init__()
        # GUI code
        pass

    def print_name(self, name):
        print(name)


class LinuxDeviceMonitor(QObject):
    devices_changed = pyqtSignal(list)

    def __init__(self):
        super().__init__()
        self._context = pyudev.Context()

        self._monitor = pyudev.Monitor.from_netlink(self._context)
        self._monitor.start()

        self._devices = set()

        self._process_devices(self._context.list_devices(), action="add")

    def fileno(self):
        return self._monitor.fileno()

    @property
    def device_names(self):
        return [pyudev.Devices.from_path(self._context, device).get("DEVNAME") for device in self._devices]

    def process_incoming(self):
        read_device = partial(pyudev._util.eintr_retry_call, self._monitor.poll, timeout=0)
        self._process_devices(iter(read_device, None))
        self.devices_changed.emit(self.device_names)

    def _process_devices(self, devices, action=None):
        for device in devices:
            action = device.action if action is None else action

            if action in ("add", "change") and self._is_usb_mass_storage_device(device):
                self._devices.add(device.sys_path)
            elif action == "remove" and device.sys_path in self._devices:
                self._devices.remove(device.sys_path)

    @classmethod
    def _read_device_flag(self, device, name):
        path = os.path.join(device.sys_path, name)
        try:
            with open(path) as data:
                return bool(int(data.read()))
        except (IOError, ValueError):
            return False

    def _is_usb_mass_storage_device(self, device):
        is_removable = self._read_device_flag(device, "removable")
        has_size = self._read_device_flag(device, "size")
        has_usb = device.get("ID_BUS") == "usb"
        has_no_disc = device.get("ID_CDROM") is None
        return is_removable and has_size and has_usb and has_no_disc


def main():
    app = QApplication(sys.argv)

    main_window = MainWindow()

    linux_device_monitor = LinuxDeviceMonitor()

    notifier = QSocketNotifier(linux_device_monitor.fileno(), QSocketNotifier.Read)
    notifier.activated.connect(linux_device_monitor.process_incoming)

    linux_device_monitor.devices_changed.connect(main_window.print_name)

    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

您可以简单地捕获异常:

改变

    @property
    def device_names(self):
        return [pyudev.Devices.from_path(self._context, device).get("DEVNAME") for device in self._devices]

整个代码:

from functools import partial
import os
import sys

import pyudev

from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QSocketNotifier, QObject, pyqtSignal


class MainWindow():

    def __init__(self, parent=None):
        super().__init__()
        # GUI code
        pass

    def print_name(self, name):
        print(name)


class LinuxDeviceMonitor(QObject):
    devices_changed = pyqtSignal(list)

    def __init__(self):
        super().__init__()
        self._context = pyudev.Context()

        self._monitor = pyudev.Monitor.from_netlink(self._context)
        self._monitor.start()

        self._devices = set()

        self._process_devices(self._context.list_devices(), action="add")

    def fileno(self):
        return self._monitor.fileno()

    def device_names(self):
        devices = []
        for device in self._devices:
            try:
                dev_name = pyudev.Devices.from_path(self._context, device).get("DEVNAME")
                devices.append(dev_name)
            except pyudev.DeviceNotFoundAtPathError:
                pass
        return devices

    def process_incoming(self):
        read_device = partial(pyudev._util.eintr_retry_call, self._monitor.poll, timeout=0)
        self._process_devices(iter(read_device, None))
        self.devices_changed.emit(self.device_names())

    def _process_devices(self, devices, action=None):
        for device in devices:
            action = device.action if action is None else action

            if action in ("add", "change") and self._is_usb_mass_storage_device(device):
                self._devices.add(device.sys_path)
            elif action == "remove" and device.sys_path in self._devices:
                self._devices.remove(device.sys_path)

    @classmethod
    def _read_device_flag(self, device, name):
        path = os.path.join(device.sys_path, name)
        try:
            with open(path) as data:
                return bool(int(data.read()))
        except (IOError, ValueError):
            return False

    def _is_usb_mass_storage_device(self, device):
        is_removable = self._read_device_flag(device, "removable")
        has_size = self._read_device_flag(device, "size")
        has_usb = device.get("ID_BUS") == "usb"
        has_no_disc = device.get("ID_CDROM") is None
        return is_removable and has_size and has_usb and has_no_disc


def main():
    app = QApplication(sys.argv)

    main_window = MainWindow()

    linux_device_monitor = LinuxDeviceMonitor()

    notifier = QSocketNotifier(linux_device_monitor.fileno(), QSocketNotifier.Read)
    notifier.activated.connect(linux_device_monitor.process_incoming)

    linux_device_monitor.devices_changed.connect(main_window.print_name)

    sys.exit(app.exec_())


if __name__ == '__main__':
    main()
插入和取出U盘时的输出:

[]
[]
[]
['/dev/sdc']
['/dev/sdc']
['/dev/sdc']
[]
[]
[]

我的第一个答案没有错,但是你可以用你自己的覆盖函数。下面是针对您的问题的一个示例。 主要变化是:

增加:

from pyudev._util import ensure_byte_string
并从系统路径重写
功能:

        pyudev.Devices.from_sys_path = self.from_sys_path

    def from_sys_path(self, context, sys_path):
        device = context._libudev.udev_device_new_from_syspath(
            context, ensure_byte_string(sys_path))
        if not device:
            return None
        return pyudev.Device(context, device)
更改:

    @property
    def device_names(self):
        return [pyudev.Devices.from_path(self._context, device).get("DEVNAME") for device in self._devices]
致:

整个代码如下所示:

from functools import partial
import os
import sys

import pyudev

from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QSocketNotifier, QObject, pyqtSignal
from pyudev._util import ensure_byte_string


class MainWindow():

    def __init__(self, parent=None):
        super().__init__()
        # GUI code
        pass

    def print_name(self, name):
        print(name)


class LinuxDeviceMonitor(QObject):
    devices_changed = pyqtSignal(list)

    def __init__(self):
        super().__init__()
        self._context = pyudev.Context()

        self._monitor = pyudev.Monitor.from_netlink(self._context)
        self._monitor.start()

        self._devices = set()

        self._process_devices(self._context.list_devices(), action="add")
        pyudev.Devices.from_sys_path = self.from_sys_path

    def from_sys_path(self, context, sys_path):
        device = context._libudev.udev_device_new_from_syspath(
            context, ensure_byte_string(sys_path))
        if not device:
            return None
        return pyudev.Device(context, device)

    def fileno(self):
        return self._monitor.fileno()

    def device_names(self):
        devices = []
        for device in self._devices:
            dev = pyudev.Devices.from_path(self._context, device)
            if dev is not None:
                devices.append(dev.get("DEVNAME"))
        return devices

    def process_incoming(self):
        read_device = partial(pyudev._util.eintr_retry_call, self._monitor.poll, timeout=0)
        self._process_devices(iter(read_device, None))
        self.devices_changed.emit(self.device_names())

    def _process_devices(self, devices, action=None):
        for device in devices:
            action = device.action if action is None else action

            if action in ("add", "change") and self._is_usb_mass_storage_device(device):
                self._devices.add(device.sys_path)
            elif action == "remove" and device.sys_path in self._devices:
                self._devices.remove(device.sys_path)

    @classmethod
    def _read_device_flag(self, device, name):
        path = os.path.join(device.sys_path, name)
        try:
            with open(path) as data:
                return bool(int(data.read()))
        except (IOError, ValueError):
            return False

    def _is_usb_mass_storage_device(self, device):
        is_removable = self._read_device_flag(device, "removable")
        has_size = self._read_device_flag(device, "size")
        has_usb = device.get("ID_BUS") == "usb"
        has_no_disc = device.get("ID_CDROM") is None
        return is_removable and has_size and has_usb and has_no_disc


def main():
    app = QApplication(sys.argv)

    main_window = MainWindow()

    linux_device_monitor = LinuxDeviceMonitor()

    notifier = QSocketNotifier(linux_device_monitor.fileno(), QSocketNotifier.Read)
    notifier.activated.connect(linux_device_monitor.process_incoming)

    linux_device_monitor.devices_changed.connect(main_window.print_name)

    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

您的IDE可能会抱怨您正在访问模块的受保护成员(pyudev.\u util),但它仍然可以工作。

太好了。捕获异常可避免出现错误消息。只是出于兴趣,有没有一种方法可以在不使用异常的情况下修复此bug?
        pyudev.Devices.from_sys_path = self.from_sys_path

    def from_sys_path(self, context, sys_path):
        device = context._libudev.udev_device_new_from_syspath(
            context, ensure_byte_string(sys_path))
        if not device:
            return None
        return pyudev.Device(context, device)
    @property
    def device_names(self):
        return [pyudev.Devices.from_path(self._context, device).get("DEVNAME") for device in self._devices]
    def device_names(self):
        devices = []
        for device in self._devices:
            dev = pyudev.Devices.from_path(self._context, device)
            if dev is not None:
                devices.append(dev.get("DEVNAME"))
        return devices
        self.devices_changed.emit(self.device_names)
        self.devices_changed.emit(self.device_names())
from functools import partial
import os
import sys

import pyudev

from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QSocketNotifier, QObject, pyqtSignal
from pyudev._util import ensure_byte_string


class MainWindow():

    def __init__(self, parent=None):
        super().__init__()
        # GUI code
        pass

    def print_name(self, name):
        print(name)


class LinuxDeviceMonitor(QObject):
    devices_changed = pyqtSignal(list)

    def __init__(self):
        super().__init__()
        self._context = pyudev.Context()

        self._monitor = pyudev.Monitor.from_netlink(self._context)
        self._monitor.start()

        self._devices = set()

        self._process_devices(self._context.list_devices(), action="add")
        pyudev.Devices.from_sys_path = self.from_sys_path

    def from_sys_path(self, context, sys_path):
        device = context._libudev.udev_device_new_from_syspath(
            context, ensure_byte_string(sys_path))
        if not device:
            return None
        return pyudev.Device(context, device)

    def fileno(self):
        return self._monitor.fileno()

    def device_names(self):
        devices = []
        for device in self._devices:
            dev = pyudev.Devices.from_path(self._context, device)
            if dev is not None:
                devices.append(dev.get("DEVNAME"))
        return devices

    def process_incoming(self):
        read_device = partial(pyudev._util.eintr_retry_call, self._monitor.poll, timeout=0)
        self._process_devices(iter(read_device, None))
        self.devices_changed.emit(self.device_names())

    def _process_devices(self, devices, action=None):
        for device in devices:
            action = device.action if action is None else action

            if action in ("add", "change") and self._is_usb_mass_storage_device(device):
                self._devices.add(device.sys_path)
            elif action == "remove" and device.sys_path in self._devices:
                self._devices.remove(device.sys_path)

    @classmethod
    def _read_device_flag(self, device, name):
        path = os.path.join(device.sys_path, name)
        try:
            with open(path) as data:
                return bool(int(data.read()))
        except (IOError, ValueError):
            return False

    def _is_usb_mass_storage_device(self, device):
        is_removable = self._read_device_flag(device, "removable")
        has_size = self._read_device_flag(device, "size")
        has_usb = device.get("ID_BUS") == "usb"
        has_no_disc = device.get("ID_CDROM") is None
        return is_removable and has_size and has_usb and has_no_disc


def main():
    app = QApplication(sys.argv)

    main_window = MainWindow()

    linux_device_monitor = LinuxDeviceMonitor()

    notifier = QSocketNotifier(linux_device_monitor.fileno(), QSocketNotifier.Read)
    notifier.activated.connect(linux_device_monitor.process_incoming)

    linux_device_monitor.devices_changed.connect(main_window.print_name)

    sys.exit(app.exec_())


if __name__ == '__main__':
    main()