Terminate threads and handle cleanup.
* Set the `WA_DeleteOnClose` attribute for the MainWindow. * Handle the case where `programdata_path` exists but is empty. * Emit a signal from `LaunchDialog` instead of using `exit()`. * Remove `LoginThread` as it was never deleted and caused a `SIGSEGV` on exit. * Handle termination and deletion of `SteamThread` and `ImageThread`. * Rename `finished` to `completed`, to not override the inherited `finished` signal. * Emit a signal from Account widget to do cleanup instead of immediately quitting.
This commit is contained in:
parent
09d9e0f9a0
commit
dc2a99c2ee
39
rare/app.py
39
rare/app.py
|
@ -5,7 +5,7 @@ import sys
|
|||
import time
|
||||
import importlib
|
||||
|
||||
from PyQt5.QtCore import QSettings, QTranslator, QFile, QIODevice, QTextStream
|
||||
from PyQt5.QtCore import QSettings, QTranslator, Qt, QFile, QIODevice, QTextStream
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QStyleFactory
|
||||
|
||||
|
@ -49,13 +49,21 @@ class App(QApplication):
|
|||
self.core.lgd.config.add_section("Legendary")
|
||||
self.core.lgd.save_config()
|
||||
|
||||
# workaround if egl sync enabled, but no programdata path
|
||||
if self.core.egl_sync_enabled and not os.path.exists(self.core.egl.programdata_path):
|
||||
self.core.lgd.config.remove_option("Legendary", "egl-sync")
|
||||
self.core.lgd.save_config()
|
||||
# workaround if egl sync enabled, but no programdata_path
|
||||
# programdata_path might be unset if logging in through the browser
|
||||
if self.core.egl_sync_enabled:
|
||||
if self.core.egl.programdata_path is None:
|
||||
self.core.lgd.config.remove_option("Legendary", "egl_sync")
|
||||
self.core.lgd.save_config()
|
||||
else:
|
||||
if not os.path.exists(self.core.egl.programdata_path):
|
||||
self.core.lgd.config.remove_option("Legendary", "egl_sync")
|
||||
self.core.lgd.save_config()
|
||||
|
||||
# set Application name for settings
|
||||
self.mainwindow = None
|
||||
self.tray_icon = None
|
||||
self.launch_dialog = None
|
||||
self.setApplicationName("Rare")
|
||||
self.setOrganizationName("Rare")
|
||||
settings = QSettings()
|
||||
|
@ -99,17 +107,20 @@ class App(QApplication):
|
|||
|
||||
# launch app
|
||||
self.launch_dialog = LaunchDialog(self.core, args.offline)
|
||||
self.launch_dialog.quit_app.connect(self.launch_dialog.close)
|
||||
self.launch_dialog.quit_app.connect(lambda ec: exit(ec))
|
||||
self.launch_dialog.start_app.connect(self.start_app)
|
||||
self.launch_dialog.start_app.connect(self.launch_dialog.close)
|
||||
|
||||
if not args.silent or args.subparser == "launch":
|
||||
self.launch_dialog.show()
|
||||
self.launch_dialog.login()
|
||||
|
||||
def start_app(self, offline=False):
|
||||
self.args.offline = offline
|
||||
self.mainwindow = MainWindow(self.core, self.args)
|
||||
self.launch_dialog.close()
|
||||
self.mainwindow.quit_app.connect(self.exit_app)
|
||||
self.tray_icon = TrayIcon(self)
|
||||
self.tray_icon.exit_action.triggered.connect(lambda: exit(0))
|
||||
self.tray_icon.exit_action.triggered.connect(self.exit_app)
|
||||
self.tray_icon.start_rare.triggered.connect(self.mainwindow.show)
|
||||
self.tray_icon.activated.connect(self.tray)
|
||||
if not offline:
|
||||
|
@ -125,13 +136,21 @@ class App(QApplication):
|
|||
self.mainwindow.show()
|
||||
logger.info("Show App")
|
||||
|
||||
def exit_app(self, exit_code=0):
|
||||
if self.tray_icon is not None:
|
||||
self.tray_icon.deleteLater()
|
||||
if self.mainwindow is not None:
|
||||
self.mainwindow.close()
|
||||
self.processEvents()
|
||||
self.exit(exit_code)
|
||||
|
||||
|
||||
def start(args):
|
||||
while True:
|
||||
app = App(args)
|
||||
exit_code = app.exec_()
|
||||
# if not restart
|
||||
if exit_code != -133742:
|
||||
break
|
||||
# restart app
|
||||
del app
|
||||
if exit_code != -133742:
|
||||
break
|
||||
|
|
|
@ -66,82 +66,71 @@ class SteamThread(QThread):
|
|||
self.progress.emit(100)
|
||||
|
||||
|
||||
class LoginThread(QThread):
|
||||
login = pyqtSignal()
|
||||
start_app = pyqtSignal(bool) # offline
|
||||
|
||||
def __init__(self, core: LegendaryCore):
|
||||
super(LoginThread, self).__init__()
|
||||
self.core = core
|
||||
|
||||
def run(self):
|
||||
logger.info("Try if you are logged in")
|
||||
try:
|
||||
if self.core.login():
|
||||
logger.info("You are logged in")
|
||||
self.start_app.emit(False)
|
||||
else:
|
||||
self.run()
|
||||
except ValueError:
|
||||
logger.info("You are not logged in. Open Login Window")
|
||||
self.login.emit()
|
||||
except ConnectionError as e:
|
||||
logger.warning(e)
|
||||
self.start_app.emit(True)
|
||||
|
||||
|
||||
class LaunchDialog(QDialog, Ui_LaunchDialog):
|
||||
quit_app = pyqtSignal(int)
|
||||
start_app = pyqtSignal(bool)
|
||||
finished = False
|
||||
completed = False
|
||||
|
||||
def __init__(self, core: LegendaryCore, offline, parent=None):
|
||||
super(LaunchDialog, self).__init__(parent)
|
||||
self.setAttribute(Qt.WA_DeleteOnClose)
|
||||
def __init__(self, core: LegendaryCore, offline=False, parent=None):
|
||||
super(LaunchDialog, self).__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
self.setAttribute(Qt.WA_DeleteOnClose, True)
|
||||
self.core = core
|
||||
self.offline = offline
|
||||
self.image_thread = None
|
||||
self.steam_thread = None
|
||||
if os.name == "nt":
|
||||
self.finished = True
|
||||
self.completed = True
|
||||
self.steam_info.setVisible(False)
|
||||
self.steam_prog_bar.setVisible(False)
|
||||
self.core = core
|
||||
if not offline:
|
||||
self.login_thread = LoginThread(core)
|
||||
self.login_thread.login.connect(self.login)
|
||||
self.login_thread.start_app.connect(self.launch)
|
||||
self.login_thread.start()
|
||||
|
||||
else:
|
||||
self.launch(offline)
|
||||
|
||||
def login(self):
|
||||
self.hide()
|
||||
if LoginDialog(core=self.core).login():
|
||||
self.show()
|
||||
self.login_thread.start()
|
||||
else:
|
||||
exit(0)
|
||||
do_launch = True
|
||||
try:
|
||||
if self.offline:
|
||||
pass
|
||||
else:
|
||||
if self.core.login():
|
||||
logger.info("You are logged in")
|
||||
else:
|
||||
raise ValueError("You are not logged in. Open Login Window")
|
||||
except ValueError as e:
|
||||
logger.info(str(e))
|
||||
do_launch = LoginDialog(core=self.core, parent=self).login()
|
||||
except ConnectionError as e:
|
||||
logger.warning(e)
|
||||
self.offline = True
|
||||
finally:
|
||||
if do_launch:
|
||||
self.show()
|
||||
self.launch()
|
||||
else:
|
||||
self.quit_app.emit(0)
|
||||
|
||||
def launch(self, offline=False):
|
||||
def launch(self):
|
||||
# self.core = core
|
||||
if not os.path.exists(p := os.path.expanduser("~/.cache/rare/images")):
|
||||
os.makedirs(p)
|
||||
self.offline = offline
|
||||
|
||||
if not offline:
|
||||
|
||||
if not self.offline:
|
||||
self.image_info.setText(self.tr("Downloading Images"))
|
||||
self.img_thread = ImageThread(self.core, self)
|
||||
self.img_thread.download_progess.connect(self.update_image_progbar)
|
||||
self.img_thread.finished.connect(self.finish)
|
||||
self.img_thread.start()
|
||||
self.image_thread = ImageThread(self.core, self)
|
||||
self.image_thread.download_progess.connect(self.update_image_progbar)
|
||||
self.image_thread.finished.connect(self.finish)
|
||||
self.image_thread.finished.connect(self.image_thread.quit)
|
||||
self.image_thread.finished.connect(self.image_thread.deleteLater)
|
||||
self.image_thread.start()
|
||||
# not disabled and not windows
|
||||
if (not QSettings().value("disable_protondb", False, bool)) and (not os.name == "nt"):
|
||||
self.steam_thread = SteamThread(self.core, self)
|
||||
self.steam_thread.progress.connect(self.update_steam_prog_bar)
|
||||
self.steam_thread.action.connect(lambda x: self.steam_info.setText(x))
|
||||
self.steam_thread.finished.connect(self.finish)
|
||||
self.steam_thread.finished.connect(self.steam_thread.quit)
|
||||
self.steam_thread.finished.connect(self.steam_thread.deleteLater)
|
||||
self.steam_thread.start()
|
||||
else:
|
||||
self.finished = True
|
||||
self.completed = True
|
||||
self.steam_info.setVisible(False)
|
||||
self.steam_prog_bar.setVisible(False)
|
||||
|
||||
|
@ -150,12 +139,14 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
|
|||
|
||||
def update_image_progbar(self, i: int):
|
||||
self.image_prog_bar.setValue(i)
|
||||
if i == 100:
|
||||
self.image_info.setText(self.tr("Ready"))
|
||||
|
||||
def finish(self):
|
||||
if self.finished:
|
||||
if self.completed:
|
||||
self.image_info.setText(self.tr("Starting..."))
|
||||
self.image_prog_bar.setValue(100)
|
||||
self.steam_prog_bar.setValue(100)
|
||||
self.start_app.emit(self.offline)
|
||||
else:
|
||||
self.finished = True
|
||||
self.completed = True
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import os
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import QSettings, QTimer
|
||||
from PyQt5.QtCore import Qt, QSettings, QTimer, pyqtSignal
|
||||
from PyQt5.QtGui import QCloseEvent
|
||||
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QApplication
|
||||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
from rare.components.tab_widget import TabWidget
|
||||
from rare.utils.rpc import DiscordRPC
|
||||
|
||||
|
@ -13,9 +13,11 @@ logger = getLogger("Window")
|
|||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
quit_app = pyqtSignal(int)
|
||||
|
||||
def __init__(self, core: LegendaryCore, args):
|
||||
super(MainWindow, self).__init__()
|
||||
self.setAttribute(Qt.WA_DeleteOnClose)
|
||||
self.settings = QSettings()
|
||||
self.core = core
|
||||
self.offline = args.offline
|
||||
|
@ -28,6 +30,7 @@ class MainWindow(QMainWindow):
|
|||
|
||||
self.setWindowTitle("Rare - GUI for legendary")
|
||||
self.tab_widget = TabWidget(core, self, args.offline)
|
||||
self.tab_widget.quit_app.connect(self.quit_app.emit)
|
||||
self.setCentralWidget(self.tab_widget)
|
||||
if not args.offline:
|
||||
self.rpc = DiscordRPC(core)
|
||||
|
|
|
@ -18,6 +18,7 @@ from rare.utils.models import InstallQueueItemModel, InstallOptionsModel
|
|||
|
||||
|
||||
class TabWidget(QTabWidget):
|
||||
quit_app = pyqtSignal(int)
|
||||
delete_presence = pyqtSignal()
|
||||
|
||||
def __init__(self, core: LegendaryCore, parent, offline):
|
||||
|
@ -52,8 +53,10 @@ class TabWidget(QTabWidget):
|
|||
self.addTab(self.account, "")
|
||||
self.setTabEnabled(disabled_tab + 1, False)
|
||||
|
||||
self.mini_widget = MiniWidget(core)
|
||||
self.mini_widget.quit_app.connect(self.quit_app.emit)
|
||||
account_action = QWidgetAction(self)
|
||||
account_action.setDefaultWidget(MiniWidget(core))
|
||||
account_action.setDefaultWidget(self.mini_widget)
|
||||
account_button = TabButtonWidget(core, 'mdi.account-circle', 'Account')
|
||||
account_button.setMenu(QMenu())
|
||||
account_button.menu().addAction(account_action)
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import webbrowser
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox, QLabel, QPushButton
|
||||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
|
||||
|
||||
class MiniWidget(QWidget):
|
||||
quit_app = pyqtSignal(int)
|
||||
|
||||
def __init__(self, core: LegendaryCore):
|
||||
super(MiniWidget, self).__init__()
|
||||
self.layout = QVBoxLayout()
|
||||
|
@ -39,5 +41,4 @@ class MiniWidget(QWidget):
|
|||
|
||||
if reply == QMessageBox.Yes:
|
||||
self.core.lgd.invalidate_userdata()
|
||||
# restart app
|
||||
QCoreApplication.instance().exit(-133742) # restart exit code
|
||||
self.quit_app.emit(-133742) # restart exit code
|
||||
|
|
Loading…
Reference in a new issue