aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2022-09-02 03:01:06 +0000
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2022-09-02 03:01:06 +0000
commitc73824dd388c7932e45c40d29bf60822ea6f3528 (patch)
tree0709fedd0c0aa2771bb5f9c32766d4ce05588580
parent789fc5d65d2e0ba95cda25e63f74dd4def87a775 (diff)
parent1249a264d86d6c91421938a3a5dd66d4d9ebeb8d (diff)
Merge branch 6.3 into wip/6.3_pypy
-rw-r--r--build_scripts/main.py2
-rw-r--r--examples/bluetooth/heartrate_game/bluetoothbaseclass.py77
-rw-r--r--examples/bluetooth/heartrate_game/connectionhandler.py88
-rw-r--r--examples/bluetooth/heartrate_game/devicefinder.py159
-rw-r--r--examples/bluetooth/heartrate_game/devicehandler.py344
-rw-r--r--examples/bluetooth/heartrate_game/deviceinfo.py75
-rw-r--r--examples/bluetooth/heartrate_game/doc/heartrate_game.rst9
-rw-r--r--examples/bluetooth/heartrate_game/heartrate_game.pyproject22
-rw-r--r--examples/bluetooth/heartrate_game/heartrate_global.py43
-rw-r--r--examples/bluetooth/heartrate_game/main.py90
-rw-r--r--examples/bluetooth/heartrate_game/qml/App.qml130
-rw-r--r--examples/bluetooth/heartrate_game/qml/BluetoothAlarmDialog.qml121
-rw-r--r--examples/bluetooth/heartrate_game/qml/BottomLine.qml59
-rw-r--r--examples/bluetooth/heartrate_game/qml/Connect.qml188
-rw-r--r--examples/bluetooth/heartrate_game/qml/GameButton.qml88
-rw-r--r--examples/bluetooth/heartrate_game/qml/GamePage.qml93
-rw-r--r--examples/bluetooth/heartrate_game/qml/GameSettings.qml101
-rw-r--r--examples/bluetooth/heartrate_game/qml/Measure.qml244
-rw-r--r--examples/bluetooth/heartrate_game/qml/SplashScreen.qml90
-rw-r--r--examples/bluetooth/heartrate_game/qml/Stats.qml99
-rw-r--r--examples/bluetooth/heartrate_game/qml/StatsLabel.qml82
-rw-r--r--examples/bluetooth/heartrate_game/qml/TitleBar.qml97
-rw-r--r--examples/bluetooth/heartrate_game/qml/images/bt_off_to_on.pngbin0 -> 6143 bytes
-rw-r--r--examples/bluetooth/heartrate_game/qml/images/heart.pngbin0 -> 2664 bytes
-rw-r--r--examples/bluetooth/heartrate_game/qml/images/logo.pngbin0 -> 31915 bytes
-rw-r--r--examples/bluetooth/heartrate_game/qml/main.qml110
-rw-r--r--examples/bluetooth/heartrate_game/qml/qmldir1
-rw-r--r--examples/bluetooth/heartrate_server/doc/heartrate_server.rst8
-rw-r--r--examples/bluetooth/heartrate_server/heartrate_server.py131
-rw-r--r--examples/bluetooth/heartrate_server/heartrate_server.pyproject3
-rw-r--r--sources/pyside6/doc/CMakeLists.txt2
-rw-r--r--sources/pyside6/doc/gettingstarted-linux.rst46
-rw-r--r--sources/shiboken6/doc/CMakeLists.txt2
-rw-r--r--tools/snippets_translate/override.py19
34 files changed, 2612 insertions, 11 deletions
diff --git a/build_scripts/main.py b/build_scripts/main.py
index c654a4e02..daf7d2f70 100644
--- a/build_scripts/main.py
+++ b/build_scripts/main.py
@@ -1274,7 +1274,7 @@ class PysideRstDocs(Command, DistUtilsCommandMixin):
elif self.name == SHIBOKEN:
self.sphinx_src = self.out_dir
- sphinx_cmd = ["sphinx-build", "-b", "html", "-c", self.sphinx_src,
+ sphinx_cmd = ["sphinx-build", "-b", "html", "-j", "auto", "-c", self.sphinx_src,
self.doc_dir, self.out_dir]
if run_process(sphinx_cmd) != 0:
raise DistutilsSetupError(f"Error running CMake for {self.doc_dir}")
diff --git a/examples/bluetooth/heartrate_game/bluetoothbaseclass.py b/examples/bluetooth/heartrate_game/bluetoothbaseclass.py
new file mode 100644
index 000000000..c214659ac
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/bluetoothbaseclass.py
@@ -0,0 +1,77 @@
+#############################################################################
+##
+## Copyright (C) 2022 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+from PySide6.QtCore import QObject, Property, Signal, Slot
+
+
+class BluetoothBaseClass(QObject):
+
+ errorChanged = Signal()
+ infoChanged = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_error = ""
+ self.m_info = ""
+
+ @Property(str, notify=errorChanged)
+ def error(self):
+ return self.m_error
+
+ @error.setter
+ def error(self, e):
+ if self.m_error != e:
+ self.m_error = e
+ self.errorChanged.emit()
+
+ @Property(str, notify=infoChanged)
+ def info(self):
+ return self.m_info
+
+ @info.setter
+ def info(self, i):
+ if self.m_info != i:
+ self.m_info = i
+ self.infoChanged.emit()
+
+ @Slot()
+ def clearMessages(self):
+ self.info = ""
+ self.error = ""
diff --git a/examples/bluetooth/heartrate_game/connectionhandler.py b/examples/bluetooth/heartrate_game/connectionhandler.py
new file mode 100644
index 000000000..b628e867a
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/connectionhandler.py
@@ -0,0 +1,88 @@
+#############################################################################
+##
+## Copyright (C) 2022 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+
+from PySide6.QtBluetooth import QBluetoothLocalDevice
+from PySide6.QtQml import QmlElement
+from PySide6.QtCore import QObject, Property, Signal, Slot
+
+from heartrate_global import simulator
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "Shared"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class ConnectionHandler(QObject):
+
+ deviceChanged = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_localDevice = QBluetoothLocalDevice()
+ self.m_localDevice.hostModeStateChanged.connect(self.hostModeChanged)
+
+ @Property(bool, notify=deviceChanged)
+ def alive(self):
+ if sys.platform == "darwin":
+ return True
+ if simulator:
+ return True
+ return (self.m_localDevice.isValid()
+ and self.m_localDevice.hostMode() != QBluetoothLocalDevice.HostPoweredOff)
+
+ @Property(bool, constant=True)
+ def requiresAddressType(self):
+ return sys.platform == "linux" # QT_CONFIG(bluez)?
+
+ @Property(str, notify=deviceChanged)
+ def name(self):
+ return self.m_localDevice.name()
+
+ @Property(str, notify=deviceChanged)
+ def address(self):
+ return self.m_localDevice.address().toString()
+
+ @Slot(QBluetoothLocalDevice.HostMode)
+ def hostModeChanged(self, mode):
+ self.deviceChanged.emit()
diff --git a/examples/bluetooth/heartrate_game/devicefinder.py b/examples/bluetooth/heartrate_game/devicefinder.py
new file mode 100644
index 000000000..7527d841e
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/devicefinder.py
@@ -0,0 +1,159 @@
+#############################################################################
+##
+## Copyright (C) 2022 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+from PySide6.QtBluetooth import (QBluetoothDeviceDiscoveryAgent,
+ QBluetoothDeviceInfo)
+from PySide6.QtQml import QmlElement
+from PySide6.QtCore import QTimer, Property, Signal, Slot
+
+from bluetoothbaseclass import BluetoothBaseClass
+from deviceinfo import DeviceInfo
+from heartrate_global import simulator
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "Shared"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class DeviceFinder(BluetoothBaseClass):
+
+ scanningChanged = Signal()
+ devicesChanged = Signal()
+
+ def __init__(self, handler, parent=None):
+ super().__init__(parent)
+ self.m_deviceHandler = handler
+ self.m_devices = []
+ self.m_demoTimer = QTimer()
+#! [devicediscovery-1]
+ self.m_deviceDiscoveryAgent = QBluetoothDeviceDiscoveryAgent(self)
+ self.m_deviceDiscoveryAgent.setLowEnergyDiscoveryTimeout(15000)
+ self.m_deviceDiscoveryAgent.deviceDiscovered.connect(self.addDevice)
+ self.m_deviceDiscoveryAgent.errorOccurred.connect(self.scanError)
+
+ self.m_deviceDiscoveryAgent.finished.connect(self.scanFinished)
+ self.m_deviceDiscoveryAgent.canceled.connect(self.scanFinished)
+#! [devicediscovery-1]
+ if simulator:
+ self.m_demoTimer.setSingleShot(True)
+ self.m_demoTimer.setInterval(2000)
+ self.m_demoTimer.timeout.connect(self.scanFinished)
+
+ @Slot()
+ def startSearch(self):
+ self.clearMessages()
+ self.m_deviceHandler.setDevice(None)
+ self.m_devices.clear()
+
+ self.devicesChanged.emit()
+
+ if simulator:
+ self.m_demoTimer.start()
+ else:
+#! [devicediscovery-2]
+ self.m_deviceDiscoveryAgent.start(QBluetoothDeviceDiscoveryAgent.LowEnergyMethod)
+#! [devicediscovery-2]
+ self.scanningChanged.emit()
+ self.info = "Scanning for devices..."
+
+#! [devicediscovery-3]
+ @Slot(QBluetoothDeviceInfo)
+ def addDevice(self, device):
+ # If device is LowEnergy-device, add it to the list
+ if device.coreConfigurations() & QBluetoothDeviceInfo.LowEnergyCoreConfiguration:
+ self.m_devices.append(DeviceInfo(device))
+ self.info = "Low Energy device found. Scanning more..."
+#! [devicediscovery-3]
+ self.devicesChanged.emit()
+#! [devicediscovery-4]
+ #...
+#! [devicediscovery-4]
+
+ @Slot(QBluetoothDeviceDiscoveryAgent.Error)
+ def scanError(self, error):
+ if error == QBluetoothDeviceDiscoveryAgent.PoweredOffError:
+ self.error = "The Bluetooth adaptor is powered off."
+ elif error == QBluetoothDeviceDiscoveryAgent.InputOutputError:
+ self.error = "Writing or reading from the device resulted in an error."
+ else:
+ self.error = "An unknown error has occurred."
+
+ @Slot()
+ def scanFinished(self):
+ if simulator:
+ # Only for testing
+ for i in range(5):
+ self.m_devices.append(DeviceInfo(QBluetoothDeviceInfo()))
+
+ if self.m_devices:
+ self.info = "Scanning done."
+ else:
+ self.error = "No Low Energy devices found."
+
+ self.scanningChanged.emit()
+ self.devicesChanged.emit()
+
+ @Slot(str)
+ def connectToService(self, address):
+ self.m_deviceDiscoveryAgent.stop()
+
+ currentDevice = None
+ for entry in self.m_devices:
+ device = entry
+ if device and device.deviceAddress == address:
+ currentDevice = device
+ break
+
+ if currentDevice:
+ self.m_deviceHandler.setDevice(currentDevice)
+
+ self.clearMessages()
+
+ @Property(bool, notify=scanningChanged)
+ def scanning(self):
+ if simulator:
+ return self.m_demoTimer.isActive()
+ return self.m_deviceDiscoveryAgent.isActive()
+
+ @Property("QVariant", notify=devicesChanged)
+ def devices(self):
+ return self.m_devices
diff --git a/examples/bluetooth/heartrate_game/devicehandler.py b/examples/bluetooth/heartrate_game/devicehandler.py
new file mode 100644
index 000000000..93b438f9c
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/devicehandler.py
@@ -0,0 +1,344 @@
+#############################################################################
+##
+## Copyright (C) 2022 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import struct
+
+from enum import IntEnum
+
+from PySide6.QtBluetooth import (QLowEnergyCharacteristic,
+ QLowEnergyController,
+ QLowEnergyDescriptor,
+ QLowEnergyService,
+ QBluetoothUuid)
+from PySide6.QtQml import QmlNamedElement, QmlUncreatable
+from PySide6.QtCore import (QByteArray, QDateTime, QRandomGenerator, QTimer,
+ Property, Signal, Slot, QEnum)
+
+from bluetoothbaseclass import BluetoothBaseClass
+from heartrate_global import simulator
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "Shared"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlNamedElement("AddressType")
+@QmlUncreatable("Enum is not a type")
+class DeviceHandler(BluetoothBaseClass):
+
+ @QEnum
+ class AddressType(IntEnum):
+ PUBLIC_ADDRESS = 1
+ RANDOM_ADDRESS = 2
+
+ measuringChanged = Signal()
+ aliveChanged = Signal()
+ statsChanged = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self.m_control = None
+ self.m_service = None
+ self.m_notificationDesc = QLowEnergyDescriptor()
+ self.m_currentDevice = None
+
+ self.m_foundHeartRateService = False
+ self.m_measuring = False
+ self.m_currentValue = 0
+ self.m_min = 0
+ self.m_max = 0
+ self.m_sum = 0
+ self.m_avg = 0.0
+ self.m_calories = 0.0
+
+ self.m_start = QDateTime()
+ self.m_stop = QDateTime()
+
+ self.m_measurements = []
+ self.m_addressType = QLowEnergyController.PublicAddress
+
+ self.m_demoTimer = QTimer()
+
+ if simulator:
+ self.m_demoTimer.setSingleShot(False)
+ self.m_demoTimer.setInterval(2000)
+ self.m_demoTimer.timeout.connect(self.updateDemoHR)
+ self.m_demoTimer.start()
+ self.updateDemoHR()
+
+ @Property(int)
+ def addressType(self):
+ if self.m_addressType == QLowEnergyController.RandomAddress:
+ return DeviceHandler.AddressType.RANDOM_ADDRESS
+ return DeviceHandler.AddressType.PUBLIC_ADDRESS
+
+ @addressType.setter
+ def addressType(self, type):
+ if type == DeviceHandler.AddressType.PUBLIC_ADDRESS:
+ self.m_addressType = QLowEnergyController.PublicAddress
+ elif type == DeviceHandler.AddressType.RANDOM_ADDRESS:
+ self.m_addressType = QLowEnergyController.RandomAddress
+
+ @Slot(QLowEnergyController.Error)
+ def controllerErrorOccurred(self, device):
+ self.error = "Cannot connect to remote device."
+
+ @Slot()
+ def controllerConnected(self):
+ self.info = "Controller connected. Search services..."
+ self.m_control.discoverServices()
+
+ @Slot()
+ def controllerDisconnected(self):
+ self.error = "LowEnergy controller disconnected"
+
+ def setDevice(self, device):
+ self.clearMessages()
+ self.m_currentDevice = device
+
+ if simulator:
+ self.info = "Demo device connected."
+ return
+
+ # Disconnect and delete old connection
+ if self.m_control:
+ self.m_control.disconnectFromDevice()
+ m_control = None
+
+ # Create new controller and connect it if device available
+ if self.m_currentDevice:
+
+ # Make connections
+#! [Connect-Signals-1]
+ self.m_control = QLowEnergyController.createCentral(self.m_currentDevice.getDevice(), self)
+#! [Connect-Signals-1]
+ self.m_control.setRemoteAddressType(self.m_addressType)
+#! [Connect-Signals-2]
+
+ m_control.serviceDiscovered.connect(self.serviceDiscovered)
+ m_control.discoveryFinished.connect(self.serviceScanDone)
+
+ self.m_control.errorOccurred.connect(self.controllerErrorOccurred)
+ self.m_control.connected.connect(self.controllerConnected)
+ self.m_control.disconnected.connect(self.controllerDisconnected)
+
+ # Connect
+ self.m_control.connectToDevice()
+#! [Connect-Signals-2]
+
+ @Slot()
+ def startMeasurement(self):
+ if self.alive:
+ self.m_start = QDateTime.currentDateTime()
+ self.m_min = 0
+ self.m_max = 0
+ self.m_avg = 0
+ self.m_sum = 0
+ self.m_calories = 0.0
+ self.m_measuring = True
+ self.m_measurements.clear()
+ self.measuringChanged.emit()
+
+ @Slot()
+ def stopMeasurement(self):
+ self.m_measuring = False
+ self.measuringChanged.emit()
+
+#! [Filter HeartRate service 1]
+ @Slot(QBluetoothUuid)
+ def serviceDiscovered(self, gatt):
+ if gatt == QBluetoothUuid(QBluetoothUuid.ServiceClassUuid.HeartRate):
+ self.info = "Heart Rate service discovered. Waiting for service scan to be done..."
+ self.m_foundHeartRateService = True
+
+#! [Filter HeartRate service 1]
+
+ @Slot()
+ def serviceScanDone(self):
+ self.info = "Service scan done."
+
+ # Delete old service if available
+ if self.m_service:
+ self.m_service = None
+
+#! [Filter HeartRate service 2]
+ # If heartRateService found, create new service
+ if self.m_foundHeartRateService:
+ self.m_service = self.m_control.createServiceObject(QBluetoothUuid(QBluetoothUuid.ServiceClassUuid.HeartRate), self)
+
+ if self.m_service:
+ self.m_service.stateChanged.connect(self.serviceStateChanged)
+ self.m_service.characteristicChanged.connect(self.updateHeartRateValue)
+ self.m_service.descriptorWritten.connect(self.confirmedDescriptorWrite)
+ self.m_service.discoverDetails()
+ else:
+ self.error = "Heart Rate Service not found."
+#! [Filter HeartRate service 2]
+
+# Service functions
+#! [Find HRM characteristic]
+ @Slot(QLowEnergyService.ServiceState)
+ def serviceStateChanged(self, switch):
+ if switch == QLowEnergyService.RemoteServiceDiscovering:
+ self.setInfo(tr("Discovering services..."))
+ elif switch == QLowEnergyService.RemoteServiceDiscovered:
+ self.setInfo(tr("Service discovered."))
+ hrChar = m_service.characteristic(QBluetoothUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement))
+ if hrChar.isValid():
+ self.m_notificationDesc = hrChar.descriptor(QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration)
+ if self.m_notificationDesc.isValid():
+ self.m_service.writeDescriptor(m_notificationDesc,
+ QByteArray.fromHex(b"0100"))
+ else:
+ self.error = "HR Data not found."
+ self.aliveChanged.emit()
+#! [Find HRM characteristic]
+
+#! [Reading value]
+ @Slot(QLowEnergyCharacteristic, QByteArray)
+ def updateHeartRateValue(self, c, value):
+ # ignore any other characteristic change. Shouldn't really happen though
+ if c.uuid() != QBluetoothUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement):
+ return
+
+ data = value.data()
+ flags = int(data[0])
+ # Heart Rate
+ hrvalue = 0
+ if flags & 0x1: # HR 16 bit little endian? otherwise 8 bit
+ hrvalue = struct.unpack("<H", data[1:3])
+ else:
+ hrvalue = struct.unpack("B", data[1:2])
+
+ self.addMeasurement(hrvalue)
+
+#! [Reading value]
+ @Slot()
+ def updateDemoHR(self):
+ randomValue = 0
+ if self.m_currentValue < 30: # Initial value
+ randomValue = 55 + QRandomGenerator.global_().bounded(30)
+ elif not self.m_measuring: # Value when relax
+ random = QRandomGenerator.global_().bounded(5)
+ randomValue = self.m_currentValue - 2 + random
+ randomValue = max(min(randomValue, 55), 75)
+ else: # Measuring
+ random = QRandomGenerator.global_().bounded(10)
+ randomValue = self.m_currentValue + random - 2
+
+ self.addMeasurement(randomValue)
+
+ @Slot(QLowEnergyCharacteristic, QByteArray)
+ def confirmedDescriptorWrite(self, d, value):
+ if (d.isValid() and d == self.m_notificationDesc
+ and value == QByteArray.fromHex(b"0000")):
+ # disabled notifications . assume disconnect intent
+ self.m_control.disconnectFromDevice()
+ self.m_service = None
+
+ @Slot()
+ def disconnectService(self):
+ self.m_foundHeartRateService = False
+
+ # disable notifications
+ if (self.m_notificationDesc.isValid() and self.m_service
+ and self.m_notificationDesc.value() == QByteArray.fromHex(b"0100")):
+ self.m_service.writeDescriptor(self.m_notificationDesc,
+ QByteArray.fromHex(b"0000"))
+ else:
+ if self.m_control:
+ self.m_control.disconnectFromDevice()
+ self.m_service = None
+
+ @Property(bool, notify=measuringChanged)
+ def measuring(self):
+ return self.m_measuring
+
+ @Property(bool, notify=aliveChanged)
+ def alive(self):
+ if simulator:
+ return True
+ if self.m_service:
+ return self.m_service.state() == QLowEnergyService.RemoteServiceDiscovered
+ return False
+
+ @Property(int, notify=statsChanged)
+ def hr(self):
+ return self.m_currentValue
+
+ @Property(int, notify=statsChanged)
+ def time(self):
+ return self.m_start.secsTo(self.m_stop)
+
+ @Property(int, notify=statsChanged)
+ def maxHR(self):
+ return self.m_max
+
+ @Property(int, notify=statsChanged)
+ def minHR(self):
+ return self.m_min
+
+ @Property(float, notify=statsChanged)
+ def average(self):
+ return self.m_avg
+
+ @Property(float, notify=statsChanged)
+ def calories(self):
+ return self.m_calories
+
+ def addMeasurement(self, value):
+ self.m_currentValue = value
+
+ # If measuring and value is appropriate
+ if self.m_measuring and value > 30 and value < 250:
+ self.m_stop = QDateTime.currentDateTime()
+ self.m_measurements.append(value)
+
+ self.m_min = value if self.m_min == 0 else min(value, self.m_min)
+ self.m_max = max(value, self.m_max)
+ self.m_sum += value
+ self.m_avg = float(self.m_sum) / len(self.m_measurements)
+ self.m_calories = ((-55.0969 + (0.6309 * self.m_avg) + (0.1988 * 94)
+ + (0.2017 * 24)) / 4.184) * 60 * self.time / 3600
+
+ self.statsChanged.emit()
diff --git a/examples/bluetooth/heartrate_game/deviceinfo.py b/examples/bluetooth/heartrate_game/deviceinfo.py
new file mode 100644
index 000000000..b0f1b8a8b
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/deviceinfo.py
@@ -0,0 +1,75 @@
+#############################################################################
+##
+## Copyright (C) 2022 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+
+from PySide6.QtCore import QObject, Property, Signal
+
+from heartrate_global import simulator
+
+
+class DeviceInfo(QObject):
+
+ deviceChanged = Signal()
+
+ def __init__(self, device):
+ super().__init__()
+ self.m_device = device
+
+ def device(self):
+ return self.m_device
+
+ def setDevice(self, device):
+ self.m_device = device
+ self.deviceChanged.emit()
+
+ @Property(str, notify=deviceChanged)
+ def deviceName(self):
+ if simulator:
+ return "Demo device"
+ return self.m_device.name()
+
+ @Property(str, notify=deviceChanged)
+ def deviceAddress(self):
+ if simulator:
+ return "00:11:22:33:44:55"
+ if sys.platform == "Darwin": # workaround for Core Bluetooth:
+ return self.m_device.deviceUuid().toString()
+ return self.m_device.address().toString()
diff --git a/examples/bluetooth/heartrate_game/doc/heartrate_game.rst b/examples/bluetooth/heartrate_game/doc/heartrate_game.rst
new file mode 100644
index 000000000..0a0938cad
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/doc/heartrate_game.rst
@@ -0,0 +1,9 @@
+Bluetooth Low Energy Heart Rate Game
+====================================
+
+The Bluetooth Low Energy Heart Rate Game shows how to develop a
+Bluetooth Low Energy application using the Qt Bluetooth API. The
+application covers the scanning for Bluetooth Low Energy devices,
+connecting to a Heart Rate service on the device, writing
+characteristics and descriptors, and receiving updates from the device
+once the heart rate has changed.
diff --git a/examples/bluetooth/heartrate_game/heartrate_game.pyproject b/examples/bluetooth/heartrate_game/heartrate_game.pyproject
new file mode 100644
index 000000000..e4c40874a
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/heartrate_game.pyproject
@@ -0,0 +1,22 @@
+{
+ "files": ["main.py",
+ "bluetoothbaseclass.py",
+ "connectionhandler.py",
+ "devicefinder.py",
+ "devicehandler.py",
+ "deviceinfo.py",
+ "heartrate_global.py",
+ "qml/main.qml",
+ "qml/App.qml",
+ "qml/BluetoothAlarmDialog.qml",
+ "qml/BottomLine.qml",
+ "qml/Connect.qml",
+ "qml/GameButton.qml",
+ "qml/GamePage.qml",
+ "qml/GameSettings.qml",
+ "qml/Measure.qml",
+ "qml/SplashScreen.qml",
+ "qml/Stats.qml",
+ "qml/StatsLabel.qml",
+ "qml/TitleBar.qml"]
+}
diff --git a/examples/bluetooth/heartrate_game/heartrate_global.py b/examples/bluetooth/heartrate_game/heartrate_global.py
new file mode 100644
index 000000000..8fb4f0f6e
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/heartrate_global.py
@@ -0,0 +1,43 @@
+#############################################################################
+##
+## Copyright (C) 2022 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import sys
+
+simulator = sys.platform == "win32"
diff --git a/examples/bluetooth/heartrate_game/main.py b/examples/bluetooth/heartrate_game/main.py
new file mode 100644
index 000000000..5cf02cbd6
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/main.py
@@ -0,0 +1,90 @@
+#############################################################################
+##
+## Copyright (C) 2022 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+"""PySide6 port of the bluetooth/heartrate-game example from Qt v6.x"""
+
+import os
+from pathlib import Path
+import sys
+from argparse import ArgumentParser, Namespace, RawDescriptionHelpFormatter
+
+from PySide6.QtQml import QQmlApplicationEngine, QQmlContext
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtCore import QCoreApplication, QLoggingCategory, QUrl
+
+from connectionhandler import ConnectionHandler
+from devicefinder import DeviceFinder
+from devicehandler import DeviceHandler
+from heartrate_global import simulator
+
+
+if __name__ == '__main__':
+ parser = ArgumentParser(prog="heartrate-game",
+ formatter_class=RawDescriptionHelpFormatter)
+
+ parser.add_argument("-v", "--verbose", action="store_true",
+ help="Generate more output")
+ parser.add_argument("-s", "--simulator", action="store_true",
+ help="Use Simulator")
+ options = parser.parse_args()
+ simulator = options.simulator
+ if options.verbose:
+ QLoggingCategory.setFilterRules("qt.bluetooth* = true")
+
+ app = QGuiApplication(sys.argv)
+
+ connectionHandler = ConnectionHandler()
+ deviceHandler = DeviceHandler()
+ deviceFinder = DeviceFinder(deviceHandler)
+
+ engine = QQmlApplicationEngine()
+ engine.setInitialProperties({
+ "connectionHandler": connectionHandler,
+ "deviceFinder": deviceFinder,
+ "deviceHandler": deviceHandler})
+
+ qml_file = os.fspath(Path(__file__).resolve().parent / "qml" / "main.qml")
+ engine.load(QUrl.fromLocalFile(qml_file))
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ ex = QCoreApplication.exec()
+ del engine
+ sys.exit(ex)
diff --git a/examples/bluetooth/heartrate_game/qml/App.qml b/examples/bluetooth/heartrate_game/qml/App.qml
new file mode 100644
index 000000000..e2b059cf0
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/App.qml
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+Item {
+ id: app
+ anchors.fill: parent
+ opacity: 0.0
+
+ Behavior on opacity { NumberAnimation { duration: 500 } }
+
+ property var lastPages: []
+ property int __currentIndex: 0
+
+ function init()
+ {
+ opacity = 1.0
+ showPage("Connect.qml")
+ }
+
+ function prevPage()
+ {
+ lastPages.pop()
+ pageLoader.setSource(lastPages[lastPages.length-1])
+ __currentIndex = lastPages.length-1;
+ }
+
+ function showPage(name)
+ {
+ lastPages.push(name)
+ pageLoader.setSource(name)
+ __currentIndex = lastPages.length-1;
+ }
+
+ TitleBar {
+ id: titleBar
+ currentIndex: __currentIndex
+
+ onTitleClicked: (index) => {
+ if (index < __currentIndex)
+ pageLoader.item.close()
+ }
+ }
+
+ Loader {
+ id: pageLoader
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: titleBar.bottom
+ anchors.bottom: parent.bottom
+
+ onStatusChanged: {
+ if (status === Loader.Ready)
+ {
+ pageLoader.item.init();
+ pageLoader.item.forceActiveFocus()
+ }
+ }
+ }
+
+ Keys.onReleased: (event) => {
+ switch (event.key) {
+ case Qt.Key_Escape:
+ case Qt.Key_Back: {
+ if (__currentIndex > 0) {
+ pageLoader.item.close()
+ event.accepted = true
+ } else {
+ Qt.quit()
+ }
+ break;
+ }
+ default: break;
+ }
+ }
+
+ BluetoothAlarmDialog {
+ id: btAlarmDialog
+ anchors.fill: parent
+ visible: !connectionHandler.alive
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/qml/BluetoothAlarmDialog.qml b/examples/bluetooth/heartrate_game/qml/BluetoothAlarmDialog.qml
new file mode 100644
index 000000000..5d4d5cb72
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/BluetoothAlarmDialog.qml
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+Item {
+ id: root
+ anchors.fill: parent
+
+ Rectangle {
+ anchors.fill: parent
+ color: "black"
+ opacity: 0.9
+ }
+
+ MouseArea {
+ id: eventEater
+ }
+
+ Rectangle {
+ id: dialogFrame
+
+ anchors.centerIn: parent
+ width: parent.width * 0.8
+ height: parent.height * 0.6
+ border.color: "#454545"
+ color: GameSettings.backgroundColor
+ radius: width * 0.05
+
+ Item {
+ id: dialogContainer
+ anchors.fill: parent
+ anchors.margins: parent.width*0.05
+
+ Image {
+ id: offOnImage
+ anchors.left: quitButton.left
+ anchors.right: quitButton.right
+ anchors.top: parent.top
+ height: GameSettings.heightForWidth(width, sourceSize)
+ source: "images/bt_off_to_on.png"
+ }
+
+ Text {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: offOnImage.bottom
+ anchors.bottom: quitButton.top
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ wrapMode: Text.WordWrap
+ font.pixelSize: GameSettings.mediumFontSize
+ color: GameSettings.textColor
+ text: qsTr("This application cannot be used without Bluetooth. Please switch Bluetooth ON to continue.")
+ }
+
+ GameButton {
+ id: quitButton
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: dialogContainer.width * 0.6
+ height: GameSettings.buttonHeight
+ onClicked: Qt.quit()
+
+ Text {
+ anchors.centerIn: parent
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.bigFontSize
+ text: qsTr("Quit")
+ }
+ }
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/qml/BottomLine.qml b/examples/bluetooth/heartrate_game/qml/BottomLine.qml
new file mode 100644
index 000000000..da6c6f530
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/BottomLine.qml
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+Rectangle {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ width: parent.width * 0.85
+ height: parent.height * 0.05
+ radius: height*0.5
+}
diff --git a/examples/bluetooth/heartrate_game/qml/Connect.qml b/examples/bluetooth/heartrate_game/qml/Connect.qml
new file mode 100644
index 000000000..e15d2c9cd
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/Connect.qml
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import Shared
+
+GamePage {
+
+ errorMessage: deviceFinder.error
+ infoMessage: deviceFinder.info
+
+ Rectangle {
+ id: viewContainer
+ anchors.top: parent.top
+ anchors.bottom:
+ // only BlueZ platform has address type selection
+ connectionHandler.requiresAddressType ? addressTypeButton.top : searchButton.top
+ anchors.topMargin: GameSettings.fieldMargin + messageHeight
+ anchors.bottomMargin: GameSettings.fieldMargin
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width - GameSettings.fieldMargin*2
+ color: GameSettings.viewColor
+ radius: GameSettings.buttonRadius
+
+
+ Text {
+ id: title
+ width: parent.width
+ height: GameSettings.fieldHeight
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.mediumFontSize
+ text: qsTr("FOUND DEVICES")
+
+ BottomLine {
+ height: 1;
+ width: parent.width
+ color: "#898989"
+ }
+ }
+
+
+ ListView {
+ id: devices
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.top: title.bottom
+ model: deviceFinder.devices
+ clip: true
+
+ delegate: Rectangle {
+ id: box
+ height:GameSettings.fieldHeight * 1.2
+ width: devices.width
+ color: index % 2 === 0 ? GameSettings.delegate1Color : GameSettings.delegate2Color
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ deviceFinder.connectToService(modelData.deviceAddress);
+ app.showPage("Measure.qml")
+ }
+ }
+
+ Text {
+ id: device
+ font.pixelSize: GameSettings.smallFontSize
+ text: modelData.deviceName
+ anchors.top: parent.top
+ anchors.topMargin: parent.height * 0.1
+ anchors.leftMargin: parent.height * 0.1
+ anchors.left: parent.left
+ color: GameSettings.textColor
+ }
+
+ Text {
+ id: deviceAddress
+ font.pixelSize: GameSettings.smallFontSize
+ text: modelData.deviceAddress
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: parent.height * 0.1
+ anchors.rightMargin: parent.height * 0.1
+ anchors.right: parent.right
+ color: Qt.darker(GameSettings.textColor)
+ }
+ }
+ }
+ }
+
+ GameButton {
+ id: addressTypeButton
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: searchButton.top
+ anchors.bottomMargin: GameSettings.fieldMargin*0.5
+ width: viewContainer.width
+ height: GameSettings.fieldHeight
+ visible: connectionHandler.requiresAddressType // only required on BlueZ
+ state: "public"
+ onClicked: state == "public" ? state = "random" : state = "public"
+
+ states: [
+ State {
+ name: "public"
+ PropertyChanges { target: addressTypeText; text: qsTr("Public Address") }
+ PropertyChanges { target: deviceHandler; addressType: AddressType.PUBLIC_ADDRESS }
+ },
+ State {
+ name: "random"
+ PropertyChanges { target: addressTypeText; text: qsTr("Random Address") }
+ PropertyChanges { target: deviceHandler; addressType: AddressType.RANDOM_ADDRESS }
+ }
+ ]
+
+ Text {
+ id: addressTypeText
+ anchors.centerIn: parent
+ font.pixelSize: GameSettings.tinyFontSize
+ color: GameSettings.textColor
+ }
+ }
+
+ GameButton {
+ id: searchButton
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: GameSettings.fieldMargin
+ width: viewContainer.width
+ height: GameSettings.fieldHeight
+ enabled: !deviceFinder.scanning
+ onClicked: deviceFinder.startSearch()
+
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: GameSettings.tinyFontSize
+ text: qsTr("START SEARCH")
+ color: searchButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/qml/GameButton.qml b/examples/bluetooth/heartrate_game/qml/GameButton.qml
new file mode 100644
index 000000000..c1f496560
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/GameButton.qml
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import "."
+
+Rectangle {
+ id: button
+ color: baseColor
+ onEnabledChanged: checkColor()
+ radius: GameSettings.buttonRadius
+
+ property color baseColor: GameSettings.buttonColor
+ property color pressedColor: GameSettings.buttonPressedColor
+ property color disabledColor: GameSettings.disabledButtonColor
+
+ signal clicked()
+
+ function checkColor()
+ {
+ if (!button.enabled) {
+ button.color = disabledColor
+ } else {
+ if (mouseArea.containsPress)
+ button.color = pressedColor
+ else
+ button.color = baseColor
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ onPressed: checkColor()
+ onReleased: checkColor()
+ onClicked: {
+ checkColor()
+ button.clicked()
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/qml/GamePage.qml b/examples/bluetooth/heartrate_game/qml/GamePage.qml
new file mode 100644
index 000000000..b921f0d62
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/GamePage.qml
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import "."
+
+Item {
+ anchors.fill: parent
+
+ property string errorMessage: ""
+ property string infoMessage: ""
+ property real messageHeight: msg.height
+ property bool hasError: errorMessage != ""
+ property bool hasInfo: infoMessage != ""
+
+ function init()
+ {
+ }
+
+ function close()
+ {
+ app.prevPage()
+ }
+
+ Rectangle {
+ id: msg
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: GameSettings.fieldHeight
+ color: hasError ? GameSettings.errorColor : GameSettings.infoColor
+ visible: hasError || hasInfo
+
+ Text {
+ id: error
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ minimumPixelSize: 5
+ font.pixelSize: GameSettings.smallFontSize
+ fontSizeMode: Text.Fit
+ color: GameSettings.textColor
+ text: hasError ? errorMessage : infoMessage
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/qml/GameSettings.qml b/examples/bluetooth/heartrate_game/qml/GameSettings.qml
new file mode 100644
index 000000000..bfb485cdf
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/GameSettings.qml
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+pragma Singleton
+import QtQuick
+
+Item {
+ property int wHeight
+ property int wWidth
+
+ // Colors
+ readonly property color backgroundColor: "#2d3037"
+ readonly property color buttonColor: "#202227"
+ readonly property color buttonPressedColor: "#6ccaf2"
+ readonly property color disabledButtonColor: "#555555"
+ readonly property color viewColor: "#202227"
+ readonly property color delegate1Color: Qt.darker(viewColor, 1.2)
+ readonly property color delegate2Color: Qt.lighter(viewColor, 1.2)
+ readonly property color textColor: "#ffffff"
+ readonly property color textDarkColor: "#232323"
+ readonly property color disabledTextColor: "#777777"
+ readonly property color sliderColor: "#6ccaf2"
+ readonly property color errorColor: "#ba3f62"
+ readonly property color infoColor: "#3fba62"
+
+ // Font sizes
+ property real microFontSize: hugeFontSize * 0.2
+ property real tinyFontSize: hugeFontSize * 0.4
+ property real smallTinyFontSize: hugeFontSize * 0.5
+ property real smallFontSize: hugeFontSize * 0.6
+ property real mediumFontSize: hugeFontSize * 0.7
+ property real bigFontSize: hugeFontSize * 0.8
+ property real largeFontSize: hugeFontSize * 0.9
+ property real hugeFontSize: (wWidth + wHeight) * 0.03
+ property real giganticFontSize: (wWidth + wHeight) * 0.04
+
+ // Some other values
+ property real fieldHeight: wHeight * 0.08
+ property real fieldMargin: fieldHeight * 0.5
+ property real buttonHeight: wHeight * 0.08
+ property real buttonRadius: buttonHeight * 0.1
+
+ // Some help functions
+ function widthForHeight(h, ss)
+ {
+ return h/ss.height * ss.width;
+ }
+
+ function heightForWidth(w, ss)
+ {
+ return w/ss.width * ss.height;
+ }
+
+}
diff --git a/examples/bluetooth/heartrate_game/qml/Measure.qml b/examples/bluetooth/heartrate_game/qml/Measure.qml
new file mode 100644
index 000000000..32bac5fcd
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/Measure.qml
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+GamePage {
+ id: measurePage
+
+ errorMessage: deviceHandler.error
+ infoMessage: deviceHandler.info
+
+ property real __timeCounter: 0;
+ property real __maxTimeCount: 60
+ property string relaxText: qsTr("Relax!\nWhen you are ready, press Start. You have %1s time to increase heartrate so much as possible.\nGood luck!").arg(__maxTimeCount)
+
+ function close()
+ {
+ deviceHandler.stopMeasurement();
+ deviceHandler.disconnectService();
+ app.prevPage();
+ }
+
+ function start()
+ {
+ if (!deviceHandler.measuring) {
+ __timeCounter = 0;
+ deviceHandler.startMeasurement()
+ }
+ }
+
+ function stop()
+ {
+ if (deviceHandler.measuring) {
+ deviceHandler.stopMeasurement()
+ }
+
+ app.showPage("Stats.qml")
+ }
+
+ Timer {
+ id: measureTimer
+ interval: 1000
+ running: deviceHandler.measuring
+ repeat: true
+ onTriggered: {
+ __timeCounter++;
+ if (__timeCounter >= __maxTimeCount)
+ measurePage.stop()
+ }
+ }
+
+ Column {
+ anchors.centerIn: parent
+ spacing: GameSettings.fieldHeight * 0.5
+
+ Rectangle {
+ id: circle
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: Math.min(measurePage.width, measurePage.height-GameSettings.fieldHeight*4) - 2*GameSettings.fieldMargin
+ height: width
+ radius: width*0.5
+ color: GameSettings.viewColor
+
+ Text {
+ id: hintText
+ anchors.centerIn: parent
+ anchors.verticalCenterOffset: -parent.height*0.1
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ width: parent.width * 0.8
+ height: parent.height * 0.6
+ wrapMode: Text.WordWrap
+ text: measurePage.relaxText
+ visible: !deviceHandler.measuring
+ color: GameSettings.textColor
+ fontSizeMode: Text.Fit
+ minimumPixelSize: 10
+ font.pixelSize: GameSettings.mediumFontSize
+ }
+
+ Text {
+ id: text
+ anchors.centerIn: parent
+ anchors.verticalCenterOffset: -parent.height*0.15
+ font.pixelSize: parent.width * 0.45
+ text: deviceHandler.hr
+ visible: deviceHandler.measuring
+ color: GameSettings.textColor
+ }
+
+ Item {
+ id: minMaxContainer
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width*0.7
+ height: parent.height * 0.15
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: parent.height*0.16
+ visible: deviceHandler.measuring
+
+ Text {
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ text: deviceHandler.minHR
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.hugeFontSize
+
+ Text {
+ anchors.left: parent.left
+ anchors.bottom: parent.top
+ font.pixelSize: parent.font.pixelSize*0.8
+ color: parent.color
+ text: "MIN"
+ }
+ }
+
+ Text {
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ text: deviceHandler.maxHR
+ color: GameSettings.textColor
+ font.pixelSize: GameSettings.hugeFontSize
+
+ Text {
+ anchors.right: parent.right
+ anchors.bottom: parent.top
+ font.pixelSize: parent.font.pixelSize*0.8
+ color: parent.color
+ text: "MAX"
+ }
+ }
+ }
+
+ Image {
+ id: heart
+ anchors.horizontalCenter: minMaxContainer.horizontalCenter
+ anchors.verticalCenter: minMaxContainer.bottom
+ width: parent.width * 0.2
+ height: width
+ source: "images/heart.png"
+ smooth: true
+ antialiasing: true
+
+ SequentialAnimation{
+ id: heartAnim
+ running: deviceHandler.alive
+ loops: Animation.Infinite
+ alwaysRunToEnd: true
+ PropertyAnimation { target: heart; property: "scale"; to: 1.2; duration: 500; easing.type: Easing.InQuad }
+ PropertyAnimation { target: heart; property: "scale"; to: 1.0; duration: 500; easing.type: Easing.OutQuad }
+ }
+ }
+ }
+
+ Rectangle {
+ id: timeSlider
+ color: GameSettings.viewColor
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: circle.width
+ height: GameSettings.fieldHeight
+ radius: GameSettings.buttonRadius
+
+ Rectangle {
+ height: parent.height
+ radius: parent.radius
+ color: GameSettings.sliderColor
+ width: Math.min(1.0,__timeCounter / __maxTimeCount) * parent.width
+ }
+
+ Text {
+ anchors.centerIn: parent
+ color: "gray"
+ text: (__maxTimeCount - __timeCounter).toFixed(0) + " s"
+ font.pixelSize: GameSettings.bigFontSize
+ }
+ }
+ }
+
+ GameButton {
+ id: startButton
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: GameSettings.fieldMargin
+ width: circle.width
+ height: GameSettings.fieldHeight
+ enabled: !deviceHandler.measuring
+ radius: GameSettings.buttonRadius
+
+ onClicked: start()
+
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: GameSettings.tinyFontSize
+ text: qsTr("START")
+ color: startButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/qml/SplashScreen.qml b/examples/bluetooth/heartrate_game/qml/SplashScreen.qml
new file mode 100644
index 000000000..ecf16c955
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/SplashScreen.qml
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import "."
+
+Item {
+ id: root
+ anchors.fill: parent
+
+ property bool appIsReady: false
+ property bool splashIsReady: false
+
+ property bool ready: appIsReady && splashIsReady
+ onReadyChanged: if (ready) readyToGo();
+
+ signal readyToGo()
+
+ function appReady()
+ {
+ appIsReady = true
+ }
+
+ function errorInLoadingApp()
+ {
+ Qt.quit()
+ }
+
+ Image {
+ anchors.centerIn: parent
+ width: Math.min(parent.height, parent.width)*0.6
+ height: GameSettings.heightForWidth(width, sourceSize)
+ source: "images/logo.png"
+ }
+
+ Timer {
+ id: splashTimer
+ interval: 1000
+ onTriggered: splashIsReady = true
+ }
+
+ Component.onCompleted: splashTimer.start()
+}
diff --git a/examples/bluetooth/heartrate_game/qml/Stats.qml b/examples/bluetooth/heartrate_game/qml/Stats.qml
new file mode 100644
index 000000000..23cbd18b9
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/Stats.qml
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+GamePage {
+
+ Column {
+ anchors.centerIn: parent
+ width: parent.width
+
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ font.pixelSize: GameSettings.hugeFontSize
+ color: GameSettings.textColor
+ text: qsTr("RESULT")
+ }
+
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ font.pixelSize: GameSettings.giganticFontSize*3
+ color: GameSettings.textColor
+ text: (deviceHandler.maxHR - deviceHandler.minHR).toFixed(0)
+ }
+
+ Item {
+ height: GameSettings.fieldHeight
+ width: 1
+ }
+
+ StatsLabel {
+ title: qsTr("MIN")
+ value: deviceHandler.minHR.toFixed(0)
+ }
+
+ StatsLabel {
+ title: qsTr("MAX")
+ value: deviceHandler.maxHR.toFixed(0)
+ }
+
+ StatsLabel {
+ title: qsTr("AVG")
+ value: deviceHandler.average.toFixed(1)
+ }
+
+
+ StatsLabel {
+ title: qsTr("CALORIES")
+ value: deviceHandler.calories.toFixed(3)
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/qml/StatsLabel.qml b/examples/bluetooth/heartrate_game/qml/StatsLabel.qml
new file mode 100644
index 000000000..6eb19ecb7
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/StatsLabel.qml
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import "."
+
+Item {
+ height: GameSettings.fieldHeight
+ width: parent.width
+
+ property alias title: leftText.text
+ property alias value: rightText.text
+
+ Text {
+ id: leftText
+ anchors.left: parent.left
+ height: parent.height
+ width: parent.width * 0.45
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: GameSettings.mediumFontSize
+ color: GameSettings.textColor
+ }
+
+ Text {
+ id: rightText
+ anchors.right: parent.right
+ height: parent.height
+ width: parent.width * 0.45
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: GameSettings.mediumFontSize
+ color: GameSettings.textColor
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/qml/TitleBar.qml b/examples/bluetooth/heartrate_game/qml/TitleBar.qml
new file mode 100644
index 000000000..1d9ec4c94
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/TitleBar.qml
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+Rectangle {
+ id: titleBar
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: GameSettings.fieldHeight
+ color: GameSettings.viewColor
+
+ property var __titles: ["CONNECT", "MEASURE", "STATS"]
+ property int currentIndex: 0
+
+ signal titleClicked(int index)
+
+ Repeater {
+ model: 3
+ Text {
+ width: titleBar.width / 3
+ height: titleBar.height
+ x: index * width
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: __titles[index]
+ font.pixelSize: GameSettings.tinyFontSize
+ color: titleBar.currentIndex === index ? GameSettings.textColor : GameSettings.disabledTextColor
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: titleClicked(index)
+ }
+ }
+ }
+
+
+ Item {
+ anchors.bottom: parent.bottom
+ width: parent.width / 3
+ height: parent.height
+ x: currentIndex * width
+
+ BottomLine{}
+
+ Behavior on x { NumberAnimation { duration: 200 } }
+ }
+
+}
diff --git a/examples/bluetooth/heartrate_game/qml/images/bt_off_to_on.png b/examples/bluetooth/heartrate_game/qml/images/bt_off_to_on.png
new file mode 100644
index 000000000..5ea1f3f06
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/images/bt_off_to_on.png
Binary files differ
diff --git a/examples/bluetooth/heartrate_game/qml/images/heart.png b/examples/bluetooth/heartrate_game/qml/images/heart.png
new file mode 100644
index 000000000..f2b3c0a3e
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/images/heart.png
Binary files differ
diff --git a/examples/bluetooth/heartrate_game/qml/images/logo.png b/examples/bluetooth/heartrate_game/qml/images/logo.png
new file mode 100644
index 000000000..ea0af7e00
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/images/logo.png
Binary files differ
diff --git a/examples/bluetooth/heartrate_game/qml/main.qml b/examples/bluetooth/heartrate_game/qml/main.qml
new file mode 100644
index 000000000..920a82669
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/main.qml
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt for Python examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Window
+import "."
+import Shared
+
+Window {
+ id: wroot
+ visible: true
+ width: 720 * .7
+ height: 1240 * .7
+ title: qsTr("HeartRateGame")
+ color: GameSettings.backgroundColor
+
+ required property ConnectionHandler connectionHandler
+ required property DeviceFinder deviceFinder
+ required property AddressType deviceHandler
+
+ Component.onCompleted: {
+ GameSettings.wWidth = Qt.binding(function() {return width})
+ GameSettings.wHeight = Qt.binding(function() {return height})
+ }
+
+ Loader {
+ id: splashLoader
+ anchors.fill: parent
+ source: "SplashScreen.qml"
+ asynchronous: false
+ visible: true
+
+ onStatusChanged: {
+ if (status === Loader.Ready) {
+ appLoader.setSource("App.qml");
+ }
+ }
+ }
+
+ Connections {
+ target: splashLoader.item
+ function onReadyToGo() {
+ appLoader.visible = true
+ appLoader.item.init()
+ splashLoader.visible = false
+ splashLoader.setSource("")
+ appLoader.item.forceActiveFocus();
+ }
+ }
+
+ Loader {
+ id: appLoader
+ anchors.fill: parent
+ visible: false
+ asynchronous: true
+ onStatusChanged: {
+ if (status === Loader.Ready)
+ splashLoader.item.appReady()
+ if (status === Loader.Error)
+ splashLoader.item.errorInLoadingApp();
+ }
+ }
+}
diff --git a/examples/bluetooth/heartrate_game/qml/qmldir b/examples/bluetooth/heartrate_game/qml/qmldir
new file mode 100644
index 000000000..5e0d2b540
--- /dev/null
+++ b/examples/bluetooth/heartrate_game/qml/qmldir
@@ -0,0 +1 @@
+singleton GameSettings 1.0 GameSettings.qml
diff --git a/examples/bluetooth/heartrate_server/doc/heartrate_server.rst b/examples/bluetooth/heartrate_server/doc/heartrate_server.rst
new file mode 100644
index 000000000..aaa1a0988
--- /dev/null
+++ b/examples/bluetooth/heartrate_server/doc/heartrate_server.rst
@@ -0,0 +1,8 @@
+Bluetooth Low Energy Heart Rate Server
+======================================
+
+The Bluetooth Low Energy Heart Rate Server is a command-line
+application that shows how to develop a Bluetooth GATT server using
+the Qt Bluetooth API. The application covers setting up a GATT
+service, advertising it and notifying clients about changes to
+characteristic values.
diff --git a/examples/bluetooth/heartrate_server/heartrate_server.py b/examples/bluetooth/heartrate_server/heartrate_server.py
new file mode 100644
index 000000000..0696d92c2
--- /dev/null
+++ b/examples/bluetooth/heartrate_server/heartrate_server.py
@@ -0,0 +1,131 @@
+#############################################################################
+##
+## Copyright (C) 2022 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt for Python examples of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+## * Neither the name of The Qt Company Ltd nor the names of its
+## contributors may be used to endorse or promote products derived
+## from this software without specific prior written permission.
+##
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+"""PySide6 port of the bluetooth/heartrate-server example from Qt v6.x"""
+
+import sys
+from enum import Enum
+
+from PySide6.QtBluetooth import (QBluetoothUuid, QLowEnergyAdvertisingData,
+ QLowEnergyAdvertisingParameters,
+ QLowEnergyCharacteristic,
+ QLowEnergyCharacteristicData,
+ QLowEnergyController,
+ QLowEnergyDescriptorData,
+ QLowEnergyServiceData)
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtCore import QByteArray, QTimer, QLoggingCategory
+
+
+class ValueChange(Enum):
+ VALUE_UP = 1
+ VALUE_DOWN = 2
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ QLoggingCategory.setFilterRules("qt.bluetooth* = true")
+
+#! [Advertising Data]
+ advertising_data = QLowEnergyAdvertisingData()
+ advertising_data.setDiscoverability(QLowEnergyAdvertisingData.DiscoverabilityGeneral)
+ advertising_data.setIncludePowerLevel(True)
+ advertising_data.setLocalName("HeartRateServer")
+ advertising_data.setServices([QBluetoothUuid.ServiceClassUuid.HeartRate])
+#! [Advertising Data]
+
+#! [Service Data]
+ char_data = QLowEnergyCharacteristicData()
+ char_data.setUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
+ char_data.setValue(QByteArray(2, 0))
+ char_data.setProperties(QLowEnergyCharacteristic.Notify)
+ client_config = QLowEnergyDescriptorData(QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration,
+ QByteArray(2, 0))
+ char_data.addDescriptor(client_config)
+
+ service_data = QLowEnergyServiceData()
+ service_data.setType(QLowEnergyServiceData.ServiceTypePrimary)
+ service_data.setUuid(QBluetoothUuid.ServiceClassUuid.HeartRate)
+ service_data.addCharacteristic(char_data)
+#! [Service Data]
+
+#! [Start Advertising]
+ le_controller = QLowEnergyController.createPeripheral()
+ service = le_controller.addService(service_data)
+ le_controller.startAdvertising(QLowEnergyAdvertisingParameters(),
+ advertising_data, advertising_data)
+#! [Start Advertising]
+
+#! [Provide Heartbeat]
+ value_change = ValueChange.VALUE_UP
+ heartbeat_timer = QTimer()
+ current_heart_rate = 60
+
+ def heartbeat_provider():
+ global current_heart_rate, value_change, current_heart_rate
+ value = QByteArray()
+ value.append(chr(0)) # Flags that specify the format of the value.
+ value.append(chr(current_heart_rate)) # Actual value.
+ characteristic = service.characteristic(QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
+ assert(characteristic.isValid())
+ # Potentially causes notification.
+ service.writeCharacteristic(characteristic, value)
+ if current_heart_rate == 60:
+ value_change = ValueChange.VALUE_UP
+ elif current_heart_rate == 100:
+ value_change = ValueChange.VALUE_DOWN
+ if value_change == ValueChange.VALUE_UP:
+ current_heart_rate += 1
+ else:
+ current_heart_rate -= 1
+
+ heartbeat_timer.timeout.connect(heartbeat_provider)
+ heartbeat_timer.start(1000)
+#! [Provide Heartbeat]
+
+ def reconnect():
+ service = le_controller.addService(service_data)
+ if not service.isNull():
+ le_controller.startAdvertising(QLowEnergyAdvertisingParameters(),
+ advertising_data, advertising_data)
+
+ le_controller.disconnected.connect(reconnect)
+
+ sys.exit(app.exec())
diff --git a/examples/bluetooth/heartrate_server/heartrate_server.pyproject b/examples/bluetooth/heartrate_server/heartrate_server.pyproject
new file mode 100644
index 000000000..de1fd14a0
--- /dev/null
+++ b/examples/bluetooth/heartrate_server/heartrate_server.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["heartrate_server.py"]
+}
diff --git a/sources/pyside6/doc/CMakeLists.txt b/sources/pyside6/doc/CMakeLists.txt
index 9d4fcf144..61133c97a 100644
--- a/sources/pyside6/doc/CMakeLists.txt
+++ b/sources/pyside6/doc/CMakeLists.txt
@@ -194,7 +194,7 @@ if (FULLDOCSBUILD)
endif()
add_custom_target(apidoc
- COMMAND ${SHIBOKEN_PYTHON_INTERPRETER} ${SPHINX_BUILD} -b ${DOC_OUTPUT_FORMAT} ${CMAKE_CURRENT_BINARY_DIR}/rst html
+ COMMAND ${SHIBOKEN_PYTHON_INTERPRETER} ${SPHINX_BUILD} -b ${DOC_OUTPUT_FORMAT} -j auto ${CMAKE_CURRENT_BINARY_DIR}/rst html
COMMENT "Generating PySide htmls..."
)
diff --git a/sources/pyside6/doc/gettingstarted-linux.rst b/sources/pyside6/doc/gettingstarted-linux.rst
index 8db657dac..900da999e 100644
--- a/sources/pyside6/doc/gettingstarted-linux.rst
+++ b/sources/pyside6/doc/gettingstarted-linux.rst
@@ -37,12 +37,12 @@ variable required::
7z x libclang-release_100-based-linux-Rhel7.6-gcc5.3-x86_64.7z
export LLVM_INSTALL_DIR=$PWD/libclang
-Getting PySide
-~~~~~~~~~~~~~~
+Getting the source
+~~~~~~~~~~~~~~~~~~
Cloning the official repository can be done by::
- git clone --recursive https://code.qt.io/pyside/pyside-setup
+ git clone https://code.qt.io/pyside/pyside-setup
Checking out the version that we want to build, for example 6.0::
@@ -56,8 +56,11 @@ Install the general dependencies::
Additionally, :command:`git checkout -b 6.0 --track origin/6.0` could be a better option
in case you want to work on it.
-Building PySide
-~~~~~~~~~~~~~~~
+Building and Installing (setuptools)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The `setuptools` approach uses the `setup.py` file to execute the build,
+install, and packaging steps.
Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
for example, :command:`/opt/Qt/6.0.0/gcc_64/bin/qtpaths`.
@@ -66,13 +69,40 @@ Build can take a few minutes, so it is recommended to use more than one CPU core
python setup.py build --qtpaths=/opt/Qt/6.0.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
-Installing PySide
-~~~~~~~~~~~~~~~~~
-
To install on the current directory, just run::
python setup.py install --qtpaths=/opt/Qt/6.0.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+Building and Installing (cmake)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The `setuptools` approach includes internal `CMake` calls when
+building and installing the project, but a CMake-only approach is only
+recommended for packaging the project for distribution builds.
+
+Assumming that Qt is in PATH, for example, the configure step can be done with::
+
+ cmake -B /path/to/the/build/directory \
+ -S /path/to/the/pyside-setup \
+ -DCMAKE_INSTALL_PREFIX=/where/to/install \
+ -DPYTHON_EXECUTABLE=/path/to/interpreter
+
+.. note:: You can add `-DFORCE_LIMITED_API=yes` in case you want to have a
+ build which will be compatible with Python 3.7+.
+
+and then for building::
+
+ cmake --build /path/to/the/build/directory --parallel X
+
+where `X` is the amount of processes you want to use.
+Finally, the install step can be done with::
+
+ cmake --install /path/to/the/build/directory
+
+.. note:: You can build only pyside6 or only shiboken6 by using
+ the diferent source directories with the option `-S`.
+
+
Test installation
~~~~~~~~~~~~~~~~~
diff --git a/sources/shiboken6/doc/CMakeLists.txt b/sources/shiboken6/doc/CMakeLists.txt
index 4cd459c23..acf9acc94 100644
--- a/sources/shiboken6/doc/CMakeLists.txt
+++ b/sources/shiboken6/doc/CMakeLists.txt
@@ -18,7 +18,7 @@ if(SPHINX_BUILD)
# conditional tag for sphinx build
#string(JOIN "_" SPHINX_TAG ${DOC_OUTPUT_FORMAT} "format")
add_custom_target(doc
- COMMAND ${SPHINX_BUILD} -b ${DOC_OUTPUT_FORMAT} -c . ${CMAKE_CURRENT_SOURCE_DIR} html
+ COMMAND ${SPHINX_BUILD} -b ${DOC_OUTPUT_FORMAT} -j auto -c . ${CMAKE_CURRENT_SOURCE_DIR} html
COMMENT "Generating shiboken documentation HTML files"
VERBATIM)
diff --git a/tools/snippets_translate/override.py b/tools/snippets_translate/override.py
index c9d5d149f..c766b3336 100644
--- a/tools/snippets_translate/override.py
+++ b/tools/snippets_translate/override.py
@@ -124,6 +124,25 @@ def python_example_snippet_mapping():
snippet_id = str(i)
result[(qt_path, snippet_id)] = pyside_path, snippet_id
+ qt_path = "qtconnectivity/examples/bluetooth/heartrate_game/devicefinder.cpp"
+ pyside_path = EXAMPLES_PATH / "bluetooth" / "heartrate_game" / "devicefinder.py"
+ for i in range(5):
+ snippet_id = f"devicediscovery-{i}"
+ result[(qt_path, snippet_id)] = pyside_path, snippet_id
+
+ qt_path = "qtconnectivity/examples/bluetooth/heartrate_game/devicehandler.cpp"
+ pyside_path = EXAMPLES_PATH / "bluetooth" / "heartrate_game" / "devicehandler.py"
+ for snippet_id in ["Connect-Signals-1", "Connect-Signals-2",
+ "Filter HeartRate service 2", "Find HRM characteristic",
+ "Reading value"]:
+ result[(qt_path, snippet_id)] = pyside_path, snippet_id
+
+ qt_path = "qtconnectivity/examples/bluetooth/heartrate_server/main.cpp"
+ pyside_path = EXAMPLES_PATH / "bluetooth" / "heartrate_server" / "heartrate_server.py"
+ for snippet_id in ["Advertising Data", "Start Advertising", "Service Data",
+ "Provide Heartbeat"]:
+ result[(qt_path, snippet_id)] = pyside_path, snippet_id
+
_python_example_snippet_mapping = result
return _python_example_snippet_mapping