400625975d
To keep dialogs in a common format and allow them to share the same properties, three classes of dialogs have been implemented inheriting from each other. The classes are `BaseDialog` -> `ButtonDialog` -> `ActionDialog` * Basedialog: is the basis of all dialogs and is responsible for rejecting close requests from the window manager and the keyboard. It also restricts access to `exec()` and `exec_()` because they are harmful. It serves as the basis of Launch and Login dialogs * ButtonDialog: is offering buttons for accepting or rejecting the presented option. It implements its own buttons and exposes abstract methods to implement handling in them. It restricts access to `close()` because these dialogs should always product a result. It is the basis of Uninstall, Selective dialogs. * ActionDialog: in addition to the ButtonDialog, it offers an action buttom with to validate the form or to make the dialog unable to close. It serves as the basis of Install and Move dialogs.
130 lines
4.5 KiB
Python
130 lines
4.5 KiB
Python
import os
|
|
import shutil
|
|
from argparse import Namespace
|
|
from datetime import datetime, timezone
|
|
from typing import Optional
|
|
|
|
import requests.exceptions
|
|
from PyQt5.QtCore import QThreadPool, QTimer, pyqtSlot, Qt
|
|
from PyQt5.QtWidgets import QApplication, QMessageBox
|
|
from requests import HTTPError
|
|
|
|
from rare.components.dialogs.launch_dialog import LaunchDialog
|
|
from rare.components.main_window import MainWindow
|
|
from rare.shared import RareCore
|
|
from rare.utils import config_helper, paths
|
|
from rare.utils.misc import ExitCodes
|
|
from rare.widgets.rare_app import RareApp, RareAppException
|
|
|
|
|
|
class RareException(RareAppException):
|
|
def __init__(self, parent=None):
|
|
super(RareException, self).__init__(parent=parent)
|
|
|
|
def _handler(self, exc_type, exc_value, exc_tb) -> bool:
|
|
if exc_type == HTTPError:
|
|
try:
|
|
if RareCore.instance() is not None:
|
|
if RareCore.instance().core().login():
|
|
return True
|
|
raise ValueError
|
|
except Exception as e:
|
|
self.logger.fatal(str(e))
|
|
QMessageBox.warning(None, "Error", self.tr("Failed to login"))
|
|
QApplication.exit(1)
|
|
return False
|
|
|
|
|
|
class Rare(RareApp):
|
|
def __init__(self, args: Namespace):
|
|
super(Rare, self).__init__(args, f"{type(self).__name__}_{{0}}.log")
|
|
self._hook.deleteLater()
|
|
self._hook = RareException(self)
|
|
self.rcore = RareCore(args=args)
|
|
self.args = RareCore.instance().args()
|
|
self.signals = RareCore.instance().signals()
|
|
self.core = RareCore.instance().core()
|
|
|
|
lang = self.settings.value("language", self.core.language_code, type=str)
|
|
self.load_translator(lang)
|
|
|
|
# set Application name for settings
|
|
self.main_window: Optional[MainWindow] = None
|
|
self.launch_dialog: Optional[LaunchDialog] = None
|
|
self.timer: Optional[QTimer] = None
|
|
|
|
# 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)
|
|
dt_now = datetime.utcnow().replace(tzinfo=timezone.utc)
|
|
td = abs(dt_exp - dt_now)
|
|
self.timer.start(int(td.total_seconds() - 60) * 1000)
|
|
self.logger.info(f"Renewed session expires at {self.core.lgd.userdata['expires_at']}")
|
|
|
|
def re_login(self):
|
|
self.logger.info("Session expires shortly. Renew session")
|
|
try:
|
|
self.core.login()
|
|
except requests.exceptions.ConnectionError:
|
|
self.timer.start(3000) # try again if no connection
|
|
return
|
|
self.poke_timer()
|
|
|
|
@pyqtSlot()
|
|
def launch_app(self):
|
|
self.launch_dialog = LaunchDialog(parent=None)
|
|
self.launch_dialog.rejected.connect(self.__on_exit_app)
|
|
# lk: the reason we use the `start_app` signal here instead of accepted, is to keep the dialog
|
|
# until the main window has been created, then we call `accept()` to close the dialog
|
|
self.launch_dialog.start_app.connect(self.__on_start_app)
|
|
self.launch_dialog.start_app.connect(self.launch_dialog.accept)
|
|
self.launch_dialog.login()
|
|
|
|
@pyqtSlot()
|
|
def __on_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)
|
|
|
|
if not self.args.silent:
|
|
self.main_window.show()
|
|
|
|
if self.args.test_start:
|
|
self.main_window.close()
|
|
self.main_window = None
|
|
self.__on_exit_app(0)
|
|
|
|
@pyqtSlot()
|
|
@pyqtSlot(int)
|
|
def __on_exit_app(self, exit_code=0):
|
|
threadpool = QThreadPool.globalInstance()
|
|
threadpool.waitForDone()
|
|
if self.timer is not None:
|
|
self.timer.stop()
|
|
self.timer.deleteLater()
|
|
self.timer = None
|
|
self.rcore.deleteLater()
|
|
del self.rcore
|
|
self.processEvents()
|
|
shutil.rmtree(paths.tmp_dir())
|
|
os.makedirs(paths.tmp_dir())
|
|
|
|
self.exit(exit_code)
|
|
|
|
|
|
def start(args) -> int:
|
|
while True:
|
|
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
|
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
|
app = Rare(args)
|
|
exit_code = app.exec()
|
|
del app
|
|
if exit_code != ExitCodes.LOGOUT:
|
|
break
|
|
return exit_code
|