Make the new version lookup run in the background

This commit is contained in:
Veronica Berglyd Olsen
2024-02-14 18:55:01 +01:00
parent bdf100e608
commit 3a1f77b8f8
+58 -27
View File
@@ -31,10 +31,10 @@ from datetime import datetime
from urllib.request import Request, urlopen from urllib.request import Request, urlopen
from PyQt5.QtGui import QDesktopServices 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 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.common import formatVersion
from novelwriter.constants import nwConst from novelwriter.constants import nwConst
@@ -52,6 +52,11 @@ class VersionInfoWidget(QWidget):
def __init__(self, parent: QWidget) -> None: def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent) 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 # Labels
self._lblInfo = QLabel("{0} {1} \u2013 {2} {3} \u2013 {4}".format( self._lblInfo = QLabel("{0} {1} \u2013 {2} {3} \u2013 {4}".format(
self.tr("Version"), formatVersion(__version__), self.tr("Version"), formatVersion(__version__),
@@ -59,15 +64,11 @@ class VersionInfoWidget(QWidget):
"<a href='#notes'>{0}</a>".format(self.tr("Release Notes")), "<a href='#notes'>{0}</a>".format(self.tr("Release Notes")),
), self) ), self)
self._lblInfo.linkActivated.connect(self._processLink) self._lblInfo.linkActivated.connect(self._processLink)
self._lblRelease = QLabel(self.tr("Latest Version: {0}").format( self._lblRelease = QLabel(self._trLatest.format(
"<a href='#update'>{0}</a>".format(self.tr("Check Now")) "<a href='#update'>{0}</a>".format(self.tr("Check Now"))
), self) ), self)
self._lblRelease.linkActivated.connect(self._processLink) 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 # Assemble
self._layout = QVBoxLayout() self._layout = QVBoxLayout()
self._layout.addWidget(self._lblInfo) self._layout.addWidget(self._lblInfo)
@@ -91,41 +92,71 @@ class VersionInfoWidget(QWidget):
elif link == "#website": elif link == "#website":
QDesktopServices.openUrl(QUrl(nwConst.URL_WEB)) QDesktopServices.openUrl(QUrl(nwConst.URL_WEB))
elif link == "#update": elif link == "#update":
shuffle(LOOKUPS) self._lblRelease.setText(self._trLatest.format(self._trChecking))
for url in LOOKUPS: lookup = _Retriever()
if result := self._pullJson(url): lookup.signals.dataReady.connect(self._updateReleaseInfo)
self._updateReleaseInfo(result) SHARED.runInThreadPool(lookup)
break
else:
self._updateReleaseInfo({})
return return
## ##
# Internal Functions # Private Slots
## ##
@pyqtSlot(dict)
def _updateReleaseInfo(self, result: dict) -> None: def _updateReleaseInfo(self, result: dict) -> None:
"""Update the widget release info.""" """Update the widget release info."""
if version := result.get("version"): if version := result.get("version"):
download = f"<a href='#website'>{__domain__}</a>" download = f"<a href='#website'>{__domain__}</a>"
self._lblRelease.setText(self._trRelease.format(version, "\u2013", download)) self._lblRelease.setText(self._trLatest.format(
f"{version} \u2013 {self._trDownload.format(download)}"
))
else: else:
self._lblRelease.setText(self._trFail) self._lblRelease.setText(self._trLatest.format(self.tr("Failed")))
return return
def _pullJson(self, url: str) -> dict | None: # END Class VersionInfoWidget
"""Pull a JSON file from a URL."""
urlReq = Request(url)
urlReq.add_header("User-Agent", nwConst.USER_AGENT)
urlReq.add_header("Accept", "application/json")
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: try:
logger.info("Contacting: %s", url) logger.info("Contacting: %s", url)
urlData = urlopen(urlReq, timeout=10) data = urlopen(req, timeout=10).read().decode()
return json.loads(urlData.read().decode()).get("release") if isinstance(release := json.loads(data).get("release"), dict):
self.signals.dataReady.emit(release)
break
except Exception: except Exception:
logger.error("Failed to retrieve data") logger.error("Failed to retrieve release info")
else:
self.signals.dataReady.emit({})
return None self._isRunning = False
# END Class VersionInfoWidget return
# END Class _Retriever
class _RetrieverSignal(QObject):
dataReady = pyqtSignal(dict)
# END Class _RetrieverSignal