From 61dad1863f9e48455158f8a069b671b19714c1f5 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 10:28:03 +0200 Subject: [PATCH 01/30] Fixed bugs in dxvk settings, make more texts selectable --- Rare/Components/Tabs/CloudSaves/SyncWidget.py | 3 ++- Rare/Components/Tabs/Downloads/DownloadTab.py | 1 - .../Tabs/Games/GameInfo/GameInfo.py | 3 +++ Rare/Components/Tabs/Settings/Dxvk.py | 19 ++++++++++++++----- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Rare/Components/Tabs/CloudSaves/SyncWidget.py b/Rare/Components/Tabs/CloudSaves/SyncWidget.py index f3b48457..9abe2aa4 100644 --- a/Rare/Components/Tabs/CloudSaves/SyncWidget.py +++ b/Rare/Components/Tabs/CloudSaves/SyncWidget.py @@ -1,7 +1,7 @@ import os from logging import getLogger -from PyQt5.QtCore import QThread, pyqtSignal +from PyQt5.QtCore import QThread, pyqtSignal, Qt from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QHBoxLayout, QLabel from Rare.Components.Dialogs.PathInputDialog import PathInputDialog @@ -130,6 +130,7 @@ class SyncWidget(QWidget): save_path_layout = QHBoxLayout() self.save_path_text = QLabel(igame.save_path) + self.save_path_text.setTextInteractionFlags(Qt.TextSelectableByMouse) self.save_path_text.setWordWrap(True) self.change_save_path = QPushButton(self.tr("Change path")) self.change_save_path.clicked.connect(self.change_path) diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py index 16a6f632..dc1946e5 100644 --- a/Rare/Components/Tabs/Downloads/DownloadTab.py +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -337,7 +337,6 @@ class UpdateWidget(QWidget): def __init__(self, core: LegendaryCore, game: InstalledGame): super(UpdateWidget, self).__init__() - print(game) self.core = core self.game = game print(self.game) diff --git a/Rare/Components/Tabs/Games/GameInfo/GameInfo.py b/Rare/Components/Tabs/Games/GameInfo/GameInfo.py index 159ee834..1ca27074 100644 --- a/Rare/Components/Tabs/Games/GameInfo/GameInfo.py +++ b/Rare/Components/Tabs/Games/GameInfo/GameInfo.py @@ -65,6 +65,7 @@ class GameInfo(QWidget): right_layout.addWidget(self.game_title) self.dev = QLabel("Error") + self.dev.setTextInteractionFlags(Qt.TextSelectableByMouse) right_layout.addWidget(self.dev) self.app_name = QLabel("Error") @@ -72,12 +73,14 @@ class GameInfo(QWidget): right_layout.addWidget(self.app_name) self.version = QLabel("Error") + self.version.setTextInteractionFlags(Qt.TextSelectableByMouse) right_layout.addWidget(self.version) self.install_size = QLabel("Error") right_layout.addWidget(self.install_size) self.install_path = QLabel("Error") + self.install_path.setTextInteractionFlags(Qt.TextSelectableByMouse) right_layout.addWidget(self.install_path) top_layout.addLayout(right_layout) diff --git a/Rare/Components/Tabs/Settings/Dxvk.py b/Rare/Components/Tabs/Settings/Dxvk.py index 993d3ba9..4fffa1c0 100644 --- a/Rare/Components/Tabs/Settings/Dxvk.py +++ b/Rare/Components/Tabs/Settings/Dxvk.py @@ -41,8 +41,10 @@ class DxvkWidget(QGroupBox): self.more_settings.setPopupMode(QToolButton.InstantPopup) self.more_settings.setMenu(QMenu()) self.more_settings.setText("More DXVK settings") + action = QWidgetAction(self) self.more_settings_widget = DxvkMoreSettingsWidget(self.dxvk_settings, self.core) + self.more_settings_widget.show_dxvk.connect(lambda x: self.show_dxvk.setChecked(x)) action.setDefaultWidget(self.more_settings_widget) self.more_settings.menu().addAction(action) @@ -71,7 +73,8 @@ class DxvkWidget(QGroupBox): def update_dxvk_active(self): if self.show_dxvk.isChecked(): if not f"{self.name}.env" in self.core.lgd.config.sections(): - self.core.lgd.config[f"{self.name}.env"] = {} + print("add section dxvk") + self.core.lgd.config.add_section(f"{self.name}.env") self.more_settings.setDisabled(False) for i in self.more_settings_widget.settings: @@ -93,11 +96,11 @@ class DxvkWidget(QGroupBox): self.core.lgd.config.remove_option(f"{self.name}.env", "DXVK_HUD") if not self.core.lgd.config[f"{self.name}.env"]: self.core.lgd.config.remove_section(f"{self.name}.env") - print("Remove Section DXVK_HUD") self.core.lgd.save_config() class DxvkMoreSettingsWidget(QWidget): + show_dxvk = pyqtSignal(bool) def __init__(self, settings: dict, core: LegendaryCore): super(DxvkMoreSettingsWidget, self).__init__() self.layout = QVBoxLayout() @@ -118,16 +121,22 @@ class DxvkMoreSettingsWidget(QWidget): y = list(self.settings[tag]) y[0] = checked self.settings[tag] = tuple(y) - # print(self.settings) + sett = [] logger.debug(self.settings) for i in self.settings: check, _ = self.settings[i] if check: sett.append(i) - if sett: + if len(sett) != 0: self.core.lgd.config[f"{self.name}.env"]["DXVK_HUD"] = ",".join(sett) - self.core.lgd.save_config() + + else: + self.core.lgd.config.remove_option(f"{self.name}.env", "DXVK_HUD") + self.show_dxvk.emit(False) + if not self.core.lgd.config.options(f"{self.name}.env"): + self.core.lgd.config.remove_section(f"{self.name}.env") + self.core.lgd.save_config() class CheckBox(QCheckBox): From d18089d293cf32ec1bf06ac49b1e19eaa7a17ef6 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 10:31:18 +0200 Subject: [PATCH 02/30] Fixed bug in DownloadTab --- Rare/Components/Tabs/Downloads/DownloadTab.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py index e2469edf..4ca02a24 100644 --- a/Rare/Components/Tabs/Downloads/DownloadTab.py +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -226,7 +226,6 @@ class DownloadTab(QWidget): return self.active_game = game - self.installing_game_widget.setText(self.tr("Installing game: ")+self.active_game.app_title) self.thread = DownloadThread(dlm, self.core, status_queue, igame, options.repair, repair_file) self.thread.status.connect(self.status) self.thread.statistics.connect(self.statistics) From 8a74c4131e013f6dd2500eb25396242699515c21 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 11:00:13 +0200 Subject: [PATCH 03/30] More uninstall options, --- Rare/Components/Dialogs/UninstallDialog.py | 44 ++++++++++++++++++++++ Rare/utils/LegendaryApi.py | 11 ++++-- 2 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 Rare/Components/Dialogs/UninstallDialog.py diff --git a/Rare/Components/Dialogs/UninstallDialog.py b/Rare/Components/Dialogs/UninstallDialog.py new file mode 100644 index 00000000..3a18248d --- /dev/null +++ b/Rare/Components/Dialogs/UninstallDialog.py @@ -0,0 +1,44 @@ +from PyQt5.QtWidgets import QDialog, QLabel, QVBoxLayout, QCheckBox, QFormLayout, QHBoxLayout, QPushButton +from qtawesome import icon + +from custom_legendary.models.game import Game + + +class UninstallDialog(QDialog): + def __init__(self, game: Game): + super(UninstallDialog, self).__init__() + self.setWindowTitle("Uninstall Game") + self.info = 0 + self.layout = QVBoxLayout() + self.info_text = QLabel(self.tr("Do you really want to uninstall {}").format(game.app_title)) + self.layout.addWidget(self.info_text) + self.keep_files = QCheckBox(self.tr("Keep Files")) + self.form = QFormLayout() + self.form.setContentsMargins(0, 10, 0, 10) + self.form.addRow(QLabel(self.tr("Do you want to keep files?")), self.keep_files) + self.layout.addLayout(self.form) + + self.button_layout = QHBoxLayout() + self.ok_button = QPushButton(icon("ei.remove-circle", color="red"), self.tr("Uninstall")) + self.ok_button.clicked.connect(self.ok) + + self.cancel_button = QPushButton(self.tr("Cancel")) + self.cancel_button.clicked.connect(self.cancel) + + self.button_layout.addStretch(1) + self.button_layout.addWidget(self.ok_button) + self.button_layout.addWidget(self.cancel_button) + self.layout.addLayout(self.button_layout) + self.setLayout(self.layout) + + def get_information(self): + self.exec_() + return self.info + + def ok(self): + self.info = {"keep_files": self.keep_files.isChecked()} + self.close() + + def cancel(self): + self.info = 0 + self.close() diff --git a/Rare/utils/LegendaryApi.py b/Rare/utils/LegendaryApi.py index e22baf89..0f5ce4cd 100644 --- a/Rare/utils/LegendaryApi.py +++ b/Rare/utils/LegendaryApi.py @@ -47,7 +47,9 @@ def launch_game(core, app_name: str, offline: bool = False, skip_version_check: return process, params -def uninstall(app_name: str, core): +def uninstall(app_name: str, core, options=None): + if not options: + options = {"keep_files": False} igame = core.get_installed_game(app_name) try: # Remove DLC first so directory is empty when game uninstall runs @@ -55,12 +57,13 @@ def uninstall(app_name: str, core): for dlc in dlcs: if (idlc := core.get_installed_game(dlc.app_name)) is not None: logger.info(f'Uninstalling DLC "{dlc.app_name}"...') - core.uninstall_game(idlc, delete_files=True) + core.uninstall_game(idlc, delete_files=not options["keep_files"]) logger.info(f'Removing "{igame.title}" from "{igame.install_path}"...') - core.uninstall_game(igame, delete_files=True, delete_root_directory=True) + core.uninstall_game(igame, delete_files=not options["keep_files"], delete_root_directory=True) logger.info('Game has been uninstalled.') - shutil.rmtree(igame.install_path) + if not options["keep_files"]: + shutil.rmtree(igame.install_path) except Exception as e: logger.warning(f'Removing game failed: {e!r}, please remove {igame.install_path} manually.') From f1349b696af2b604cf15bfae457afec52567ac9c Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 11:00:37 +0200 Subject: [PATCH 04/30] Show text when dl finished --- Rare/Components/Tabs/Downloads/DownloadTab.py | 1 + Rare/Components/Tabs/Games/GameInfo/GameInfo.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py index 4ca02a24..6280b08c 100644 --- a/Rare/Components/Tabs/Downloads/DownloadTab.py +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -278,6 +278,7 @@ class DownloadTab(QWidget): if text == "dl_finished": pass elif text == "finish": + self.installing_game.setText(self.tr("Download finished. Reload library")) try: from notifypy import Notify except ModuleNotFoundError: diff --git a/Rare/Components/Tabs/Games/GameInfo/GameInfo.py b/Rare/Components/Tabs/Games/GameInfo/GameInfo.py index 1ca27074..b67c64e8 100644 --- a/Rare/Components/Tabs/Games/GameInfo/GameInfo.py +++ b/Rare/Components/Tabs/Games/GameInfo/GameInfo.py @@ -6,6 +6,7 @@ from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QLabel, QHBoxLayo QProgressBar, QStackedWidget, QGroupBox from qtawesome import icon +from Rare.Components.Dialogs.UninstallDialog import UninstallDialog from Rare.Components.Tabs.Games.GameInfo.GameSettings import GameSettings from Rare.utils import LegendaryApi from Rare.utils.LegendaryApi import VerifyThread @@ -98,10 +99,13 @@ class GameInfo(QWidget): self.setLayout(self.layout) def uninstall(self): - if QMessageBox.question(self, "Uninstall", self.tr("Are you sure to uninstall {}").format(self.game.app_title), - QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: - LegendaryApi.uninstall(self.game.app_name, self.core) - self.update_list.emit() + infos = UninstallDialog(self.game).get_information() + if infos == 0: + print("Cancel Uninstall") + return + + LegendaryApi.uninstall(self.game.app_name, self.core, infos) + self.update_list.emit() def repair(self): repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{self.game.app_name}.repair') From d1818dc11a9fef6c8092e04be89a2cae3c3fd649 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 11:14:45 +0200 Subject: [PATCH 05/30] Fixed bug, on reload list: wrong view --- Rare/Components/Tabs/Games/GamesTab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rare/Components/Tabs/Games/GamesTab.py b/Rare/Components/Tabs/Games/GamesTab.py index 93c27038..08ab89cf 100644 --- a/Rare/Components/Tabs/Games/GamesTab.py +++ b/Rare/Components/Tabs/Games/GamesTab.py @@ -29,7 +29,7 @@ class GameTab(QWidget): self.setLayout(self.layout) def update_list(self): - self.default_widget.game_list.update_list(not self.default_widget.head_bar.view.isChecked()) + self.default_widget.game_list.update_list(self.default_widget.head_bar.view.isChecked()) self.layout.setCurrentIndex(0) def show_info(self, app_name): From a5f21934992ab309da30d6af7cabf92f5097b2e2 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 11:25:36 +0200 Subject: [PATCH 06/30] Dxvk better --- Rare/Components/Tabs/Settings/Dxvk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rare/Components/Tabs/Settings/Dxvk.py b/Rare/Components/Tabs/Settings/Dxvk.py index 4fffa1c0..14cdbcc7 100644 --- a/Rare/Components/Tabs/Settings/Dxvk.py +++ b/Rare/Components/Tabs/Settings/Dxvk.py @@ -129,7 +129,7 @@ class DxvkMoreSettingsWidget(QWidget): if check: sett.append(i) if len(sett) != 0: - self.core.lgd.config[f"{self.name}.env"]["DXVK_HUD"] = ",".join(sett) + self.core.lgd.config.set(f"{self.name}.env", "DXVK_HUD", ",".join(sett)) else: self.core.lgd.config.remove_option(f"{self.name}.env", "DXVK_HUD") From 144498d3336807cec57f5d1830d96b5140cd5cd0 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 12:10:42 +0200 Subject: [PATCH 07/30] Changed transformation mode for pixmaps --- Rare/Components/Tabs/Games/GameWidgets/BaseInstalledWidget.py | 2 ++ Rare/Components/Tabs/Games/GameWidgets/InstalledIconWidget.py | 2 +- Rare/Components/Tabs/Games/GameWidgets/InstalledListWidget.py | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Rare/Components/Tabs/Games/GameWidgets/BaseInstalledWidget.py b/Rare/Components/Tabs/Games/GameWidgets/BaseInstalledWidget.py index 0b1b5224..3a654610 100644 --- a/Rare/Components/Tabs/Games/GameWidgets/BaseInstalledWidget.py +++ b/Rare/Components/Tabs/Games/GameWidgets/BaseInstalledWidget.py @@ -25,6 +25,8 @@ class BaseInstalledWidget(QGroupBox): self.game_running = False self.update_available = self.core.get_asset(self.game.app_name, True).build_version != igame.version + + self.setContentsMargins(0, 0, 0, 0) # self.setStyleSheet("border-radius: 5px") diff --git a/Rare/Components/Tabs/Games/GameWidgets/InstalledIconWidget.py b/Rare/Components/Tabs/Games/GameWidgets/InstalledIconWidget.py index b2dda22a..85f511fe 100644 --- a/Rare/Components/Tabs/Games/GameWidgets/InstalledIconWidget.py +++ b/Rare/Components/Tabs/Games/GameWidgets/InstalledIconWidget.py @@ -37,7 +37,7 @@ class GameWidgetInstalled(BaseInstalledWidget): if self.pixmap: w = 200 - self.pixmap = self.pixmap.scaled(w, int(w * 4 / 3)) + self.pixmap = self.pixmap.scaled(w, int(w * 4 / 3), transformMode=Qt.SmoothTransformation) self.image = ClickableLabel() self.image.setObjectName("game_widget") self.image.setPixmap(self.pixmap) diff --git a/Rare/Components/Tabs/Games/GameWidgets/InstalledListWidget.py b/Rare/Components/Tabs/Games/GameWidgets/InstalledListWidget.py index 1a30231d..c25b634f 100644 --- a/Rare/Components/Tabs/Games/GameWidgets/InstalledListWidget.py +++ b/Rare/Components/Tabs/Games/GameWidgets/InstalledListWidget.py @@ -1,7 +1,7 @@ import os from logging import getLogger -from PyQt5.QtCore import QProcess, pyqtSignal +from PyQt5.QtCore import QProcess, pyqtSignal, Qt from PyQt5.QtWidgets import QHBoxLayout, QLabel, QPushButton, QStyle, QVBoxLayout from qtawesome import icon @@ -30,7 +30,7 @@ class InstalledListWidget(BaseInstalledWidget): self.childLayout = QVBoxLayout() if self.pixmap: - self.pixmap = self.pixmap.scaled(180, 240) + self.pixmap = self.pixmap.scaled(180, 240, transformMode=Qt.SmoothTransformation) self.image = QLabel() self.image.setPixmap(self.pixmap) self.layout.addWidget(self.image) From b96932aab14267ac036b23ee8e26b89f925ed4b8 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 12:28:10 +0200 Subject: [PATCH 08/30] Changed size of cached images --- Rare/utils/utils.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Rare/utils/utils.py b/Rare/utils/utils.py index d25436bf..44440b29 100644 --- a/Rare/utils/utils.py +++ b/Rare/utils/utils.py @@ -4,7 +4,7 @@ import shutil from logging import getLogger import requests -from PIL import Image +from PIL import Image, UnidentifiedImageError from PyQt5.QtCore import pyqtSignal, QLocale, QSettings from Rare import lang_path @@ -38,6 +38,7 @@ def download_image(game, force=False): if not os.path.isdir(f"{IMAGE_DIR}/" + game.app_name): os.mkdir(f"{IMAGE_DIR}/" + game.app_name) + # to git picture updates if not os.path.isfile(f"{IMAGE_DIR}/{game.app_name}/image.json"): json_data = {"DieselGameBoxTall": None, "DieselGameBoxLogo": None} else: @@ -56,7 +57,13 @@ def download_image(game, force=False): url = image["url"] with open(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png", "wb") as f: f.write(requests.get(url).content) - f.close() + try: + img = Image.open(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png") + img = img.resize((200, int(200*4/3))) + img.save(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png") + except UnidentifiedImageError as e: + logger.warning(e) + # scale and grey if not os.path.isfile(f'{IMAGE_DIR}/' + game.app_name + '/UninstalledArt.png'): @@ -67,6 +74,7 @@ def download_image(game, force=False): bg = Image.open(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png") uninstalledArt = bg.convert('L') + uninstalledArt = uninstalledArt.resize((200, int(200*4/3))) uninstalledArt.save(f'{IMAGE_DIR}/{game.app_name}/UninstalledArt.png') elif os.path.isfile(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png"): bg: Image.Image = Image.open(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png") @@ -91,7 +99,7 @@ def download_image(game, force=False): uninstalledArt = uninstalledArt.convert('L') uninstalledArt.save(f'{IMAGE_DIR}/' + game.app_name + '/UninstalledArt.png') else: - logger.warning(f"File {IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png dowsn't exist") + logger.warning(f"File {IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png doesn't exist") def get_lang(): From 478fd179376fce24a6fef4f449c159ffff68a269 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 13:41:52 +0200 Subject: [PATCH 09/30] GroupBox, List view bug fixed --- Rare/Components/Tabs/Games/GameList.py | 1 + Rare/Components/Tabs/Games/ImportWidget.py | 25 ++++++++++++++-------- Rare/Styles/RareStyle.qss | 7 ++++++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/Rare/Components/Tabs/Games/GameList.py b/Rare/Components/Tabs/Games/GameList.py index 695f50cf..e51316da 100644 --- a/Rare/Components/Tabs/Games/GameList.py +++ b/Rare/Components/Tabs/Games/GameList.py @@ -40,6 +40,7 @@ class GameList(QStackedWidget): self.icon_scrollarea.setWidgetResizable(True) self.icon_scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + self.list_scrollarea.setWidgetResizable(True) self.list_scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.info_text = self.tr("Installed Games: {} Available Games: {}").format( diff --git a/Rare/Components/Tabs/Games/ImportWidget.py b/Rare/Components/Tabs/Games/ImportWidget.py index 1a5bd4b6..454ce669 100644 --- a/Rare/Components/Tabs/Games/ImportWidget.py +++ b/Rare/Components/Tabs/Games/ImportWidget.py @@ -4,7 +4,8 @@ import string from logging import getLogger from PyQt5.QtCore import pyqtSignal -from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QPushButton, QVBoxLayout, QFileDialog, QMessageBox, QLineEdit +from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QPushButton, QVBoxLayout, QFileDialog, QMessageBox, QLineEdit, \ + QGroupBox from qtawesome import icon from Rare.utils import LegendaryApi @@ -34,11 +35,13 @@ class ImportWidget(QWidget): self.title = QLabel("

Import Game{self.tr('Import existing game from Epic Games Launcher')}

") - self.layout.addWidget(self.import_one_game) + # self.import_one_game = QLabel(f"

{self.tr('Import existing game from Epic Games Launcher')}

") + self.import_one_game = QGroupBox(self.tr('Import existing game from Epic Games Launcher')) + self.import_one_game.setObjectName("group") + self.gb_layout = QVBoxLayout() self.import_game_info = QLabel(self.tr("Select path to game")) - self.layout.addWidget(self.import_game_info) + self.gb_layout.addWidget(self.import_game_info) self.override_app_name_label = QLabel(self.tr("Override app name (Only if imported game from legendary or the app could not find the app name)")) self.app_name_input = QLineEdit() @@ -52,17 +55,21 @@ class ImportWidget(QWidget): self.path_edit = PathEdit(os.path.expanduser("~"), QFileDialog.DirectoryOnly) self.path_edit.text_edit.textChanged.connect(self.path_changed) - self.layout.addWidget(self.path_edit) + self.gb_layout.addWidget(self.path_edit) - self.layout.addWidget(self.override_app_name_label) - self.layout.addWidget(self.app_name_input) + self.gb_layout.addWidget(self.override_app_name_label) + self.gb_layout.addWidget(self.app_name_input) self.info_label = QLabel("") - self.layout.addWidget(self.info_label) + self.gb_layout.addWidget(self.info_label) self.import_button = QPushButton(self.tr("Import Game")) - self.layout.addWidget(self.import_button) + self.gb_layout.addWidget(self.import_button) self.import_button.clicked.connect(self.import_game) + self.import_one_game.setLayout(self.gb_layout) + + self.layout.addWidget(self.import_one_game) + self.layout.addStretch(1) self.auto_import = QLabel(f"

{self.tr('Auto import all existing games')}

") diff --git a/Rare/Styles/RareStyle.qss b/Rare/Styles/RareStyle.qss index d6670020..addacfa3 100644 --- a/Rare/Styles/RareStyle.qss +++ b/Rare/Styles/RareStyle.qss @@ -30,8 +30,15 @@ QTabBar::tab:hover#main_tab_bar { } +QGroupBox{ + border: none; +} + QGroupBox#group{ font-size: 15px; + border: 1px solid white; + padding: 8px; + margin-top: 10px; } QTabBar::tab:disabled { From d0f4e3b7c32cf47c0307597d240787fd1c078a59 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 20:55:59 +0200 Subject: [PATCH 10/30] Cloud saves: use max width; Groupbox Style --- Rare/Components/Tabs/CloudSaves/CloudSaves.py | 6 +- Rare/Components/Tabs/CloudSaves/SyncWidget.py | 15 +- Rare/Components/Tabs/Games/ImportWidget.py | 5 +- Rare/Components/Tabs/Settings/Legendary.py | 5 +- Rare/Styles/RareStyle.qss | 10 +- Rare/languages/de.qm | Bin 16458 -> 17684 bytes Rare/languages/de.ts | 184 ++++++++++++------ 7 files changed, 146 insertions(+), 79 deletions(-) diff --git a/Rare/Components/Tabs/CloudSaves/CloudSaves.py b/Rare/Components/Tabs/CloudSaves/CloudSaves.py index 72b52b6f..9dc45abb 100644 --- a/Rare/Components/Tabs/CloudSaves/CloudSaves.py +++ b/Rare/Components/Tabs/CloudSaves/CloudSaves.py @@ -48,12 +48,9 @@ class SyncSaves(QScrollArea): def setup_ui(self, saves: list): self.start_thread.disconnect() - - self.main_layout = QVBoxLayout() self.title = QLabel( f"

" + self.tr("Cloud Saves") + "

\n" + self.tr("Found Saves for folowing Games")) - self.main_layout.addWidget(self.title) saves_games = [] @@ -78,7 +75,6 @@ class SyncSaves(QScrollArea): logger.info(f'Got {len(latest_save)} remote save game(s)') self.widgets = [] - for igame in self.igames: game = self.core.get_game(igame.app_name) if not game.supports_cloud_saves: @@ -92,7 +88,9 @@ class SyncSaves(QScrollArea): self.widgets.append(sync_widget) self.widget = QWidget() + self.main_layout.addStretch(1) self.widget.setLayout(self.main_layout) + self.setWidgetResizable(True) self.setWidget(self.widget) def reload(self): diff --git a/Rare/Components/Tabs/CloudSaves/SyncWidget.py b/Rare/Components/Tabs/CloudSaves/SyncWidget.py index 7b4f6956..1f24795c 100644 --- a/Rare/Components/Tabs/CloudSaves/SyncWidget.py +++ b/Rare/Components/Tabs/CloudSaves/SyncWidget.py @@ -2,7 +2,7 @@ import os from logging import getLogger from PyQt5.QtCore import QThread, pyqtSignal, Qt -from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QHBoxLayout, QLabel +from PyQt5.QtWidgets import QVBoxLayout, QPushButton, QHBoxLayout, QLabel, QGroupBox from Rare.Components.Dialogs.PathInputDialog import PathInputDialog from custom_legendary.core import LegendaryCore @@ -37,12 +37,14 @@ class _DownloadThread(QThread): self.core.download_saves(self.app_name, self.latest_save.manifest_name, self.save_path, clean_dir=True) -class SyncWidget(QWidget): +class SyncWidget(QGroupBox): reload = pyqtSignal() def __init__(self, igame: InstalledGame, save, core: LegendaryCore): - super(SyncWidget, self).__init__() + super(SyncWidget, self).__init__(igame.title) + self.setObjectName("group") self.layout = QVBoxLayout() + self.setContentsMargins(10, 20, 10, 20) self.thr = None self.core = core self.save = save @@ -70,7 +72,7 @@ class SyncWidget(QWidget): self.logger.info('No cloud or local savegame found.') return - game_title = QLabel(f"

{igame.title}

") + # game_title = QLabel(f"

{igame.title}

") if self.dt_local: local_save_date = QLabel( @@ -124,7 +126,7 @@ class SyncWidget(QWidget): self.upload_button.clicked.connect(self.upload) self.download_button.clicked.connect(self.download) self.info_text = QLabel(status) - self.layout.addWidget(game_title) + # self.layout.addWidget(game_title) self.layout.addWidget(local_save_date) self.layout.addWidget(cloud_save_date) @@ -133,6 +135,7 @@ class SyncWidget(QWidget): self.save_path_text.setTextInteractionFlags(Qt.TextSelectableByMouse) self.save_path_text.setWordWrap(True) self.change_save_path = QPushButton(self.tr("Change path")) + self.change_save_path.setFixedWidth(100) self.change_save_path.clicked.connect(self.change_path) save_path_layout.addWidget(self.save_path_text) save_path_layout.addWidget(self.change_save_path) @@ -142,7 +145,7 @@ class SyncWidget(QWidget): button_layout.addWidget(self.upload_button) button_layout.addWidget(self.download_button) self.layout.addLayout(button_layout) - + self.layout.addStretch(1) self.setLayout(self.layout) def change_path(self): diff --git a/Rare/Components/Tabs/Games/ImportWidget.py b/Rare/Components/Tabs/Games/ImportWidget.py index 454ce669..a4c149a6 100644 --- a/Rare/Components/Tabs/Games/ImportWidget.py +++ b/Rare/Components/Tabs/Games/ImportWidget.py @@ -111,6 +111,7 @@ class ImportWidget(QWidget): if not path: path = self.path_edit.text() if not app_name: + # try to find app name if a_n := self.find_app_name(path): app_name = a_n else: @@ -141,7 +142,7 @@ class ImportWidget(QWidget): continue app_name = self.find_app_name(json_path) if not app_name: - logger.warning("Could not find app name") + logger.warning("Could not find app name at " + game_path) continue if LegendaryApi.import_game(self.core, app_name, game_path + path): @@ -166,4 +167,4 @@ class ImportWidget(QWidget): QMessageBox.information(self, "Imported Games", self.tr("Successfully imported {} Games. Reloading Library").format(imported)) self.update_list.emit() else: - QMessageBox.information(self, "Imported Games", "No Games were found") + QMessageBox.information(self, "Imported Games", self.tr("No Games were found")) diff --git a/Rare/Components/Tabs/Settings/Legendary.py b/Rare/Components/Tabs/Settings/Legendary.py index fcc2c428..64cb402b 100644 --- a/Rare/Components/Tabs/Settings/Legendary.py +++ b/Rare/Components/Tabs/Settings/Legendary.py @@ -39,8 +39,7 @@ class LegendarySettings(QWidget): #cleanup self.clean_layout = QVBoxLayout() - self.cleanup_widget = QGroupBox() - self.cleanup_widget.setTitle(self.tr("Cleanup")) + self.cleanup_widget = QGroupBox(self.tr("Cleanup")) self.clean_button = QPushButton(self.tr("Remove everything")) self.clean_button.clicked.connect(lambda: self.cleanup(False)) self.clean_layout.addWidget(self.clean_button) @@ -92,7 +91,7 @@ class LegendarySettings(QWidget): after = self.core.lgd.get_dir_size() logger.info(f'Cleanup complete! Removed {(before - after) / 1024 / 1024:.02f} MiB.') - if cleaned := (before-after) != 0: + if cleaned := (before-after) > 0: QMessageBox.information(self, "Cleanup", self.tr("Cleanup complete! Successfully removed {} MB").format(round(cleaned / 1024 / 1024, 3))) else: QMessageBox.information(self, "Cleanup", "Nothing to clean") diff --git a/Rare/Styles/RareStyle.qss b/Rare/Styles/RareStyle.qss index addacfa3..672096d1 100644 --- a/Rare/Styles/RareStyle.qss +++ b/Rare/Styles/RareStyle.qss @@ -31,14 +31,22 @@ QTabBar::tab:hover#main_tab_bar { } QGroupBox{ + padding: 4px; + margin: 8px; +} + +QGroupBox#game_widget_icon{ border: none; + padding: 0; + margin: 0; } QGroupBox#group{ font-size: 15px; + font-weight: bold; border: 1px solid white; - padding: 8px; margin-top: 10px; + padding: 8px; } QTabBar::tab:disabled { diff --git a/Rare/languages/de.qm b/Rare/languages/de.qm index c3cc547d1d2a8bcd0a21d4c19f23bdf4d6fda13b..e7d7940be62c485dacb6b014900e6c17213d2924 100644 GIT binary patch delta 2060 zcmaJ=X;4#F6#haIvL`@55Lq512q=pZQI@c%NQ)@$Q$(Ofc|vMPm?Vf%FifpetsN{p zZN+^ZTia1bY&$C8F6d~-w$|!s>q4v4soH^BwY9eA5%h;s`y)BIC+~jSIqz(}VD)=~ z55nC0AbNK_5WL${b>Bg}_7o6!&r=PT5aRbapb;YZ>o!2-KxXi2Kwg9~6Q%*uaO5R4 z0Rz%7ap?&l;|>Z!qk!SJFsw)VrIXEvA zrI0h<@j{>TL@Ylc4BBe|h876pWF*X|)>EVFgw8XQNU)c1Md@51)+ub9`ZEx4O}Ob` z3t%h|?zt-hLP~}0{5fNiDD=0>fT}^1#OZ*f%c8Y;t-#a!-lCZnOS)VSNdPAu<-39i z+N60?NQhyGv|vB=leS8y8xjaWCEZqc6iBm3J3spi80eIZXq-XG^muAmhRpbS7Ckpv zX57?C$p*`slci+jf$Z**w}Hf3S#QftV30}ncNGZ;SR;1_79FISZI{QiQzNTaK6F_< z&GeDnxI2gd=E#dzrIF$^`QksnC4dh3(w?Q1P^o->tP6-bE^l2;gqb(w?RlGM1V6}s zET-K_IG_-Gv4oZ_-BX!B1v8G8Q}0p?IX#n>w^OmQA{_8*a4SwvXMp5<#m#76isY)| z*Mr+=<|~yE`@f>M;DR#g=sFSrXT^{hm;NRh7$t^2Cy$G{<~re1nb zHb-@=yOJCgsLpOBz^GxWZsrtizfScm?jc2YTP@g5qlnp}9z2^~r@>9?7-kwUQl}nP zwS!*6sp>-AMB4sKYDXy%NpjU4!8x=ekJO#JQfcYx)#vXKZopXeAKHbK_=vljv3yh+ zJ2VqY)Z}Q7X2vcd0WQ2JhRp8s9?(yn5k@E!(yIeH`!e+0;OHr^4R}~TGxPa zFvAK4bHKt*A2XbA(3!Sy=i`HK-=sGO(N=BGg}7U}IjRYYwJ{wX@7TTeY=}8y!BNrAK#I zC`*756)+%^yPg>6X0RA_)JgR8Xo~@F`e=<6hRi&x!)dZun8kI*L^?{Suj=pRm`xU2 zxquRc9yzd~h74M$nn(t3%#KCW<87DI$@&6VoolntWtlQhe(thYfKEqle^PY0-!Grr zT++#dR}qrF&N$Vw3D*oMMNO>g@};6a;uGxCeF^XjHAj# ziela{*{zg#MC>ah6?0qSN4GqP_6y?H81$MM^l)E1o+vo0e>lha`ziQ58GaTpb2k&i zGa_?rjLTNd*jb9w#nhOrPR40ts-It{ba}#;@%p08xCADm&H?awQ;|L{3hqH5oDzW2TKpWdZf2{p+*8_kR2Rey-Q?w%}@m;9U=M z0Yq>10It0@^lAj#IRQA$*)S{yZVnd!z5uZiNDpoY)(0b_ z=@Jn4Jhr*}03oT!%3lT8Y8XDO1++aj4B7?5GM)J$^f&-8N6g~*5Cd%f4krwifFuc% z(tKbYkB9PHBGMyZQnmnoU4q1fe*yPIL7Uet!1EQs)3-c;pc8`S4N4-EC48Y(1K6{| z&qs(5+b0~&RRe*a3cu9RbGviGN2b4s=oll4CIR*djNNo4%#ejM&S&+2|9M6yB}R7T zHq`#aR9@K*c=a*Od-egovrJp&JwW{p(>q`VV$L&t_eFqPCNsQtj_nt@|8^5ltcb!b z-au%as5AW{;5}%=5T)qstyARS9nqk4HxS^&imE%zr093nf4q|vU$CK15F2(ao1AE2 zBNxVj;52r79ue?)*@nIi>`U@4VB=l3Xy_@R>}KC75|X9S?D4WoK=dkmajFW?OtC}O z0$|fJd##lmQ{{>szc2z}G^mkF(>v0_MXvnDIY54!tNs2Eo%6Vx>Z4>`J2zKPU2#p}<{j@+bUV0t z(-JKfN}}wWDPoNzeJ2qJZINv2r{|#}Nw!%ZOqSJ3PS%YAQ8SXMPyPX1ilh;3IpoNK z4L!o7@y|U&_eP}gy;I~!oiw>Hg9iU1?TVC8#+lOl^#=jn9qAM69Izorx>8O=)Xg#{ z!vIAimiZ6UInE>tI@Ca2GRWdTa;7;GGIL&QEHRIj)%^V}DGZb~J#He4#$^2gCd$r7 zHk{r|*(b_=%%gJZoaBPf>Zx3DHuMaU2Tty$?gYt?6nO#;2j!F58X&w+KIdmoPRz(3 z44ec!%!-ZuUz0$+B7C%)_Cc}Jv5+DiHY;9ih$Jfm6h=E*AU~oQ6p%$;3yOvP$<%Hr zowS!JB4?%F2UND$UCM-pMIhomWyvv$Wb+%!I#U$jIAcQ>4`urkx+l4=T+~dEL+#2{ z-4Z!+n-{!KPWv*vZvlNB-b=i{CJWG;5A(U+WTo3x-nfSba%uc!*A!~EfuA}ZLnYJk z)4#fql~Md3j<1ul3{~=)196;cYd%jp+*LWJ85*>xDws{B=pLvJOrN3ezhBjre~J{> zs>a5t3(=d3=}#kc6YG(RQdHtK6l0(Do~V(t{;9~9SbURR)itPt=KnVpcoijUH&A4$ zUgsC+mQ+!wF_l$mj8*ibvP@G|T3l+Z+*?wjsj1^=tmN6f2MW_m_m^3A=%TEL)h)c` bkv7hH%x91h BaseInstalledWidget - + Do you want to launch {} Möchtest du {} starten @@ -83,40 +83,45 @@ Die Größe des Downloads ist 0. Spiel existiert bereits - + Installation finished Installation abgeschlossen - + Installing Game: No active download Installierendes Spiel: Kein aktiver Download - + Download speed Geschwindigkeit - + Cache used Benutzter Cache - + Downloaded Runtergeladen - + Time left: Zeit übrig: - + Finished Download of game {} Downlaod von {} abgeschlossen + + + Download finished. Reload library + Download abgeschlossen. Spiele neu laden + DxvkWidget @@ -159,32 +164,32 @@ GameActions - + Uninstall game Spiel deinstallieren - + Uninstall Deinstallieren - + Verify Game Spieldateien überprüfen - + Verify Überprüfen - + Repair Game Spiel reparieren - + Repair Reparieren @@ -192,50 +197,50 @@ GameInfo - + Repair file does not exist or game does not need a repair. Please verify game first Reparationsdatei existiert nicht oder das Spiel braucht keine Reperatur. Bitte das spiel zuerst überprüfen - + Verification failed, {} file(s) corrupted, {} file(s) are missing. Do you want to repair them? Überprüfung fehlgeschlagen, {} Datei(en) fehlerhaft, {} Datei(en) fehlen. Willst du das Spiel reparieren? - + Developer: Entwickler: - + Install size: Größe: - + Install path: Installationsordner: Are you sure to uninstall {} - Möchtest du {} wirklich deinstallieren + Möchtest du {} wirklich deinstallieren GameList - + Launch Starten - + Game running Spiel läuft - + Installed Games: {} Available Games: {} Installierte Spiele: {} Verfügbare Spiele: {} @@ -360,22 +365,22 @@ Keine valide Session gefunden - + Back Zurück - + Select path to game Wähle den Pfad zum Spiel - + Import Game Spiel importieren - + Import all games from Epic Games Launcher Alle Spiele aus dem Epic Games Launcher importieren @@ -385,45 +390,55 @@ {} Spiele erfolgreich importiert - + Override app name (Only if imported game from legendary or the app could not find the app name) App Name überschreiben (Nur falls das Spiel von Legendary importiert wird oder der App Name nicht gefunden wird - + Could not find app name Konnte den Appnamen nicht finden - + Successfully imported {}. Reload library Erfolgreich {} importiert. Spiele neu laden - + Failed to import {} {} Konnte nicht importiert werden - + Successfully imported {} Games. Reloading Library Erfolgreich {} Spiele importiert. Spiele neu laden + + + Import existing game from Epic Games Launcher + Ein bereits existierendes Spiel aus dem Epic Games Launcher importieren + + + + No Games were found + Keine Spiele wurden gefunden + InfoTabs - + Back Zurück - + Game Info Spielinfo - + Settings Einstellungen @@ -431,27 +446,42 @@ InstallDialog - + Max workers (0: Default) Maximale Anzahl Downloadprozessen(Standard: 0) + + + <h3>Install {}</h3> + <h3>Installiere {}</h3> + + + + Force download + Download erzwingen + + + + Ignore free space (Warning!) + Freien Speicherplatz ignorieren (Achtung!) + InstallInfoDialog - + Download size: {}GB Install size: {}GB Downloadgröße: {}GB Installationsgröße: {} GB - + Install Installieren - + Cancel Abbruch @@ -518,22 +548,22 @@ Installationsgröße: {} GB Maximale Anzahl Downloadprozesse (Weniger: langsamer)(Standard: 0) - + Cleanup Aufräumen - + Remove everything Alles aufräumen - + Clean, but keep manifests Aufräumen, aber Manifests behalten - + Cleanup complete! Successfully removed {} MB Fertig! Es wurden {} MB entfernt @@ -695,97 +725,97 @@ Installationsgröße: {} GB SyncWidget - + Path not found Ordner nicht gefunden - + Local Save date: Lokales Speicherdatum: - + No Local Save files Keine Lokalen Dateien - + Cloud save date: Online Speicherdatum: - + No Cloud saves Keine Online Speicherstände - + Game is up to date Spiel ist aktuell - + Upload anyway Trotzdem hochladen - + Download anyway Trotzdem herunterladen - + Cloud save is newer Online Speicherstand ist aktueller - + Download Cloud saves Online Speicherstand herunterladen - + Upload Saves Spielstände hochladen - + Local save is newer Lokaler Speicher ist aktueller - + Upload saves Spielstände hochladen - + Download saves Spielstand herunterladen - + Change path Pfad ändern - + Uploading... Hochladen... - + Upload finished Hochladen abgeschlossen - + Downloading... Runterladen... - + Download finished Download abgeschlossen @@ -798,10 +828,38 @@ Installationsgröße: {} GB Spiele + + UninstallDialog + + + Do you really want to uninstall {} + Möchtest du wirklich {} deinstallieren + + + + Keep Files + Dateien behalten + + + + Do you want to keep files? + Willst du die Dateien behalten? + + + + Uninstall + Deinstallieren + + + + Cancel + Abbruch + + UpdateWidget - + Update Game Spiel updaten From 89269c6daad0adfe44a5ae029d4ef3473cfd3114 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Tue, 6 Apr 2021 21:28:22 +0200 Subject: [PATCH 11/30] Uninstalled icons fix, Download tab fix --- Rare/Components/Tabs/Downloads/DownloadTab.py | 1 + Rare/Components/Tabs/Games/GameWidgets/UninstalledIconWidget.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py index 6280b08c..3a546081 100644 --- a/Rare/Components/Tabs/Downloads/DownloadTab.py +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -304,6 +304,7 @@ class DownloadTab(QWidget): self.dl_speed.setText("") self.cache_used.setText("") self.downloaded.setText("") + self.time_left.setText("") elif text == "error": QMessageBox.warning(self, "warn", "Download error") diff --git a/Rare/Components/Tabs/Games/GameWidgets/UninstalledIconWidget.py b/Rare/Components/Tabs/Games/GameWidgets/UninstalledIconWidget.py index a5eec79b..6eaf478a 100644 --- a/Rare/Components/Tabs/Games/GameWidgets/UninstalledIconWidget.py +++ b/Rare/Components/Tabs/Games/GameWidgets/UninstalledIconWidget.py @@ -18,7 +18,7 @@ class IconWidgetUninstalled(BaseUninstalledWidget): def __init__(self, game: Game, core: LegendaryCore, pixmap): super(IconWidgetUninstalled, self).__init__(game, core, pixmap) self.layout = QVBoxLayout() - + self.setObjectName("game_widget_icon") if self.pixmap: w = 200 self.pixmap = self.pixmap.scaled(w, int(w * 4 / 3)) From e89772b5fd2a5c01cf97923ef23f3f559d04efe0 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 10:44:16 +0200 Subject: [PATCH 12/30] Show info, if update available; moved tabs to __init__.py --- Rare/Components/Dialogs/Login/ImportWidget.py | 13 +- Rare/Components/TabWidget.py | 9 +- Rare/Components/Tabs/Account/AccountWidget.py | 5 +- Rare/Components/Tabs/CloudSaves/CloudSaves.py | 121 ------------------ Rare/Components/Tabs/CloudSaves/__init__.py | 121 ++++++++++++++++++ Rare/Components/Tabs/Games/GamesTab.py | 107 ---------------- Rare/Components/Tabs/Games/__init__.py | 107 ++++++++++++++++ Rare/Components/Tabs/Settings/About.py | 14 +- Rare/Components/Tabs/Settings/SettingsTab.py | 22 ---- Rare/Components/Tabs/Settings/__init__.py | 24 ++++ Rare/__init__.py | 2 +- Rare/utils/utils.py | 10 +- 12 files changed, 289 insertions(+), 266 deletions(-) delete mode 100644 Rare/Components/Tabs/CloudSaves/CloudSaves.py delete mode 100644 Rare/Components/Tabs/Games/GamesTab.py delete mode 100644 Rare/Components/Tabs/Settings/SettingsTab.py diff --git a/Rare/Components/Dialogs/Login/ImportWidget.py b/Rare/Components/Dialogs/Login/ImportWidget.py index a526c39a..a54c3a46 100644 --- a/Rare/Components/Dialogs/Login/ImportWidget.py +++ b/Rare/Components/Dialogs/Login/ImportWidget.py @@ -88,12 +88,15 @@ class ImportWidget(QWidget): if os.name != "nt": self.core.egl.appdata_path = os.path.join(self.data_path, f"drive_c/users/{getuser()}/Local Settings/Application Data/EpicGamesLauncher/Saved/Config/Windows") + try: + if self.core.auth_import(): + logger.info(f"Logged in as {self.core.lgd.userdata['displayName']}") + self.success.emit() + else: + logger.warning("Failed to import existing session") + except Exception as e: + logger.warning(e) - if self.core.auth_import(): - logger.info(f"Logged in as {self.core.lgd.userdata['displayName']}") - self.success.emit() - else: - print("Lol") logger.warning("Error: No valid session found") self.login_text.setText(self.tr("Error: No valid session found")) self.import_button.setText(self.tr("Import")) diff --git a/Rare/Components/TabWidget.py b/Rare/Components/TabWidget.py index 3be3189e..08469487 100644 --- a/Rare/Components/TabWidget.py +++ b/Rare/Components/TabWidget.py @@ -3,10 +3,10 @@ from PyQt5.QtWidgets import QTabWidget, QWidget from qtawesome import icon from Rare.Components.TabUtils import TabBar, TabButtonWidget -from Rare.Components.Tabs.CloudSaves.CloudSaves import SyncSaves +from Rare.Components.Tabs.CloudSaves import SyncSaves from Rare.Components.Tabs.Downloads.DownloadTab import DownloadTab -from Rare.Components.Tabs.Games.GamesTab import GameTab -from Rare.Components.Tabs.Settings.SettingsTab import SettingsTab +from Rare.Components.Tabs.Games import GameTab +from Rare.Components.Tabs.Settings import SettingsTab from Rare.utils.Models import InstallOptions from custom_legendary.core import LegendaryCore @@ -43,7 +43,8 @@ class TabWidget(QTabWidget): self.addTab(self.account, "") self.setTabEnabled(disabled_tab + 1, False) # self.settings = SettingsTab(core) - self.addTab(self.settings, icon("fa.gear", color='white'), None) + + self.addTab(self.settings, icon("fa.gear", color='white'), "(!)" if self.settings.about.update_available else "") self.setIconSize(QSize(25, 25)) self.tabBar().setTabButton(3, self.tabBar().RightSide, TabButtonWidget(core)) diff --git a/Rare/Components/Tabs/Account/AccountWidget.py b/Rare/Components/Tabs/Account/AccountWidget.py index 9d16b589..79f08aa7 100644 --- a/Rare/Components/Tabs/Account/AccountWidget.py +++ b/Rare/Components/Tabs/Account/AccountWidget.py @@ -20,7 +20,7 @@ class MiniWidget(QWidget): self.layout.addWidget(QLabel(self.tr("Logged in as ") + username)) self.open_browser = QPushButton(self.tr("Account settings")) - self.open_browser.clicked.connect(self.open_account) + self.open_browser.clicked.connect(lambda: webbrowser.open("https://www.epicgames.com/account/personal?productName=epicgames")) self.layout.addWidget(self.open_browser) self.logout_button = QPushButton(self.tr("Logout")) @@ -37,5 +37,4 @@ class MiniWidget(QWidget): self.core.lgd.invalidate_userdata() QCoreApplication.exit() - def open_account(self): - webbrowser.open("https://www.epicgames.com/account/personal?productName=epicgames") + diff --git a/Rare/Components/Tabs/CloudSaves/CloudSaves.py b/Rare/Components/Tabs/CloudSaves/CloudSaves.py deleted file mode 100644 index 9dc45abb..00000000 --- a/Rare/Components/Tabs/CloudSaves/CloudSaves.py +++ /dev/null @@ -1,121 +0,0 @@ -from logging import getLogger - -from PyQt5.QtCore import QThread, pyqtSignal, Qt -from PyQt5.QtWidgets import * - -from Rare.Components.Dialogs.PathInputDialog import PathInputDialog -from Rare.Components.Tabs.CloudSaves.SyncWidget import SyncWidget -from Rare.utils.QtExtensions import WaitingSpinner -from custom_legendary.core import LegendaryCore -from custom_legendary.models.game import SaveGameStatus - -logger = getLogger("Sync Saves") - - -class LoadThread(QThread): - signal = pyqtSignal(list) - - def __init__(self, core: LegendaryCore): - super(LoadThread, self).__init__() - self.core = core - - def run(self) -> None: - saves = self.core.get_save_games() - self.signal.emit(saves) - - -class SyncSaves(QScrollArea): - - def __init__(self, core: LegendaryCore): - super(SyncSaves, self).__init__() - self.core = core - self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) - self.load_saves() - - def load_saves(self): - self.widget = QWidget() - layout = QVBoxLayout() - layout.addWidget(WaitingSpinner()) - layout.addWidget(QLabel("

Loading Cloud Saves

")) - layout.addStretch() - self.widget.setLayout(layout) - self.setWidget(self.widget) - - self.start_thread = LoadThread(self.core) - self.start_thread.signal.connect(self.setup_ui) - self.start_thread.start() - self.igames = self.core.get_installed_list() - - def setup_ui(self, saves: list): - self.start_thread.disconnect() - self.main_layout = QVBoxLayout() - self.title = QLabel( - f"

" + self.tr("Cloud Saves") + "

\n" + self.tr("Found Saves for folowing Games")) - self.main_layout.addWidget(self.title) - - saves_games = [] - for i in saves: - if not i.app_name in saves_games and self.core.is_installed(i.app_name): - saves_games.append(i.app_name) - if len(saves_games) == 0: - # QMessageBox.information(self.tr("No Games Found"), self.tr("Your games don't support cloud save")) - self.title.setText( - f"

" + self.tr("Cloud Saves") + "

\n" + self.tr("Your games does not support Cloud Saves")) - self.setWidget(self.title) - return - - self.sync_all_button = QPushButton(self.tr("Sync all games")) - self.sync_all_button.clicked.connect(self.sync_all) - self.main_layout.addWidget(self.sync_all_button) - - latest_save = {} - for i in sorted(saves, key=lambda a: a.datetime): - latest_save[i.app_name] = i - - logger.info(f'Got {len(latest_save)} remote save game(s)') - - self.widgets = [] - for igame in self.igames: - game = self.core.get_game(igame.app_name) - if not game.supports_cloud_saves: - continue - if latest_save.get(igame.app_name): - sync_widget = SyncWidget(igame, latest_save[igame.app_name], self.core) - else: - continue - sync_widget.reload.connect(self.reload) - self.main_layout.addWidget(sync_widget) - self.widgets.append(sync_widget) - - self.widget = QWidget() - self.main_layout.addStretch(1) - self.widget.setLayout(self.main_layout) - self.setWidgetResizable(True) - self.setWidget(self.widget) - - def reload(self): - self.setWidget(QWidget()) - self.load_saves() - self.update() - - def sync_all(self): - logger.info("Sync all Games") - for w in self.widgets: - if not w.igame.save_path: - save_path = self.core.get_save_path(w.igame.app_name) - if '%' in save_path or '{' in save_path: - self.logger.info_label("Could not find save_path") - save_path = PathInputDialog(self.tr("Found no savepath"), - self.tr("No save path was found. Please select path or skip")) - if save_path == "": - continue - else: - w.igame.save_path = save_path - if w.res == SaveGameStatus.SAME_AGE: - continue - if w.res == SaveGameStatus.REMOTE_NEWER: - logger.info("Download") - w.download() - elif w.res == SaveGameStatus.LOCAL_NEWER: - logger.info("Upload") - w.upload() diff --git a/Rare/Components/Tabs/CloudSaves/__init__.py b/Rare/Components/Tabs/CloudSaves/__init__.py index e69de29b..9dc45abb 100644 --- a/Rare/Components/Tabs/CloudSaves/__init__.py +++ b/Rare/Components/Tabs/CloudSaves/__init__.py @@ -0,0 +1,121 @@ +from logging import getLogger + +from PyQt5.QtCore import QThread, pyqtSignal, Qt +from PyQt5.QtWidgets import * + +from Rare.Components.Dialogs.PathInputDialog import PathInputDialog +from Rare.Components.Tabs.CloudSaves.SyncWidget import SyncWidget +from Rare.utils.QtExtensions import WaitingSpinner +from custom_legendary.core import LegendaryCore +from custom_legendary.models.game import SaveGameStatus + +logger = getLogger("Sync Saves") + + +class LoadThread(QThread): + signal = pyqtSignal(list) + + def __init__(self, core: LegendaryCore): + super(LoadThread, self).__init__() + self.core = core + + def run(self) -> None: + saves = self.core.get_save_games() + self.signal.emit(saves) + + +class SyncSaves(QScrollArea): + + def __init__(self, core: LegendaryCore): + super(SyncSaves, self).__init__() + self.core = core + self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + self.load_saves() + + def load_saves(self): + self.widget = QWidget() + layout = QVBoxLayout() + layout.addWidget(WaitingSpinner()) + layout.addWidget(QLabel("

Loading Cloud Saves

")) + layout.addStretch() + self.widget.setLayout(layout) + self.setWidget(self.widget) + + self.start_thread = LoadThread(self.core) + self.start_thread.signal.connect(self.setup_ui) + self.start_thread.start() + self.igames = self.core.get_installed_list() + + def setup_ui(self, saves: list): + self.start_thread.disconnect() + self.main_layout = QVBoxLayout() + self.title = QLabel( + f"

" + self.tr("Cloud Saves") + "

\n" + self.tr("Found Saves for folowing Games")) + self.main_layout.addWidget(self.title) + + saves_games = [] + for i in saves: + if not i.app_name in saves_games and self.core.is_installed(i.app_name): + saves_games.append(i.app_name) + if len(saves_games) == 0: + # QMessageBox.information(self.tr("No Games Found"), self.tr("Your games don't support cloud save")) + self.title.setText( + f"

" + self.tr("Cloud Saves") + "

\n" + self.tr("Your games does not support Cloud Saves")) + self.setWidget(self.title) + return + + self.sync_all_button = QPushButton(self.tr("Sync all games")) + self.sync_all_button.clicked.connect(self.sync_all) + self.main_layout.addWidget(self.sync_all_button) + + latest_save = {} + for i in sorted(saves, key=lambda a: a.datetime): + latest_save[i.app_name] = i + + logger.info(f'Got {len(latest_save)} remote save game(s)') + + self.widgets = [] + for igame in self.igames: + game = self.core.get_game(igame.app_name) + if not game.supports_cloud_saves: + continue + if latest_save.get(igame.app_name): + sync_widget = SyncWidget(igame, latest_save[igame.app_name], self.core) + else: + continue + sync_widget.reload.connect(self.reload) + self.main_layout.addWidget(sync_widget) + self.widgets.append(sync_widget) + + self.widget = QWidget() + self.main_layout.addStretch(1) + self.widget.setLayout(self.main_layout) + self.setWidgetResizable(True) + self.setWidget(self.widget) + + def reload(self): + self.setWidget(QWidget()) + self.load_saves() + self.update() + + def sync_all(self): + logger.info("Sync all Games") + for w in self.widgets: + if not w.igame.save_path: + save_path = self.core.get_save_path(w.igame.app_name) + if '%' in save_path or '{' in save_path: + self.logger.info_label("Could not find save_path") + save_path = PathInputDialog(self.tr("Found no savepath"), + self.tr("No save path was found. Please select path or skip")) + if save_path == "": + continue + else: + w.igame.save_path = save_path + if w.res == SaveGameStatus.SAME_AGE: + continue + if w.res == SaveGameStatus.REMOTE_NEWER: + logger.info("Download") + w.download() + elif w.res == SaveGameStatus.LOCAL_NEWER: + logger.info("Upload") + w.upload() diff --git a/Rare/Components/Tabs/Games/GamesTab.py b/Rare/Components/Tabs/Games/GamesTab.py deleted file mode 100644 index 08ab89cf..00000000 --- a/Rare/Components/Tabs/Games/GamesTab.py +++ /dev/null @@ -1,107 +0,0 @@ -from PyQt5.QtCore import QSettings, QSize -from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QLineEdit, QPushButton, QStackedLayout, QLabel -from qtawesome import icon - -from Rare.Components.Tabs.Games.GameInfo.GameInfo import InfoTabs -from Rare.Components.Tabs.Games.GameList import GameList -from Rare.Components.Tabs.Games.ImportWidget import ImportWidget -from Rare.utils.QtExtensions import SelectViewWidget - - -class GameTab(QWidget): - def __init__(self, core): - super(GameTab, self).__init__() - self.layout = QStackedLayout() - self.default_widget = Games(core) - self.default_widget.game_list.show_game_info.connect(self.show_info) - self.default_widget.head_bar.import_game.clicked.connect(lambda: self.layout.setCurrentIndex(2)) - self.layout.addWidget(self.default_widget) - self.game_info = InfoTabs(core) - self.game_info.info.update_list.connect(self.update_list) - self.layout.addWidget(self.game_info) - - self.default_widget.head_bar.refresh_list.clicked.connect(self.update_list) - - self.import_widget = ImportWidget(core) - self.layout.addWidget(self.import_widget) - self.import_widget.back_button.clicked.connect(lambda: self.layout.setCurrentIndex(0)) - self.import_widget.update_list.connect(self.update_list) - self.setLayout(self.layout) - - def update_list(self): - self.default_widget.game_list.update_list(self.default_widget.head_bar.view.isChecked()) - self.layout.setCurrentIndex(0) - - def show_info(self, app_name): - self.game_info.update_game(app_name) - self.game_info.setCurrentIndex(1) - self.layout.setCurrentIndex(1) - - -class Games(QWidget): - def __init__(self, core): - super(Games, self).__init__() - self.layout = QVBoxLayout() - - self.head_bar = GameListHeadBar() - self.head_bar.setObjectName("head_bar") - - self.game_list = GameList(core) - - self.head_bar.search_bar.textChanged.connect( - lambda: self.game_list.filter(self.head_bar.search_bar.text())) - - self.head_bar.installed_only.stateChanged.connect(lambda: - self.game_list.installed_only( - self.head_bar.installed_only.isChecked())) - self.layout.addWidget(self.head_bar) - self.layout.addWidget(self.game_list) - # self.layout.addStretch(1) - self.head_bar.view.toggled.connect(self.toggle_view) - - self.setLayout(self.layout) - - def toggle_view(self): - self.game_list.setCurrentIndex(1 if self.head_bar.view.isChecked() else 0) - settings = QSettings() - settings.setValue("icon_view", not self.head_bar.view.isChecked()) - - -class GameListHeadBar(QWidget): - def __init__(self): - super(GameListHeadBar, self).__init__() - self.layout = QHBoxLayout() - self.installed_only = QCheckBox(self.tr("Installed only")) - self.settings = QSettings() - self.installed_only.setChecked(self.settings.value("installed_only", False, bool)) - self.layout.addWidget(self.installed_only) - - self.layout.addStretch(1) - - self.import_game = QPushButton(icon("mdi.import", color="white"), self.tr("Import Game")) - self.layout.addWidget(self.import_game) - - self.layout.addStretch(1) - - self.search_bar = QLineEdit() - self.search_bar.setObjectName("search_bar") - self.search_bar.setFrame(False) - icon_label = QLabel() - icon_label.setPixmap(icon("fa.search", color="white").pixmap(QSize(20, 20))) - self.layout.addWidget(icon_label) - self.search_bar.setMinimumWidth(200) - self.search_bar.setPlaceholderText(self.tr("Search Game")) - self.layout.addWidget(self.search_bar) - - self.layout.addStretch(2) - - checked = QSettings().value("icon_view", True, bool) - - self.view = SelectViewWidget(checked) - self.layout.addWidget(self.view) - self.layout.addStretch(1) - self.refresh_list = QPushButton() - self.refresh_list.setIcon(icon("fa.refresh", color="white")) # Reload icon - self.layout.addWidget(self.refresh_list) - - self.setLayout(self.layout) diff --git a/Rare/Components/Tabs/Games/__init__.py b/Rare/Components/Tabs/Games/__init__.py index e69de29b..08ab89cf 100644 --- a/Rare/Components/Tabs/Games/__init__.py +++ b/Rare/Components/Tabs/Games/__init__.py @@ -0,0 +1,107 @@ +from PyQt5.QtCore import QSettings, QSize +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QLineEdit, QPushButton, QStackedLayout, QLabel +from qtawesome import icon + +from Rare.Components.Tabs.Games.GameInfo.GameInfo import InfoTabs +from Rare.Components.Tabs.Games.GameList import GameList +from Rare.Components.Tabs.Games.ImportWidget import ImportWidget +from Rare.utils.QtExtensions import SelectViewWidget + + +class GameTab(QWidget): + def __init__(self, core): + super(GameTab, self).__init__() + self.layout = QStackedLayout() + self.default_widget = Games(core) + self.default_widget.game_list.show_game_info.connect(self.show_info) + self.default_widget.head_bar.import_game.clicked.connect(lambda: self.layout.setCurrentIndex(2)) + self.layout.addWidget(self.default_widget) + self.game_info = InfoTabs(core) + self.game_info.info.update_list.connect(self.update_list) + self.layout.addWidget(self.game_info) + + self.default_widget.head_bar.refresh_list.clicked.connect(self.update_list) + + self.import_widget = ImportWidget(core) + self.layout.addWidget(self.import_widget) + self.import_widget.back_button.clicked.connect(lambda: self.layout.setCurrentIndex(0)) + self.import_widget.update_list.connect(self.update_list) + self.setLayout(self.layout) + + def update_list(self): + self.default_widget.game_list.update_list(self.default_widget.head_bar.view.isChecked()) + self.layout.setCurrentIndex(0) + + def show_info(self, app_name): + self.game_info.update_game(app_name) + self.game_info.setCurrentIndex(1) + self.layout.setCurrentIndex(1) + + +class Games(QWidget): + def __init__(self, core): + super(Games, self).__init__() + self.layout = QVBoxLayout() + + self.head_bar = GameListHeadBar() + self.head_bar.setObjectName("head_bar") + + self.game_list = GameList(core) + + self.head_bar.search_bar.textChanged.connect( + lambda: self.game_list.filter(self.head_bar.search_bar.text())) + + self.head_bar.installed_only.stateChanged.connect(lambda: + self.game_list.installed_only( + self.head_bar.installed_only.isChecked())) + self.layout.addWidget(self.head_bar) + self.layout.addWidget(self.game_list) + # self.layout.addStretch(1) + self.head_bar.view.toggled.connect(self.toggle_view) + + self.setLayout(self.layout) + + def toggle_view(self): + self.game_list.setCurrentIndex(1 if self.head_bar.view.isChecked() else 0) + settings = QSettings() + settings.setValue("icon_view", not self.head_bar.view.isChecked()) + + +class GameListHeadBar(QWidget): + def __init__(self): + super(GameListHeadBar, self).__init__() + self.layout = QHBoxLayout() + self.installed_only = QCheckBox(self.tr("Installed only")) + self.settings = QSettings() + self.installed_only.setChecked(self.settings.value("installed_only", False, bool)) + self.layout.addWidget(self.installed_only) + + self.layout.addStretch(1) + + self.import_game = QPushButton(icon("mdi.import", color="white"), self.tr("Import Game")) + self.layout.addWidget(self.import_game) + + self.layout.addStretch(1) + + self.search_bar = QLineEdit() + self.search_bar.setObjectName("search_bar") + self.search_bar.setFrame(False) + icon_label = QLabel() + icon_label.setPixmap(icon("fa.search", color="white").pixmap(QSize(20, 20))) + self.layout.addWidget(icon_label) + self.search_bar.setMinimumWidth(200) + self.search_bar.setPlaceholderText(self.tr("Search Game")) + self.layout.addWidget(self.search_bar) + + self.layout.addStretch(2) + + checked = QSettings().value("icon_view", True, bool) + + self.view = SelectViewWidget(checked) + self.layout.addWidget(self.view) + self.layout.addStretch(1) + self.refresh_list = QPushButton() + self.refresh_list.setIcon(icon("fa.refresh", color="white")) # Reload icon + self.layout.addWidget(self.refresh_list) + + self.setLayout(self.layout) diff --git a/Rare/Components/Tabs/Settings/About.py b/Rare/Components/Tabs/Settings/About.py index ab766115..4e9592da 100644 --- a/Rare/Components/Tabs/Settings/About.py +++ b/Rare/Components/Tabs/Settings/About.py @@ -1,6 +1,9 @@ -from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel +import webbrowser + +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton from Rare import __version__ +from Rare.utils.utils import get_latest_version class About(QWidget): @@ -13,6 +16,15 @@ class About(QWidget): self.version = QLabel("Version: " + __version__) self.layout.addWidget(self.version) + latest_tag = get_latest_version() + self.update_available = latest_tag != __version__ + if latest_tag != __version__: + print(f"Update available: {__version__} -> {latest_tag}") + self.update_available = QLabel(self.tr("Update available: {} -> {}").format(__version__, latest_tag)) + self.layout.addWidget(self.update_available) + self.open_browser = QPushButton(self.tr("Download latest release")) + self.layout.addWidget(self.open_browser) + self.open_browser.clicked.connect(lambda: webbrowser.open("https://github.com/Dummerle/Rare/releases/latest")) self.dev = QLabel(self.tr("Developer:") + "Dummerle") self.dev.setToolTip("Github") diff --git a/Rare/Components/Tabs/Settings/SettingsTab.py b/Rare/Components/Tabs/Settings/SettingsTab.py deleted file mode 100644 index dfe5443b..00000000 --- a/Rare/Components/Tabs/Settings/SettingsTab.py +++ /dev/null @@ -1,22 +0,0 @@ -import os - -from PyQt5.QtWidgets import QTabWidget - -from Rare.Components.Tabs.Settings.About import About -from Rare.Components.Tabs.Settings.Legendary import LegendarySettings -from Rare.Components.Tabs.Settings.Linux import LinuxSettings -from Rare.Components.Tabs.Settings.Rare import RareSettings -from Rare.utils.QtExtensions import SideTabBar - - -class SettingsTab(QTabWidget): - def __init__(self, core): - super(SettingsTab, self).__init__() - self.core = core - self.setTabBar(SideTabBar()) - self.setTabPosition(QTabWidget.West) - self.addTab(RareSettings(), "Rare") - self.addTab(LegendarySettings(core), "Legendary") - if os.name != "nt": - self.addTab(LinuxSettings(core), "Linux") - self.addTab(About(), "About") diff --git a/Rare/Components/Tabs/Settings/__init__.py b/Rare/Components/Tabs/Settings/__init__.py index e69de29b..6674fb52 100644 --- a/Rare/Components/Tabs/Settings/__init__.py +++ b/Rare/Components/Tabs/Settings/__init__.py @@ -0,0 +1,24 @@ +import os + +from PyQt5.QtWidgets import QTabWidget + +from Rare.Components.Tabs.Settings.About import About +from Rare.Components.Tabs.Settings.Legendary import LegendarySettings +from Rare.Components.Tabs.Settings.Linux import LinuxSettings +from Rare.Components.Tabs.Settings.Rare import RareSettings +from Rare.utils.QtExtensions import SideTabBar + + +class SettingsTab(QTabWidget): + def __init__(self, core): + super(SettingsTab, self).__init__() + self.core = core + self.setTabBar(SideTabBar()) + self.setTabPosition(QTabWidget.West) + self.addTab(RareSettings(), "Rare") + self.addTab(LegendarySettings(core), "Legendary") + if os.name != "nt": + self.addTab(LinuxSettings(core), "Linux") + self.about = About() + + self.addTab(self.about, "About (!)" if self.about.update_available else "About") diff --git a/Rare/__init__.py b/Rare/__init__.py index b9ef77b6..aeb56677 100644 --- a/Rare/__init__.py +++ b/Rare/__init__.py @@ -1,5 +1,5 @@ import os -__version__ = "0.9.8.3" +__version__ = "0.9.8" style_path = os.path.join(os.path.dirname(__file__), "Styles/") lang_path = os.path.join(os.path.dirname(__file__), "languages/") diff --git a/Rare/utils/utils.py b/Rare/utils/utils.py index 44440b29..859ab96f 100644 --- a/Rare/utils/utils.py +++ b/Rare/utils/utils.py @@ -7,11 +7,11 @@ import requests from PIL import Image, UnidentifiedImageError from PyQt5.QtCore import pyqtSignal, QLocale, QSettings -from Rare import lang_path +from Rare import lang_path, __version__ from custom_legendary.core import LegendaryCore logger = getLogger("Utils") -s = QSettings("Rare", "Rare") +s = QSettings() IMAGE_DIR = s.value("img_dir", os.path.expanduser("~/.cache/rare"), type=str) logger.info("IMAGE DIRECTORY: " + IMAGE_DIR) @@ -118,3 +118,9 @@ def get_possible_langs(): if i.endswith(".qm"): langs.append(i.split(".")[0]) return langs + + +def get_latest_version(): + resp = requests.get("https://api.github.com/repos/Dummerle/Rare/releases/latest") + tag = json.loads(resp.content.decode("utf-8"))["tag_name"] + return tag From 95f075bfe14003f955654c5e3b72496fd3eaf455 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 11:50:35 +0200 Subject: [PATCH 13/30] Restart app on logout, set logging to file --- Rare/Components/Tabs/Account/AccountWidget.py | 8 +++---- Rare/Main.py | 23 +++++++++++++++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Rare/Components/Tabs/Account/AccountWidget.py b/Rare/Components/Tabs/Account/AccountWidget.py index 79f08aa7..8dd68ae5 100644 --- a/Rare/Components/Tabs/Account/AccountWidget.py +++ b/Rare/Components/Tabs/Account/AccountWidget.py @@ -20,7 +20,8 @@ class MiniWidget(QWidget): self.layout.addWidget(QLabel(self.tr("Logged in as ") + username)) self.open_browser = QPushButton(self.tr("Account settings")) - self.open_browser.clicked.connect(lambda: webbrowser.open("https://www.epicgames.com/account/personal?productName=epicgames")) + self.open_browser.clicked.connect( + lambda: webbrowser.open("https://www.epicgames.com/account/personal?productName=epicgames")) self.layout.addWidget(self.open_browser) self.logout_button = QPushButton(self.tr("Logout")) @@ -35,6 +36,5 @@ class MiniWidget(QWidget): if reply == QMessageBox.Yes: self.core.lgd.invalidate_userdata() - QCoreApplication.exit() - - + # restart app + QCoreApplication.instance().exit(-133742) # restart exit code diff --git a/Rare/Main.py b/Rare/Main.py index bb3b1566..c1c6222a 100644 --- a/Rare/Main.py +++ b/Rare/Main.py @@ -2,6 +2,7 @@ import configparser import logging import os import sys +import time from PyQt5.QtCore import QSettings, QTranslator from PyQt5.QtGui import QIcon @@ -13,10 +14,17 @@ from Rare.Components.MainWindow import MainWindow from Rare.utils.utils import get_lang from custom_legendary.core import LegendaryCore +start_time = time.strftime('%y-%m-%d--%H:%M') # year-month-day-hour-minute +file_name = os.path.expanduser(f"~/.cache/rare/logs/Rare_{start_time}.log") +if not os.path.exists(os.path.dirname(file_name)): + os.makedirs(os.path.dirname(file_name)) + logging.basicConfig( format='[%(name)s] %(levelname)s: %(message)s', - level=logging.INFO -) + level=logging.INFO, + filename=file_name, + filemode="w" + ) logger = logging.getLogger("Rare") @@ -40,6 +48,7 @@ class App(QApplication): self.core.lgd.save_config() # set Application name for settings + self.mainwindow = None self.setApplicationName("Rare") self.setOrganizationName("Rare") settings = QSettings() @@ -70,5 +79,11 @@ class App(QApplication): def start(): - app = App() - app.exec_() + while True: + app = App() + exit_code = app.exec_() + # if not restart + if exit_code != -133742: + break + # restart app + del app From 92a4c318a3ac13331649e2a812f754bbfcb7d008 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 11:51:17 +0200 Subject: [PATCH 14/30] On update finished set Tabbar text, update readme and contributing --- Rare/Components/TabWidget.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Rare/Components/TabWidget.py b/Rare/Components/TabWidget.py index 08469487..4762ce78 100644 --- a/Rare/Components/TabWidget.py +++ b/Rare/Components/TabWidget.py @@ -23,7 +23,7 @@ class TabWidget(QTabWidget): self.addTab(self.game_list, self.tr("Games")) self.downloadTab = DownloadTab(core, updates) self.addTab(self.downloadTab, "Downloads" + (" (" + str(len(updates)) + ")" if len(updates) != 0 else "")) - self.downloadTab.finished.connect(self.game_list.default_widget.game_list.update_list) + self.downloadTab.finished.connect(self.dl_finished) self.game_list.default_widget.game_list.install_game.connect(lambda x: self.downloadTab.install_game(x)) self.game_list.game_info.info.verify_game.connect(lambda app_name: self.downloadTab.install_game( @@ -48,6 +48,10 @@ class TabWidget(QTabWidget): self.setIconSize(QSize(25, 25)) self.tabBar().setTabButton(3, self.tabBar().RightSide, TabButtonWidget(core)) + def dl_finished(self): + self.game_list.default_widget.game_list.update_list() + self.setTabText(1, "Downloads") + def resizeEvent(self, event): self.tabBar().setMinimumWidth(self.width()) super(TabWidget, self).resizeEvent(event) From e0eac1d22b015bc908de408ea9bdecbcde5344ad Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 11:52:03 +0200 Subject: [PATCH 15/30] Change default image dir; use qgroupbox --- CONTRIBUTING.md | 7 ++++--- README.md | 2 ++ Rare/Components/Tabs/Settings/Legendary.py | 9 +++++---- Rare/Components/Tabs/Settings/Rare.py | 10 +++++----- Rare/__init__.py | 2 +- Rare/utils/utils.py | 4 ++-- 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5df27713..7da7f6ba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,8 +2,6 @@ ## What you can do -To Contribute first fork the repository - ### Add translations 1. Execute ```pylupdate5 $(find -name "*.py") -ts Rare/languages/{your lang (two letters)}.ts``` in project directrory @@ -21,4 +19,7 @@ exmples: Select one Card of the project and implement it or make other changes -If you made your changes, create a pull request +##Git crash-course +To contribute fork the repository and clone **your** repo. Then make your changes, add it to git with `git add .` and upload it to Github with `git commit -m "message"` and `git push` or with your IDE. + +If you uploaded your changes, create a pull request diff --git a/README.md b/README.md index 7b227675..8f89c49a 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ There are more options to contribute. - If you are a designer, you can add Stylesheets or create a logo or a banner - You can translate the application +**Note:** Pull Requests please to "dev" branch + More Information is in CONTRIBUTING.md ## Images diff --git a/Rare/Components/Tabs/Settings/Legendary.py b/Rare/Components/Tabs/Settings/Legendary.py index 64cb402b..e3c36fef 100644 --- a/Rare/Components/Tabs/Settings/Legendary.py +++ b/Rare/Components/Tabs/Settings/Legendary.py @@ -10,14 +10,15 @@ from custom_legendary.core import LegendaryCore logger = getLogger("LegendarySettings") -class LegendarySettings(QWidget): +class LegendarySettings(QGroupBox): def __init__(self, core: LegendaryCore): super(LegendarySettings, self).__init__() + self.setTitle(self.tr("Legendary settings")) self.layout = QVBoxLayout() self.core = core - self.title = QLabel("

" + self.tr("Legendary settings") + "

") - self.layout.addWidget(self.title) - + #self.title = QLabel("

" + self.tr("Legendary settings") + "

") + #self.layout.addWidget(self.title) + self.setObjectName("group") # Default installation directory self.select_path = PathEdit(core.get_default_install_dir(), type_of_file=QFileDialog.DirectoryOnly, infotext="Default") diff --git a/Rare/Components/Tabs/Settings/Rare.py b/Rare/Components/Tabs/Settings/Rare.py index 6587ab1b..329de040 100644 --- a/Rare/Components/Tabs/Settings/Rare.py +++ b/Rare/Components/Tabs/Settings/Rare.py @@ -3,7 +3,7 @@ import shutil from logging import getLogger from PyQt5.QtCore import QSettings -from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QFileDialog, QComboBox, QPushButton, QCheckBox +from PyQt5.QtWidgets import QVBoxLayout, QFileDialog, QComboBox, QPushButton, QCheckBox, QGroupBox from Rare.Components.Tabs.Settings.SettingsWidget import SettingsWidget from Rare.utils.QtExtensions import PathEdit @@ -12,18 +12,18 @@ from Rare.utils.utils import get_lang, get_possible_langs logger = getLogger("RareSettings") -class RareSettings(QWidget): +class RareSettings(QGroupBox): def __init__(self): super(RareSettings, self).__init__() + self.setTitle(self.tr("Rare settings")) + self.setObjectName("group") self.layout = QVBoxLayout() - self.title = QLabel("

" + self.tr("Rare settings") + "

") - self.layout.addWidget(self.title) settings = QSettings() img_dir = settings.value("img_dir", type=str) language = settings.value("language", type=str) # default settings if not img_dir: - settings.setValue("img_dir", os.path.expanduser("~/.cache/rare/")) + settings.setValue("img_dir", os.path.expanduser("~/.cache/rare/images/")) if not language: settings.setValue("language", get_lang()) del settings diff --git a/Rare/__init__.py b/Rare/__init__.py index aeb56677..4b051139 100644 --- a/Rare/__init__.py +++ b/Rare/__init__.py @@ -1,5 +1,5 @@ import os -__version__ = "0.9.8" +__version__ = "0.9.9" style_path = os.path.join(os.path.dirname(__file__), "Styles/") lang_path = os.path.join(os.path.dirname(__file__), "languages/") diff --git a/Rare/utils/utils.py b/Rare/utils/utils.py index 859ab96f..39b7ff6c 100644 --- a/Rare/utils/utils.py +++ b/Rare/utils/utils.py @@ -11,8 +11,8 @@ from Rare import lang_path, __version__ from custom_legendary.core import LegendaryCore logger = getLogger("Utils") -s = QSettings() -IMAGE_DIR = s.value("img_dir", os.path.expanduser("~/.cache/rare"), type=str) +s = QSettings("Rare", "Rare") +IMAGE_DIR = s.value("img_dir", os.path.expanduser("~/.cache/rare/images/"), type=str) logger.info("IMAGE DIRECTORY: " + IMAGE_DIR) From a9e02ae1a50aa31939f53e8b63a6adfad05e1bf9 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 12:11:31 +0200 Subject: [PATCH 16/30] Close popup if download active --- Rare/Components/MainWindow.py | 14 +++++- Rare/languages/de.qm | Bin 17684 -> 18133 bytes Rare/languages/de.ts | 86 ++++++++++++++++++++-------------- 3 files changed, 64 insertions(+), 36 deletions(-) diff --git a/Rare/Components/MainWindow.py b/Rare/Components/MainWindow.py index a0116212..19027788 100644 --- a/Rare/Components/MainWindow.py +++ b/Rare/Components/MainWindow.py @@ -1,4 +1,5 @@ -from PyQt5.QtWidgets import QMainWindow +from PyQt5.QtGui import QCloseEvent +from PyQt5.QtWidgets import QMainWindow, QMessageBox from Rare.Components.TabWidget import TabWidget @@ -8,5 +9,14 @@ class MainWindow(QMainWindow): super(MainWindow, self).__init__() self.setGeometry(0, 0, 1200, 800) self.setWindowTitle("Rare - GUI for legendary") - self.setCentralWidget(TabWidget(core)) + self.tab_widget = TabWidget(core) + self.setCentralWidget(self.tab_widget) self.show() + + def closeEvent(self, e: QCloseEvent): + if self.tab_widget.downloadTab.active_game is None: + e.accept() + elif QMessageBox.question(self, "Close", self.tr("There is a download active. Do you really want to exit app?"), QMessageBox.Yes, QMessageBox.No) == QMessageBox.Yes: + e.accept() + else: + e.ignore() diff --git a/Rare/languages/de.qm b/Rare/languages/de.qm index e7d7940be62c485dacb6b014900e6c17213d2924..7312743e566e5ea5312b0b14ab84fa5292c5dfcb 100644 GIT binary patch delta 1546 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 diff --git a/Rare/languages/de.ts b/Rare/languages/de.ts index dc0d4079..016760a2 100644 --- a/Rare/languages/de.ts +++ b/Rare/languages/de.ts @@ -4,12 +4,12 @@ About - + Developer: Entwickler: - + Legendary developer: Legendary Entwickler: @@ -19,10 +19,20 @@ Dies ist eine beta version, also können Bugs entstehen. Wenn du einen Bug bemerkst, kontaktiere mich, indem du einen Issue auf <a href='https://github.com/Dummerle/Rare/issues'>Github</a> erstellst oder mir auf Discord eine Nachricht schickst. Ebenso bei einem Wunsch für Features - + This is a beta version, so you can get bugs. If you get a bug, please report it by creating a Issue on <a href='https://github.com/Dummerle/Rare/issues'>Github</a>. You can also contact me on Discord (Dummerle#7419). Or you can join the <a href='https://discord.gg/YvmABK9YSk'>Discord server</a> Dies ist eine Betaversion, also können Bugs und andere Unschönheiten auftreten. Falls ein Bug auftritt, bitte auf <a href='https://github.com/Dummerle/Rare/issues'>Github</a> melden, indem du einen Issue erstellst oder auf Discord. (Dummerle#7419). Ein Rare <a href='https://discord.gg/YvmABK9YSk'>Discord server</a> existiert ebenfalls + + + Update available: {} -> {} + Update verfügbar: {} -> {} + + + + Download latest release + Neueste Version herunterladen + BaseInstalledWidget @@ -88,27 +98,27 @@ Installation abgeschlossen - + Installing Game: No active download Installierendes Spiel: Kein aktiver Download - + Download speed Geschwindigkeit - + Cache used Benutzter Cache - + Downloaded Runtergeladen - + Time left: Zeit übrig: @@ -248,17 +258,17 @@ GameListHeadBar - + Installed only Nur Installierte - + Import Game Spiel importieren - + Search Game Spiel suchen @@ -335,7 +345,7 @@ ImportWidget - + Import Importieren @@ -360,7 +370,7 @@ Laden... - + Error: No valid session found Keine valide Session gefunden @@ -533,37 +543,37 @@ Installationsgröße: {} GB LegendarySettings - + Legendary settings Legendary Einstellungen - + Default installation directory Standardordner für Installationen - + Max workers for Download (Less: slower download)(0: Default) Maximale Anzahl Downloadprozesse (Weniger: langsamer)(Standard: 0) - + Cleanup Aufräumen - + Remove everything Alles aufräumen - + Clean, but keep manifests Aufräumen, aber Manifests behalten - + Cleanup complete! Successfully removed {} MB Fertig! Es wurden {} MB entfernt @@ -612,6 +622,14 @@ Installationsgröße: {} GB Dies öffnet den Browser, Einloggen und den Text kopieren + + MainWindow + + + There is a download active. Do you really want to exit app? + Ein Download läuft noch. Möchtest du die App wirklich beenden? + + MiniWidget @@ -625,12 +643,12 @@ Installationsgröße: {} GB Accounteinstellungen - + Logout Ausloggen - + Do you really want to logout Willst du dich wirklich abmelden @@ -659,7 +677,7 @@ Installationsgröße: {} GB RareSettings - + Rare settings Rare Einstellungen @@ -692,32 +710,32 @@ Installationsgröße: {} GB SyncSaves - + Cloud Saves Cloud Speicherstände - + Found Saves for folowing Games Spielstände für folgende Spiele gefunden - + Your games does not support Cloud Saves Deine Spiele unterstützen keine Online Speicherstände - + Sync all games Alle Spiele synchronisieren - + Found no savepath Kein Speicherort gefunden - + No save path was found. Please select path or skip Kein Speicherort wurde gefunden. Wähle einen Ordner oder überspringe @@ -800,22 +818,22 @@ Installationsgröße: {} GB Pfad ändern - + Uploading... Hochladen... - + Upload finished Hochladen abgeschlossen - + Downloading... Runterladen... - + Download finished Download abgeschlossen @@ -859,7 +877,7 @@ Installationsgröße: {} GB UpdateWidget - + Update Game Spiel updaten From 65f08007c11d0384182f2b08abfb8469659e7c7c Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 14:46:27 +0200 Subject: [PATCH 17/30] Add System Tray Icon + settings --- Rare/Components/MainWindow.py | 10 ++++++---- Rare/Components/TabWidget.py | 1 - Rare/Components/Tabs/Settings/Rare.py | 21 +++++++++++++-------- Rare/Components/TrayIcon.py | 22 ++++++++++++++++++++++ Rare/Main.py | 18 ++++++++++++++++-- Rare/__main__.py | 17 ----------------- 6 files changed, 57 insertions(+), 32 deletions(-) create mode 100644 Rare/Components/TrayIcon.py diff --git a/Rare/Components/MainWindow.py b/Rare/Components/MainWindow.py index 19027788..d22c9ea9 100644 --- a/Rare/Components/MainWindow.py +++ b/Rare/Components/MainWindow.py @@ -1,3 +1,4 @@ +from PyQt5.QtCore import QSettings from PyQt5.QtGui import QCloseEvent from PyQt5.QtWidgets import QMainWindow, QMessageBox @@ -14,9 +15,10 @@ class MainWindow(QMainWindow): self.show() def closeEvent(self, e: QCloseEvent): - if self.tab_widget.downloadTab.active_game is None: - e.accept() - elif QMessageBox.question(self, "Close", self.tr("There is a download active. Do you really want to exit app?"), QMessageBox.Yes, QMessageBox.No) == QMessageBox.Yes: + if QSettings().value("sys_tray", True, bool): + self.hide() + e.ignore() + elif self.tab_widget.downloadTab.active_game is not None and QMessageBox.question(self, "Close", self.tr("There is a download active. Do you really want to exit app?"), QMessageBox.Yes, QMessageBox.No) == QMessageBox.Yes: e.accept() else: - e.ignore() + e.accept() diff --git a/Rare/Components/TabWidget.py b/Rare/Components/TabWidget.py index 4762ce78..26b5adab 100644 --- a/Rare/Components/TabWidget.py +++ b/Rare/Components/TabWidget.py @@ -31,7 +31,6 @@ class TabWidget(QTabWidget): self.tabBarClicked.connect(lambda x: self.game_list.layout.setCurrentIndex(0) if x == 0 else None) - # Commented, because it is not finished self.cloud_saves = SyncSaves(core) self.addTab(self.cloud_saves, "Cloud Saves") diff --git a/Rare/Components/Tabs/Settings/Rare.py b/Rare/Components/Tabs/Settings/Rare.py index 329de040..966fdc52 100644 --- a/Rare/Components/Tabs/Settings/Rare.py +++ b/Rare/Components/Tabs/Settings/Rare.py @@ -19,14 +19,9 @@ class RareSettings(QGroupBox): self.setObjectName("group") self.layout = QVBoxLayout() settings = QSettings() - img_dir = settings.value("img_dir", type=str) - language = settings.value("language", type=str) - # default settings - if not img_dir: - settings.setValue("img_dir", os.path.expanduser("~/.cache/rare/images/")) - if not language: - settings.setValue("language", get_lang()) - del settings + img_dir = settings.value("img_dir",os.path.expanduser("~/.cache/rare/images/"), type=str) + language = settings.value("language", get_lang(), type=str) + # select Image dir self.select_path = PathEdit(img_dir, type_of_file=QFileDialog.DirectoryOnly) self.select_path.text_edit.textChanged.connect(lambda t: self.save_path_button.setDisabled(False)) @@ -50,6 +45,12 @@ class RareSettings(QGroupBox): self.select_lang.currentIndexChanged.connect(self.update_lang) self.layout.addWidget(self.lang_widget) + self.exit_to_sys_tray = QCheckBox("Hide to System Tray Icon") + self.exit_to_sys_tray.setChecked(settings.value("sys_tray", True, bool)) + self.exit_to_sys_tray.stateChanged.connect(self.update_sys_tray) + self.sys_tray_widget = SettingsWidget(self.tr("Exit to System Tray Icon"), self.exit_to_sys_tray) + self.layout.addWidget(self.sys_tray_widget) + self.game_start_accept = QCheckBox(self.tr("Confirm launch of game")) self.game_start_accept.stateChanged.connect(self.update_start_confirm) self.game_start_accept_widget = SettingsWidget(self.tr("Confirm launch of game"), self.game_start_accept) @@ -59,6 +60,10 @@ class RareSettings(QGroupBox): self.setLayout(self.layout) + def update_sys_tray(self): + settings = QSettings() + settings.setValue("sys_tray", self.exit_to_sys_tray.isChecked()) + def update_start_confirm(self): settings = QSettings() settings.setValue("confirm_start", self.game_start_accept.isChecked()) diff --git a/Rare/Components/TrayIcon.py b/Rare/Components/TrayIcon.py new file mode 100644 index 00000000..3b80f061 --- /dev/null +++ b/Rare/Components/TrayIcon.py @@ -0,0 +1,22 @@ +from PyQt5.QtCore import QCoreApplication +from PyQt5.QtWidgets import QSystemTrayIcon, QMenu, QAction +from qtawesome import icon + + +class TrayIcon(QSystemTrayIcon): + def __init__(self, parent): + super(TrayIcon, self).__init__(parent) + self.setIcon(icon("ei.cogs", color="white")) + self.setVisible(True) + self.setToolTip("Rare") + + self.menu = QMenu() + + self.start_rare = QAction("Rare") + self.menu.addAction(self.start_rare) + + self.exit_action = QAction(self.tr("Exit")) + self.menu.addSeparator() + self.menu.addAction(self.exit_action) + + self.setContextMenu(self.menu) diff --git a/Rare/Main.py b/Rare/Main.py index c1c6222a..a4283de4 100644 --- a/Rare/Main.py +++ b/Rare/Main.py @@ -6,11 +6,12 @@ import time from PyQt5.QtCore import QSettings, QTranslator from PyQt5.QtGui import QIcon -from PyQt5.QtWidgets import QApplication +from PyQt5.QtWidgets import QApplication, QSystemTrayIcon from Rare import lang_path, style_path from Rare.Components.Launch.LaunchDialog import LaunchDialog from Rare.Components.MainWindow import MainWindow +from Rare.Components.TrayIcon import TrayIcon from Rare.utils.utils import get_lang from custom_legendary.core import LegendaryCore @@ -67,6 +68,8 @@ class App(QApplication): self.setStyleSheet(open(style_path + "RareStyle.qss").read()) self.setWindowIcon(QIcon(style_path + "Logo.png")) + # tray icon + # launch app self.launch_dialog = LaunchDialog(self.core) self.launch_dialog.start_app.connect(self.start_app) @@ -74,9 +77,20 @@ class App(QApplication): def start_app(self): self.mainwindow = MainWindow(self.core) - # close launch dialog after app widgets were created + self.tray_icon = TrayIcon(self) + self.tray_icon.exit_action.triggered.connect(lambda: exit(0)) + self.tray_icon.start_rare.triggered.connect(self.mainwindow.show) + self.tray_icon.activated.connect(self.tray) + self.mainwindow.tab_widget.downloadTab.finished.connect(lambda: self.tray_icon.showMessage( + self.tr("Download finished"), self.tr("Download finished. Game is playable now"), + QSystemTrayIcon.Information, 4000)) self.launch_dialog.close() + def tray(self, reason): + if reason == QSystemTrayIcon.DoubleClick: + self.mainwindow.show() + logger.info("Show App") + def start(): while True: diff --git a/Rare/__main__.py b/Rare/__main__.py index 77a3c6b8..209f7c10 100644 --- a/Rare/__main__.py +++ b/Rare/__main__.py @@ -14,20 +14,3 @@ def main(): if __name__ == '__main__': main() - -""" - tray = QSystemTrayIcon() - tray.setIcon(icon("fa.gamepad", color="white")) - tray.setVisible(True) - menu = QMenu() - option1 = QAction("Geeks for Geeks") - option1.triggered.connect(lambda: app.exec_()) - option2 = QAction("GFG") - menu.addAction(option1) - menu.addAction(option2) - # To quit the app - quit = QAction("Quit") - quit.triggered.connect(app.quit) - menu.addAction(quit) - # Adding options to the System Tray - tray.setContextMenu(menu)""" From e0262620e0a4e5b8d3c9eac2a8003ce20178701b Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 14:46:51 +0200 Subject: [PATCH 18/30] Add Download queue and fixed bug in AccountWidget.py --- Rare/Components/Tabs/Downloads/DownloadTab.py | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py index 3a546081..71ae2d2a 100644 --- a/Rare/Components/Tabs/Downloads/DownloadTab.py +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -6,7 +6,7 @@ import time from logging import getLogger from multiprocessing import Queue as MPQueue -from PyQt5.QtCore import QThread, pyqtSignal, Qt +from PyQt5.QtCore import QThread, pyqtSignal, Qt, QCoreApplication from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, QDialog, \ QListWidget, QHBoxLayout @@ -123,6 +123,7 @@ class DownloadThread(QThread): class DownloadTab(QWidget): finished = pyqtSignal() thread: QThread + dl_queue = [] def __init__(self, core: LegendaryCore, updates: list): super(DownloadTab, self).__init__() @@ -158,6 +159,9 @@ class DownloadTab(QWidget): self.layout.addLayout(self.mini_layout) + self.queue_label = QLabel(self.tr("Download queue: Empty")) + self.layout.addWidget(self.queue_label) + self.update_title = QLabel(f"

{self.tr('Updates')}

") self.update_title.setStyleSheet(""" QLabel{ @@ -183,8 +187,13 @@ class DownloadTab(QWidget): def stop_download(self): self.thread.kill = True - def install_game(self, options: InstallOptions): - game = self.core.get_game(options.app_name, update_meta=True) + def install_game(self, options: InstallOptions, no_confirm=False): + if self.active_game is not None: + self.dl_queue.append(options) + self.queue_label.setText(self.tr("Download queue: ") + ", ".join(self.core.get_game(i.app_name).app_title for i in self.dl_queue)) + return + self.queue_label.setText( + self.tr("Download queue: ") + ", ".join(self.core.get_game(i.app_name).app_title for i in self.dl_queue)) status_queue = MPQueue() try: dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download( @@ -222,8 +231,9 @@ class DownloadTab(QWidget): QMessageBox.information(self, "Warning", self.tr("Download size is 0. Game already exists")) return # Information - if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept(): - return + if not no_confirm: + if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept(): + return self.active_game = game self.thread = DownloadThread(dlm, self.core, status_queue, igame, options.repair, repair_file) @@ -231,10 +241,7 @@ class DownloadTab(QWidget): self.thread.statistics.connect(self.statistics) self.thread.start() self.kill_button.setDisabled(False) - self.installing_game.setText("Installing Game: " + self.active_game.app_title) - - for i in self.update_widgets.values(): - i.update_button.setDisabled(True) + self.installing_game.setText(self.tr("Installing Game: ") + self.active_game.app_title) def sdl_prompt(self, app_name: str = '', title: str = '') -> list: sdl = QDialog() @@ -279,6 +286,7 @@ class DownloadTab(QWidget): pass elif text == "finish": self.installing_game.setText(self.tr("Download finished. Reload library")) + try: from notifypy import Notify except ModuleNotFoundError: @@ -290,10 +298,16 @@ class DownloadTab(QWidget): notification.send() # QMessageBox.information(self, "Info", "Download finished") logger.info("Download finished: " + self.active_game.app_title) + + if self.dl_queue[0].app_name == self.active_game.app_name: + self.dl_queue.pop(0) + if self.active_game.app_name in self.update_widgets.keys(): self.update_widgets[self.active_game.app_name].setVisible(False) self.update_widgets.pop(self.active_game.app_name) + self.active_game = None + for i in self.update_widgets.values(): i.update_button.setDisabled(False) @@ -305,6 +319,12 @@ class DownloadTab(QWidget): self.cache_used.setText("") self.downloaded.setText("") self.time_left.setText("") + + if len(self.dl_queue) != 0: + self.install_game(self.dl_queue[0]) + else: + self.queue_label.setText(self.tr("Download queue: Empty")) + elif text == "error": QMessageBox.warning(self, "warn", "Download error") @@ -327,16 +347,13 @@ class DownloadTab(QWidget): return str(datetime.timedelta(seconds=seconds)) def update_game(self, app_name: str): - print("Update ", app_name) + logger.info("Update " + app_name) infos = InstallDialog(app_name, self.core, True).get_information() if infos != 0: path, max_workers, force, ignore_free_space = infos self.install_game(InstallOptions(app_name=app_name, max_workers=max_workers, path=path, force=force, ignore_free_space=ignore_free_space)) - def repair(self): - pass - class UpdateWidget(QWidget): update = pyqtSignal(str) From 033a02c8c8408d25256bee852983f23e643f7481 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 14:53:55 +0200 Subject: [PATCH 19/30] fixed bug in AccountWidget.py --- Rare/Components/Tabs/Account/AccountWidget.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Rare/Components/Tabs/Account/AccountWidget.py b/Rare/Components/Tabs/Account/AccountWidget.py index 8dd68ae5..8aff5de0 100644 --- a/Rare/Components/Tabs/Account/AccountWidget.py +++ b/Rare/Components/Tabs/Account/AccountWidget.py @@ -12,12 +12,12 @@ class MiniWidget(QWidget): self.layout = QVBoxLayout() self.core = core self.layout.addWidget(QLabel("Account")) - username = str(self.core.lgd.userdata.get("display_name")) + username = self.core.lgd.userdata.get("display_name") if not username: self.core.login() - username = str(self.core.lgd.userdata.get("display_name")) + username = self.core.lgd.userdata.get("display_name") - self.layout.addWidget(QLabel(self.tr("Logged in as ") + username)) + self.layout.addWidget(QLabel(self.tr("Logged in as ") + str(username))) self.open_browser = QPushButton(self.tr("Account settings")) self.open_browser.clicked.connect( @@ -37,4 +37,4 @@ class MiniWidget(QWidget): if reply == QMessageBox.Yes: self.core.lgd.invalidate_userdata() # restart app - QCoreApplication.instance().exit(-133742) # restart exit code + QCoreApplication.instance().exit_action(-133742) # restart exit code From 4237358909ce896c11508db5108b96d18a648e42 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 15:34:52 +0200 Subject: [PATCH 20/30] Download Tab: Show Summary direct; more translations --- Rare/Components/Tabs/Downloads/DownloadTab.py | 30 +++--- Rare/languages/de.qm | Bin 18133 -> 19018 bytes Rare/languages/de.ts | 86 ++++++++++++++---- 3 files changed, 84 insertions(+), 32 deletions(-) diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py index 71ae2d2a..3dcbe6a6 100644 --- a/Rare/Components/Tabs/Downloads/DownloadTab.py +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -187,13 +187,8 @@ class DownloadTab(QWidget): def stop_download(self): self.thread.kill = True - def install_game(self, options: InstallOptions, no_confirm=False): - if self.active_game is not None: - self.dl_queue.append(options) - self.queue_label.setText(self.tr("Download queue: ") + ", ".join(self.core.get_game(i.app_name).app_title for i in self.dl_queue)) - return - self.queue_label.setText( - self.tr("Download queue: ") + ", ".join(self.core.get_game(i.app_name).app_title for i in self.dl_queue)) + def install_game(self, options: InstallOptions): + status_queue = MPQueue() try: dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download( @@ -230,10 +225,21 @@ class DownloadTab(QWidget): if not analysis.dl_size: QMessageBox.information(self, "Warning", self.tr("Download size is 0. Game already exists")) return + # Information - if not no_confirm: - if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept(): - return + + if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept(): + return + + if self.active_game is None: + self.start_installation(dlm, game, status_queue, igame, repair_file, options) + else: + self.dl_queue.append((dlm, game, status_queue, igame, repair_file, options)) + self.queue_label.setText( + self.tr("Download queue: ") + ", ".join( + i[1].app_title for i in self.dl_queue) if self.dl_queue else self.tr("Empty")) + + def start_installation(self, dlm, game, status_queue, igame, repair_file, options: InstallOptions): self.active_game = game self.thread = DownloadThread(dlm, self.core, status_queue, igame, options.repair, repair_file) @@ -299,7 +305,7 @@ class DownloadTab(QWidget): # QMessageBox.information(self, "Info", "Download finished") logger.info("Download finished: " + self.active_game.app_title) - if self.dl_queue[0].app_name == self.active_game.app_name: + if self.dl_queue[0][1] == self.active_game.app_name: self.dl_queue.pop(0) if self.active_game.app_name in self.update_widgets.keys(): @@ -321,7 +327,7 @@ class DownloadTab(QWidget): self.time_left.setText("") if len(self.dl_queue) != 0: - self.install_game(self.dl_queue[0]) + self.start_installation(*self.dl_queue[0]) else: self.queue_label.setText(self.tr("Download queue: Empty")) diff --git a/Rare/languages/de.qm b/Rare/languages/de.qm index 7312743e566e5ea5312b0b14ab84fa5292c5dfcb..9022e87c32cc0e733c65dc7a23da9eed60552a86 100644 GIT binary patch delta 1889 zcmaJ>3rtf76g{PV^!r+*bo@jfDi#Ehj|fx-iUn6inNk;jI4V@>R@zF7(4p4pbh^zc z7M&; zy!8<-Mg+l!TY-pMK2)<1{3rl&PJ-Ed76^~_VfyEeH0eG0ECk<>&wN!a9ba`kQ9em&*(E`vU9t0#gc+bLF64dOc>meSk#&6O(4hYiueRq6=ixjQ&J268ABrf+m z6;BV8kNy5~{!PRTUyx1{1}*0Yv{7UBDL*iHBPr7GLz+^6gjM`_FC(yw~G;1rhgMSAl>QL9%-YkhVgw zS$7nOUGKxxLq5!TENHs6lPo_dI3k`&jk_4ZA`%vr&cvMAOiuLpFb+)8MLjvx%B0>t z1B`mW6wDzZahX1hpU%83tp|o#n6l;`U_c4;iL1oaJFGnfYJX;PZU9Br)x zqV-I(r^(lG! zTzGRSg)L&WutRx^R#7MHa6TcSKZw!>tN~sx6X~XskhFHulzntREnlQhNgyY4Mb&$L z0|eJa+m@W5#=)Z2-A{pmyTzkwrvo9weHc|K&U_=A-e2t!XEwG{I%CB3sUpgNT6}lu zav*uX_>t!(FsNSq+(tq|+9biV4%5!2NMerBwf3?kZsjrx-AGC1*CAAIlgwGCA>lhE zHN8IrDeaOK4_5#gV#&T(Cxy9Ja^_UMQkT`ma;UPeG&fzE<_HJ^S0CO;;zbv71JL` z>;-uYS4dykC3&K42YtsM$!83iOy9L!Zl6m+#1Zl)fsh1km7fdKQdqaiTX$tpSP#kD z{-j1BHS%s{H956WG2ZKJPJtr7h#D(yE2i(_(?E%e;$LzpTZxMDwy&sQbg!bmXeU{C zMse~Cg)O_%1mp&Lx&--*dtdg%5dJuujYpNRK?^IYV1XG1Slz|!h-eP)(z%6x9NvKu z4!lPPhXr(QrwS9H*?noPv%zVYXEt&c zYZc7~Z%a>-WG_EF8RLlCjuMnnS1;QQIxHxK(OsLWjI=`oj*?4%4|D|d_gGkItTbvk z_rd^Gc;^3eB=m7$67iad+1SsfdX+7=+~#n4DLuE?@w_m}f7CGlz7wr5@OTMsMYzh{ z5D*&PPdh+ou{#WAv&pic&j1bQSr||i<0?f?pM-L%zlxCDYLkO=Sh+%{-C-=}^r8xb xlhc)0E$;Q2*F2r+=cEaN)Z-Q7`|AbXdhf18dMfOz%Sw-R8*_s^ceM9-{{SKL;4uII delta 1288 zcmX9-Yfw}L7(Kgp@9y4v_ud6I-YhKgS{^RKvH^y?MJSLAoIwJD8jYqXX0S#|U5Rm! zQ9~XYuOTxTwHjd9Nr&Vm?@XI%(9~&kM9LT@#qb3s)Mxwa-0$8y-#Ono-*>S|cJ;9A zh~BE0f}*Ak@Z4iZ|I=Wb#{sutJ6f#pa{3O?{sPO-0|1+bc#kGPSc~M$96-H@3{wN( zDk7_T6i7IN&H4bqd=NQ>tAH{C`KNaSM!}Az3FJSc+QIjKv?3`7@4x*B2Ne?IWvSa|7_Lmn|Rg2Ew(n zXPzR!pOa@SQ=cSO{z{zzaQsbvZis{`tK`FZ&VWfT9|<#9=|i1-p>l~PIL;_yXfh`~ z<8YaXg=379>rqm)o^k6o17W^Q7)PaJwjGVHGCP0B2K=uwwMB0MA)A=`*M0|Fx|xnX z8?fOi(>1RE^byS9^E!S>p?`24kV+JhjebB>m7+Pr+7ASU(gUG6$Bqd@itZboWOc9N zJhzRAYgolcB+Pr7HI6j{&P{eSwy_bHa>=2;Sj*ipAo38KT|h#N*X$VlfPF(~A%PrQ zI(qz9ngw>iR!j|&*-Ld~r9;0`_k9ImIji)a`vX{8 zrmT;q(cN2>t!YH4iBMkoh9-(#P~O)p5>~3rLo`v`jLJOg4@69=5@SaIgQQ9`>xeK@ zReq%h5aU(5f89?)hE>;{KO-wwRMwfjv|OHks#)C~$~IgzTlts->`+HL){^r()EQey zNYu1?b1%J*dRd)oHc_M7>RmlI0mUu#i9N$a?5v*p_8-7w6Bkv#l^hzkqmKg@m!3d` zeq3C~6b(4awOG_N&ZEo)bpJle z_97qmwHrkw@C9}2NqHy#$=}m7X*plLSPi_W;(LQDfxx4De-jZV*7AcH9YAmof2)9Y zDZECIo!v{zC)v?3FBmq`vIbZ)g^-DDw9Re8!BQVUS0YU08mQqTVJ66toaqw&>^lMY z6lr{Wf1<0X)I<)qk^pG7=(ZDqN%Q(X3sr|T6%ItCSMly6ot_A~rmvA9;(|Q_)RU zwTc(!%ILnIiWk46#(|7DXBelbo`|bqkIBJ4N%j>*6I>w$7SovpZI_IO93c9Vw66Rl zoo9fQ=a)s-a7L;qB0jadH0hCwJZv~FO`VDb{Boqr_o%Upi}XadiyUawraZSSAxoQC zC{gBB+O4M;AjYIEzP6FF{!v?Y`3yy}q-`neBxgpn7shCZ;ws*u-0s-I*i5#uDBaE0 gBs4I#Wo?Sg_O4@z%oeJ9(`XBcn-pyR>4Eb90H?526#xJL diff --git a/Rare/languages/de.ts b/Rare/languages/de.ts index 016760a2..92f56cc6 100644 --- a/Rare/languages/de.ts +++ b/Rare/languages/de.ts @@ -34,6 +34,19 @@ Neueste Version herunterladen
+ + App + + + Download finished + Download abgeschlossen + + + + Download finished. Game is playable now + Downlaod abgeschlossen. Spiel kann jetzt gespielt werden + + BaseInstalledWidget @@ -68,70 +81,90 @@ DownloadTab - + No active Download Kein aktiver Download - + Stop Download Download anhalten - + No updates available Keine Updates verfügbar - + Error preparing download Fehler beim Vorbereiten des Downloads - + Download size is 0. Game already exists Die Größe des Downloads ist 0. Spiel existiert bereits - + Installation finished Installation abgeschlossen - + Installing Game: No active download Installierendes Spiel: Kein aktiver Download - + Download speed Geschwindigkeit - + Cache used Benutzter Cache - + Downloaded Runtergeladen - + Time left: Zeit übrig: - + Finished Download of game {} Downlaod von {} abgeschlossen - + Download finished. Reload library Download abgeschlossen. Spiele neu laden + + + Download queue: Empty + Anschließende Downloads: Keine + + + + Download queue: + Anschließende Downloads: + + + + Empty + Keine + + + + Installing Game: + Installierendes Spiel: + DxvkWidget @@ -625,7 +658,7 @@ Installationsgröße: {} GB MainWindow - + There is a download active. Do you really want to exit app? Ein Download läuft noch. Möchtest du die App wirklich beenden? @@ -682,30 +715,35 @@ Installationsgröße: {} GB Rare Einstellungen - + Save Speichern - + Image Directory Ordner für Bilder - + Language Sprache - + Restart Application to activate changes Starte die App neu um die Änderungen zu aktivieren - + Confirm launch of game Start des Spiels bestätigen + + + Exit to System Tray Icon + Beim verlassen auf das System Tray Icon minimieren + SyncSaves @@ -846,6 +884,14 @@ Installationsgröße: {} GB Spiele + + TrayIcon + + + Exit + Schließen + + UninstallDialog @@ -877,7 +923,7 @@ Installationsgröße: {} GB UpdateWidget - + Update Game Spiel updaten From d8aa6541b910f18c85390a17e81179c2643c8cfc Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 16:50:36 +0200 Subject: [PATCH 21/30] Download queue; delete option --- .../Tabs/Downloads/DlQueueWidget.py | 93 ++++++++++++ Rare/Components/Tabs/Downloads/DownloadTab.py | 139 +++--------------- .../Tabs/Downloads/DownloadThread.py | 117 +++++++++++++++ Rare/Components/Tabs/Settings/Rare.py | 2 +- 4 files changed, 232 insertions(+), 119 deletions(-) create mode 100644 Rare/Components/Tabs/Downloads/DlQueueWidget.py create mode 100644 Rare/Components/Tabs/Downloads/DownloadThread.py diff --git a/Rare/Components/Tabs/Downloads/DlQueueWidget.py b/Rare/Components/Tabs/Downloads/DlQueueWidget.py new file mode 100644 index 00000000..b3e5535b --- /dev/null +++ b/Rare/Components/Tabs/Downloads/DlQueueWidget.py @@ -0,0 +1,93 @@ +from PyQt5.QtCore import pyqtSignal +from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, QWidget +from qtawesome import icon + + +class DlWidget(QWidget): + move_up = pyqtSignal(str) # app_name + move_down = pyqtSignal(str) # app_name + remove = pyqtSignal(str) # app_name + + def __init__(self, index, item): + super(DlWidget, self).__init__() + self.app_name = item[1].app_name + self.layout = QHBoxLayout() + + self.left_layout = QVBoxLayout() + self.move_up_button = QPushButton(icon("fa.arrow-up", color="white"), "") + if index == 0: + self.move_up_button.setDisabled(True) + self.move_up_button.clicked.connect(lambda: self.move_up.emit(self.app_name)) + self.move_up_button.setFixedWidth(20) + self.left_layout.addWidget(self.move_up_button) + + self.move_down_buttton = QPushButton(icon("fa.arrow-down", color="white"), "") + self.move_down_buttton.clicked.connect(lambda: self.move_down.emit(self.app_name)) + self.left_layout.addWidget(self.move_down_buttton) + self.move_down_buttton.setFixedWidth(20) + + self.layout.addLayout(self.left_layout) + + self.right_layout = QVBoxLayout() + self.title = QLabel(item[1].app_title) + self.right_layout.addWidget(self.title) + + dl_size = item[-1].dl_size + install_size = item[-1].install_size + + self.size = QHBoxLayout() + + self.size.addWidget(QLabel(self.tr("Download size: {} GB").format(dl_size/1024**3))) + self.size.addWidget(QLabel(self.tr("Install size: {} GB").format(install_size/1024**3))) + self.right_layout.addLayout(self.size) + + self.delete = QPushButton(self.tr("Remove Download")) + self.delete.clicked.connect(lambda: self.remove.emit(self.app_name)) + self.right_layout.addWidget(self.delete) + + self.layout.addLayout(self.right_layout) + self.setLayout(self.layout) + + +class DlQueueWidget(QGroupBox): + update_list = pyqtSignal(list) + dl_queue = [] + def __init__(self): + + super(DlQueueWidget, self).__init__() + self.setTitle(self.tr("Download Queue")) + self.layout = QVBoxLayout() + self.setObjectName("group") + self.text = QLabel(self.tr("No downloads in queue")) + self.layout.addWidget(self.text) + + self.setLayout(self.layout) + + def update_queue(self, dl_queue: list): + self.dl_queue = dl_queue + self.setLayout(QVBoxLayout()) + QWidget().setLayout(self.layout) + self.layout = QVBoxLayout() + + if len(dl_queue) == 0: + self.layout.addWidget(QLabel(self.tr("No downloads in queue"))) + self.setLayout(self.layout) + return + + for index, item in enumerate(dl_queue): + widget = DlWidget(index, item) + widget.remove.connect(self.remove) + self.layout.addWidget(widget) + + self.setLayout(self.layout) + + def remove(self, app_name): + for index, i in enumerate(self.dl_queue): + if i[1].app_name == app_name: + self.dl_queue.pop(index) + break + else: + print("BUG! ", app_name) + return + self.update_list.emit(self.dl_queue) + self.update_queue(self.dl_queue) diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py index 3dcbe6a6..bb5faff7 100644 --- a/Rare/Components/Tabs/Downloads/DownloadTab.py +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -1,19 +1,16 @@ import datetime -import os -import queue -import subprocess -import time from logging import getLogger from multiprocessing import Queue as MPQueue -from PyQt5.QtCore import QThread, pyqtSignal, Qt, QCoreApplication +from PyQt5.QtCore import QThread, pyqtSignal, Qt from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, QDialog, \ QListWidget, QHBoxLayout from Rare.Components.Dialogs.InstallDialog import InstallInfoDialog, InstallDialog -from Rare.utils.Models import InstallOptions, KillDownloadException +from Rare.Components.Tabs.Downloads.DlQueueWidget import DlQueueWidget +from Rare.Components.Tabs.Downloads.DownloadThread import DownloadThread +from Rare.utils.Models import InstallOptions from custom_legendary.core import LegendaryCore -from custom_legendary.downloader.manager import DLManager from custom_legendary.models.downloading import UIUpdate from custom_legendary.models.game import Game, InstalledGame from custom_legendary.utils.selective_dl import games @@ -21,105 +18,6 @@ from custom_legendary.utils.selective_dl import games logger = getLogger("Download") -class DownloadThread(QThread): - status = pyqtSignal(str) - statistics = pyqtSignal(UIUpdate) - kill = False - - def __init__(self, dlm: DLManager, core: LegendaryCore, status_queue: MPQueue, igame, repair=False, - repair_file=None): - super(DownloadThread, self).__init__() - self.dlm = dlm - self.core = core - self.status_queue = status_queue - self.igame = igame - self.repair = repair - self.repair_file = repair_file - - def run(self): - start_time = time.time() - try: - - self.dlm.start() - time.sleep(1) - while self.dlm.is_alive(): - if self.kill: - # raise KillDownloadException() - # TODO kill download queue, workers - pass - try: - self.statistics.emit(self.status_queue.get(timeout=1)) - except queue.Empty: - pass - - self.dlm.join() - - except KillDownloadException: - self.status.emit("stop") - logger.info("Downlaod can be continued later") - self.dlm.kill() - return - - except Exception as e: - logger.error(f"Installation failed after {time.time() - start_time:.02f} seconds: {e}") - self.status.emit("error") - return - - 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: - self._handle_postinstall(postinstall, self.igame) - - dlcs = self.core.get_dlc_for_game(self.igame.app_name) - if dlcs: - print('The following DLCs are available for this game:') - for dlc in dlcs: - print(f' - {dlc.app_title} (App name: {dlc.app_name}, version: {dlc.app_version})') - print('Manually installing DLCs works the same; just use the DLC app name instead.') - - # install_dlcs = QMessageBox.question(self, "", "Do you want to install the prequisites", QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes - # TODO - if game.supports_cloud_saves and not game.is_dlc: - logger.info('This game supports cloud saves, syncing is handled by the "sync-saves" command.') - logger.info(f'To download saves for this game run "legendary sync-saves {game.app_name}"') - old_igame = self.core.get_installed_game(game.app_name) - if old_igame and self.repair and os.path.exists(self.repair_file): - if old_igame.needs_verification: - old_igame.needs_verification = False - self.core.install_game(old_igame) - - logger.debug('Removing repair file.') - os.remove(self.repair_file) - if old_igame and old_igame.install_tags != self.igame.install_tags: - old_igame.install_tags = self.igame.install_tags - self.logger.info('Deleting now untagged files.') - self.core.uninstall_tag(old_igame) - self.core.install_game(old_igame) - - self.status.emit("finish") - - def _handle_postinstall(self, postinstall, igame): - print('This game lists the following prequisites to be installed:') - print(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}') - if os.name == 'nt': - if QMessageBox.question(self, "", "Do you want to install the prequisites", - QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: - self.core.prereq_installed(igame.app_name) - req_path, req_exec = os.path.split(postinstall['path']) - work_dir = os.path.join(igame.install_path, req_path) - fullpath = os.path.join(work_dir, req_exec) - subprocess.call([fullpath, postinstall['args']], cwd=work_dir) - else: - self.core.prereq_installed(self.igame.app_name) - - else: - logger.info('Automatic installation not available on Linux.') - - class DownloadTab(QWidget): finished = pyqtSignal() thread: QThread @@ -159,8 +57,9 @@ class DownloadTab(QWidget): self.layout.addLayout(self.mini_layout) - self.queue_label = QLabel(self.tr("Download queue: Empty")) - self.layout.addWidget(self.queue_label) + self.queue_widget = DlQueueWidget() + self.layout.addWidget(self.queue_widget) + self.queue_widget.update_list.connect(self.update_dl_queue) self.update_title = QLabel(f"

{self.tr('Updates')}

") self.update_title.setStyleSheet(""" @@ -184,6 +83,10 @@ class DownloadTab(QWidget): self.setLayout(self.layout) + def update_dl_queue(self, dl_queue): + self.dl_queue = dl_queue + + def stop_download(self): self.thread.kill = True @@ -232,15 +135,13 @@ class DownloadTab(QWidget): return if self.active_game is None: - self.start_installation(dlm, game, status_queue, igame, repair_file, options) + self.start_installation(dlm, game, status_queue, igame, repair_file, options, analysis) else: - self.dl_queue.append((dlm, game, status_queue, igame, repair_file, options)) - self.queue_label.setText( - self.tr("Download queue: ") + ", ".join( - i[1].app_title for i in self.dl_queue) if self.dl_queue else self.tr("Empty")) - - def start_installation(self, dlm, game, status_queue, igame, repair_file, options: InstallOptions): + self.dl_queue.append((dlm, game, status_queue, igame, repair_file, options, analysis)) + self.queue_widget.update_queue(self.dl_queue) + def start_installation(self, dlm, game, status_queue, igame, repair_file, options: InstallOptions, analysis): + print("start installation", game.app_title) self.active_game = game self.thread = DownloadThread(dlm, self.core, status_queue, igame, options.repair, repair_file) self.thread.status.connect(self.status) @@ -305,8 +206,10 @@ class DownloadTab(QWidget): # QMessageBox.information(self, "Info", "Download finished") logger.info("Download finished: " + self.active_game.app_title) - if self.dl_queue[0][1] == self.active_game.app_name: - self.dl_queue.pop(0) + if self.dl_queue: + if self.dl_queue[0][1] == self.active_game.app_name: + self.dl_queue.pop(0) + self.queue_widget.update_queue(self.dl_queue) if self.active_game.app_name in self.update_widgets.keys(): self.update_widgets[self.active_game.app_name].setVisible(False) @@ -329,7 +232,7 @@ class DownloadTab(QWidget): if len(self.dl_queue) != 0: self.start_installation(*self.dl_queue[0]) else: - self.queue_label.setText(self.tr("Download queue: Empty")) + self.queue_widget.update_queue(self.dl_queue) elif text == "error": QMessageBox.warning(self, "warn", "Download error") diff --git a/Rare/Components/Tabs/Downloads/DownloadThread.py b/Rare/Components/Tabs/Downloads/DownloadThread.py new file mode 100644 index 00000000..3fce81ad --- /dev/null +++ b/Rare/Components/Tabs/Downloads/DownloadThread.py @@ -0,0 +1,117 @@ +import os +import queue +import subprocess +import time +from logging import getLogger +from multiprocessing import Queue as MPQueue + +from PyQt5.QtCore import QThread, pyqtSignal +from PyQt5.QtWidgets import QMessageBox + +from Rare.utils.Models import KillDownloadException + +from custom_legendary.core import LegendaryCore +from custom_legendary.downloader.manager import DLManager +from custom_legendary.models.downloading import UIUpdate + +logger = getLogger("Download") + + +class DownloadThread(QThread): + status = pyqtSignal(str) + statistics = pyqtSignal(UIUpdate) + kill = False + + def __init__(self, dlm: DLManager, core: LegendaryCore, status_queue: MPQueue, igame, repair=False, + repair_file=None): + super(DownloadThread, self).__init__() + self.dlm = dlm + self.core = core + self.status_queue = status_queue + self.igame = igame + self.repair = repair + self.repair_file = repair_file + + def run(self): + start_time = time.time() + try: + + self.dlm.start() + time.sleep(1) + while self.dlm.is_alive(): + if self.kill: + # raise KillDownloadException() + # TODO kill download queue, workers + pass + try: + self.statistics.emit(self.status_queue.get(timeout=1)) + except queue.Empty: + pass + + self.dlm.join() + + except KillDownloadException: + self.status.emit("stop") + logger.info("Downlaod can be continued later") + self.dlm.kill() + return + + except Exception as e: + logger.error(f"Installation failed after {time.time() - start_time:.02f} seconds: {e}") + self.status.emit("error") + return + + 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: + self._handle_postinstall(postinstall, self.igame) + + dlcs = self.core.get_dlc_for_game(self.igame.app_name) + if dlcs: + print('The following DLCs are available for this game:') + for dlc in dlcs: + print(f' - {dlc.app_title} (App name: {dlc.app_name}, version: {dlc.app_version})') + print('Manually installing DLCs works the same; just use the DLC app name instead.') + + # install_dlcs = QMessageBox.question(self, "", "Do you want to install the prequisites", QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes + # TODO + if game.supports_cloud_saves and not game.is_dlc: + logger.info('This game supports cloud saves, syncing is handled by the "sync-saves" command.') + logger.info(f'To download saves for this game run "legendary sync-saves {game.app_name}"') + old_igame = self.core.get_installed_game(game.app_name) + if old_igame and self.repair and os.path.exists(self.repair_file): + if old_igame.needs_verification: + old_igame.needs_verification = False + self.core.install_game(old_igame) + + logger.debug('Removing repair file.') + os.remove(self.repair_file) + if old_igame and old_igame.install_tags != self.igame.install_tags: + old_igame.install_tags = self.igame.install_tags + self.logger.info('Deleting now untagged files.') + self.core.uninstall_tag(old_igame) + self.core.install_game(old_igame) + + self.status.emit("finish") + + def _handle_postinstall(self, postinstall, igame): + print('This game lists the following prequisites to be installed:') + print(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}') + if os.name == 'nt': + if QMessageBox.question(self, "", "Do you want to install the prequisites", + QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: + self.core.prereq_installed(igame.app_name) + req_path, req_exec = os.path.split(postinstall['path']) + work_dir = os.path.join(igame.install_path, req_path) + fullpath = os.path.join(work_dir, req_exec) + subprocess.call([fullpath, postinstall['args']], cwd=work_dir) + else: + self.core.prereq_installed(self.igame.app_name) + + else: + logger.info('Automatic installation not available on Linux.') + diff --git a/Rare/Components/Tabs/Settings/Rare.py b/Rare/Components/Tabs/Settings/Rare.py index 966fdc52..ec5f8ab9 100644 --- a/Rare/Components/Tabs/Settings/Rare.py +++ b/Rare/Components/Tabs/Settings/Rare.py @@ -45,7 +45,7 @@ class RareSettings(QGroupBox): self.select_lang.currentIndexChanged.connect(self.update_lang) self.layout.addWidget(self.lang_widget) - self.exit_to_sys_tray = QCheckBox("Hide to System Tray Icon") + self.exit_to_sys_tray = QCheckBox(self.tr("Hide to System Tray Icon")) self.exit_to_sys_tray.setChecked(settings.value("sys_tray", True, bool)) self.exit_to_sys_tray.stateChanged.connect(self.update_sys_tray) self.sys_tray_widget = SettingsWidget(self.tr("Exit to System Tray Icon"), self.exit_to_sys_tray) From 9b2ca344ebca306be2ae0b0e8bcf14a7bdb297c5 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 17:44:59 +0200 Subject: [PATCH 22/30] Download queue; move up and down --- .../Tabs/Downloads/DlQueueWidget.py | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Rare/Components/Tabs/Downloads/DlQueueWidget.py b/Rare/Components/Tabs/Downloads/DlQueueWidget.py index b3e5535b..bdb0db00 100644 --- a/Rare/Components/Tabs/Downloads/DlQueueWidget.py +++ b/Rare/Components/Tabs/Downloads/DlQueueWidget.py @@ -37,8 +37,8 @@ class DlWidget(QWidget): self.size = QHBoxLayout() - self.size.addWidget(QLabel(self.tr("Download size: {} GB").format(dl_size/1024**3))) - self.size.addWidget(QLabel(self.tr("Install size: {} GB").format(install_size/1024**3))) + self.size.addWidget(QLabel(self.tr("Download size: {} GB").format(dl_size / 1024 ** 3))) + self.size.addWidget(QLabel(self.tr("Install size: {} GB").format(install_size / 1024 ** 3))) self.right_layout.addLayout(self.size) self.delete = QPushButton(self.tr("Remove Download")) @@ -52,6 +52,7 @@ class DlWidget(QWidget): class DlQueueWidget(QGroupBox): update_list = pyqtSignal(list) dl_queue = [] + def __init__(self): super(DlQueueWidget, self).__init__() @@ -77,7 +78,11 @@ class DlQueueWidget(QGroupBox): for index, item in enumerate(dl_queue): widget = DlWidget(index, item) widget.remove.connect(self.remove) + widget.move_up.connect(self.move_up) + widget.move_down.connect(self.move_down) self.layout.addWidget(widget) + if index + 1 == len(dl_queue): + widget.move_down_buttton.setDisabled(True) self.setLayout(self.layout) @@ -91,3 +96,29 @@ class DlQueueWidget(QGroupBox): return self.update_list.emit(self.dl_queue) self.update_queue(self.dl_queue) + + def move_up(self, app_name): + index: int + + for i, item in enumerate(self.dl_queue): + if item[1].app_name == app_name: + index = i + break + else: + return + self.dl_queue.insert(index - 1, self.dl_queue.pop(index)) + self.update_list.emit(self.dl_queue) + self.update_queue(self.dl_queue) + + def move_down(self, app_name): + index: int + + for i, item in enumerate(self.dl_queue): + if item[1].app_name == app_name: + index = i + break + else: + return + self.dl_queue.insert(index + 1, self.dl_queue.pop(index)) + self.update_list.emit(self.dl_queue) + self.update_queue(self.dl_queue) From 81032c94f5efeddb0c89a9fd777df8c51dda08c4 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 18:17:56 +0200 Subject: [PATCH 23/30] Disable uninstalled widget when download; translations --- Rare/Components/Tabs/Games/GameList.py | 13 ++- .../GameWidgets/BaseUninstalledWidget.py | 1 + Rare/languages/de.qm | Bin 19018 -> 19542 bytes Rare/languages/de.ts | 88 +++++++++++++----- 4 files changed, 79 insertions(+), 23 deletions(-) diff --git a/Rare/Components/Tabs/Games/GameList.py b/Rare/Components/Tabs/Games/GameList.py index e51316da..08c94737 100644 --- a/Rare/Components/Tabs/Games/GameList.py +++ b/Rare/Components/Tabs/Games/GameList.py @@ -101,6 +101,7 @@ class GameList(QStackedWidget): if not game.app_name in installed: uninstalled_games.append(game) + # add uninstalled games for game in uninstalled_games: if os.path.exists(f"{IMAGE_DIR}/{game.app_name}/UninstalledArt.png"): pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/UninstalledArt.png") @@ -116,10 +117,10 @@ class GameList(QStackedWidget): pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/UninstalledArt.png") icon_widget = IconWidgetUninstalled(game, self.core, pixmap) - icon_widget.install_game.connect(self.install_game.emit) + icon_widget.install_game.connect(self.install) list_widget = ListWidgetUninstalled(self.core, game, pixmap) - list_widget.install_game.connect(self.install_game.emit) + list_widget.install_game.connect(self.install) self.icon_layout.addWidget(icon_widget) self.list_layout.addWidget(list_widget) @@ -144,6 +145,14 @@ class GameList(QStackedWidget): if self.settings.value("installed_only", False, bool): self.installed_only(True) + def install(self, options: InstallOptions): + icon_widget, list_widget = self.widgets[options.app_name] + icon_widget.mousePressEvent = lambda e: None + icon_widget.installing = True + list_widget.install_button.setDisabled(True) + list_widget.installing = True + self.install_game.emit(options) + def finished(self, app_name): self.widgets[app_name][0].info_text = "" self.widgets[app_name][0].info_label.setText("") diff --git a/Rare/Components/Tabs/Games/GameWidgets/BaseUninstalledWidget.py b/Rare/Components/Tabs/Games/GameWidgets/BaseUninstalledWidget.py index 80b48177..74804e51 100644 --- a/Rare/Components/Tabs/Games/GameWidgets/BaseUninstalledWidget.py +++ b/Rare/Components/Tabs/Games/GameWidgets/BaseUninstalledWidget.py @@ -17,6 +17,7 @@ class BaseUninstalledWidget(QGroupBox): self.game = game self.core = core self.pixmap = pixmap + self.installing = False self.setContentsMargins(0, 0, 0, 0) diff --git a/Rare/languages/de.qm b/Rare/languages/de.qm index 9022e87c32cc0e733c65dc7a23da9eed60552a86..73a052a29bade56c6a06417ac54cc1ab200e5f8f 100644 GIT binary patch delta 1843 zcmaJ>X;4#V6g|mH$ogJ@5CIi<3Ti-91Q9{X8qgr(C{mG%nvno$W1uF8h#E!*t+hxo z)`B`Zs8m}!#lk>`3U0XIR%@$ut4ys{sn)&P;?`b_Wmy1>L9K@1oRJbVw4g6-J1Y)9Kuhx0WK+s>AM!-7$l^o z1Bwplhpz$p7GvVFc8Z^mG@n60#5AO5zW^i?k+owHF!Y=gL)Red8J+FwRnC`WGc0+( z1A(R3;0_>$!NxWQh!bLSmJLvrqOGC`kon-SEF18ehC7N(px-qCqe}pWpB2Q#`~?iS zC8!#B0T`4m=zh-^2)!hD){h0)3ZcH6@+JEUCsgu)CQX=|ngBcG@xrh65_3tpa9^ed z82PL4KrmhK=oTLJCMV=ug?CH-q(X8=G=>ywRxqw7i4b?3argX)m_{*P^+f1;G zjvg&e4DQd^j!pu!wam(ybBLgtshaW&Ipoc3YH0)#E;C=;6xjj(In%VDM_Dnmr{|Bv zPLa>O^MKoNQK-Woh|CjJ>sx`KKb#oh=S1CkQT>HFYDKeXw`@8Pk}hiB;R*QO5R2v$ z(Se)A+Jn{P)C(sD=ZQz2m`Y9_5rYD_c$T6D7;;ve-_{Md z=Zinc7gEn%#2X9RiE)s)wS$Nb-YsrxoJ|Qh@rg>Z+GDVb`fDp0vC$=<>jpU(=~5Lz zR``fq)+bXzRj$jKog^>jitc1HxI7u)QQK{)i;Zel-9HcSzzQ4*>obCCOoGD%>n7 zJhKaMwMrIUDyMUhqWE+28I@5)yiQ*F0cUmm=u!bQ-oi#j1QipwDr|;i)_@_M+m-5I0)m zg^J%=wvdxS$^i}EkkVPo(0%KP0F;x}Mr!pF<(tdz(eRp;Z?dmW*?o|{N(6s`QLiFzly&UywMq3?o$9kPwKBXKDkQ`QB% z(axIJQ7gsCPK^D8U0f1D!{_BhpFDQWL%Po@*j?6oax$0Q-!+%^KgpFnR!fBfPqC*p z9DOhjvR(Wk8migs{bo9kF|aR!ACcwdoM0>Ua#$=kcs8x;(2<;$PY0qKxRAnaK)`)2 z(|;oEe-US$Nkrs#xq6X=2yNhw^wrVuF6TP7N78WnaVKw6J?}E^v3ddGFt$XM*kiRW zOqH5V1zm5cGPX0M$U`;zToMgixN7dnPl-sUswTUREZw2ne~^YRe!7Vo{qG3g@5<;T+>WBc}AOn1Rw}H6rh;0nh6cap}8IBh#ldl z_2}hdH7zto@uiFTaS1()q&oAz5`;QxM`$EAqVXzlkXmoC+6-nh|9^2rSsl(((Z{f2 z4mm+GdKIXq80QwuH}d~5_)k^Gc&@XtTa_qie8+7k!*j?$A<;IVhl8~@ha{sg$6zyB z8wDCSE;Rspl4zwn2C8X(dD}#SEyUW4YbZi4Y`w7_uM`+;rUDB;ugGFCS@L>FX!LUm fERNNq`!u@ydwGj>Jy{(MV^c)qO*zJf=oId6aUt2e delta 1563 zcmb7EYfMvT7=GH*)ApR6a|$h8D+&|=i_j@hk&6`+3L6t-DlUlNbjXGY0<$nBsC66FY7+Zd%g1n4Fq`dk~Jd9W*abWEo0?qsXyq!utQAQ zsZ78U&#ar54_NmyH8X!DM|_y37B?_ui233MIUBHvX@9n!I;`-!ebEJY^(dn1f`FJR zMMGLEFm{6nqmOtn>As@*(k`<6u;M5;hlp>oinmBuU@RNj-9SzZc`zJogc*ZF;bc86;DPitr+M0L^d5qbJp zb#*0$&97S3ufIm4s8{t@JRqT8t7E*^0h0^WX){Pj%mwxI1GFELuFkTJBPUbT%l7>Y zC@!nFujn9Rqq=wRLtxZiZbHp$z!c`ez;e$1Vj{g?&AIGNy_C*St}$9o8L)6SR;~u3 z4s!S0R{{Ss;j6?xO8ksV%1*FxvPpiGT9g;HzT zuL1p%~gvx3DawCwrr;ibP0h6&eH9u6rwsdlK==a^aV8Y zI^mVIw<)w+g}JMuX*72Qrxy`wvxK8Ea?Y|u_~qjf0Ou+YuD_m4onH`*!6#{!xuRt! zrF3eOn6PR9m~=%f+C;NsQ$0BOxVWMshC-+J;HcNc50|}6g$Bjidvs2iB<^!IlY>vi z&Vj|$u~j_1jfg@E#d8J;n3yLHm`_la=88|O4**wyza-m1vkSi{jhRm$kg=zwQ1dLh zWoM;`(w%gV-;=U~GU#4Ql5-vj;ryg#g^C1ikxu$LD6HG1-raE&)+18i??hy(lm_+7 z$f=FmDLaden...
+ + DlQueueWidget + + + Download Queue + Eingereihte Downloads + + + + No downloads in queue + Keine eingereihten Downloads + + + + DlWidget + + + Download size: {} GB + Download Größe: {} GB + + + + Install size: {} GB + Installierte Größe: {} GB + + + + Remove Download + Download löschen + + DownloadTab - + No active Download Kein aktiver Download - + Stop Download Download anhalten - + No updates available Keine Updates verfügbar - + Error preparing download Fehler beim Vorbereiten des Downloads - + Download size is 0. Game already exists Die Größe des Downloads ist 0. Spiel existiert bereits - + Installation finished Installation abgeschlossen - + Installing Game: No active download Installierendes Spiel: Kein aktiver Download - + Download speed Geschwindigkeit - + Cache used Benutzter Cache - + Downloaded Runtergeladen - + Time left: Zeit übrig: - + Finished Download of game {} Downlaod von {} abgeschlossen - + Download finished. Reload library Download abgeschlossen. Spiele neu laden Download queue: Empty - Anschließende Downloads: Keine + Anschließende Downloads: Keine Download queue: - Anschließende Downloads: + Anschließende Downloads: Empty - Keine + Keine - + Installing Game: Installierendes Spiel: + + + Updates + Updates + DxvkWidget @@ -273,12 +309,12 @@ GameList - + Launch Starten - + Game running Spiel läuft @@ -370,10 +406,15 @@ IconWidgetUninstalled - + Install Game Spiel installieren + + + Installation running + Installation läuft + ImportWidget @@ -744,6 +785,11 @@ Installationsgröße: {} GB Exit to System Tray Icon Beim verlassen auf das System Tray Icon minimieren + + + Hide to System Tray Icon + In das System Tray Icon minimieren + SyncSaves @@ -923,7 +969,7 @@ Installationsgröße: {} GB UpdateWidget - + Update Game Spiel updaten From 77d740470f33d8408e13127039f787ebbaa6e119 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 18:18:50 +0200 Subject: [PATCH 24/30] Download tab: Updates in Groupbox; Fixed bug in Legendary settings --- .../Tabs/Downloads/DlQueueWidget.py | 9 ++++- Rare/Components/Tabs/Downloads/DownloadTab.py | 34 +++++++++---------- .../GameWidgets/UninstalledIconWidget.py | 13 +++++-- Rare/Components/Tabs/Settings/Legendary.py | 10 +++--- 4 files changed, 39 insertions(+), 27 deletions(-) diff --git a/Rare/Components/Tabs/Downloads/DlQueueWidget.py b/Rare/Components/Tabs/Downloads/DlQueueWidget.py index bdb0db00..943a48b2 100644 --- a/Rare/Components/Tabs/Downloads/DlQueueWidget.py +++ b/Rare/Components/Tabs/Downloads/DlQueueWidget.py @@ -1,7 +1,11 @@ +from logging import getLogger + from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, QWidget from qtawesome import icon +logger = getLogger("QueueWidget") + class DlWidget(QWidget): move_up = pyqtSignal(str) # app_name @@ -65,6 +69,7 @@ class DlQueueWidget(QGroupBox): self.setLayout(self.layout) def update_queue(self, dl_queue: list): + logger.info("Update Queue " + ", ".join(i[1].app_title for i in dl_queue)) self.dl_queue = dl_queue self.setLayout(QVBoxLayout()) QWidget().setLayout(self.layout) @@ -92,7 +97,7 @@ class DlQueueWidget(QGroupBox): self.dl_queue.pop(index) break else: - print("BUG! ", app_name) + logger.warning("BUG! ", app_name) return self.update_list.emit(self.dl_queue) self.update_queue(self.dl_queue) @@ -105,6 +110,7 @@ class DlQueueWidget(QGroupBox): index = i break else: + logger.warning("Could not find appname" + app_name) return self.dl_queue.insert(index - 1, self.dl_queue.pop(index)) self.update_list.emit(self.dl_queue) @@ -118,6 +124,7 @@ class DlQueueWidget(QGroupBox): index = i break else: + logger.warning("Could not find appname" + app_name) return self.dl_queue.insert(index + 1, self.dl_queue.pop(index)) self.update_list.emit(self.dl_queue) diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py index bb5faff7..d58586cd 100644 --- a/Rare/Components/Tabs/Downloads/DownloadTab.py +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -4,7 +4,7 @@ from multiprocessing import Queue as MPQueue from PyQt5.QtCore import QThread, pyqtSignal, Qt from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, QDialog, \ - QListWidget, QHBoxLayout + QListWidget, QHBoxLayout, QGroupBox from Rare.Components.Dialogs.InstallDialog import InstallInfoDialog, InstallDialog from Rare.Components.Tabs.Downloads.DlQueueWidget import DlQueueWidget @@ -61,24 +61,23 @@ class DownloadTab(QWidget): self.layout.addWidget(self.queue_widget) self.queue_widget.update_list.connect(self.update_dl_queue) - self.update_title = QLabel(f"

{self.tr('Updates')}

") - self.update_title.setStyleSheet(""" - QLabel{ - margin-top: 20px; - } - """) - self.layout.addWidget(self.update_title) + self.updates = QGroupBox(self.tr("Updates")) + self.updates.setObjectName("group") + self.update_layout = QVBoxLayout() self.update_widgets = {} - if not updates: - self.update_text = QLabel(self.tr("No updates available")) - self.layout.addWidget(self.update_text) - else: - for igame in updates: - widget = UpdateWidget(core, igame) - self.update_widgets[igame.app_name] = widget - self.layout.addWidget(widget) - widget.update.connect(self.update_game) + self.update_text = QLabel(self.tr("No updates available")) + self.update_text.setVisible(len(updates) == 0) + self.update_layout.addWidget(self.update_text) + + for igame in updates: + widget = UpdateWidget(core, igame) + self.update_widgets[igame.app_name] = widget + self.update_layout.addWidget(widget) + widget.update.connect(self.update_game) + + self.updates.setLayout(self.update_layout) + self.layout.addWidget(self.updates) self.layout.addStretch(1) self.setLayout(self.layout) @@ -86,7 +85,6 @@ class DownloadTab(QWidget): def update_dl_queue(self, dl_queue): self.dl_queue = dl_queue - def stop_download(self): self.thread.kill = True diff --git a/Rare/Components/Tabs/Games/GameWidgets/UninstalledIconWidget.py b/Rare/Components/Tabs/Games/GameWidgets/UninstalledIconWidget.py index 6eaf478a..32131581 100644 --- a/Rare/Components/Tabs/Games/GameWidgets/UninstalledIconWidget.py +++ b/Rare/Components/Tabs/Games/GameWidgets/UninstalledIconWidget.py @@ -37,10 +37,17 @@ class IconWidgetUninstalled(BaseUninstalledWidget): self.setFixedWidth(self.sizeHint().width()) def mousePressEvent(self, e) -> None: - self.install() + if not self.installing: + self.install() def enterEvent(self, e): - self.info_label.setText(self.tr("Install Game")) + if not self.installing: + self.info_label.setText(self.tr("Install Game")) + else: + self.info_label.setText(self.tr("Installation running")) def leaveEvent(self, e): - self.info_label.setText("") + if self.installing: + self.info_label.setText("Installation...") + else: + self.info_label.setText("") diff --git a/Rare/Components/Tabs/Settings/Legendary.py b/Rare/Components/Tabs/Settings/Legendary.py index e3c36fef..6046e677 100644 --- a/Rare/Components/Tabs/Settings/Legendary.py +++ b/Rare/Components/Tabs/Settings/Legendary.py @@ -59,20 +59,20 @@ class LegendarySettings(QGroupBox): self.core.lgd.config["Legendary"]["install_dir"] = self.select_path.text() if self.select_path.text() == "" and "install_dir" in self.core.lgd.config["Legendary"].keys(): self.core.lgd.config["Legendary"].pop("install_dir") - logger.info("Remove install_dir section") else: logger.info("Set config install_dir to " + self.select_path.text()) self.core.lgd.save_config() def max_worker_save(self, num_workers: str): - self.core.lgd.config["Legendary"]["max_workers"] = num_workers if num_workers == "": - self.core.lgd.config["Legendary"].pop("max_workers") + self.core.lgd.config.remove_option("Legendary", "max_workers") + self.core.lgd.save_config() return num_workers = int(num_workers) if num_workers == 0: - self.core.lgd.config["Legendary"].pop("max_workers") - logger.info("Updating config for max_workers") + self.core.lgd.config.remove_option("Legendary", "max_workers") + else: + self.core.lgd.config.set("Legendary", "max_workers", str(num_workers)) self.core.lgd.save_config() def cleanup(self, keep_manifests): From 31dc90fde737da282b065b6047efe9acad33fc90 Mon Sep 17 00:00:00 2001 From: Siltent Assasin <81620892+siltentassasin@users.noreply.github.com> Date: Wed, 7 Apr 2021 19:03:12 +0200 Subject: [PATCH 25/30] French Support --- README.md | 39 ++- Rare/languages/fr.qm | Bin 0 -> 17729 bytes Rare/languages/fr.ts | 818 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 842 insertions(+), 15 deletions(-) create mode 100644 Rare/languages/fr.qm create mode 100644 Rare/languages/fr.ts diff --git a/README.md b/README.md index 8f89c49a..f8d3df82 100644 --- a/README.md +++ b/README.md @@ -8,36 +8,45 @@ recommend to make a backup. If you run into an issue, please report it by creati ![Discord Shield](https://discordapp.com/api/guilds/826881530310819914/widget.png?style=shield) - -### Requirements - -- requests, -- pillow -- pyqt5 -- notify-py -- QtAwesome - ## Installation ### Installation via pip (recommend) -Execute *(sudo) pip install Rare* for all users Or *pip install Rare --user* for only one user +Execute `pip install Rare` for all users Or `pip install Rare --user` for only one user **Note**: On Linux must be /home/user/.local/bin in PATH ### Windows Simple -Download Rare.exe and place it somewhere in PATH +Download Rare.exe from the [releases page](https://github.com/Dummerle/Rare/releases) and place it somewhere in PATH **Note** Using the exe file could cause errors ### Linux -- For Arch Linux is an AUR package available: [rare-git](https://aur.archlinux.org/packages/rare-git) - or [rare](https://aur.archlinux.org/packages/rare) -- For Debian-based Distros is a .deb package at the releases -- Other distributions have to install it with pip or clone the repo and install it manually: *python3 setup.py install (--user)* +#### Arch based + +There are some AUR packages available: + - [rare](https://aur.archlinux.org/packages/rare) + - [rare-git](https://aur.archlinux.org/packages/rare-git) + +#### Debian based + +There is a `.deb` package available from the [releases page](https://github.com/Dummerle/Rare/releases): `sudo dpkg –i Rare.deb` + +#### Other + +Install via `pip`. + +## Run from source +1. Run `pip install -r requirements.txt` to get dependencies. If you use `pacman` you can run `sudo pacman --needed -S python-wheel python-setuptools python-pyqt5 python-qtawesome python-requests python-pillow` +2. For unix operating systems run `sh start.sh`. For windows run `set PYTHONPATH=%CD% && python Rare` + +## Why Rare? + +Rare uses much less RAM than electron based apps such as [HeroicGL](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) and EpicGL which allows the games to run better. +Rare supports all major platforms (Windows, Linux, MacOS) unlike the alternatives. ## Features diff --git a/Rare/languages/fr.qm b/Rare/languages/fr.qm new file mode 100644 index 0000000000000000000000000000000000000000..d5ae23413ef3b1e3a94fe38e43038a4b3213191b GIT binary patch literal 17729 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 literal 0 HcmV?d00001 diff --git a/Rare/languages/fr.ts b/Rare/languages/fr.ts new file mode 100644 index 00000000..2aa8f3d8 --- /dev/null +++ b/Rare/languages/fr.ts @@ -0,0 +1,818 @@ + + + + About + + + Developer: + Développeur + + + + Legendary developer: + Legendary Développeur + + + + This is a beta version, so you can get bugs. If you get a bug, please report it by creating a Issue on <a href='https://github.com/Dummerle/Rare/issues'>Github</a>. You can also contact me on Discord (Dummerle#7419). Or you can join the <a href='https://discord.gg/YvmABK9YSk'>Discord server</a> + Il s'agit d'une version bêta, vous pouvez donc rencontrer des bogues. Si vous rencontrez un bug, veuillez le signaler en créant un Issue sur <a href='https://github.com/Dummerle/Rare/issues'>Github</a>. Vous pouvez également me contacter sur Discord (Dummerle#7419). Ou vous pouvez rejoindre le <a href='https://discord.gg/YvmABK9YSk'>serveur Discord</a> + + + + BaseInstalledWidget + + + Do you want to launch {} + Voulez-vous lancer {} + + + + BrowserLogin + + + Opens a browser. You login and copy the json code in the field below. Click <a href='{}'>here</a> to open Browser + Ouvre un navigateur. Vous vous connectez et copiez le code json dans le champ ci-dessous. Cliquez <a href='{}'>here</a> pour ouvrir un navigateur + + + + Insert SID here + Insérer le SID ici + + + + Login + Login + + + + Loading... + charge... + + + + DownloadTab + + + No active Download + Aucun téléchargement actif + + + + Stop Download + Stop Télécharger + + + + No updates available + Aucune mise à jour disponible + + + + Error preparing download + Erreur lors de la préparation du téléchargement + + + + Download size is 0. Game already exists + La taille du téléchargement est de 0. Le jeu existe déjà + + + + Installing game: + Installation du jeu: + + + + Installation finished + Installation terminée + + + + Finished Download of game {} + Fin du téléchargement du jeu {} + + + + Installing Game: No active download + Installation du jeu: Aucun téléchargement actif + + + + Download speed + Vitesse de téléchargement + + + + Cache used + Cache utilisé + + + + Downloaded + Téléchargé + + + + Time left: + Il reste du temps: + + + + DxvkWidget + + + GPU usage + GPU Utilisation + + + + Used Memory + utilisé Memory + + + + Device info + Info sur le dispositif + + + + DXVK version + DXVK version + + + + D3D Level of application + D3D Niveau d'application + + + + Frame time graph + Graphique de temps de trame + + + + dxvk settings + dxvk paramètres + + + + GameActions + + + Uninstall game + Désinstaller le jeu + + + + Uninstall + Désinstaller + + + + Verify Game + Vérifier le jeu + + + + Verify + Vérifier + + + + Repair Game + Jeu de réparation + + + + Repair + Réparation + + + + GameInfo + + + Are you sure to uninstall {} + Etes-vous sûr de désinstaller {} + + + + Repair file does not exist or game does not need a repair. Please verify game first + Le fichier de réparation n'existe pas ou le jeu ne nécessite pas de réparation. Veuillez d'abord vérifier le jeu. + + + + Verification failed, {} file(s) corrupted, {} file(s) are missing. Do you want to repair them? + La vérification a échoué, {} fichier(s) corrompu(s), {} fichier(s) manquant(s). Voulez-vous les réparer ? + + + + Developer: + Développeur: + + + + Install size: + Taille d'installation: + + + + Install path: + Chemin d'installation: + + + + GameList + + + Installed Games: {} Available Games: {} + Jeux installés: {} Jeux disponibles: {} + + + + Launch + Lancer + + + + Game running + Jeu en cours + + + + GameListHeadBar + + + Installed only + Installé uniquement + + + + Import Game + Jeu d'importation + + + + Search Game + Rechercher un jeu + + + + GameSettings + + + Launch Game offline + Lancer le jeu hors ligne + + + + Skip update check before launching + Sauter la vérification de la mise à jour avant le lancement + + + + Save + Sauvez + + + + Wrapper (e.g. optirun) + Wrapper (p.e. optirun) + + + + Proton Wrapper + Enveloppeur de Proton + + + + Proton prefix + Préfixe du proton + + + + No permission to create folder + Pas de permission pour créer un dossier + + + + Please select path for proton prefix + Veuillez sélectionner le chemin pour le préfixe proton + + + + GameWidgetInstalled + + + Update available + Mise à jour disponible + + + + Start game without version check + Démarrer le jeu sans vérifier la version + + + + Game running + Jeu en cours + + + + IconWidgetUninstalled + + + Install Game + Installer le jeu + + + + ImportWidget + + + Import + Importer + + + + Could not find EGL program data + Impossible de trouver les données du programme EGL + + + + Found EGL program Data. Do you want to import them? + Les données du programme EGL ont été trouvées. Voulez-vous les importer ? + + + + Could not find any Epic Games login data + Impossible de trouver les données de connexion d'Epic Games + + + + Loading... + Chargement... + + + + Error: No valid session found + Erreur : Aucune session valide n'a été trouvée + + + + Back + Dos + + + + Select path to game + Sélectionnez le chemin vers le jeu + + + + Override app name (Only if imported game from legendary or the app could not find the app name) + Remplacer le nom de l'application (uniquement si le jeu a été importé depuis le légendaire ou si l'application n'a pas pu trouver le nom de l'application) + + + + Import Game + Import jeu + + + + Import all games from Epic Games Launcher + Importer tous les jeux du Epic Games Launcher + + + + Could not find app name + Impossible de trouver le nom de l'application + + + + Successfully imported {}. Reload library + Importation réussie de {}. Recharger la bibliothèque + + + + Failed to import {} + Impossible d'importer {} + + + + Successfully imported {} Games. Reloading Library + Importation réussie de {} Jeux. Bibliothèque de rechargement + + + + InfoTabs + + + Back + Dos + + + + Game Info + Info de jeu + + + + Settings + Paramètres + + + + InstallDialog + + + <h3>Install {}</h3> + <h3>Installer {}</h3> + + + + Max workers (0: Default) + Travailleurs maximum (0: Par défaut) + + + + Force download + Téléchargement forcé + + + + Ignore free space (Warning!) + Ignorer l'espace libre (Attention!) + + + + InstallInfoDialog + + + Download size: {}GB +Install size: {}GB + Taille du téléchargement: {}GB +Taille de l'installation: {}GB + + + + Install + Installer + + + + Cancel + Annuler + + + + InstalledListWidget + + + Launch + Lancer + + + + Developer: + Développeur: + + + + LaunchDialog + + + Launching Rare + Lancer Rare + + + + Logging in + Se connecter + + + + Downloading Images + Téléchargement d'images + + + + Starting... + Démarrage... + + + + LaunchThread + + + Downloading Images + Téléchargement d'images + + + + LegendarySettings + + + Legendary settings + Legendary paramètres + + + + Default installation directory + Répertoire d'installation par défaut + + + + Max workers for Download (Less: slower download)(0: Default) + Nombre maximum de travailleurs pour le téléchargement (Moins: téléchargement plus lent)(0: Défaut) + + + + Cleanup + Nettoyage + + + + Remove everything + Enlever tout + + + + Clean, but keep manifests + Nettoyer, mais garder les manifestes + + + + Cleanup complete! Successfully removed {} MB + Nettoyage terminé ! J'ai réussi à supprimer {} MB + + + + LinuxSettings + + + Linux settings + Linux paramètres + + + + Default Wine Prefix + Défaut Wine Prefix + + + + Default Wine executable + Défaut Wine exécutable + + + + ListWidgetUninstalled + + + Install + Installer + + + + LoginDialog + + + Select one option to Login + Sélectionnez une option pour vous connecter + + + + Use Browser + Utiliser le navigateur + + + + This opens your default browser. Login and copy the text + Cela ouvre votre navigateur par défaut. Connectez-vous et copiez le texte + + + + MiniWidget + + + Logged in as + Connecté en tant que + + + + Account settings + paramètres du compte + + + + Logout + Déconnexion + + + + Do you really want to logout + Voulez-vous vraiment vous déconnecter? + + + + PathEdit + + + Select Path + Sélectionner le chemin + + + + Choose Path + Choisir le chemin + + + + PathInputDialog + + + Cancel + Annuler + + + + RareSettings + + + Rare settings + Rare paramètres + + + + Save + Sauvez + + + + Image Directory + Répertoire d'images + + + + Language + Langue + + + + Confirm launch of game + Confirmation du lancement du jeu + + + + Restart Application to activate changes + Redémarrez l'application pour activer les changements + + + + SyncSaves + + + Cloud Saves + Cloud Saves + + + + Found Saves for folowing Games + Sauvegardes trouvées pour les jeux suivants + + + + Your games does not support Cloud Saves + Vos jeux ne prennent pas en charge les sauvegardes en nuage + + + + Sync all games + Sync tous les jeux + + + + Found no savepath + Pas de chemin de sauvegarde trouvé + + + + No save path was found. Please select path or skip + Aucun chemin de sauvegarde n'a été trouvé. Veuillez sélectionner le chemin ou passer + + + + SyncWidget + + + Path not found + Chemin non trouvé + + + + Local Save date: + Local Save date: + + + + No Local Save files + Pas de fichiers de sauvegarde locaux + + + + Cloud save date: + Cloud save date: + + + + No Cloud saves + Pas Cloud saves + + + + Game is up to date + Le jeu est à jour + + + + Upload anyway + Télécharger quand même + + + + Download anyway + Télécharger en tout cas + + + + Cloud save is newer + La sauvegarde en nuage est plus récente + + + + Download Cloud saves + Télécharger Cloud saves + + + + Upload Saves + Upload Saves + + + + Local save is newer + La sauvegarde locale est plus récente + + + + Upload saves + Upload Saves + + + + Download saves + Télécharger les sauvegardes + + + + Change path + Changement de trajectoire + + + + Uploading... + Téléchargement... + + + + Upload finished + Téléchargement terminé + + + + Downloading... + Téléchargement... + + + + Download finished + Téléchargement terminé + + + + TabWidget + + + Games + Jeus + + + + UpdateWidget + + + Update Game + Jeu de mise à jour + + + From b04703a74a33d7495a5fbd431b5d798aa28427a1 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 19:11:18 +0200 Subject: [PATCH 26/30] Fix gamelist bug --- Rare/Components/Tabs/Games/GameList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rare/Components/Tabs/Games/GameList.py b/Rare/Components/Tabs/Games/GameList.py index 08c94737..56765f5b 100644 --- a/Rare/Components/Tabs/Games/GameList.py +++ b/Rare/Components/Tabs/Games/GameList.py @@ -112,7 +112,7 @@ class GameList(QStackedWidget): pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/UninstalledArt.png") else: - logger.warning(f"No Image found: {self.game.app_title}") + logger.warning(f"No Image found: {game.app_title}") download_image(game, force=True) pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/UninstalledArt.png") From 5b738c104ce8c678b43babf9cb239cffccc91b26 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 19:11:55 +0200 Subject: [PATCH 27/30] French settings --- Rare/Components/Tabs/Settings/Rare.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Rare/Components/Tabs/Settings/Rare.py b/Rare/Components/Tabs/Settings/Rare.py index ec5f8ab9..b586f531 100644 --- a/Rare/Components/Tabs/Settings/Rare.py +++ b/Rare/Components/Tabs/Settings/Rare.py @@ -19,7 +19,7 @@ class RareSettings(QGroupBox): self.setObjectName("group") self.layout = QVBoxLayout() settings = QSettings() - img_dir = settings.value("img_dir",os.path.expanduser("~/.cache/rare/images/"), type=str) + img_dir = settings.value("img_dir", os.path.expanduser("~/.cache/rare/images/"), type=str) language = settings.value("language", get_lang(), type=str) # select Image dir @@ -32,13 +32,15 @@ class RareSettings(QGroupBox): # Select lang self.select_lang = QComboBox() - languages = ["English", "Deutsch"] + languages = ["English", "Deutsch", "Français"] self.select_lang.addItems(languages) if language in get_possible_langs(): if language == "de": self.select_lang.setCurrentIndex(1) elif language == "en": self.select_lang.setCurrentIndex(0) + elif language == "fr": + self.select_lang.setCurrentIndex(2) else: self.select_lang.setCurrentIndex(0) self.lang_widget = SettingsWidget(self.tr("Language"), self.select_lang) @@ -78,7 +80,8 @@ class RareSettings(QGroupBox): settings.setValue("language", "en") elif i == 1: settings.setValue("language", "de") - del settings + elif i == 2: + settings.setValue("language", "fr") self.lang_widget.info_text.setText(self.tr("Restart Application to activate changes")) def update_path(self): From eed7ead83754ce41b4ea9700ef0cafcfc5920602 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 19:17:09 +0200 Subject: [PATCH 28/30] Changed default image_dir in gamelist --- Rare/Components/Tabs/Games/GameList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rare/Components/Tabs/Games/GameList.py b/Rare/Components/Tabs/Games/GameList.py index 56765f5b..8ac22d05 100644 --- a/Rare/Components/Tabs/Games/GameList.py +++ b/Rare/Components/Tabs/Games/GameList.py @@ -54,7 +54,7 @@ class GameList(QStackedWidget): self.list_layout = QVBoxLayout() self.list_layout.addWidget(QLabel(self.info_text)) - IMAGE_DIR = self.settings.value("img_dir", os.path.expanduser("~/.cache/rare"), str) + IMAGE_DIR = self.settings.value("img_dir", os.path.expanduser("~/.cache/rare/images"), str) self.updates = [] self.widgets = {} From 0fd3d7fc0f46f3b3e96a83008697127e1e1e5337 Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 7 Apr 2021 19:53:07 +0200 Subject: [PATCH 29/30] Some typos --- Rare/Components/Tabs/Downloads/DownloadTab.py | 25 ++++++++----------- Rare/utils/utils.py | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Rare/Components/Tabs/Downloads/DownloadTab.py b/Rare/Components/Tabs/Downloads/DownloadTab.py index d58586cd..5da62057 100644 --- a/Rare/Components/Tabs/Downloads/DownloadTab.py +++ b/Rare/Components/Tabs/Downloads/DownloadTab.py @@ -45,7 +45,6 @@ class DownloadTab(QWidget): self.layout.addLayout(self.info_layout) self.mini_layout = QHBoxLayout() - self.prog_bar = QProgressBar() self.prog_bar.setMaximum(100) self.mini_layout.addWidget(self.prog_bar) @@ -219,13 +218,7 @@ class DownloadTab(QWidget): i.update_button.setDisabled(False) self.finished.emit() - self.installing_game.setText(self.tr("Installing Game: No active download")) - self.prog_bar.setValue(0) - - self.dl_speed.setText("") - self.cache_used.setText("") - self.downloaded.setText("") - self.time_left.setText("") + self.reset_infos() if len(self.dl_queue) != 0: self.start_installation(*self.dl_queue[0]) @@ -236,12 +229,16 @@ class DownloadTab(QWidget): QMessageBox.warning(self, "warn", "Download error") elif text == "stop": - self.kill_button.setDisabled(True) - self.installing_game.setText(self.tr("Installing Game: No active download")) - self.prog_bar.setValue(0) - self.dl_speed.setText("") - self.cache_used.setText("") - self.downloaded.setText("") + self.reset_infos() + + def reset_infos(self): + self.kill_button.setDisabled(True) + self.installing_game.setText(self.tr("Installing Game: No active download")) + self.prog_bar.setValue(0) + self.dl_speed.setText("") + self.time_left.setText("") + self.cache_used.setText("") + self.downloaded.setText("") def statistics(self, ui_update: UIUpdate): self.prog_bar.setValue(ui_update.progress) diff --git a/Rare/utils/utils.py b/Rare/utils/utils.py index 39b7ff6c..517e8796 100644 --- a/Rare/utils/utils.py +++ b/Rare/utils/utils.py @@ -12,7 +12,7 @@ from custom_legendary.core import LegendaryCore logger = getLogger("Utils") s = QSettings("Rare", "Rare") -IMAGE_DIR = s.value("img_dir", os.path.expanduser("~/.cache/rare/images/"), type=str) +IMAGE_DIR = s.value("img_dir", os.path.expanduser("~/.cache/rare/images"), type=str) logger.info("IMAGE DIRECTORY: " + IMAGE_DIR) From a0aa538bbbd500d31a6e56ce5bf324195038c126 Mon Sep 17 00:00:00 2001 From: BuildTools Date: Wed, 7 Apr 2021 21:02:47 +0200 Subject: [PATCH 30/30] Mall typo in GameList.py --- Rare/Components/Tabs/Games/GameList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rare/Components/Tabs/Games/GameList.py b/Rare/Components/Tabs/Games/GameList.py index 8ac22d05..d9f99dd8 100644 --- a/Rare/Components/Tabs/Games/GameList.py +++ b/Rare/Components/Tabs/Games/GameList.py @@ -81,7 +81,7 @@ class GameList(QStackedWidget): icon_widget.show_info.connect(self.show_game_info.emit) list_widget.show_info.connect(self.show_game_info.emit) - icon_widget.launch_signal.connect(self.launch) + icon_widget.finish_signal.connect(self.launch) icon_widget.finish_signal.connect(self.finished) list_widget.launch_signal.connect(self.launch) list_widget.launch_signal.connect(self.finished)