From 149a226182b549fa13290e5f2ed9ed23770948d5 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Thu, 14 Sep 2023 21:47:47 +0300 Subject: [PATCH] Library: Move image loading from RareCore to the GameWidget Instead of loading images in the showEvent of the MainWindow, load them in the showEvent of each widget. It seems to reduce the startup stuttering this way. With some more work we can only load the images for the widgets that are currently visible and reduce the stutter even more. At the same time, reduce the number of concurrent downloads in the image manager and add a timeout so we won't halt. The exception from the timeout is just logged at this point, and the download is not requeued. --- rare/components/main_window.py | 9 ------ .../tabs/games/game_widgets/game_widget.py | 10 +++++-- rare/shared/image_manager.py | 10 +++++-- rare/shared/rare_core.py | 29 +++---------------- 4 files changed, 19 insertions(+), 39 deletions(-) diff --git a/rare/components/main_window.py b/rare/components/main_window.py index c443c2475..318fc6b66 100644 --- a/rare/components/main_window.py +++ b/rare/components/main_window.py @@ -143,15 +143,6 @@ def center_window(self): self.resize(window_size) self.move(screen_rect.center() - self.rect().adjusted(0, 0, decor_width, decor_height).center()) - # lk: For the gritty details see `RareCore.load_pixmaps()` method - # Just before the window is shown, fire a timer to load game icons - # This is by nature a little iffy because we don't really know if the - # has been shown, and it might make the window delay as widgets being are updated. - # Still better than showing a hanged window frame for a few seconds. - def showEvent(self, a0: QShowEvent) -> None: - if not self._window_launched: - QTimer.singleShot(100, self.rcore.load_pixmaps) - @pyqtSlot() def show(self) -> None: super(MainWindow, self).show() diff --git a/rare/components/tabs/games/game_widgets/game_widget.py b/rare/components/tabs/games/game_widgets/game_widget.py index ee506cf34..d71bc20bf 100644 --- a/rare/components/tabs/games/game_widgets/game_widget.py +++ b/rare/components/tabs/games/game_widgets/game_widget.py @@ -1,8 +1,9 @@ import platform +import random from logging import getLogger -from PyQt5.QtCore import pyqtSignal, Qt, pyqtSlot, QObject, QEvent -from PyQt5.QtGui import QMouseEvent +from PyQt5.QtCore import pyqtSignal, Qt, pyqtSlot, QObject, QEvent, QTimer +from PyQt5.QtGui import QMouseEvent, QShowEvent from PyQt5.QtWidgets import QMessageBox, QAction from rare.models.game import RareGame @@ -105,6 +106,11 @@ def __init__(self, rgame: RareGame, parent=None): # lk: attributes as `GameWidgetUi` class __slots__ = "ui" + def showEvent(self, a0: QShowEvent) -> None: + if self.rgame.pixmap.isNull(): + QTimer.singleShot(random.randrange(42, 361, 7), self.rgame.load_pixmap) + super().showEvent(a0) + @pyqtSlot() def update_state(self): if self.rgame.is_idle: diff --git a/rare/shared/image_manager.py b/rare/shared/image_manager.py index b2cfbfe71..e3085d8d9 100644 --- a/rare/shared/image_manager.py +++ b/rare/shared/image_manager.py @@ -82,7 +82,7 @@ def __init__(self, signals: GlobalSignals, core: LegendaryCore): self.device = ImageSize.Preset(1, QApplication.instance().devicePixelRatio()) self.threadpool = QThreadPool() - self.threadpool.setMaxThreadCount(8) + self.threadpool.setMaxThreadCount(6) def __img_dir(self, app_name: str) -> Path: return self.image_dir.joinpath(app_name) @@ -182,8 +182,12 @@ def __download(self, updates, json_data, game, use_async: bool = False) -> bool: logger.info(f"Downloading {image['type']} for {game.app_name} ({game.app_title})") json_data[image["type"]] = image["md5"] payload = {"resize": 1, "w": ImageSize.Image.size.width(), "h": ImageSize.Image.size.height()} - # cache_data[image["type"]] = requests.get(image["url"], params=payload, timeout=2).content - cache_data[image["type"]] = requests.get(image["url"], params=payload).content + try: + # cache_data[image["type"]] = requests.get(image["url"], params=payload).content + cache_data[image["type"]] = requests.get(image["url"], params=payload, timeout=10).content + except Exception as e: + logger.error(e) + return False self.__convert(game, cache_data) # lk: don't keep the cache if there is no logo (kept for me) diff --git a/rare/shared/rare_core.py b/rare/shared/rare_core.py index 6dbf808a8..12302c03a 100644 --- a/rare/shared/rare_core.py +++ b/rare/shared/rare_core.py @@ -336,31 +336,6 @@ def __post_init(self) -> None: # self.fetch_entitlements() self.resolve_origin() - def load_pixmaps(self) -> None: - """ - Load pixmaps for all games - - This exists here solely to fight signal and possibly threading issues. - The initial image loading at startup should not be done in the RareGame class - for two reasons. It will delay startup due to widget updates and the image - might become availabe before the UI is brought up. In case of the second, we - will get both a long queue of signals to be serviced and some of them might - be not connected yet so the widget won't be updated. So do the loading here - by calling this after the MainWindow has finished initializing. - - @return: None - """ - def __load_pixmaps() -> None: - # time.sleep(0.1) - for rgame in self.__library.values(): - # self.__image_manager.download_image(rgame.game, rgame.set_pixmap, 0, False) - rgame.load_pixmap() - # lk: activity perception delay - time.sleep(0.0005) - - pixmap_worker = QRunnable.create(__load_pixmaps) - QThreadPool.globalInstance().start(pixmap_worker) - @property def games_and_dlcs(self) -> Iterator[RareGame]: for app_name in self.__library: @@ -378,6 +353,10 @@ def installed_games(self) -> Iterator[RareGame]: def origin_games(self) -> Iterator[RareGame]: return self.__filter_games(lambda game: game.is_origin and not game.is_dlc) + @property + def ubisoft_games(self) -> Iterator[RareGame]: + return self.__filter_games(lambda game: game.is_ubisoft and not game.is_dlc) + @property def game_list(self) -> Iterator[Game]: for game in self.games: