1
0
Fork 0
mirror of synced 2024-05-19 12:02:54 +12:00

Implement image manager

Signed-off-by: loathingKernel <142770+loathingKernel@users.noreply.github.com>
This commit is contained in:
loathingKernel 2022-06-18 21:45:36 +03:00
parent 7073a6ad7a
commit 3a28f2f0a2
21 changed files with 652 additions and 296 deletions

View file

@ -8,22 +8,24 @@ import time
import traceback
from argparse import Namespace
from datetime import datetime
from typing import Optional
import legendary
import requests.exceptions
from PyQt5.QtCore import Qt, QThreadPool, QSettings, QTranslator, QTimer
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMessageBox
from requests import HTTPError
import legendary
# noinspection PyUnresolvedReferences
import rare.resources.resources
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.utils.paths import cache_dir, resources_path, tmp_dir
from rare.components.dialogs.launch_dialog import LaunchDialog
from rare.components.main_window import MainWindow
from rare.components.tray_icon import TrayIcon
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.shared.image_manager import ImageManagerSingleton
from rare.utils import legendary_utils, config_helper
from rare.utils.paths import cache_dir, resources_path, tmp_dir
from rare.utils.utils import set_color_pallete, set_style_sheet
start_time = time.strftime("%y-%m-%d--%H-%M") # year-month-day-hour-minute
@ -54,8 +56,8 @@ def excepthook(exc_type, exc_value, exc_tb):
class App(QApplication):
mainwindow: MainWindow = None
tray_icon: QSystemTrayIcon = None
mainwindow: Optional[MainWindow] = None
tray_icon: Optional[QSystemTrayIcon] = None
def __init__(self, args: Namespace):
super(App, self).__init__(sys.argv)
@ -63,7 +65,7 @@ class App(QApplication):
self.window_launched = False
self.setQuitOnLastWindowClosed(False)
if hasattr(Qt, 'AA_UseHighDpiPixmaps'):
if hasattr(Qt, "AA_UseHighDpiPixmaps"):
self.setAttribute(Qt.AA_UseHighDpiPixmaps)
# init Legendary
@ -102,6 +104,7 @@ class App(QApplication):
self.settings = QSettings()
self.signals = GlobalSignalsSingleton(init=True)
self.image_manager = ImageManagerSingleton(init=True)
self.signals.exit_app.connect(self.exit_app)
self.signals.send_notification.connect(

View file

@ -1,45 +1,77 @@
import os
import platform
from logging import getLogger
from PyQt5.QtCore import Qt, pyqtSignal, QRunnable, QObject, QThreadPool, QSettings
from PyQt5.QtWidgets import QDialog, QApplication
from legendary.core import LegendaryCore
from requests.exceptions import ConnectionError, HTTPError
from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton, ApiResultsSingleton
from rare.components.dialogs.login import LoginDialog
from rare.models.apiresults import ApiResults
from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton, ApiResultsSingleton
from rare.shared.image_manager import ImageManagerSingleton
from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog
from rare.utils.models import ApiResults
from rare.utils.paths import image_dir
from rare.utils.utils import download_images, CloudWorker
from rare.utils.utils import CloudWorker
logger = getLogger("Login")
class LaunchDialogSignals(QObject):
image_progress = pyqtSignal(int)
result = pyqtSignal(object, str)
class LaunchWorker(QRunnable):
class Signals(QObject):
progress = pyqtSignal(int)
result = pyqtSignal(object, str)
class ImageWorker(QRunnable):
def __init__(self):
super(ImageWorker, self).__init__()
self.signals = LaunchDialogSignals()
super(LaunchWorker, self).__init__()
self.setAutoDelete(True)
self.signals = LaunchWorker.Signals()
self.core = LegendaryCoreSingleton()
def run(self):
download_images(self.signals.image_progress, self.signals.result, self.core)
self.signals.image_progress.emit(100)
pass
class ApiRequestWorker(QRunnable):
class ImageWorker(LaunchWorker):
def __init__(self):
super(ImageWorker, self).__init__()
self.image_manager = ImageManagerSingleton()
def run(self):
# Download Images
games, dlcs = self.core.get_game_and_dlc_list(update_assets=True, skip_ue=False)
self.signals.result.emit((games, dlcs), "gamelist")
dlc_list = [dlc[0] for dlc in dlcs.values()]
na_games, na_dlcs = self.core.get_non_asset_library_items(force_refresh=False, skip_ue=False)
self.signals.result.emit(na_games, "no_assets")
na_dlc_list = [dlc[0] for dlc in na_dlcs.values()]
game_list = games + dlc_list + na_games + na_dlc_list
fetched = [False] * len(game_list)
def set_fetched(idx):
fetched[idx] = True
self.signals.progress.emit(sum(fetched))
for i, game in enumerate(game_list):
if game.app_title == "Unreal Engine":
game.app_title += f" {game.app_name.split('_')[-1]}"
self.core.lgd.set_game_meta(game.app_name, game)
self.image_manager.download_image_blocking(game)
self.signals.progress.emit(int(i / len(game_list) * 100))
# self.image_manager.download_image(
# game,
# load_callback=lambda: set_fetched(i),
# priority=i
# )
# while not all(fetched):
# continue
self.signals.progress.emit(100)
class ApiRequestWorker(LaunchWorker):
def __init__(self):
super(ApiRequestWorker, self).__init__()
self.signals = LaunchDialogSignals()
self.setAutoDelete(True)
self.core = LegendaryCoreSingleton()
self.settings = QSettings()
def run(self) -> None:
@ -106,14 +138,11 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
self.quit_app.emit(0)
def launch(self):
# self.core = core
if not os.path.exists(image_dir):
os.makedirs(image_dir)
if not self.args.offline:
self.image_info.setText(self.tr("Downloading Images"))
image_worker = ImageWorker()
image_worker.signals.image_progress.connect(self.update_image_progbar)
image_worker.signals.progress.connect(self.update_image_progbar)
image_worker.signals.result.connect(self.handle_api_worker_result)
self.thread_pool.start(image_worker)
@ -124,9 +153,7 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
# cloud save from another worker, because it is used in cloud_save_utils too
cloud_worker = CloudWorker()
cloud_worker.signals.result_ready.connect(
lambda x: self.handle_api_worker_result(x, "saves")
)
cloud_worker.signals.result_ready.connect(lambda x: self.handle_api_worker_result(x, "saves"))
self.thread_pool.start(cloud_worker)
else:
@ -159,13 +186,9 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
self.api_results.dlcs,
) = self.core.get_game_and_dlc_list(False)
elif text == "32bit":
self.api_results.bit32_games = (
[i.app_name for i in result[0]] if result else []
)
self.api_results.bit32_games = [i.app_name for i in result[0]] if result else []
elif text == "mac":
self.api_results.mac_games = (
[i.app_name for i in result[0]] if result else []
)
self.api_results.mac_games = [i.app_name for i in result[0]] if result else []
elif text == "no_assets":
self.api_results.no_asset_games = result if result else []

View file

@ -3,12 +3,17 @@ from typing import Tuple, Dict, Union, List
from PyQt5.QtCore import QSettings, QObjectCleanupHandler
from PyQt5.QtWidgets import QStackedWidget, QVBoxLayout, QWidget
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton, ApiResultsSingleton
from legendary.models.game import InstalledGame, Game
from rare.shared import (
LegendaryCoreSingleton,
GlobalSignalsSingleton,
ArgumentsSingleton,
ApiResultsSingleton,
)
from rare.shared.image_manager import ImageManagerSingleton
from rare.ui.components.tabs.games.games_tab import Ui_GamesTab
from rare.utils.extra_widgets import FlowLayout
from rare.utils.utils import get_pixmap, download_image, get_uninstalled_pixmap
from .cloud_save_utils import CloudSaveUtils
from .cloud_save_utils import CloudSaveUtils
from .game_info import GameInfoTabs
@ -41,6 +46,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
self.signals = GlobalSignalsSingleton()
self.args = ArgumentsSingleton()
self.api_results = ApiResultsSingleton()
self.image_manager = ImageManagerSingleton()
self.settings = QSettings()
self.game_list: List[Game] = self.api_results.game_list
@ -71,7 +77,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
for i in self.game_list:
if i.app_name.startswith("UE_4"):
pixmap = get_pixmap(i.app_name)
pixmap = self.image_manager.get_pixmap(i.app_name)
if pixmap.isNull():
continue
self.ue_name = i.app_name
@ -216,15 +222,15 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
)
def add_installed_widget(self, app_name):
pixmap = get_pixmap(app_name)
pixmap = self.image_manager.get_pixmap(app_name)
try:
if pixmap.isNull():
logger.info(f"{app_name} has a corrupt image.")
if app_name in self.no_asset_names and self.core.get_asset(app_name).namespace != "ue":
download_image(self.core.get_game(app_name), force=True)
pixmap = get_pixmap(app_name)
self.image_manager.download_image_blocking(self.core.get_game(app_name), force=True)
pixmap = self.image_manager.get_pixmap(app_name)
elif self.ue_name:
pixmap = get_pixmap(self.ue_name)
pixmap = self.image_manager.get_pixmap(self.ue_name)
icon_widget = InstalledIconWidget(app_name, pixmap, self.game_utils)
list_widget = InstalledListWidget(app_name, pixmap, self.game_utils)
@ -242,15 +248,15 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
return icon_widget, list_widget
def add_uninstalled_widget(self, game):
pixmap = get_uninstalled_pixmap(game.app_name)
pixmap = self.image_manager.get_pixmap(game.app_name, color=False)
try:
if pixmap.isNull():
if self.core.get_asset(game.app_name).namespace != "ue":
logger.warning(f"{game.app_title} has a corrupt image. Reloading...")
download_image(game, force=True)
pixmap = get_uninstalled_pixmap(game.app_name)
self.image_manager.download_image_blocking(game, force=True)
pixmap = self.image_manager.get_pixmap(game.app_name, color=False)
elif self.ue_name:
pixmap = get_uninstalled_pixmap(self.ue_name)
pixmap = self.image_manager.get_pixmap(self.ue_name, color=False)
icon_widget = IconWidgetUninstalled(game, self.core, pixmap)
list_widget = ListWidgetUninstalled(self.core, game, pixmap)

View file

@ -1,14 +1,14 @@
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QPixmap, QResizeEvent
from PyQt5.QtWidgets import QFrame, QWidget, QMessageBox
from legendary.models.game import Game
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton
from rare.components.tabs.games.game_utils import GameUtils
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.ui.components.tabs.games.game_info.game_dlc import Ui_GameDlc
from rare.ui.components.tabs.games.game_info.game_dlc_widget import Ui_GameDlcWidget
from rare.utils.models import InstallOptionsModel
from rare.utils.utils import get_pixmap
class GameDlc(QWidget, Ui_GameDlc):
@ -90,14 +90,17 @@ class GameDlcWidget(QFrame, Ui_GameDlcWidget):
def __init__(self, dlc: Game, installed: bool, parent=None):
super(GameDlcWidget, self).__init__(parent=parent)
self.image_manager = ImageManagerSingleton()
self.setupUi(self)
self.dlc = dlc
self.image.setFixedSize(ImageSize.Smaller.size)
self.dlc_name.setText(dlc.app_title)
self.version.setText(dlc.app_version())
self.app_name.setText(dlc.app_name)
self.pixmap = get_pixmap(dlc.app_name)
self.pixmap = self.image_manager.get_pixmap(dlc.app_name)
if installed:
self.action_button.setProperty("uninstall", 1)

View file

@ -1,8 +1,8 @@
import os
import platform
import shutil
from pathlib import Path
from logging import getLogger
from pathlib import Path
from typing import Tuple
from PyQt5.QtCore import (
@ -25,20 +25,21 @@ from PyQt5.QtWidgets import (
QMessageBox,
QWidgetAction,
)
from legendary.models.game import Game, InstalledGame, VerifyResult
from rare.legendary.legendary.utils.lfs import validate_files
from legendary.utils.lfs import validate_files
from rare.shared import (
LegendaryCoreSingleton,
GlobalSignalsSingleton,
ArgumentsSingleton,
)
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
from rare.utils.extra_widgets import PathEdit
from rare.utils.legendary_utils import VerifyWorker
from rare.utils.models import InstallOptionsModel
from rare.utils.steam_grades import SteamWorker
from rare.utils.utils import get_size, get_pixmap
from rare.utils.utils import get_size
logger = getLogger("GameInfo")
@ -56,6 +57,7 @@ class GameInfo(QWidget, Ui_GameInfo):
self.core = LegendaryCoreSingleton()
self.signals = GlobalSignalsSingleton()
self.args = ArgumentsSingleton()
self.image_manager = ImageManagerSingleton()
self.game_utils = game_utils
if platform.system() == "Windows":
@ -281,11 +283,10 @@ class GameInfo(QWidget, Ui_GameInfo):
self.igame = self.core.get_installed_game(self.game.app_name)
self.title.setTitle(self.game.app_title)
pixmap = get_pixmap(self.game.app_name)
pixmap = self.image_manager.get_pixmap(self.game.app_name)
if pixmap.isNull():
pixmap = get_pixmap(self.parent().parent().parent().ue_name)
w = 200
pixmap = pixmap.scaled(w, int(w * 4 / 3))
pixmap = self.image_manager.get_pixmap(self.parent().parent().parent().ue_name)
pixmap = pixmap.scaled(ImageSize.Display.size)
self.image.setPixmap(pixmap)
self.app_name.setText(self.game.app_name)

View file

@ -3,15 +3,20 @@ import platform
from PyQt5.QtCore import Qt, QThreadPool
from PyQt5.QtGui import QKeyEvent
from PyQt5.QtWidgets import QWidget, QTreeView
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton, ApiResultsSingleton
from legendary.models.game import Game
from rare.shared import (
LegendaryCoreSingleton,
GlobalSignalsSingleton,
ArgumentsSingleton,
ApiResultsSingleton,
)
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
from rare.utils.extra_widgets import SideTabWidget
from rare.utils.json_formatter import QJsonModel
from rare.utils.models import InstallOptionsModel
from rare.utils.steam_grades import SteamWorker
from rare.utils.utils import get_pixmap
class UninstalledInfoTabs(SideTabWidget):
@ -71,6 +76,8 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.core = LegendaryCoreSingleton()
self.signals = GlobalSignalsSingleton()
self.api_results = ApiResultsSingleton()
self.image_manager = ImageManagerSingleton()
self.install_button.clicked.connect(self.install_game)
if platform.system() != "Windows":
self.steam_worker = SteamWorker(self.core)
@ -104,11 +111,10 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
available_platforms.append("macOS")
self.platform.setText(", ".join(available_platforms))
pixmap = get_pixmap(game.app_name)
pixmap = self.image_manager.get_pixmap(game.app_name, color=False)
if pixmap.isNull():
pixmap = get_pixmap(self.ue_default_name)
w = 200
pixmap = pixmap.scaled(w, int(w * 4 / 3))
pixmap = self.image_manager.get_pixmap(self.ue_default_name, color=False)
pixmap = pixmap.scaled(ImageSize.Display.size)
self.image.setPixmap(pixmap)
self.app_name.setText(self.game.app_name)

View file

@ -6,9 +6,9 @@ from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, QStandardPaths, Qt, QB
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction, QLabel
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.components.tabs.games.game_utils import GameUtils
from rare.utils import utils
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.utils.utils import create_desktop_link
logger = getLogger("Game")
@ -25,6 +25,7 @@ class BaseInstalledWidget(QGroupBox):
self.core = LegendaryCoreSingleton()
self.signals = GlobalSignalsSingleton()
self.args = ArgumentsSingleton()
self.image_manager = ImageManagerSingleton()
self.game_utils = game_utils
self.syncing_cloud_saves = False
@ -63,8 +64,9 @@ class BaseInstalledWidget(QGroupBox):
pass
self.image = QLabel()
self.image.setFixedSize(ImageSize.Display.size)
self.image.setPixmap(
pixmap.scaled(200, int(200 * 4 / 3), transformMode=Qt.SmoothTransformation)
pixmap.scaled(ImageSize.Display.size, transformMode=Qt.SmoothTransformation)
)
self.game_running = False
self.offline = self.args.offline
@ -140,11 +142,9 @@ class BaseInstalledWidget(QGroupBox):
)
def reload_image(self):
utils.download_image(self.game, True)
pm = utils.get_pixmap(self.game.app_name)
self.image.setPixmap(
pm.scaled(200, int(200 * 4 / 3), transformMode=Qt.SmoothTransformation)
)
self.image_manager.download_image_blocking(self.game, force=True)
pm = self.image_manager.get_pixmap(self.game.app_name, color=True)
self.image.setPixmap(pm.scaled(ImageSize.Display.size, transformMode=Qt.SmoothTransformation))
def create_desktop_link(self, type_of_link):
if type_of_link == "desktop":

View file

@ -2,9 +2,9 @@ from logging import getLogger
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import QGroupBox, QLabel, QAction
from legendary.models.game import Game
from rare.utils import utils
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
logger = getLogger("Uninstalled")
@ -14,15 +14,16 @@ class BaseUninstalledWidget(QGroupBox):
def __init__(self, game, core, pixmap):
super(BaseUninstalledWidget, self).__init__()
self.image_manager = ImageManagerSingleton()
self.game = game
if self.game.app_title == "Unreal Engine":
self.game.app_title = f"{self.game.app_title} {self.game.app_name.split('_')[-1]}"
self.core = core
self.image = QLabel()
self.image.setPixmap(
pixmap.scaled(200, int(200 * 4 / 3), transformMode=Qt.SmoothTransformation)
)
self.image.setFixedSize(ImageSize.Display.size)
self.image.setPixmap(pixmap.scaled(ImageSize.Display.size, transformMode=Qt.SmoothTransformation))
self.installing = False
self.setContextMenuPolicy(Qt.ActionsContextMenu)
self.setContentsMargins(0, 0, 0, 0)
@ -32,11 +33,9 @@ class BaseUninstalledWidget(QGroupBox):
self.addAction(reload_image)
def reload_image(self):
utils.download_image(self.game, True)
pm = utils.get_uninstalled_pixmap(self.game.app_name)
self.image.setPixmap(
pm.scaled(200, int(200 * 4 / 3), transformMode=Qt.SmoothTransformation)
)
self.image_manager.download_image_blocking(self.game, force=True)
pm = self.image_manager.get_pixmap(self.game.app_name, color=False)
self.image.setPixmap(pm.scaled(ImageSize.Display.size, transformMode=Qt.SmoothTransformation))
def install(self):
self.show_uninstalled_info.emit(self.game)

View file

@ -1,14 +1,16 @@
from logging import getLogger
from PyQt5.QtCore import QEvent, pyqtSignal, QSize, Qt
from PyQt5.QtCore import QEvent, pyqtSignal, Qt
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QPushButton, QLabel
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QPushButton, QWidget
from rare.shared import LegendaryCoreSingleton
from rare.components.tabs.games.game_widgets.base_installed_widget import (
BaseInstalledWidget,
)
from rare.shared import LegendaryCoreSingleton
from rare.shared.image_manager import ImageSize
from rare.utils.utils import icon
from rare.widgets.elide_label import ElideLabel
logger = getLogger("GameWidgetInstalled")
@ -21,55 +23,54 @@ class InstalledIconWidget(BaseInstalledWidget):
self.setObjectName("game_widget_icon")
self.setContextMenuPolicy(Qt.ActionsContextMenu)
self.layout = QVBoxLayout()
layout = QVBoxLayout()
self.core = LegendaryCoreSingleton()
if self.update_available:
logger.info(f"Update available for game: {self.game.app_name}")
self.layout.addWidget(self.image)
layout.addWidget(self.image)
self.game_utils.finished.connect(self.game_finished)
self.game_utils.cloud_save_finished.connect(self.sync_finished)
self.title_label = QLabel(f"<h4>{self.game.app_title}</h4>")
self.title_label.setAutoFillBackground(False)
self.title_label.setWordWrap(True)
self.title_label.setFixedWidth(175)
miniwidget = QWidget(self)
miniwidget.setFixedWidth(ImageSize.Display.size.width())
minilayout = QHBoxLayout()
minilayout.setContentsMargins(0, 0, 0, 0)
minilayout.setSpacing(0)
miniwidget.setLayout(minilayout)
self.title_label = ElideLabel(f"<h4>{self.game.app_title}</h4>", parent=miniwidget)
self.title_label.setObjectName("game_widget")
minilayout.addWidget(self.title_label)
minilayout.addWidget(self.title_label, stretch=2)
# Info Button
self.menu_btn = QPushButton()
self.menu_btn = QPushButton(parent=miniwidget)
self.menu_btn.setIcon(icon("ei.info-circle"))
# self.menu_btn.setObjectName("installed_menu_button")
self.menu_btn.setIconSize(QSize(18, 18))
self.menu_btn.enterEvent = lambda x: self.info_label.setText(
self.tr("Information")
)
self.menu_btn.enterEvent = lambda x: self.info_label.setText(self.tr("Information"))
self.menu_btn.leaveEvent = lambda x: self.enterEvent(None)
# remove Border
self.menu_btn.setObjectName("menu_button")
self.menu_btn.clicked.connect(lambda: self.show_info.emit(self.game.app_name))
self.menu_btn.setFixedWidth(17)
minilayout.addWidget(self.menu_btn)
minilayout.addStretch(1)
self.layout.addLayout(minilayout)
self.menu_btn.setFixedSize(22, 22)
minilayout.addWidget(self.menu_btn, stretch=0)
minilayout.setAlignment(Qt.AlignTop)
layout.addWidget(miniwidget)
self.info_label = QLabel("")
self.info_label = ElideLabel(" ", parent=self)
self.info_label.setFixedWidth(ImageSize.Display.size.width())
self.leaveEvent(None)
self.info_label.setAutoFillBackground(False)
self.info_label.setObjectName("info_label")
self.layout.addWidget(self.info_label)
layout.addWidget(self.info_label)
if self.igame and self.igame.needs_verification:
self.info_label.setText(self.texts["needs_verification"])
self.setLayout(self.layout)
self.setFixedWidth(self.sizeHint().width())
self.setLayout(layout)
self.game_utils.game_launched.connect(self.game_started)
@ -99,7 +100,7 @@ class InstalledIconWidget(BaseInstalledWidget):
elif self.igame and self.igame.needs_verification:
self.info_label.setText(self.texts["needs_verification"])
else:
self.info_label.setText("")
self.info_label.setText(" ") # invisible text, cheap way to always vertical have size in label
def mousePressEvent(self, e: QMouseEvent):
# left button

View file

@ -1,14 +1,13 @@
from PyQt5.QtCore import Qt, QRect
from PyQt5.QtGui import QPaintEvent, QPainter, QPixmap, QPen, QFont, QColor
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QHBoxLayout, QWidget
from legendary.models.game import Game
from rare.shared import LegendaryCoreSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.utils.utils import (
get_pixmap,
optimal_text_background,
text_color_for_background,
get_uninstalled_pixmap,
)
@ -24,11 +23,9 @@ class InstallingGameWidget(QWidget):
self.core = LegendaryCoreSingleton()
self.pixmap = QPixmap()
w = 200
# self.pixmap = self.pixmap.scaled(w, int(w * 4 / 3), transformMode=Qt.SmoothTransformation)
self.image_widget = PaintWidget()
self.setContentsMargins(4, 4, 4, 4)
self.image_widget.setFixedSize(w, int(w * 4 / 3))
self.setContentsMargins(0, 0, 0, 0)
self.image_widget.setFixedSize(ImageSize.Display.size)
self.layout().addWidget(self.image_widget)
self.title_label = QLabel(f"<h4>Error</h4>")
@ -62,18 +59,18 @@ class PaintWidget(QWidget):
def __init__(self):
super(PaintWidget, self).__init__()
self.core = LegendaryCoreSingleton()
self.image_manager = ImageManagerSingleton()
def set_game(self, app_name: str):
game = self.core.get_game(app_name, False)
self.color_image = get_pixmap(game.app_name)
w = 200
self.color_image = self.image_manager.get_pixmap(game.app_name, color=False)
self.color_image = self.color_image.scaled(
w, int(w * 4 // 3), transformMode=Qt.SmoothTransformation
ImageSize.Display.size, transformMode=Qt.SmoothTransformation
)
self.setFixedSize(self.color_image.size())
self.bw_image = get_uninstalled_pixmap(app_name)
self.setFixedSize(ImageSize.Display.size)
self.bw_image = self.image_manager.get_pixmap(app_name, color=False)
self.bw_image = self.bw_image.scaled(
w, int(w * 4 // 3), transformMode=Qt.SmoothTransformation
ImageSize.Display.size, transformMode=Qt.SmoothTransformation
)
self.progress = 0

View file

@ -1,12 +1,15 @@
from logging import getLogger
from PyQt5.QtWidgets import QVBoxLayout, QLabel
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QVBoxLayout, QWidget, QHBoxLayout
from legendary.core import LegendaryCore
from legendary.models.game import Game
from rare.components.tabs.games.game_widgets.base_uninstalled_widget import (
BaseUninstalledWidget,
)
from rare.shared.image_manager import ImageSize
from rare.widgets.elide_label import ElideLabel
logger = getLogger("Uninstalled")
@ -14,19 +17,31 @@ logger = getLogger("Uninstalled")
class IconWidgetUninstalled(BaseUninstalledWidget):
def __init__(self, game: Game, core: LegendaryCore, pixmap):
super(IconWidgetUninstalled, self).__init__(game, core, pixmap)
self.layout = QVBoxLayout()
layout = QVBoxLayout()
self.setObjectName("game_widget_icon")
self.layout.addWidget(self.image)
layout.addWidget(self.image)
self.title_label = QLabel(f"<h3>{game.app_title}</h3>")
self.title_label.setWordWrap(True)
self.layout.addWidget(self.title_label)
miniwidget = QWidget(self)
miniwidget.setFixedWidth(ImageSize.Display.size.width())
minilayout = QHBoxLayout()
minilayout.setContentsMargins(0, 0, 0, 0)
minilayout.setSpacing(0)
miniwidget.setLayout(minilayout)
self.info_label = QLabel("")
self.layout.addWidget(self.info_label)
self.title_label = ElideLabel(f"<h4>{game.app_title}</h4>", parent=miniwidget)
self.title_label.setObjectName("game_widget")
minilayout.addWidget(self.title_label, stretch=2)
self.setLayout(self.layout)
self.setFixedWidth(self.sizeHint().width())
minilayout.setAlignment(Qt.AlignTop)
layout.addWidget(miniwidget)
self.info_label = ElideLabel(" ", parent=self)
self.info_label.setFixedWidth(ImageSize.Display.size.width())
self.leaveEvent(None)
self.info_label.setObjectName("info_label")
layout.addWidget(self.info_label)
self.setLayout(layout)
def mousePressEvent(self, e) -> None:
# left button
@ -47,4 +62,4 @@ class IconWidgetUninstalled(BaseUninstalledWidget):
if self.installing:
self.info_label.setText("Installation...")
else:
self.info_label.setText("")
self.info_label.setText(" ") # invisible text, cheap way to always have vertical size in label

3
rare/models/__init__.py Normal file
View file

@ -0,0 +1,3 @@
"""
Models and data structures module for Rare
"""

22
rare/models/apiresults.py Normal file
View file

@ -0,0 +1,22 @@
from dataclasses import dataclass
from typing import Optional, List, Dict
@dataclass
class ApiResults:
game_list: Optional[List] = None
dlcs: Optional[Dict] = None
bit32_games: Optional[List] = None
mac_games: Optional[List] = None
no_asset_games: Optional[List] = None
saves: Optional[List] = None
def __bool__(self):
return (
self.game_list is not None
and self.dlcs is not None
and self.bit32_games is not None
and self.mac_games is not None
and self.no_asset_games is not None
and self.saves is not None
)

29
rare/models/signals.py Normal file
View file

@ -0,0 +1,29 @@
from PyQt5.QtCore import QObject, pyqtSignal
from rare.utils.models import InstallOptionsModel
class GlobalSignals(QObject):
exit_app = pyqtSignal(int) # exit code
send_notification = pyqtSignal(str) # app_title
set_main_tab_index = pyqtSignal(int) # tab index
update_download_tab_text = pyqtSignal()
dl_progress = pyqtSignal(int) # 0-100
# set visibility of installing widget in games tab
installation_started = pyqtSignal(str) # app_name
add_download = pyqtSignal(str)
install_game = pyqtSignal(InstallOptionsModel)
installation_finished = pyqtSignal(bool, str)
overlay_installation_finished = pyqtSignal()
update_gamelist = pyqtSignal(list)
game_uninstalled = pyqtSignal(str) # appname
set_discord_rpc = pyqtSignal(str) # app_name of running game
rpc_settings_updated = pyqtSignal()
wine_prefix_updated = pyqtSignal()

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

View file

@ -1,9 +1,17 @@
"""
Shared controller resources module
Each of the objects in this module should be instantiated ONCE
and only ONCE!
"""
from argparse import Namespace
from typing import Optional
from legendary.core import LegendaryCore
from rare.utils.models import ApiResults, GlobalSignals
from rare.models.apiresults import ApiResults
from rare.models.signals import GlobalSignals
_legendary_core_singleton: Optional[LegendaryCore] = None
_global_signals_singleton: Optional[GlobalSignals] = None

View file

@ -0,0 +1,367 @@
from __future__ import annotations
import hashlib
import json
import pickle
import zlib
from logging import getLogger
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Tuple, Dict, Union, Type, List, Callable, Optional
import requests
from PyQt5.QtCore import (
Qt,
pyqtSignal,
QObject,
QSize,
QThreadPool,
QRunnable,
)
from PyQt5.QtGui import (
QPixmap,
QImage,
QPainter,
)
from PyQt5.QtWidgets import QApplication
from legendary.models.game import Game
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton
from rare.utils.paths import image_dir, resources_path
if TYPE_CHECKING:
pass
logger = getLogger("ImageManager")
class ImageSize:
class Preset:
__img_factor = 67
__size: QSize
__divisor: float = 1.0
__pixel_ratio: float = 1.0
# lk: for prettier images set this to true
__smooth_transform: bool = False
def __init__(self, divisor: float, pixel_ratio: float):
self.__pixel_ratio = pixel_ratio
self.__divisor = divisor
self.__size = QSize(self.__img_factor * 3, self.__img_factor * 4) * pixel_ratio / divisor
if divisor > 2:
self.__smooth_transform = False
@property
def size(self) -> QSize:
return self.__size
@property
def divisor(self) -> float:
return self.__divisor
@property
def smooth(self) -> bool:
return self.__smooth_transform
@property
def pixel_ratio(self) -> float:
return self.__pixel_ratio
Image = Preset(1, 2)
"""! @brief Size and pixel ratio of the image on disk"""
Display = Preset(1, 1)
"""! @brief Size and pixel ratio for displaying"""
Normal = Display
"""! @brief Same as Display"""
Small = Preset(3, 1)
"""! @brief Small image size for displaying"""
Smaller = Preset(4, 1)
"""! @brief Smaller image size for displaying"""
Icon = Preset(5, 1)
"""! @brief Smaller image size for UI icons"""
class ImageManager(QObject):
class Worker(QRunnable):
class Signals(QObject):
# str: app_name
completed = pyqtSignal(str)
def __init__(self, func: Callable, updates: List, json_data: Dict, game: Game):
super(ImageManager.Worker, self).__init__()
self.signals = ImageManager.Worker.Signals()
self.setAutoDelete(True)
self.func = func
self.updates = updates
self.json_data = json_data
self.game = game
def run(self):
self.func(self.updates, self.json_data, self.game)
logger.debug(f" Emitting singal for game {self.game.app_name} - {self.game.app_title}")
self.signals.completed.emit(self.game.app_name)
# lk: the ordering in __img_types matters for the order of fallbacks
__img_types: List = ["DieselGameBoxTall", "Thumbnail", "DieselGameBoxLogo"]
__dl_retries = 1
__worker_app_names: List[str] = list()
def __init__(self):
super(QObject, self).__init__()
self.core = LegendaryCoreSingleton()
self.signals = GlobalSignalsSingleton()
self.image_dir = Path(image_dir)
if not self.image_dir.is_dir():
self.image_dir.mkdir()
logger.info(f"Created image directory at {self.image_dir}")
self.device = ImageSize.Preset(1, QApplication.instance().devicePixelRatio())
self.threadpool = QThreadPool()
self.threadpool.setMaxThreadCount(8)
def __img_dir(self, app_name: str) -> Path:
return self.image_dir.joinpath(app_name)
def __img_json(self, app_name: str) -> Path:
return self.__img_dir(app_name).joinpath("image.json")
def __img_cache(self, app_name: str) -> Path:
return self.__img_dir(app_name).joinpath("image.cache")
def __img_color(self, app_name: str) -> Path:
return self.__img_dir(app_name).joinpath("installed.png")
def __img_gray(self, app_name: str) -> Path:
return self.__img_dir(app_name).joinpath("uninstalled.png")
def __prepare_download(self, game: Game, force: bool = False) -> Tuple[List, Dict]:
if force and self.__img_dir(game.app_name).exists():
self.__img_color(game.app_name).unlink(missing_ok=True)
self.__img_color(game.app_name).unlink(missing_ok=True)
if not self.__img_dir(game.app_name).is_dir():
self.__img_dir(game.app_name).mkdir()
# Load image checksums
if not self.__img_json(game.app_name).is_file():
json_data: Dict = dict(zip(self.__img_types, [None] * len(self.__img_types)))
else:
json_data = json.load(open(self.__img_json(game.app_name), "r"))
# lk: fast path for games without images, convert Rare's logo
if not game.metadata["keyImages"]:
if not self.__img_color(game.app_name).is_file() or not self.__img_gray(game.app_name).is_file():
cache_data: Dict = dict(zip(self.__img_types, [None] * len(self.__img_types)))
cache_data["DieselGameBoxTall"] = open(
resources_path.joinpath("images", "cover.png"), "rb"
).read()
# cache_data["DieselGameBoxLogo"] = open(
# resources_path.joinpath("images", "Rare_nonsquared.png"), "rb").read()
self.__convert(game, cache_data)
json_data["cache"] = None
json_data["scale"] = ImageSize.Image.pixel_ratio
json_data["size"] = ImageSize.Image.size.__str__()
json.dump(json_data, open(self.__img_json(game.app_name), "w"))
# lk: Find updates or initialize if images are missing.
# lk: `updates` will be empty for games without images
# lk: so everything below it is skipped
if not self.__img_color(game.app_name).is_file() or not self.__img_gray(game.app_name).is_file():
updates = [image for image in game.metadata["keyImages"] if image["type"] in self.__img_types]
else:
updates = list()
for image in game.metadata["keyImages"]:
if image["type"] in self.__img_types:
if json_data[image["type"]] != image["md5"]:
updates.append(image)
return updates, json_data
def __download(self, updates, json_data, game) -> bool:
# Decompress existing image.cache
if not self.__img_cache(game.app_name).is_file():
cache_data = dict(zip(self.__img_types, [None] * len(self.__img_types)))
else:
cache_data = self.__decompress(game)
# lk: filter updates again against the cache now that it is available
updates = [
image
for image in updates
if cache_data[image["type"]] is None or json_data[image["type"]] != image["md5"]
]
for image in updates:
logger.info(f"Downloading {image['type']} for {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).content
self.__convert(game, cache_data)
# lk: don't keep the cache if there is no logo (kept for me)
# if cache_data["DieselGameBoxLogo"] is not None:
# self.__compress(game, cache_data)
self.__compress(game, cache_data)
# hash image cache
try:
with open(self.__img_cache(game.app_name), "rb") as archive:
archive_hash = hashlib.md5(archive.read()).hexdigest()
except FileNotFoundError:
archive_hash = None
json_data["cache"] = archive_hash
json_data["scale"] = ImageSize.Image.pixel_ratio
json_data["size"] = {"w": ImageSize.Image.size.width(), "h": ImageSize.Image.size.height()}
# write image.json
with open(self.__img_json(game.app_name), "w") as file:
json.dump(json_data, file)
return bool(updates)
def __convert(self, game, images, force=False) -> None:
for image in [self.__img_color(game.app_name), self.__img_gray(game.app_name)]:
if force and image.exists():
image.unlink(missing_ok=True)
cover_data = None
for image_type in self.__img_types:
if images[image_type] is not None:
cover_data = images[image_type]
break
cover = QImage()
cover.loadFromData(cover_data)
cover.convertToFormat(QImage.Format_ARGB32_Premultiplied)
# lk: Images are not always 4/3, crop them to size
factor = min(cover.width() // 3, cover.height() // 4)
rem_w = (cover.width() - factor * 3) // 2
rem_h = (cover.height() - factor * 4) // 2
cover = cover.copy(rem_w, rem_h, factor * 3, factor * 4)
if images["DieselGameBoxLogo"] is not None:
logo = QImage()
logo.loadFromData(images["DieselGameBoxLogo"])
logo.convertToFormat(QImage.Format_ARGB32_Premultiplied)
if logo.width() > cover.width():
logo = logo.scaled(cover.width(), cover.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
painter = QPainter(cover)
painter.drawImage((cover.width() - logo.width()) // 2, cover.height() - logo.height(), logo)
painter.end()
cover = cover.scaled(ImageSize.Image.size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
# this is not required if we ever want to re-apply the alpha channel
# cover = cover.convertToFormat(QImage.Format_Indexed8)
# add the alpha channel back to the cover
cover = cover.convertToFormat(QImage.Format_ARGB32_Premultiplied)
cover.save(
str(self.__img_color(game.app_name)),
format="PNG",
)
# quick way to convert to grayscale
cover = cover.convertToFormat(QImage.Format_Grayscale8)
# add the alpha channel back to the grayscale cover
cover = cover.convertToFormat(QImage.Format_ARGB32_Premultiplied)
cover.save(
str(self.__img_gray(game.app_name)),
format="PNG",
)
def __compress(self, game: Game, data: Dict) -> None:
archive = open(self.__img_cache(game.app_name), "wb")
cdata = zlib.compress(pickle.dumps(data), level=-1)
archive.write(cdata)
archive.close()
def __decompress(self, game: Game) -> Dict:
archive = open(self.__img_cache(game.app_name), "rb")
data = zlib.decompress(archive.read())
archive.close()
data = pickle.loads(data)
return data
def download_image(
self, game: Game, load_callback: Callable[[], None], priority: int, force: bool = False
) -> None:
updates, json_data = self.__prepare_download(game, force)
if not updates:
load_callback()
return
if updates and game.app_name not in self.__worker_app_names:
image_worker = ImageManager.Worker(self.__download, updates, json_data, game)
self.__worker_app_names.append(game.app_name)
image_worker.signals.completed.connect(load_callback)
image_worker.signals.completed.connect(lambda app_name: self.__worker_app_names.remove(app_name))
self.threadpool.start(image_worker, priority)
def download_image_blocking(self, game: Game, force: bool = False) -> None:
updates, json_data = self.__prepare_download(game, force)
if not updates:
return
if updates:
self.__download(updates, json_data, game)
def __get_cover(
self, container: Union[Type[QPixmap], Type[QImage]], app_name: str, color: bool = True
) -> Union[QPixmap, QImage]:
ret = container()
if not app_name:
raise RuntimeError("app_name is an empty string")
if color:
if self.__img_color(app_name).is_file():
ret.load(str(self.__img_color(app_name)))
else:
if self.__img_gray(app_name).is_file():
ret.load(str(self.__img_gray(app_name)))
if not ret.isNull():
ret.setDevicePixelRatio(ImageSize.Image.pixel_ratio)
# lk: Scaling happens at painting. It might be inefficient so leave this here as an alternative
# lk: If this is uncommented, the transformation in ImageWidget should be adjusted also
ret = ret.scaled(self.device.size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
ret.setDevicePixelRatio(self.device.pixel_ratio)
return ret
def get_pixmap(self, app_name: str, color: bool = True) -> QPixmap:
"""
Use when the image is to be presented directly on the screen.
@param app_name: The RareGame object for this game
@param color: True to load the colored pixmap, False to load the grayscale
@return: QPixmap
"""
pixmap: QPixmap = self.__get_cover(QPixmap, app_name, color)
return pixmap
def get_image(self, app_name: str, color: bool = True) -> QImage:
"""
Use when the image has to be manipulated before being rendered.
@param app_name: The RareGame object for this game
@param color: True to load the colored image, False to load the grayscale
@return: QImage
"""
image: QImage = self.__get_cover(QImage, app_name, color)
return image
_image_manager_singleton: Optional[ImageManager] = None
def ImageManagerSingleton(init: bool = False) -> ImageManager:
global _image_manager_singleton
if _image_manager_singleton is None and not init:
raise RuntimeError("Uninitialized use of ImageManagerSingleton")
if _image_manager_singleton is None:
_image_manager_singleton = ImageManager()
return _image_manager_singleton

View file

@ -4,8 +4,6 @@ from dataclasses import field, dataclass
from multiprocessing import Queue
from typing import Union, List, Optional
from PyQt5.QtCore import QObject, pyqtSignal
from legendary.core import LegendaryCore
from legendary.downloader.mp.manager import DLManager
from legendary.models.downloading import AnalysisResult, ConditionCheckResult
@ -103,49 +101,3 @@ class PathSpec:
return prefixes[0]
else:
return prefixes[:results]
@dataclass
class ApiResults:
game_list: Optional[list] = None
dlcs: Optional[dict] = None
bit32_games: Optional[list] = None
mac_games: Optional[list] = None
no_asset_games: Optional[list] = None
saves: Optional[list] = None
def __bool__(self):
return (
self.game_list is not None
and self.dlcs is not None
and self.bit32_games is not None
and self.mac_games is not None
and self.no_asset_games is not None
and self.saves is not None
)
class GlobalSignals(QObject):
exit_app = pyqtSignal(int) # exit code
send_notification = pyqtSignal(str) # app_title
set_main_tab_index = pyqtSignal(int) # tab index
update_download_tab_text = pyqtSignal()
dl_progress = pyqtSignal(int) # 0-100
# set visibility of installing widget in games tab
installation_started = pyqtSignal(str) # app_name
add_download = pyqtSignal(str)
install_game = pyqtSignal(InstallOptionsModel)
installation_finished = pyqtSignal(bool, str)
overlay_installation_finished = pyqtSignal()
update_gamelist = pyqtSignal(list)
game_uninstalled = pyqtSignal(str) # appname
set_discord_rpc = pyqtSignal(str) # app_name of running game
rpc_settings_updated = pyqtSignal()
wine_prefix_updated = pyqtSignal()

View file

@ -1,13 +1,11 @@
import json
import math
import os
import platform
import shlex
import shutil
import subprocess
import sys
from logging import getLogger
from typing import Tuple, List
from typing import List, Tuple
import qtawesome
import requests
@ -18,11 +16,10 @@ from PyQt5.QtCore import (
QRunnable,
QSettings,
QStandardPaths,
Qt,
QFile,
QDir,
)
from PyQt5.QtGui import QPalette, QColor, QPixmap, QImage
from PyQt5.QtGui import QPalette, QColor, QImage
from PyQt5.QtWidgets import qApp, QStyleFactory
from legendary.models.game import Game
from requests.exceptions import HTTPError
@ -45,85 +42,6 @@ from legendary.core import LegendaryCore
logger = getLogger("Utils")
settings = QSettings("Rare", "Rare")
def download_images(progress: pyqtSignal, results: pyqtSignal, core: LegendaryCore):
if not os.path.isdir(image_dir):
os.makedirs(image_dir)
logger.info("Create Image dir")
# Download Images
games, dlcs = core.get_game_and_dlc_list(True, skip_ue=False)
results.emit((games, dlcs), "gamelist")
dlc_list = []
for i in dlcs.values():
dlc_list.append(i[0])
no_assets = core.get_non_asset_library_items()[0]
results.emit(no_assets, "no_assets")
game_list = games + dlc_list + no_assets
for i, game in enumerate(game_list):
if game.app_title == "Unreal Engine":
game.app_title += f" {game.app_name.split('_')[-1]}"
core.lgd.set_game_meta(game.app_name, game)
try:
download_image(game)
except json.decoder.JSONDecodeError:
shutil.rmtree(f"{image_dir}/{game.app_name}")
download_image(game)
progress.emit(i * 100 // len(game_list))
def download_image(game, force=False):
if force and os.path.exists(f"{image_dir}/{game.app_name}"):
shutil.rmtree(f"{image_dir}/{game.app_name}")
if not os.path.isdir(f"{image_dir}/{game.app_name}"):
os.mkdir(f"{image_dir}/{game.app_name}")
# to get picture updates
if not os.path.isfile(f"{image_dir}/{game.app_name}/image.json"):
json_data = {
"DieselGameBoxTall": None,
"DieselGameBoxLogo": None,
"Thumbnail": None,
}
else:
json_data = json.load(open(f"{image_dir}/{game.app_name}/image.json", "r"))
# Download
for image in game.metadata["keyImages"]:
if (
image["type"] == "DieselGameBoxTall"
or image["type"] == "DieselGameBoxLogo"
or image["type"] == "Thumbnail"
):
if image["type"] not in json_data.keys():
json_data[image["type"]] = None
if json_data[image["type"]] != image["md5"] or not os.path.isfile(
f"{image_dir}/{game.app_name}/{image['type']}.png"
):
# Download
json_data[image["type"]] = image["md5"]
# os.remove(f"{image_dir}/{game.app_name}/{image['type']}.png")
json.dump(
json_data, open(f"{image_dir}/{game.app_name}/image.json", "w")
)
logger.info(f"Download Image for Game: {game.app_title}")
url = image["url"]
resp = requests.get(url)
img = QImage()
img.loadFromData(resp.content)
img = img.scaled(
200,
200 * 4 // 3,
Qt.KeepAspectRatio,
transformMode=Qt.SmoothTransformation,
)
img.save(
os.path.join(image_dir, game.app_name, image["type"] + ".png"),
format="PNG",
)
color_role_map = {
0: "WindowText",
1: "Button",
@ -272,16 +190,7 @@ def create_desktop_link(app_name=None, core: LegendaryCore = None, type_of_link=
if not for_rare:
igame = core.get_installed_game(app_name)
if os.path.exists(p := os.path.join(image_dir, igame.app_name, "Thumbnail.png")):
icon = p
elif os.path.exists(
p := os.path.join(image_dir, igame.app_name, "DieselGameBoxLogo.png")
):
icon = p
else:
icon = os.path.join(
os.path.join(image_dir, igame.app_name, "DieselGameBoxTall.png")
)
icon = os.path.join(os.path.join(image_dir, igame.app_name, "installed.png"))
icon = icon.replace(".png", "")
if platform.system() == "Linux":
@ -397,22 +306,6 @@ def create_desktop_link(app_name=None, core: LegendaryCore = None, type_of_link=
return False
def get_pixmap(app_name: str) -> QPixmap:
for img in ["FinalArt.png", "DieselGameBoxTall.png", "DieselGameBoxLogo.png"]:
if os.path.exists(image := os.path.join(image_dir, app_name, img)):
pixmap = QPixmap(image)
break
else:
pixmap = QPixmap()
return pixmap
def get_uninstalled_pixmap(app_name: str) -> QPixmap:
pm = get_pixmap(app_name)
grey_image = pm.toImage().convertToFormat(QImage.Format_Grayscale8)
return QPixmap.fromImage(grey_image)
def optimal_text_background(image: list) -> Tuple[int, int, int]:
"""
Finds an optimal background color for text on the image by calculating the

3
rare/widgets/__init__.py Normal file
View file

@ -0,0 +1,3 @@
"""
Reusable widgets module for Rare
"""

View file

@ -0,0 +1,25 @@
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFontMetrics, QPaintEvent
from PyQt5.QtWidgets import QLabel
class ElideLabel(QLabel):
__text: str = ""
def __init__(self, text="", parent=None, flags=Qt.WindowFlags()):
super(ElideLabel, self).__init__(parent=parent, flags=flags)
if text:
self.setText(text)
def setText(self, a0: str) -> None:
self.__text = a0
self.__setElideText(a0)
def __setElideText(self, a0: str):
metrics = QFontMetrics(self.font())
elided_text = metrics.elidedText(a0, Qt.ElideRight, self.width())
super(ElideLabel, self).setText(elided_text)
def paintEvent(self, a0: QPaintEvent) -> None:
self.__setElideText(self.__text)
super(ElideLabel, self).paintEvent(a0)