From 3a1f77b8f8887880b76fe4ecd2f0ba47a91357e4 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 14 Feb 2024 18:55:01 +0100 Subject: [PATCH] Make the new version lookup run in the background --- novelwriter/extensions/versioninfo.py | 95 ++++++++++++++++++--------- 1 file changed, 63 insertions(+), 32 deletions(-) diff --git a/novelwriter/extensions/versioninfo.py b/novelwriter/extensions/versioninfo.py index 09321e3e..a60db5fe 100644 --- a/novelwriter/extensions/versioninfo.py +++ b/novelwriter/extensions/versioninfo.py @@ -31,10 +31,10 @@ from datetime import datetime from urllib.request import Request, urlopen from PyQt5.QtGui import QDesktopServices -from PyQt5.QtCore import QUrl, pyqtSlot +from PyQt5.QtCore import QObject, QRunnable, QUrl, pyqtSignal, pyqtSlot from PyQt5.QtWidgets import QLabel, QVBoxLayout, QWidget -from novelwriter import CONFIG, __version__, __date__, __domain__ +from novelwriter import CONFIG, SHARED, __version__, __date__, __domain__ from novelwriter.common import formatVersion from novelwriter.constants import nwConst @@ -52,6 +52,11 @@ class VersionInfoWidget(QWidget): def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) + # Label Strings + self._trLatest = self.tr("Latest Version: {0}") + self._trChecking = self.tr("Checking ...") + self._trDownload = self.tr("Download from {0}") + # Labels self._lblInfo = QLabel("{0} {1} \u2013 {2} {3} \u2013 {4}".format( self.tr("Version"), formatVersion(__version__), @@ -59,15 +64,11 @@ class VersionInfoWidget(QWidget): "{0}".format(self.tr("Release Notes")), ), self) self._lblInfo.linkActivated.connect(self._processLink) - self._lblRelease = QLabel(self.tr("Latest Version: {0}").format( + self._lblRelease = QLabel(self._trLatest.format( "{0}".format(self.tr("Check Now")) ), self) self._lblRelease.linkActivated.connect(self._processLink) - # New Release - self._trRelease = self.tr("Latest Version: {0} {1} Download from {2}") - self._trFail = self.tr("Could not retrieve version information.") - # Assemble self._layout = QVBoxLayout() self._layout.addWidget(self._lblInfo) @@ -91,41 +92,71 @@ class VersionInfoWidget(QWidget): elif link == "#website": QDesktopServices.openUrl(QUrl(nwConst.URL_WEB)) elif link == "#update": - shuffle(LOOKUPS) - for url in LOOKUPS: - if result := self._pullJson(url): - self._updateReleaseInfo(result) - break - else: - self._updateReleaseInfo({}) + self._lblRelease.setText(self._trLatest.format(self._trChecking)) + lookup = _Retriever() + lookup.signals.dataReady.connect(self._updateReleaseInfo) + SHARED.runInThreadPool(lookup) return ## - # Internal Functions + # Private Slots ## + @pyqtSlot(dict) def _updateReleaseInfo(self, result: dict) -> None: """Update the widget release info.""" if version := result.get("version"): download = f"{__domain__}" - self._lblRelease.setText(self._trRelease.format(version, "\u2013", download)) + self._lblRelease.setText(self._trLatest.format( + f"{version} \u2013 {self._trDownload.format(download)}" + )) else: - self._lblRelease.setText(self._trFail) + self._lblRelease.setText(self._trLatest.format(self.tr("Failed"))) return - def _pullJson(self, url: str) -> dict | None: - """Pull a JSON file from a URL.""" - urlReq = Request(url) - urlReq.add_header("User-Agent", nwConst.USER_AGENT) - urlReq.add_header("Accept", "application/json") - - try: - logger.info("Contacting: %s", url) - urlData = urlopen(urlReq, timeout=10) - return json.loads(urlData.read().decode()).get("release") - except Exception: - logger.error("Failed to retrieve data") - - return None - # END Class VersionInfoWidget + + +class _Retriever(QRunnable): + + def __init__(self) -> None: + super().__init__() + self._isRunning = False + self.signals = _RetrieverSignal() + return + + def isRunning(self) -> bool: + return self._isRunning + + @pyqtSlot() + def run(self) -> None: + """Poll the urls in the background.""" + self._isRunning = True + + shuffle(LOOKUPS) + for url in LOOKUPS: + req = Request(url) + req.add_header("User-Agent", nwConst.USER_AGENT) + req.add_header("Accept", "application/json") + try: + logger.info("Contacting: %s", url) + data = urlopen(req, timeout=10).read().decode() + if isinstance(release := json.loads(data).get("release"), dict): + self.signals.dataReady.emit(release) + break + except Exception: + logger.error("Failed to retrieve release info") + else: + self.signals.dataReady.emit({}) + + self._isRunning = False + + return + +# END Class _Retriever + + +class _RetrieverSignal(QObject): + dataReady = pyqtSignal(dict) + +# END Class _RetrieverSignal