以下代码显示了 Linux 上控制台中新插入的 USB 内存棒的名称(作为 PyQt5 GUI 的替代品)。
不幸的是,一旦您拔掉 USB 内存棒而没有正确弹出它,控制台中就会出现 pyudev.device._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()
最佳答案
我的第一个答案没有任何问题,但您可以用自己的函数覆盖函数。以下是针对您的问题的示例。 主要变化是:
添加:
from pyudev._util import ensure_byte_string
并覆盖了from_sys_path
函数:
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()
您的 IDE 可能会提示您正在访问模块的 protected 成员 (pyudev._util),但它仍然可以工作。
关于python - Linux : Display names of all currently attached USB sticks on PyQt5 GUI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55366051/