Skip to content Skip to sidebar Skip to footer

Notify Qml For 'usb Device Inserted' Events Using Pyqt5 And Pyudev

I have a GUI application (made with PyQt5 and QML) and would like to get notify when a usb device is plugged or unplugged from the computer. After some investigation, I have found

Solution 1:

The best thing to do is to modify the GUI in QML, for which the Monitor and Device object must be accessible from QML. Only QObjects receive notifications so I will create 2 classes that wrap with a light layer to both classes using q-properties and slots.

pyqtudev.py

from PyQt5 import QtCore
from pyudev import Context, Monitor, Device
from pyudev.pyqt5 import MonitorObserver

classQtUdevDevice(QtCore.QObject):
    def__init__(self, parent=None):
        super(QtUdevDevice, self).__init__(parent)
        self.m_dev = Nonedefinitialize(self, dev):
        self.m_dev = dev

    @QtCore.pyqtSlot(result=bool)defisValid(self):
        return self.m_dev isnotNone    @QtCore.pyqtProperty(str, constant=True)defdevType(self):
        ifnot self.isValid():
            return""if self.m_dev.device_type isNone:
            return""return self.m_dev.device_type

    @QtCore.pyqtProperty(str, constant=True)defsubsystem(self):
        ifnot self.isValid():
            return""return self.m_dev.subsystem

    @QtCore.pyqtProperty(str, constant=True)defname(self):
        ifnot self.isValid():
            return""return self.m_dev.sys_name

    @QtCore.pyqtProperty(str, constant=True)defdriver(self):
        ifnot self.isValid():
            return""if self.m_dev.driver isNone:
            return""return self.m_dev.driver

    @QtCore.pyqtProperty(str, constant=True)defdeviceNode(self):
        ifnot self.isValid():
            return""if self.m_dev.device_node isNone:
            return""return self.m_dev.device_node

    @QtCore.pyqtProperty(list, constant=True)defalternateDeviceSymlinks(self):
        returnlist(self.m_dev.device_links)

    @QtCore.pyqtProperty(str, constant=True)defsysfsPath(self):
        ifnot self.isValid():
            return""return self.m_dev.sys_path

    @QtCore.pyqtProperty(int, constant=True)defsysfsNumber(self):
        ifnot self.isValid():
            return -1if self.m_dev.sys_number isNone:
            return -1returnint(self.m_dev.sys_number)

    @QtCore.pyqtSlot(str, result=str)defproperty(self, name):
        ifnot self.isValid():
            return""
        v = self.m_dev.properties.get(name)
        return v if v isnotNoneelse""    @QtCore.pyqtSlot(str, result=bool)defhasProperty(self, name):
        ifnot self.isValid():
            returnFalsereturn self.m_dev.properties.get(name) isnotNone    @QtCore.pyqtProperty(list, constant=True)defdeviceProperties(self):
        ifnot self.isValid():
            return []
        returnlist(self.m_dev.properties)

    @QtCore.pyqtProperty(list, constant=True)defsysfsProperties(self):
        ifnot self.isValid():
            return []
        returnlist(self.m_dev.attributes.available_attributes)

    @QtCore.pyqtProperty(QtCore.QObject, constant=True)defparentDevice(self):
        ifnot self.isValid:
            returnif self.m_dev.parent:
            parent_device = QtUdevDevice()
            parent_device.initialize(self.m_dev.parent)
            return parent_device

    @QtCore.pyqtProperty(str, constant=True)defaction(self):
        ifnot self.isValid():
            return""if self.m_dev.action isNone:
            return""return self.m_dev.action

    def__repr__(self):
        if self.isValid():
            return"UdevDevice({})".format(self.sysfsPath())
        return"Invalid UdevDevice"classQtMonitorObserver(QtCore.QObject):
    deviceEvent = QtCore.pyqtSignal(QtUdevDevice, arguments=["device"])
    deviceAdded = QtCore.pyqtSignal(QtUdevDevice, arguments=["device"])
    deviceRemoved = QtCore.pyqtSignal(QtUdevDevice, arguments=["device"])
    deviceChanged = QtCore.pyqtSignal(QtUdevDevice, arguments=["device"])
    deviceOnlined = QtCore.pyqtSignal(QtUdevDevice, arguments=["device"])
    deviceOfflined = QtCore.pyqtSignal(QtUdevDevice, arguments=["device"])

    def__init__(self, parent=None):
        super(QtMonitorObserver, self).__init__(parent)
        context = Context()
        self._monitor = Monitor.from_netlink(context)
        self._observer = MonitorObserver(self._monitor, self)
        self._observer.deviceEvent.connect(self.setup_new_signals)

    @QtCore.pyqtSlot()defstart(self):
        self._monitor.start()

    @QtCore.pyqtSlot(str)deffilter_by(self, filter):
        self._monitor.filter_by(subsystem=filter)

    @QtCore.pyqtSlot(str)deffilter_by_tag(self, tag):
        self._monitor.filter_by_tag(tag)

    @QtCore.pyqtSlot()defremove_filter(self):
        self._monitor.remove_filter()

    @QtCore.pyqtSlot(Device)defsetup_new_signals(self, device):
        new_signals_map = {
            'add': self.deviceAdded,
            'remove': self.deviceRemoved,
            'change': self.deviceChanged,
            'online': self.deviceOnlined,
            'offline': self.deviceOfflined,
        }
        signal = new_signals_map.get(device.action)
        qtdevice = QtUdevDevice()
        qtdevice.initialize(device)
        if signal:
            signal.emit(qtdevice)
        self.deviceEvent.emit(qtdevice)

main.py

import os
import sys
from PyQt5 import QtCore, QtGui, QtQml

from pyqtudev import QtMonitorObserver

def run():
    app = QtGui.QGuiApplication(sys.argv)
    engine = QtQml.QQmlApplicationEngine()
    observer = QtMonitorObserver()
    engine.rootContext().setContextProperty("observer", observer)
    directory = os.path.dirname(os.path.abspath(__file__))
    engine.load(QtCore.QUrl.fromLocalFile(os.path.join(directory, 'main.qml')))
    if not engine.rootObjects():
        return -1
    return app.exec_()

if __name__ == "__main__":
    sys.exit(run())

main.qml

import QtQuick 2.11
import QtQuick.Window2.2
import QtQuick.Controls2.2

ApplicationWindow {    
    visible: true
    width: Screen.width/2
    height: Screen.height/2
    Connections {
        target: observer
        onDeviceEvent: {
            console.log(device, device.name, device.action, device.parentDevice)
            if(device.hasProperty("ID_VENDOR_ID")){
                console.log(device.property("ID_VENDOR_ID"))
            }
        }
    }
    Component.onCompleted: {
        observer.start()
        observer.filter_by("usb")
    } 
}

Post a Comment for "Notify Qml For 'usb Device Inserted' Events Using Pyqt5 And Pyudev"