1
0
Fork 0
mirror of synced 2024-06-02 10:44:40 +12:00
Rare/rare/components/tabs/games/__init__.py
loathingKernel 3a28f2f0a2 Implement image manager
Signed-off-by: loathingKernel <142770+loathingKernel@users.noreply.github.com>
2022-06-19 17:12:59 +03:00

523 lines
22 KiB
Python

from logging import getLogger
from typing import Tuple, Dict, Union, List
from PyQt5.QtCore import QSettings, QObjectCleanupHandler
from PyQt5.QtWidgets import QStackedWidget, QVBoxLayout, QWidget
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 .cloud_save_utils import CloudSaveUtils
from .cloud_save_utils import CloudSaveUtils
from .game_info import GameInfoTabs
from .game_info.uninstalled_info import UninstalledInfoTabs
from .game_utils import GameUtils
from .game_widgets.base_installed_widget import BaseInstalledWidget
from .game_widgets.base_uninstalled_widget import BaseUninstalledWidget
from .game_widgets.installed_icon_widget import InstalledIconWidget
from .game_widgets.installed_list_widget import InstalledListWidget
from .game_widgets.installing_game_widget import InstallingGameWidget
from .game_widgets.uninstalled_icon_widget import IconWidgetUninstalled
from .game_widgets.uninstalled_list_widget import ListWidgetUninstalled
from .head_bar import GameListHeadBar
from .import_sync import ImportSyncTabs
logger = getLogger("GamesTab")
class GamesTab(QStackedWidget, Ui_GamesTab):
widgets: Dict[str, Tuple[
Union[InstalledIconWidget, IconWidgetUninstalled], Union[InstalledListWidget, ListWidgetUninstalled]]] = dict()
running_games = list()
updates = set()
active_filter = 0
def __init__(self, parent=None):
super(GamesTab, self).__init__(parent=parent)
self.setupUi(self)
self.core = LegendaryCoreSingleton()
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
self.dlcs = self.api_results.dlcs
self.bit32 = self.api_results.bit32_games
self.mac_games = self.api_results.mac_games
self.no_assets = self.api_results.no_asset_games
self.game_utils = GameUtils(parent=self)
self.head_bar = GameListHeadBar(self)
self.head_bar.import_clicked.connect(self.show_import)
self.head_bar.egl_sync_clicked.connect(self.show_egl_sync)
self.games.layout().insertWidget(0, self.head_bar)
self.game_info_tabs = GameInfoTabs(self.dlcs, self.game_utils, self)
self.game_info_tabs.back_clicked.connect(lambda: self.setCurrentIndex(0))
self.addWidget(self.game_info_tabs)
self.game_info_tabs.info.verification_finished.connect(
self.verification_finished
)
self.game_info_tabs.info.uninstalled.connect(lambda x: self.setCurrentIndex(0))
self.import_sync_tabs = ImportSyncTabs(self)
self.import_sync_tabs.back_clicked.connect(lambda: self.setCurrentIndex(0))
self.addWidget(self.import_sync_tabs)
for i in self.game_list:
if i.app_name.startswith("UE_4"):
pixmap = self.image_manager.get_pixmap(i.app_name)
if pixmap.isNull():
continue
self.ue_name = i.app_name
logger.debug(f"Found Unreal AppName {self.ue_name}")
break
else:
logger.warning("No Unreal engine in library found")
self.ue_name = ""
self.uninstalled_info_tabs = UninstalledInfoTabs(self.ue_name, self)
self.uninstalled_info_tabs.back_clicked.connect(lambda: self.setCurrentIndex(0))
self.addWidget(self.uninstalled_info_tabs)
# navigation
self.head_bar.import_game.clicked.connect(lambda: self.setCurrentIndex(2))
self.no_asset_names = []
if not self.args.offline:
for game in self.no_assets:
self.no_asset_names.append(game.app_name)
else:
self.no_assets = []
self.installed = self.core.get_installed_list()
self.setup_game_list()
if not self.settings.value("icon_view", True, bool):
self.scroll_widget.layout().insertWidget(1, self.list_view)
self.head_bar.view.list()
else:
self.scroll_widget.layout().insertWidget(1, self.icon_view)
self.head_bar.search_bar.textChanged.connect(lambda x: self.filter_games("", x))
self.head_bar.filterChanged.connect(self.filter_games)
self.head_bar.refresh_list.clicked.connect(self.update_list)
self.head_bar.view.toggled.connect(self.toggle_view)
f = self.settings.value("filter", 0, int)
if f >= len(self.head_bar.available_filters):
f = 0
self.active_filter = self.head_bar.available_filters[f]
self.filter_games(self.active_filter)
# signals
self.signals.dl_progress.connect(self.installing_widget.set_status)
self.signals.installation_started.connect(self.installation_started)
self.signals.update_gamelist.connect(self.update_list)
self.signals.installation_finished.connect(
self.installation_finished
)
self.signals.game_uninstalled.connect(lambda name: self.update_list([name]))
self.game_utils.update_list.connect(self.update_list)
self.game_list_scroll_area.horizontalScrollBar().setDisabled(True)
def installation_finished(self, app_name: str):
self.installing_widget.setVisible(False)
self.installing_widget.set_game("")
self.filter_games("")
def installation_started(self, app_name: str):
game = self.core.get_game(app_name, False)
if not game:
return
if game.is_dlc:
return
self.installing_widget.set_game(app_name)
self.installing_widget.setVisible(True)
i_widget, l_widget = self.widgets.get(app_name, (None, None))
if not i_widget or not l_widget:
return
i_widget.setVisible(False)
l_widget.setVisible(False)
def verification_finished(self, igame: InstalledGame):
# only if igame needs verification
i_widget, l_widget = self.widgets[igame.app_name]
i_widget.igame = igame
l_widget.igame = igame
i_widget.leaveEvent(None)
l_widget.update_text()
def show_import(self):
self.setCurrentIndex(2)
self.import_sync_tabs.show_import()
def show_egl_sync(self, idx):
self.setCurrentIndex(2)
self.import_sync_tabs.show_egl_sync()
def show_game_info(self, app_name):
self.game_info_tabs.update_game(app_name)
self.setCurrentIndex(1)
def show_uninstalled_info(self, game):
self.uninstalled_info_tabs.update_game(game)
self.setCurrentIndex(3)
def setup_game_list(self):
self.icon_view = QWidget()
self.icon_view.setLayout(FlowLayout())
self.list_view = QWidget()
self.list_view.setLayout(QVBoxLayout())
self.update_count_games_label()
# add installing game widget for icon view: List view not supported
self.installing_widget = InstallingGameWidget()
self.icon_view.layout().addWidget(self.installing_widget)
self.installing_widget.setVisible(False)
# add installed games
for igame in sorted(self.core.get_installed_list(), key=lambda x: x.title):
icon_widget, list_widget = self.add_installed_widget(igame.app_name)
self.icon_view.layout().addWidget(icon_widget)
self.list_view.layout().addWidget(list_widget)
for game in self.no_assets:
icon_widget, list_widget = self.add_installed_widget(game.app_name)
if not icon_widget or not list_widget:
logger.warning(f"Ignoring {game.app_name} in game list")
continue
self.icon_view.layout().addWidget(icon_widget)
self.list_view.layout().addWidget(list_widget)
self.uninstalled_games = []
for game in sorted(self.game_list, key=lambda x: x.app_title):
if not self.core.is_installed(game.app_name):
self.uninstalled_games.append(game)
icon_widget, list_widget = self.add_uninstalled_widget(game)
self.icon_view.layout().addWidget(icon_widget)
self.list_view.layout().addWidget(list_widget)
def update_count_games_label(self):
self.count_games_label.setText(
self.tr("Installed Games: {} Available Games: {}").format(
len(self.core.get_installed_list()), len(self.game_list)
)
)
def add_installed_widget(self, 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":
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 = 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)
except Exception as e:
logger.error(f"{app_name} is broken. Don't add it to game list: {e}")
return None, None
self.widgets[app_name] = (icon_widget, list_widget)
icon_widget.show_info.connect(self.show_game_info)
list_widget.show_info.connect(self.show_game_info)
if icon_widget.update_available:
self.updates.add(app_name)
return icon_widget, list_widget
def add_uninstalled_widget(self, game):
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...")
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 = self.image_manager.get_pixmap(self.ue_name, color=False)
icon_widget = IconWidgetUninstalled(game, self.core, pixmap)
list_widget = ListWidgetUninstalled(self.core, game, pixmap)
except Exception as e:
logger.error(f"{game.app_name} is broken. Don't add it to game list: {e}")
return None, None
icon_widget.show_uninstalled_info.connect(self.show_uninstalled_info)
list_widget.show_uninstalled_info.connect(self.show_uninstalled_info)
self.widgets[game.app_name] = (icon_widget, list_widget)
return icon_widget, list_widget
def filter_games(self, filter_name="all", search_text: str = ""):
if not search_text and (t := self.head_bar.search_bar.text()):
search_text = t
if filter_name:
self.active_filter = filter_name
if not filter_name and (t := self.active_filter):
filter_name = t
def get_visibility(widget):
app_name = widget.game.app_name
if not isinstance(widget,
InstallingGameWidget) and self.installing_widget.game and self.installing_widget.game.app_name == app_name:
visible = False
elif filter_name == "installed":
visible = self.core.is_installed(app_name)
elif filter_name == "offline":
if self.core.is_installed(app_name) and not isinstance(widget, InstallingGameWidget):
visible = widget.igame.can_run_offline
else:
visible = False
elif filter_name == "32bit" and self.bit32:
visible = app_name in self.bit32
elif filter_name == "mac" and self.mac_games:
visible = app_name in self.mac_games
elif filter_name == "installable":
visible = app_name not in self.no_asset_names
elif filter_name == "include_ue":
visible = True
elif filter_name == "all":
# All visible except ue
try:
visible = self.core.get_asset(app_name, update=False).namespace != "ue"
except ValueError:
visible = True
else:
visible = True
if (
search_text.lower() not in app_name.lower()
and search_text.lower() not in widget.game.app_title.lower()
):
visible = False
return visible
for t in self.widgets.values():
visible = get_visibility(t[0])
for w in t:
w.setVisible(visible)
if self.installing_widget.game:
self.installing_widget.setVisible(get_visibility(self.installing_widget))
def update_list(self, app_names: list = None):
logger.debug(f"Updating list for {app_names}")
if app_names:
update_list = False
for app_name in app_names:
if widgets := self.widgets.get(app_name):
# from update
if self.core.is_installed(widgets[0].game.app_name) and isinstance(
widgets[0], BaseInstalledWidget
):
logger.debug(f"Update Gamelist: Updated: {app_name}")
igame = self.core.get_installed_game(app_name)
for w in widgets:
w.igame = igame
w.update_available = (
self.core.get_asset(
w.game.app_name, w.igame.platform, False
).build_version
!= igame.version
)
widgets[0].leaveEvent(None)
# new installed
elif self.core.is_installed(app_name) and isinstance(
widgets[0], BaseUninstalledWidget
):
logger.debug(f"Update Gamelist: New installed {app_name}")
self.widgets[app_name][0].deleteLater()
self.widgets[app_name][1].deleteLater()
self.widgets.pop(app_name)
self.add_installed_widget(app_name)
update_list = True
# uninstalled
elif not self.core.is_installed(
widgets[0].game.app_name
) and isinstance(widgets[0], BaseInstalledWidget):
logger.debug(f"Update list: Uninstalled: {app_name}")
self.widgets[app_name][0].deleteLater()
self.widgets[app_name][1].deleteLater()
self.widgets.pop(app_name)
game = self.core.get_game(app_name, False)
try:
self.add_uninstalled_widget(game)
except Exception:
pass
update_list = True
# do not update, if only update finished
if update_list:
self._update_games()
else:
installed_names = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
uninstalled_names = []
games = self.core.get_game_list(True)
for game in sorted(games, key=lambda x: x.app_title):
if not game.app_name in installed_names:
uninstalled_names.append(game.app_name)
new_installed_games = list(
set(installed_names) - set([i.app_name for i in self.installed])
)
new_uninstalled_games = list(
set(uninstalled_names)
- set([i.app_name for i in self.uninstalled_games])
)
if (not new_uninstalled_games) and (not new_installed_games):
return
if new_installed_games:
for name in new_installed_games:
self.widgets[name][0].deleteLater()
self.widgets[name][1].deleteLater()
self.widgets.pop(name)
self.add_installed_widget(name)
for name in new_uninstalled_games:
self.icon_view.layout().removeWidget(self.widgets[name][0])
self.list_view.layout().removeWidget(self.widgets[name][1])
self.widgets[name][0].deleteLater()
self.widgets[name][1].deleteLater()
self.widgets.pop(name)
game = self.core.get_game(name, False)
try:
self.add_uninstalled_widget(game)
except Exception:
pass
for igame in sorted(
self.core.get_installed_list(), key=lambda x: x.title
):
i_widget, list_widget = self.widgets[igame.app_name]
self.icon_view.layout().addWidget(i_widget)
self.list_view.layout().addWidget(list_widget)
self.installed = self.core.get_installed_list()
for game in self.no_assets:
i_widget, list_widget = self.widgets[game.app_name]
self.icon_view.layout().addWidget(i_widget)
self.list_view.layout().addWidget(list_widget)
# get Uninstalled games
self.uninstalled_games = []
games, self.dlcs = self.core.get_game_and_dlc_list()
for game in sorted(games, key=lambda x: x.app_title):
if (
not self.core.is_installed(game.app_name)
and game.app_name not in self.no_asset_names
):
i_widget, list_widget = self.widgets[game.app_name]
self.icon_view.layout().addWidget(i_widget)
self.list_view.layout().addWidget(list_widget)
self.uninstalled_games.append(game)
self.update_count_games_label()
def _update_games(self):
icon_layout = FlowLayout()
list_layout = QVBoxLayout()
icon_layout.addWidget(self.installing_widget)
for igame in sorted(self.core.get_installed_list(), key=lambda x: x.title) + self.no_assets:
i_widget, l_widget = self.widgets.get(igame.app_name, (None, None))
if i_widget and l_widget:
icon_layout.addWidget(i_widget)
list_layout.addWidget(l_widget)
else:
logger.warning("Found installed game, without widget. Generating widget... ")
try:
i_widget, l_widget = self.add_installed_widget(igame.app_name)
icon_layout.addWidget(i_widget)
list_layout.addWidget(l_widget)
except Exception as e:
logger.error(str(e))
continue
# get Uninstalled games
self.game_list, self.dlcs = self.core.get_game_and_dlc_list(update_assets=False)
# add uninstalled games
for game in sorted(self.game_list, key=lambda x: x.app_title):
if self.core.is_installed(game.app_name) or game.app_name in self.no_asset_names:
continue
i_widget, list_widget = self.widgets.get(game.app_name, (None, None))
if i_widget and list_widget:
icon_layout.addWidget(i_widget)
list_layout.addWidget(list_widget)
else:
logger.warning("Found installed game, without widget. Generating widget... ")
try:
i_widget, l_widget = self.add_uninstalled_widget(game)
if not i_widget or not l_widget:
logger.warning(f"Ignoring {game.app_name}")
continue
icon_layout.addWidget(i_widget)
list_layout.addWidget(l_widget)
except Exception as e:
logger.error(str(e))
continue
QObjectCleanupHandler().add(self.icon_view.layout())
QObjectCleanupHandler().add(self.list_view.layout())
self.icon_view.setLayout(icon_layout)
self.list_view.setLayout(list_layout)
self.icon_view.setParent(None)
self.list_view.setParent(None)
# insert widget in layout
self.scroll_widget.layout().insertWidget(
1, self.icon_view if self.head_bar.view.isChecked() else self.list_view
)
def toggle_view(self):
self.settings.setValue("icon_view", not self.head_bar.view.isChecked())
if not self.head_bar.view.isChecked():
self.scroll_widget.layout().replaceWidget(self.list_view, self.icon_view)
self.list_view.setParent(None)
else:
self.scroll_widget.layout().replaceWidget(self.icon_view, self.list_view)
self.icon_view.setParent(None)
self.game_list_scroll_area.verticalScrollBar().setValue(0)