1
0
Fork 0
mirror of synced 2024-10-01 01:37:51 +13:00
Rare/rare/components/tabs/games/game_widgets/game_widget.py
loathingKernel c063f5f5b9
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.
2023-11-29 12:50:02 +02:00

261 lines
10 KiB
Python

import platform
import random
from logging import getLogger
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
from rare.shared import (
LegendaryCoreSingleton,
GlobalSignalsSingleton,
ArgumentsSingleton,
ImageManagerSingleton,
)
from rare.utils.paths import desktop_links_supported, desktop_link_path, create_desktop_link
from .library_widget import LibraryWidget
logger = getLogger("GameWidget")
class GameWidget(LibraryWidget):
show_info = pyqtSignal(RareGame)
def __init__(self, rgame: RareGame, parent=None):
super(GameWidget, self).__init__(parent=parent)
self.core = LegendaryCoreSingleton()
self.signals = GlobalSignalsSingleton()
self.args = ArgumentsSingleton()
self.image_manager = ImageManagerSingleton()
self.rgame: RareGame = rgame
self.setContextMenuPolicy(Qt.ActionsContextMenu)
self.launch_action = QAction(self.tr("Launch"), self)
self.launch_action.triggered.connect(self._launch)
self.install_action = QAction(self.tr("Install"), self)
self.install_action.triggered.connect(self._install)
self.desktop_link_action = QAction(self)
self.desktop_link_action.triggered.connect(
lambda: self._create_link(self.rgame.folder_name, "desktop")
)
self.menu_link_action = QAction(self)
self.menu_link_action.triggered.connect(
lambda: self._create_link(self.rgame.folder_name, "start_menu")
)
self.reload_action = QAction(self.tr("Reload Image"), self)
self.reload_action.triggered.connect(self._on_reload_image)
self.uninstall_action = QAction(self.tr("Uninstall"), self)
self.uninstall_action.triggered.connect(self._uninstall)
self.update_actions()
# signals
self.rgame.signals.widget.update.connect(lambda: self.setPixmap(self.rgame.pixmap))
self.rgame.signals.widget.update.connect(self.update_buttons)
self.rgame.signals.widget.update.connect(self.update_state)
self.rgame.signals.game.installed.connect(self.update_actions)
self.rgame.signals.game.uninstalled.connect(self.update_actions)
self.rgame.signals.progress.start.connect(
lambda: self.showProgress(
self.image_manager.get_pixmap(self.rgame.app_name, True),
self.image_manager.get_pixmap(self.rgame.app_name, False)
)
)
self.rgame.signals.progress.update.connect(
lambda p: self.updateProgress(p)
)
self.rgame.signals.progress.finish.connect(
lambda e: self.hideProgress(e)
)
self.state_strings = {
RareGame.State.IDLE: "",
RareGame.State.RUNNING: self.tr("Running..."),
RareGame.State.DOWNLOADING: self.tr("Downloading..."),
RareGame.State.VERIFYING: self.tr("Verifying..."),
RareGame.State.MOVING: self.tr("Moving..."),
RareGame.State.UNINSTALLING: self.tr("Uninstalling..."),
RareGame.State.SYNCING: self.tr("Syncing saves..."),
"has_update": self.tr("Update available"),
"needs_verification": self.tr("Needs verification"),
"not_can_launch": self.tr("Can't launch"),
"save_not_up_to_date": self.tr("Save is not up-to-date")
}
self.hover_strings = {
"info": self.tr("Show information"),
"install": self.tr("Install game"),
"can_launch": self.tr("Launch game"),
"is_foreign": self.tr("Launch offline"),
"has_update": self.tr("Launch without version check"),
"is_origin": self.tr("Launch/Link"),
"not_can_launch": self.tr("Can't launch"),
}
# lk: abstract class for typing, the `self.ui` attribute should be used
# lk: by the Ui class in the children. It must contain at least the same
# 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:
if self.rgame.has_update:
self.ui.status_label.setText(self.state_strings["has_update"])
elif self.rgame.needs_verification:
self.ui.status_label.setText(self.state_strings["needs_verification"])
elif not self.rgame.can_launch and self.rgame.is_installed:
self.ui.status_label.setText(self.state_strings["not_can_launch"])
elif self.rgame.igame and (
self.rgame.game.supports_cloud_saves or self.rgame.game.supports_mac_cloud_saves
) and not self.rgame.is_save_up_to_date:
self.ui.status_label.setText(self.state_strings["save_not_up_to_date"])
else:
self.ui.status_label.setText(self.state_strings[self.rgame.state])
else:
self.ui.status_label.setText(self.state_strings[self.rgame.state])
self.ui.status_label.setVisible(bool(self.ui.status_label.text()))
@pyqtSlot()
def update_buttons(self):
self.ui.install_btn.setVisible(not self.rgame.is_installed)
self.ui.install_btn.setEnabled(not self.rgame.is_installed)
self.ui.launch_btn.setVisible(self.rgame.is_installed)
self.ui.launch_btn.setEnabled(self.rgame.can_launch)
@pyqtSlot()
def update_actions(self):
for action in self.actions():
self.removeAction(action)
if self.rgame.is_installed or self.rgame.is_origin:
self.addAction(self.launch_action)
else:
self.addAction(self.install_action)
if desktop_links_supported() and self.rgame.is_installed:
if desktop_link_path(self.rgame.folder_name, "desktop").exists():
self.desktop_link_action.setText(self.tr("Remove Desktop link"))
else:
self.desktop_link_action.setText(self.tr("Create Desktop link"))
self.addAction(self.desktop_link_action)
if desktop_link_path(self.rgame.folder_name, "start_menu").exists():
self.menu_link_action.setText(self.tr("Remove Start Menu link"))
else:
self.menu_link_action.setText(self.tr("Create Start Menu link"))
self.addAction(self.menu_link_action)
self.addAction(self.reload_action)
if self.rgame.is_installed and not self.rgame.is_origin:
self.addAction(self.uninstall_action)
def eventFilter(self, a0: QObject, a1: QEvent) -> bool:
if a0 is self.ui.launch_btn:
if a1.type() == QEvent.Enter:
if not self.rgame.can_launch:
self.ui.tooltip_label.setText(self.hover_strings["not_can_launch"])
elif self.rgame.is_origin:
self.ui.tooltip_label.setText(self.hover_strings["is_origin"])
elif self.rgame.has_update:
self.ui.tooltip_label.setText(self.hover_strings["has_update"])
elif self.rgame.is_foreign and self.rgame.can_run_offline:
self.ui.tooltip_label.setText(self.hover_strings["is_foreign"])
elif self.rgame.can_launch:
self.ui.tooltip_label.setText(self.hover_strings["can_launch"])
return True
if a1.type() == QEvent.Leave:
self.ui.tooltip_label.setText(self.hover_strings["info"])
# return True
if a0 is self.ui.install_btn:
if a1.type() == QEvent.Enter:
self.ui.tooltip_label.setText(self.hover_strings["install"])
return True
if a1.type() == QEvent.Leave:
self.ui.tooltip_label.setText(self.hover_strings["info"])
# return True
if a0 is self:
if a1.type() == QEvent.Enter:
self.ui.tooltip_label.setText(self.hover_strings["info"])
return super(GameWidget, self).eventFilter(a0, a1)
def mousePressEvent(self, e: QMouseEvent) -> None:
# left button
if e.button() == 1:
self.show_info.emit(self.rgame)
# right
elif e.button() == 2:
super(GameWidget, self).mousePressEvent(e)
@pyqtSlot()
def _on_reload_image(self) -> None:
self.rgame.refresh_pixmap()
@pyqtSlot()
@pyqtSlot(bool, bool)
def _launch(self, offline=False, skip_version_check=False):
if offline or (self.rgame.is_foreign and self.rgame.can_run_offline):
offline = True
if self.rgame.has_update:
skip_version_check = True
self.rgame.launch(
offline=offline, skip_update_check=skip_version_check
)
@pyqtSlot()
def _install(self):
self.show_info.emit(self.rgame)
@pyqtSlot()
def _uninstall(self):
self.show_info.emit(self.rgame)
def _create_link(self, name, link_type):
if not desktop_links_supported():
QMessageBox.warning(
self,
self.tr("Warning"),
self.tr("Creating shortcuts is currently unsupported on {}").format(platform.system()),
)
return
shortcut_path = desktop_link_path(name, link_type)
if not shortcut_path.exists():
try:
if not create_desktop_link(
app_name=self.rgame.app_name,
app_title=self.rgame.app_title,
link_name=self.rgame.folder_name,
link_type=link_type,
):
raise PermissionError
except PermissionError:
QMessageBox.warning(self, "Error", "Could not create shortcut.")
return
if link_type == "desktop":
self.desktop_link_action.setText(self.tr("Remove Desktop link"))
elif link_type == "start_menu":
self.menu_link_action.setText(self.tr("Remove Start Menu link"))
else:
if shortcut_path.exists():
shortcut_path.unlink(missing_ok=True)
if link_type == "desktop":
self.desktop_link_action.setText(self.tr("Create Desktop link"))
elif link_type == "start_menu":
self.menu_link_action.setText(self.tr("Create Start Menu link"))