From c3c9b0f059e2d4e026df244bb1f860c4ca37efd8 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Wed, 13 Sep 2023 21:36:11 +0300 Subject: [PATCH] AccountWidget: Add a Quit button in the widget in case the system tray is unavailable (for example running in a gamescope session) * Do not show the launch window while instantiating the application. This probably was causing numerous issues because it was running outside of the applications event loop. This also fixes the exit button on the login dialog requiring `sys.exit()` to quit Rare. Now it goes through the proper cleanup procedures. * Make slot and signal names more uniform * Fix a problem with RareCore connecting RareGames to the same signals multiple times when the library was refreshed. --- rare/components/__init__.py | 31 +++++++++++--------- rare/components/dialogs/launch_dialog.py | 4 +-- rare/components/dialogs/login/__init__.py | 35 +++++++++-------------- rare/components/main_window.py | 6 ++-- rare/components/tabs/__init__.py | 33 +++++++++++---------- rare/components/tabs/account/__init__.py | 25 +++++++++++++--- rare/shared/rare_core.py | 2 +- 7 files changed, 76 insertions(+), 60 deletions(-) diff --git a/rare/components/__init__.py b/rare/components/__init__.py index f5af6d2d..8df9e34f 100644 --- a/rare/components/__init__.py +++ b/rare/components/__init__.py @@ -55,14 +55,9 @@ class Rare(RareApp): self.launch_dialog: Optional[LaunchDialog] = None self.timer: Optional[QTimer] = None - # launch app - self.launch_dialog = LaunchDialog(parent=None) - self.launch_dialog.quit_app.connect(self.launch_dialog.close) - self.launch_dialog.quit_app.connect(lambda x: sys.exit(x)) - self.launch_dialog.start_app.connect(self.start_app) - self.launch_dialog.start_app.connect(self.launch_dialog.close) - - self.launch_dialog.login() + # This launches the application after it has been instantiated. + # The timer's signal will be serviced once we call `exec()` on the application + QTimer.singleShot(0, self.launch_app) def poke_timer(self): dt_exp = datetime.fromisoformat(self.core.lgd.userdata['expires_at'][:-1]).replace(tzinfo=timezone.utc) @@ -80,13 +75,23 @@ class Rare(RareApp): return self.poke_timer() + @pyqtSlot() + def launch_app(self): + self.launch_dialog = LaunchDialog(parent=None) + self.launch_dialog.exit_app.connect(self.launch_dialog.close) + self.launch_dialog.exit_app.connect(self.__on_exit_app) + self.launch_dialog.start_app.connect(self.start_app) + self.launch_dialog.start_app.connect(self.launch_dialog.close) + self.launch_dialog.login() + + @pyqtSlot() def start_app(self): self.timer = QTimer() self.timer.timeout.connect(self.re_login) self.poke_timer() self.main_window = MainWindow() - self.main_window.exit_app.connect(self.on_exit_app) + self.main_window.exit_app.connect(self.__on_exit_app) if not self.args.silent: self.main_window.show() @@ -94,11 +99,11 @@ class Rare(RareApp): if self.args.test_start: self.main_window.close() self.main_window = None - self.on_exit_app(0) + self.__on_exit_app(0) @pyqtSlot() @pyqtSlot(int) - def on_exit_app(self, exit_code=0): + def __on_exit_app(self, exit_code=0): threadpool = QThreadPool.globalInstance() threadpool.waitForDone() if self.timer is not None: @@ -119,9 +124,7 @@ def start(args) -> int: QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) app = Rare(args) - exit_code = app.exec_() - # if not restart - # restart app + exit_code = app.exec() del app if exit_code != -133742: break diff --git a/rare/components/dialogs/launch_dialog.py b/rare/components/dialogs/launch_dialog.py index ee81fd12..ead7ac9d 100644 --- a/rare/components/dialogs/launch_dialog.py +++ b/rare/components/dialogs/launch_dialog.py @@ -14,7 +14,7 @@ logger = getLogger("LaunchDialog") class LaunchDialog(QDialog): - quit_app = pyqtSignal(int) + exit_app = pyqtSignal(int) start_app = pyqtSignal() def __init__(self, parent=None): @@ -74,7 +74,7 @@ class LaunchDialog(QDialog): self.show() self.launch() else: - self.quit_app.emit(0) + self.exit_app.emit(0) def launch(self): self.progress_info.setText(self.tr("Preparing Rare")) diff --git a/rare/components/dialogs/login/__init__.py b/rare/components/dialogs/login/__init__.py index 66a1f346..f9ca2b59 100644 --- a/rare/components/dialogs/login/__init__.py +++ b/rare/components/dialogs/login/__init__.py @@ -1,7 +1,6 @@ -from dataclasses import dataclass from logging import getLogger -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt5.QtWidgets import QLayout, QDialog, QMessageBox, QFrame from legendary.core import LegendaryCore @@ -15,13 +14,6 @@ from .import_login import ImportLogin logger = getLogger("LoginDialog") -@dataclass -class LoginPages: - landing: int - browser: int - import_egl: int - - class LandingPage(QFrame): def __init__(self, parent=None): super(LandingPage, self).__init__(parent=parent) @@ -31,8 +23,7 @@ class LandingPage(QFrame): class LoginDialog(QDialog): - logged_in: bool = False - pages = LoginPages(landing=0, browser=1, import_egl=2) + exit_app: pyqtSignal = pyqtSignal(int) def __init__(self, core: LegendaryCore, parent=None): super(LoginDialog, self).__init__(parent=parent) @@ -51,6 +42,8 @@ class LoginDialog(QDialog): self.ui = Ui_LoginDialog() self.ui.setupUi(self) + self.logged_in: bool = False + self.core = core self.args = ArgumentsSingleton() @@ -59,16 +52,16 @@ class LoginDialog(QDialog): self.ui.login_stack_layout.addWidget(self.login_stack) self.landing_page = LandingPage(self.login_stack) - self.login_stack.insertWidget(self.pages.landing, self.landing_page) + self.login_stack.insertWidget(0, self.landing_page) self.browser_page = BrowserLogin(self.core, self.login_stack) - self.login_stack.insertWidget(self.pages.browser, self.browser_page) + self.login_stack.insertWidget(1, self.browser_page) self.browser_page.success.connect(self.login_successful) self.browser_page.changed.connect( lambda: self.ui.next_button.setEnabled(self.browser_page.is_valid()) ) self.import_page = ImportLogin(self.core, self.login_stack) - self.login_stack.insertWidget(self.pages.import_egl, self.import_page) + self.login_stack.insertWidget(2, self.import_page) self.import_page.success.connect(self.login_successful) self.import_page.changed.connect(lambda: self.ui.next_button.setEnabled(self.import_page.is_valid())) @@ -84,34 +77,34 @@ class LoginDialog(QDialog): self.ui.back_button.clicked.connect(self.back_clicked) self.ui.next_button.clicked.connect(self.next_clicked) - self.login_stack.setCurrentIndex(self.pages.landing) + self.login_stack.setCurrentWidget(self.landing_page) self.layout().setSizeConstraint(QLayout.SetFixedSize) def back_clicked(self): self.ui.back_button.setEnabled(False) self.ui.next_button.setEnabled(True) - self.login_stack.slideInIndex(self.pages.landing) + self.login_stack.slideInWidget(self.landing_page) def browser_radio_clicked(self): - self.login_stack.slideInIndex(self.pages.browser) + self.login_stack.slideInWidget(self.browser_page) self.ui.back_button.setEnabled(True) self.ui.next_button.setEnabled(False) def import_radio_clicked(self): - self.login_stack.slideInIndex(self.pages.import_egl) + self.login_stack.slideInWidget(self.import_page) self.ui.back_button.setEnabled(True) self.ui.next_button.setEnabled(self.import_page.is_valid()) def next_clicked(self): - if self.login_stack.currentIndex() == self.pages.landing: + if self.login_stack.currentWidget() is self.landing_page: if self.landing_page.ui.login_browser_radio.isChecked(): self.browser_radio_clicked() if self.landing_page.ui.login_import_radio.isChecked(): self.import_radio_clicked() - elif self.login_stack.currentIndex() == self.pages.browser: + elif self.login_stack.currentWidget() is self.browser_page: self.browser_page.do_login() - elif self.login_stack.currentIndex() == self.pages.import_egl: + elif self.login_stack.currentWidget() is self.import_page: self.import_page.do_login() def login(self): diff --git a/rare/components/main_window.py b/rare/components/main_window.py index 1efd4f66..318fc6b6 100644 --- a/rare/components/main_window.py +++ b/rare/components/main_window.py @@ -44,7 +44,7 @@ class MainWindow(QMainWindow): self.setWindowTitle("Rare - GUI for legendary") self.tab_widget = MainTabWidget(self) - self.tab_widget.exit_app.connect(self.on_exit_app) + self.tab_widget.exit_app.connect(self.__on_exit_app) self.setCentralWidget(self.tab_widget) # Set up status bar stuff (jumping through a lot of hoops) @@ -112,7 +112,7 @@ class MainWindow(QMainWindow): self.timer.start() self.tray_icon: TrayIcon = TrayIcon(self) - self.tray_icon.exit_app.connect(self.on_exit_app) + self.tray_icon.exit_app.connect(self.__on_exit_app) self.tray_icon.show_app.connect(self.show) self.tray_icon.activated.connect(lambda r: self.toggle() if r == self.tray_icon.DoubleClick else None) @@ -203,7 +203,7 @@ class MainWindow(QMainWindow): @pyqtSlot() @pyqtSlot(int) - def on_exit_app(self, exit_code=0) -> None: + def __on_exit_app(self, exit_code=0) -> None: self.__exit_code = exit_code self.close() diff --git a/rare/components/tabs/__init__.py b/rare/components/tabs/__init__.py index 91a6d177..697984a6 100644 --- a/rare/components/tabs/__init__.py +++ b/rare/components/tabs/__init__.py @@ -51,7 +51,7 @@ class MainTabWidget(QTabWidget): self.setTabEnabled(button_index, False) self.account_widget = AccountWidget(self) - self.account_widget.logout.connect(self.logout) + self.account_widget.exit_app.connect(self.__on_exit_app) account_action = QWidgetAction(self) account_action.setDefaultWidget(self.account_widget) account_button = TabButtonWidget("mdi.account-circle", "Account", fallback_icon="fa.user") @@ -93,25 +93,28 @@ class MainTabWidget(QTabWidget): self.tab_bar.setMinimumWidth(self.width()) super(MainTabWidget, self).resizeEvent(event) - @pyqtSlot() - def logout(self): + @pyqtSlot(int) + def __on_exit_app(self, exit_code: int): # FIXME: Don't allow logging out if there are active downloads if self.downloads_tab.is_download_active: QMessageBox.warning( self, - self.tr("Logout"), - self.tr("There are active downloads. Stop them before logging out."), + self.tr("Quit") if exit_code == self.account_widget.ExitCodes.EXIT else self.tr("Logout"), + self.tr("There are active downloads. Stop them before trying to quit."), ) return # FIXME: End of FIXME - reply = QMessageBox.question( - self, - self.tr("Logout"), - self.tr("Do you really want to logout {}?").format(self.core.lgd.userdata.get("display_name")), - buttons=(QMessageBox.Yes | QMessageBox.No), - defaultButton=QMessageBox.No, - ) + if exit_code == self.account_widget.ExitCodes.LOGOUT: + reply = QMessageBox.question( + self, + self.tr("Logout"), + self.tr("Do you really want to logout {}?").format(self.core.lgd.userdata.get("display_name")), + buttons=(QMessageBox.Yes | QMessageBox.No), + defaultButton=QMessageBox.No, + ) - if reply == QMessageBox.Yes: - self.core.lgd.invalidate_userdata() - self.exit_app.emit(-133742) # restart exit code + if reply == QMessageBox.Yes: + self.core.lgd.invalidate_userdata() + else: + return + self.exit_app.emit(exit_code) # restart exit code diff --git a/rare/components/tabs/account/__init__.py b/rare/components/tabs/account/__init__.py index 91835cce..5806082b 100644 --- a/rare/components/tabs/account/__init__.py +++ b/rare/components/tabs/account/__init__.py @@ -1,15 +1,21 @@ import webbrowser +from enum import IntEnum -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox, QLabel, QPushButton +from PyQt5.QtCore import pyqtSignal, pyqtSlot +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton from rare.utils.misc import icon class AccountWidget(QWidget): + exit_app: pyqtSignal = pyqtSignal(int) logout: pyqtSignal = pyqtSignal() + class ExitCodes(IntEnum): + EXIT = 0 + LOGOUT = -133742 + def __init__(self, parent): super(AccountWidget, self).__init__(parent=parent) self.core = LegendaryCoreSingleton() @@ -25,11 +31,22 @@ class AccountWidget(QWidget): "https://www.epicgames.com/account/personal?productName=epicgames" ) ) - self.logout_button = QPushButton(self.tr("Logout")) - self.logout_button.clicked.connect(self.logout) + self.logout_button = QPushButton(self.tr("Logout"), parent=self) + self.logout_button.clicked.connect(self.__on_logout) + self.quit_button = QPushButton(self.tr("Quit"), parent=self) + self.quit_button.clicked.connect(self.__on_quit) layout = QVBoxLayout(self) layout.addWidget(QLabel(self.tr("Account"))) layout.addWidget(QLabel(self.tr("Logged in as {}").format(username))) layout.addWidget(self.open_browser) layout.addWidget(self.logout_button) + layout.addWidget(self.quit_button) + + @pyqtSlot() + def __on_quit(self): + self.exit_app.emit(AccountWidget.ExitCodes.EXIT) + + @pyqtSlot() + def __on_logout(self): + self.exit_app.emit(AccountWidget.ExitCodes.LOGOUT) diff --git a/rare/shared/rare_core.py b/rare/shared/rare_core.py index 69132637..e4cb2a0e 100644 --- a/rare/shared/rare_core.py +++ b/rare/shared/rare_core.py @@ -179,8 +179,8 @@ class RareCore(QObject): del self.__args self.__args = None + del self.__eos_overlay RareCore.__instance = None - super(RareCore, self).deleteLater() def __validate_install(self, rgame: RareGame):