From d3eabac818570c942a80d99f35180644d8715986 Mon Sep 17 00:00:00 2001 From: BuildTools Date: Mon, 8 Mar 2021 17:20:28 +0100 Subject: [PATCH] updates and toggle button --- Rare/Components/TabWidget.py | 1 + Rare/Components/Tabs/Downloads/DownloadTab.py | 38 +++++++- Rare/Components/Tabs/Games/GameList.py | 3 +- .../Tabs/Games/GameWidgetInstalled.py | 10 +- .../Tabs/Games/GameWidgetListUninstalled.py | 16 ++-- Rare/Components/Tabs/Games/GamesTab.py | 15 ++- Rare/utils/Models.py | 12 +++ Rare/utils/QtExtensions.py | 93 ++++++++++++++++++- 8 files changed, 164 insertions(+), 24 deletions(-) create mode 100644 Rare/utils/Models.py diff --git a/Rare/Components/TabWidget.py b/Rare/Components/TabWidget.py index 73ea4a08..97d28958 100644 --- a/Rare/Components/TabWidget.py +++ b/Rare/Components/TabWidget.py @@ -17,6 +17,7 @@ class TabWidget(QTabWidget): self.setTabBar(TabBar(disabled_tab)) self.settings = SettingsTab(core) self.game_list = GameTab(core) + self.game_list.default_widget.game_list.update_game.connect(lambda: self.setCurrentIndex(1)) updates = self.game_list.default_widget.game_list.updates self.addTab(self.game_list, self.tr("Games")) self.downloadTab = DownloadTab(core, updates) diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py index 2757d218..540154ac 100644 --- a/Rare/Components/Tabs/Downloads/DownloadTab.py +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -10,6 +10,7 @@ from legendary.models.game import Game from notifypy import Notify from Rare.Components.Dialogs.InstallDialog import InstallInfoDialog +from Rare.utils.Models import InstallOptions logger = getLogger("Download") @@ -37,6 +38,7 @@ class DownloadThread(QThread): else: self.status.emit("dl_finished") end_t = time.time() + game = self.core.get_game(self.igame.app_name) postinstall = self.core.install_game(self.igame) if postinstall: @@ -129,13 +131,34 @@ class DownloadTab(QWidget): self.setLayout(self.layout) - def install_game(self, options: {}): - game = self.core.get_game(options["app_name"]) + def install_game(self, options: InstallOptions): + game = self.core.get_game(options.app_name, update_meta=True) + if self.core.is_installed(options.app_name): + igame = self.core.get_installed_game(options.app_name) + if igame.needs_verification and not options.repair: + options.repair = True + + if not game: + QMessageBox.warning(self, "Error", "Could not find Game in your library") + return + + if game.is_dlc: + return + + if options.repair: + repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{options.app_name}.repair') + + if not self.core.is_installed(game.app_name): + return + if not os.path.exists(repair_file): + logger.info("Game has not been verified yet") + return + self.repair() dlm, analysis, igame = self.core.prepare_download( game=game, - base_path=options["options"]["path"], - max_workers=options["options"]["max_workers"]) + base_path=options.path, + max_workers=options.max_workers) if not analysis.dl_size: QMessageBox.information(self, "Warning", self.tr("Download size is 0. Game already exists")) return @@ -146,7 +169,7 @@ class DownloadTab(QWidget): self.installing_game_widget.setText("") self.installing_game.setText("Installing Game: " + game.app_title) res = self.core.check_installation_conditions(analysis=analysis, install=igame, game=game, - updating=self.core.is_installed(options["app_name"]), + updating=self.core.is_installed(options.app_name), ) if res.warnings: for w in sorted(res.warnings): @@ -179,6 +202,11 @@ class DownloadTab(QWidget): def update_game(self, app_name: str): print("Update ", app_name) + self.install_game(InstallOptions(app_name)) + + + def repair(self): + pass class UpdateWidget(QWidget): diff --git a/Rare/Components/Tabs/Games/GameList.py b/Rare/Components/Tabs/Games/GameList.py index a7439abe..914ae176 100644 --- a/Rare/Components/Tabs/Games/GameList.py +++ b/Rare/Components/Tabs/Games/GameList.py @@ -12,7 +12,7 @@ from Rare.utils.QtExtensions import FlowLayout class GameList(QScrollArea): install_game = pyqtSignal(dict) show_game_info = pyqtSignal(str) - + update_game = pyqtSignal() def __init__(self, core: LegendaryCore): super(GameList, self).__init__() self.core = core @@ -45,6 +45,7 @@ class GameList(QScrollArea): widget = GameWidget(game, self.core) if widget.update_available: self.updates.append(widget.game.app_name) + widget.update_game.connect(self.update_game.emit) self.layout.addWidget(widget) widget.update_list.connect(self.update_list) diff --git a/Rare/Components/Tabs/Games/GameWidgetInstalled.py b/Rare/Components/Tabs/Games/GameWidgetInstalled.py index 6d8454e7..2b0a511d 100644 --- a/Rare/Components/Tabs/Games/GameWidgetInstalled.py +++ b/Rare/Components/Tabs/Games/GameWidgetInstalled.py @@ -17,6 +17,7 @@ logger = getLogger("GameWidgetInstalled") class GameWidgetInstalled(QWidget): update_list = pyqtSignal() show_info = pyqtSignal(str) + update_game = pyqtSignal() def __init__(self, core: LegendaryCore, game: InstalledGame): super(GameWidgetInstalled, self).__init__() @@ -79,7 +80,9 @@ class GameWidgetInstalled(QWidget): self.setFixedWidth(self.sizeHint().width()) def enterEvent(self, a0: QEvent) -> None: - if not self.running: + if self.update_available: + self.info_label.setText("Please update Game") + elif not self.running: self.info_label.setText("Start Game") def leaveEvent(self, a0: QEvent) -> None: @@ -89,7 +92,7 @@ class GameWidgetInstalled(QWidget): self.launch() def launch(self, offline=False): - if not self.running: + if not self.running and not self.update_available: logger.info("Launching " + self.game.title) self.proc = LegendaryApi.launch_game(self.core, self.game.app_name, offline) if not self.proc: @@ -98,6 +101,8 @@ class GameWidgetInstalled(QWidget): self.proc.finished.connect(self.finished) self.info_label.setText(self.tr("Game running")) self.running = True + if self.update_available: + self.update_game.emit() def finished(self): self.info_label.setText("") @@ -121,4 +126,3 @@ class Menu(QMenu): super(Menu, self).__init__() self.addAction(self.tr("Game info"), lambda: self.action.emit("info")) self.addAction(self.tr("Uninstall"), lambda: self.action.emit("uninstall")) - diff --git a/Rare/Components/Tabs/Games/GameWidgetListUninstalled.py b/Rare/Components/Tabs/Games/GameWidgetListUninstalled.py index 34186738..5da1004c 100644 --- a/Rare/Components/Tabs/Games/GameWidgetListUninstalled.py +++ b/Rare/Components/Tabs/Games/GameWidgetListUninstalled.py @@ -8,12 +8,14 @@ from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QVBoxLayout, QPushButt from legendary.core import LegendaryCore from Rare.Components.Dialogs.InstallDialog import InstallDialog +from Rare.utils.Models import InstallOptions logger = getLogger("Game") class UninstalledGameWidget(QWidget): - install_game = pyqtSignal(dict) + install_game = pyqtSignal(InstallOptions) + def __init__(self, core: LegendaryCore, game): super(UninstalledGameWidget, self).__init__() self.title = game.app_title @@ -57,11 +59,9 @@ class UninstalledGameWidget(QWidget): infos = InstallDialog().get_information() if infos != 0: path, max_workers = infos - self.install_game.emit({ - "app_name": self.game.app_name, - "options": { - "path": path, - "max_workers": max_workers - } - }) + self.install_game.emit(InstallOptions( + app_name=self.game.app_name, + path=path, + max_workers=max_workers, + repair=False)) # wait for update of legendary to get progress diff --git a/Rare/Components/Tabs/Games/GamesTab.py b/Rare/Components/Tabs/Games/GamesTab.py index 56d5ac3d..85a0af95 100644 --- a/Rare/Components/Tabs/Games/GamesTab.py +++ b/Rare/Components/Tabs/Games/GamesTab.py @@ -4,6 +4,7 @@ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QLineE from Rare.Components.Tabs.Games.GameInfo.GameInfo import InfoTabs from Rare.Components.Tabs.Games.GameList import GameList +from Rare.utils.QtExtensions import Switch class GameTab(QWidget): @@ -43,7 +44,7 @@ class Games(QWidget): self.layout.addWidget(self.head_bar) self.layout.addWidget(self.game_list) # self.layout.addStretch(1) - self.head_bar.view.stateChanged.connect( + self.head_bar.view.toggled.connect( lambda: self.game_list.update_list(not self.head_bar.view.isChecked())) self.setLayout(self.layout) @@ -64,10 +65,16 @@ class GameListHeadBar(QWidget): self.layout.addStretch() self.list_view = QLabel(self.tr("List view")) - self.layout.addWidget(self.list_view) - self.view = QCheckBox(self.tr("Icon view")) - self.view.setChecked(not QSettings().value("icon_view", True, bool)) + + self.icon_view = QLabel(self.tr("Icon view")) + + self.view = Switch() + checked = not QSettings().value("icon_view", True, bool) + self.view.setChecked(checked) + self.layout.addWidget(self.icon_view) self.layout.addWidget(self.view) + self.layout.addWidget(self.list_view) + self.refresh_list = QPushButton() self.refresh_list.setIcon(self.style().standardIcon(getattr(QStyle, "SP_BrowserReload"))) # Reload icon self.layout.addWidget(self.refresh_list) diff --git a/Rare/utils/Models.py b/Rare/utils/Models.py new file mode 100644 index 00000000..d5a61cb8 --- /dev/null +++ b/Rare/utils/Models.py @@ -0,0 +1,12 @@ +import os + + +class InstallOptions: + def __init__(self, app_name: str, path: str = os.path.expanduser("~/legendary"), + max_workers: int = os.cpu_count() * 2, repair: bool = False, + download_only: bool = False): + self.app_name = app_name + self.path = path + self.max_workers = max_workers + self.repair = repair + self.download_only = download_only diff --git a/Rare/utils/QtExtensions.py b/Rare/utils/QtExtensions.py index fc5794d8..06b6caa4 100644 --- a/Rare/utils/QtExtensions.py +++ b/Rare/utils/QtExtensions.py @@ -1,9 +1,10 @@ import os -from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal -from PyQt5.QtGui import QMovie +from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal, QPropertyAnimation, pyqtSlot, QPointF, QEasingCurve, \ + QObject, pyqtProperty +from PyQt5.QtGui import QMovie, QPainter, QPalette, QLinearGradient, QGradient from PyQt5.QtWidgets import QLayout, QStyle, QSizePolicy, QLabel, QFileDialog, QHBoxLayout, QWidget, QLineEdit, \ - QPushButton, QStyleOptionTab, QStylePainter, QTabBar + QPushButton, QStyleOptionTab, QStylePainter, QTabBar, QAbstractButton, QCheckBox from Rare import style_path @@ -191,3 +192,89 @@ class WaitingSpinner(QLabel): self.movie = QMovie(style_path+"Icons/loader.gif") self.setMovie(self.movie) self.movie.start() + + +class SwitchPrivate(QObject): + def __init__(self, q, parent=None): + QObject.__init__(self, parent=parent) + self.mPointer = q + self.mPosition = 0.0 + self.mGradient = QLinearGradient() + self.mGradient.setSpread(QGradient.PadSpread) + + self.animation = QPropertyAnimation(self) + self.animation.setTargetObject(self) + self.animation.setPropertyName(b'position') + self.animation.setStartValue(0.0) + self.animation.setEndValue(1.0) + self.animation.setDuration(200) + self.animation.setEasingCurve(QEasingCurve.InOutExpo) + + self.animation.finished.connect(self.mPointer.update) + + @pyqtProperty(float) + def position(self): + return self.mPosition + + @position.setter + def position(self, value): + self.mPosition = value + self.mPointer.update() + + def draw(self, painter): + r = self.mPointer.rect() + margin = r.height()/10 + shadow = self.mPointer.palette().color(QPalette.Dark) + light = self.mPointer.palette().color(QPalette.Light) + button = self.mPointer.palette().color(QPalette.Button) + painter.setPen(Qt.NoPen) + + self.mGradient.setColorAt(0, shadow.darker(130)) + self.mGradient.setColorAt(1, light.darker(130)) + self.mGradient.setStart(0, r.height()) + self.mGradient.setFinalStop(0, 0) + painter.setBrush(self.mGradient) + painter.drawRoundedRect(r, r.height()/2, r.height()/2) + + self.mGradient.setColorAt(0, shadow.darker(140)) + self.mGradient.setColorAt(1, light.darker(160)) + self.mGradient.setStart(0, 0) + self.mGradient.setFinalStop(0, r.height()) + painter.setBrush(self.mGradient) + painter.drawRoundedRect(r.adjusted(margin, margin, -margin, -margin), r.height()/2, r.height()/2) + + self.mGradient.setColorAt(0, button.darker(130)) + self.mGradient.setColorAt(1, button) + + painter.setBrush(self.mGradient) + + x = r.height()/2.0 + self.mPosition*(r.width()-r.height()) + painter.drawEllipse(QPointF(x, r.height()/2), r.height()/2-margin, r.height()/2-margin) + + @pyqtSlot(bool, name='animate') + def animate(self, checked): + self.animation.setDirection(QPropertyAnimation.Forward if checked else QPropertyAnimation.Backward) + self.animation.start() + + +class Switch(QAbstractButton): + def __init__(self): + QAbstractButton.__init__(self) + self.dPtr = SwitchPrivate(self) + self.setCheckable(True) + self.clicked.connect(self.dPtr.animate) + + def sizeHint(self): + return QSize(42, 21) + + def paintEvent(self, event): + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + self.dPtr.draw(painter) + + def setChecked(self, a0: bool) -> None: + super().setChecked(a0) + self.dPtr.animate(a0) + + def resizeEvent(self, event): + self.update()