1
0
Fork 0
mirror of synced 2024-06-02 18:54:41 +12:00

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:
Stelios Tsampas 2021-06-07 23:12:26 +03:00
parent 09d9e0f9a0
commit dc2a99c2ee
5 changed files with 88 additions and 71 deletions

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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