From 739c9e3d12a1b88630afbed5cc24673e4d120186 Mon Sep 17 00:00:00 2001 From: Giovanni Santini Date: Tue, 2 Apr 2024 18:45:14 +0200 Subject: [PATCH 1/2] fix: QT Positioning Rather than manually fetching the position, have our background object receive the various updates of the location and magically update the config. This is because QT needs its loop running in order to have the location working. Fixes #274 --- yin_yang/position.py | 85 ++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 26 deletions(-) diff --git a/yin_yang/position.py b/yin_yang/position.py index 79fc891..d628eb8 100644 --- a/yin_yang/position.py +++ b/yin_yang/position.py @@ -1,13 +1,55 @@ import logging -from time import sleep import requests -from PySide6.QtCore import QObject -from PySide6.QtPositioning import QGeoPositionInfoSource, QGeoCoordinate, QGeoPositionInfo +from PySide6.QtCore import QObject, Slot +from PySide6.QtPositioning import ( + QGeoPositionInfoSource, + QGeoCoordinate, + QGeoPositionInfo, +) logger = logging.getLogger(__name__) +class QTPositionReceiver(QObject): + """Small handler for QT positioning service.""" + + def __init__(self): + super().__init__() + + # Create the position service and hook it to us + self._positionSource = QGeoPositionInfoSource.createSource( + "geoclue2", {"desktopId": "Yin-Yang"}, self + ) + self._positionSource.positionUpdated.connect(self.handlePosition) + + # Start the position service. + # This will only work after the app calls `exec()`. + self._positionSource.startUpdates() + + # Get the initial last position. + self._lastPosition = self._positionSource.lastKnownPosition() + + @Slot(QGeoPositionInfo) + def handlePosition(self, position: QGeoPositionInfo): + """Track the position provided by the service.""" + self._lastPosition = position + + # TODO: Here trigger a refresh. + # I tried the following but it seem to not work: + ## Trigger a refresh. Ugly but does the job + # from yin_yang.config import config + # _ = config.location + # del config + + def lastKnownPosition(self): + """Return the last known position, if valid.""" + return self._lastPosition + + +position_handler = QTPositionReceiver() + + def get_current_location() -> QGeoCoordinate: try: return get_qt_position() @@ -19,32 +61,23 @@ def get_current_location() -> QGeoCoordinate: except TypeError as e: logger.warning(e) - raise TypeError('Unable to get current location') - - -parent = QObject() -location_source = QGeoPositionInfoSource.createDefaultSource(parent) + raise TypeError("Unable to get current location") def get_qt_position() -> QGeoCoordinate: - if location_source is None: - raise TypeError('Location source is none') - - pos: QGeoPositionInfo = location_source.lastKnownPosition() - if pos is None: - location_source.requestUpdate(10) - tries = 0 - while pos is None and tries < 10: - pos = location_source.lastKnownPosition() - tries += 1 - sleep(1) + """Get the position via QT service""" + # Fetch the last known position + global position_handler + pos: QGeoPositionInfo = position_handler.lastKnownPosition() + coordinate = pos.coordinate() if not coordinate.isValid(): - raise TypeError('Coordinates are not valid') + raise TypeError("Coordinates are not valid") return coordinate + # there is a freedesktop portal for getting the location, # but it's not implemented by KDE, so I have no use for it @@ -52,20 +85,20 @@ def get_qt_position() -> QGeoCoordinate: def get_ipinfo_position() -> QGeoCoordinate: # use the old method as a fallback try: - response = requests.get('https://www.ipinfo.io/loc') + response = requests.get("https://www.ipinfo.io/loc") except Exception as e: logger.error(e) - raise TypeError('Error while sending a request to get location') + raise TypeError("Error while sending a request to get location") if not response.ok: - raise TypeError('Failed to get location from ipinfo.io') + raise TypeError("Failed to get location from ipinfo.io") - loc_response = response.text.removesuffix('\n').split(',') + loc_response = response.text.removesuffix("\n").split(",") loc: [float] = [float(coordinate) for coordinate in loc_response] - assert len(loc) == 2, 'The returned location should have exactly 2 values.' + assert len(loc) == 2, "The returned location should have exactly 2 values." coordinate = QGeoCoordinate(loc[0], loc[1]) if not coordinate.isValid(): - raise TypeError('Coordinates are not valid') + raise TypeError("Coordinates are not valid") return coordinate From 4fb491eae4f43cb579f941d24c643d1c7971637b Mon Sep 17 00:00:00 2001 From: Giovanni Santini Date: Mon, 3 Jun 2024 21:14:50 +0200 Subject: [PATCH 2/2] fix: Remove TODO comment Remove the TODO and the hackish code for updating the position. Didn't work, plus it is better to have a safe default. --- yin_yang/position.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/yin_yang/position.py b/yin_yang/position.py index d628eb8..328a497 100644 --- a/yin_yang/position.py +++ b/yin_yang/position.py @@ -35,13 +35,6 @@ def handlePosition(self, position: QGeoPositionInfo): """Track the position provided by the service.""" self._lastPosition = position - # TODO: Here trigger a refresh. - # I tried the following but it seem to not work: - ## Trigger a refresh. Ugly but does the job - # from yin_yang.config import config - # _ = config.location - # del config - def lastKnownPosition(self): """Return the last known position, if valid.""" return self._lastPosition