rgov
rgov

Reputation: 4359

Scanning for Bluetooth devices with PyQt5.QtBluetooth

I am trying to use PyQt5 (5.11.3) to use the QtBluetooth APIs to scan for Bluetooth devices.

The device discovery APIs are listed as supported for my platform (macOS 10.14.2) and I have been able to do other Bluetooth-related things such as get the host controller's address and list addresses of connected devices.

Here's an attempt that sets a callback (slot in Qt parlance?) whenever a new device is detected, an error happens, or scanning finishes. Also, a timer prints out whether scanning is currently active, and a list of any devices that were found so far.

Although my error callback is never fired, and the timer always prints out that the scan is active, and my system logs show me that the Bluetooth controller is in scan mode, I do not see any devices. I know that I should see at least a few.

#!/usr/bin/env python
import signal
import sys

from PyQt5 import QtBluetooth as QtBt
from PyQt5 import QtCore


class Application(QtCore.QCoreApplication):
  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.scan_for_devices()
    self.exec()

  def display_status(self):
    print(self.agent.isActive(), self.agent.discoveredDevices())

  def foo(self, *args, **kwargs):
    print('foo', args, kwargs)

  def scan_for_devices(self):
    self.agent = QtBt.QBluetoothDeviceDiscoveryAgent(self)
    self.agent.deviceDiscovered.connect(self.foo)
    self.agent.finished.connect(self.foo)
    self.agent.error.connect(self.foo)
    self.agent.setLowEnergyDiscoveryTimeout(1000)

    timer = QtCore.QTimer(self.agent)
    timer.start(500)
    timer.timeout.connect(self.display_status)

    self.agent.start()


if __name__ == '__main__':
  import sys
  app = Application(sys.argv)

The C++ example does work on my system, though: http://doc.qt.io/qt-5/qtbluetooth-btscanner-example.html

Upvotes: 1

Views: 3527

Answers (1)

rgov
rgov

Reputation: 4359

It turns out this is a known but (seemingly?) undocumented issue with Qt on macOS. The background is QTBUG-46625. Ordinarily, a QtCoreApplication on macOS uses Qt's own event loop. But the Bluetooth frameworks on macOS require the CoreFoundation event loop, which can be activated like this before an instance of QtCoreApplication is created:

if sys.platform == 'darwin':
    os.environ['QT_EVENT_DISPATCHER_CORE_FOUNDATION'] = '1'

Alternatively, you can subclass QtWidget.QApplication instead.

Unfortunately, this causes a dock icon to appear for the Python process. I haven't worked out how to hide it.

Upvotes: 2

Related Questions