[PyQt] QThread, Slots, Signals, meta type information error

Repscher, Benedikt Benedikt.Repscher at webasto.com
Fri Mar 1 16:39:45 GMT 2019


I'm having an Issue regarding QThreads, Slots and Signals and meta type information.

The Issue arises when I try to connect a signal, e.g.
QBluetoothDeviceDiscoveryAgent.deviceDiscovered
to a slot, e.g.
deviceDiscovered(device)
where the device is of type QBluetoothDeviceInfo

The signal-slot connection is done from within a QThread.

The error that arises is
QObject::connect: Cannot queue arguments of type 'QBluetoothDeviceInfo'
(Make sure 'QBluetoothDeviceInfo' is registered using qRegisterMetaType().)

I've tried generating the meta type info explicitly using
char_id = QMetaType.type("QBluetoothDeviceInfo")
this doesn't help though (and returns 0 anyways)


What really baffles me is, that this error only occurs when I implement the QThread the traditional way by subclassing QThread and overriding run().
When I use the approach with the controller that moves the QObject to a QThread (using moveToThread()), then the code works.

------------------------ code that works ------------------------
from PyQt5.QtBluetooth import QLowEnergyController,\
    QBluetoothDeviceDiscoveryAgent, QBluetoothDeviceInfo
from PyQt5.QtCore import pyqtSlot, QThread, QCoreApplication, QObject,\
    pyqtSignal
import sys

class BTLEWorker(QObject):    
    def __init__(self, app:QCoreApplication=None, parent:QObject=None, *args, **kwargs):    
        self.app = app
            
        self.discovery_agent = QBluetoothDeviceDiscoveryAgent
        self.le_controller = QLowEnergyController
                        
        super(BTLEWorker, self).__init__(parent=parent, *args, **kwargs)
                    
    def run(self):
        self.discovery_agent = QBluetoothDeviceDiscoveryAgent()
                                
        self.discovery_agent.deviceDiscovered.connect(self.onDeviceDiscovered)
        self.discovery_agent.finished.connect(self.onDeviceDiscoveryFinished)
        self.discovery_agent.error.connect(self.onDeviceDiscoveryError)
        self.discovery_agent.setLowEnergyDiscoveryTimeout(1000)
        self.discovery_agent.start()
    
        self.enterLoop()
                            
    def enterLoop(self):
        th = self.thread()
        th.exec_()
        
    def exitLoop(self):
        th = self.thread()
        th.exit()
     
    #       
    # Slots for QDeviceDiscoveryAgent
    #                
    @pyqtSlot(QBluetoothDeviceInfo)
    def onDeviceDiscovered(self, devinfo: QBluetoothDeviceInfo):
        address = devinfo.address() #: :type address: QBluetoothAddress
        
        if devinfo.coreConfigurations() & QBluetoothDeviceInfo.LowEnergyCoreConfiguration:
            print("Last device discovered", devinfo.name(), address.toString())
            
    @pyqtSlot()
    def onDeviceDiscoveryFinished(self):
        print("Discovery finished")
        
        self.exitLoop()
    
    @pyqtSlot(QBluetoothDeviceDiscoveryAgent.Error)
    def onDeviceDiscoveryError(self, error: QBluetoothDeviceDiscoveryAgent.Error):
        print("Bluetooth error", error)
        
        self.exitLoop()
    

class Controller(QObject):
    operate = pyqtSignal()
    
    def __init__(self, app=None, parent=None, *args, **kwargs):
        """
        @type app: QApplication
        @type parent: QObject
        """
        super(Controller, self).__init__(parent=parent, *args, **kwargs)
                
        self.app = app
        
        self.worker = BTLEWorker()
        self.worker_thread = QThread()
        self.worker.moveToThread(self.worker_thread)
        
        self.worker_thread.finished.connect(self.quit)
        self.operate.connect(self.worker.run)
                
        self.worker_thread.start()
                
    @pyqtSlot()
    def quit(self):
        """
        @rtype int
        """
        self.app.exit()
        
                
if __name__ == '__main__':    
    qapp = QCoreApplication(sys.argv)
    
    c = Controller(qapp)
    c.operate.emit()
    
    sys.exit(qapp.exec_())
------------------------ end of code that works ------------------------

------------------------ code that DOENST work ------------------------
from PyQt5.QtBluetooth import QLowEnergyController,\
    QBluetoothDeviceDiscoveryAgent, QBluetoothDeviceInfo
from PyQt5.QtCore import pyqtSlot, QThread, QCoreApplication, QObject
import sys


class BTLEWorker(QThread):    
    def __init__(self, app:QCoreApplication=None, parent:QObject=None, *args, **kwargs):    
        self.app = app
            
        self.discovered_devices = list()
        
        self.discovery_agent = QBluetoothDeviceDiscoveryAgent
        self.le_controller = QLowEnergyController
                        
        super(BTLEWorker, self).__init__(parent=parent, *args, **kwargs)
                
    def run(self):
        self.discovery_agent = QBluetoothDeviceDiscoveryAgent()
                                
        self.discovery_agent.deviceDiscovered.connect(self.onDeviceDiscovered) #THIS FAILES
        self.discovery_agent.finished.connect(self.onDeviceDiscoveryFinished)
        self.discovery_agent.error.connect(self.onDeviceDiscoveryError)
        self.discovery_agent.setLowEnergyDiscoveryTimeout(1000)
        self.discovery_agent.start()
    
        self.enterLoop()
        
        self.app.exit()
                            
    def enterLoop(self):
        self.exec_()
        
    def exitLoop(self):
        self.exit()
     
    #       
    # Slots for QDeviceDiscoveryAgent
    #                
    @pyqtSlot(QBluetoothDeviceInfo)
    def onDeviceDiscovered(self, devinfo: QBluetoothDeviceInfo):
        address = devinfo.address() #: :type address: QBluetoothAddress
        
        if devinfo.coreConfigurations() & QBluetoothDeviceInfo.LowEnergyCoreConfiguration:
            self.discovered_devices.append(devinfo)
            print("Last device discovered", devinfo.name(), address.toString())
            
    @pyqtSlot()
    def onDeviceDiscoveryFinished(self):
        print("Discovery finished")
        
        self.exitLoop()
    
    @pyqtSlot(QBluetoothDeviceDiscoveryAgent.Error)
    def onDeviceDiscoveryError(self, error: QBluetoothDeviceDiscoveryAgent.Error):
        print("Bluetooth error", error)
        
        self.exitLoop()    
    
                
if __name__ == '__main__':    
    qapp = QCoreApplication(sys.argv)
    
    c = BTLEWorker(qapp)
    c.start()
    
    sys.exit(qapp.exec_())        
------------------------ end of code that DOENST work ------------------------

I would really like to understand whats happening here.
I couldn't find any answer when looking through the Qt and PyQt documentations

Best regards

Benedikt
    



More information about the PyQt mailing list