1
0
Fork 0
mirror of synced 2024-06-17 18:14:44 +12:00

Merge pull request #237 from loathingKernel/fixups

Update for legendary 0.20.28
This commit is contained in:
Dummerle 2022-09-04 22:05:22 +02:00 committed by GitHub
commit 2a21ca8c66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 466 additions and 379 deletions

View file

@ -30,7 +30,7 @@ pywebview = [
{ version = "^3.6.3", extras = ["cef"], platform = "windows", optional = true },
{ version = "^3.6.3", extras = ["gtk"], platform = "linux", optional = true },
]
legendary-gl = "^0.20.27"
legendary-gl = "^0.20.28"
typing-extensions = "^4.3.0"

View file

@ -45,10 +45,7 @@ def main():
)
subparsers = parser.add_subparsers(title="Commands", dest="subparser")
launch_parser = subparsers.add_parser("launch")
launch_parser.add_argument("app_name", help="Name of the app", metavar="<App Name>")
launch_minimal_parser = subparsers.add_parser("start")
launch_minimal_parser = subparsers.add_parser("start", aliases=["launch"])
launch_minimal_parser.add_argument("app_name", help="AppName of the game to launch",
metavar="<App Name>", action="store")
launch_minimal_parser.add_argument("--offline", help="Launch game offline",
@ -56,11 +53,11 @@ def main():
launch_minimal_parser.add_argument("--skip_update_check", help="Do not check for updates",
action="store_true")
launch_minimal_parser.add_argument('--wine-bin', dest='wine_bin', action='store', metavar='<wine binary>',
default=os.environ.get('LGDRY_WINE_BINARY', None),
help='Set WINE binary to use to launch the app')
default=os.environ.get('LGDRY_WINE_BINARY', None),
help='Set WINE binary to use to launch the app')
launch_minimal_parser.add_argument('--wine-prefix', dest='wine_pfx', action='store', metavar='<wine pfx path>',
default=os.environ.get('LGDRY_WINE_PREFIX', None),
help='Set WINE prefix to use')
default=os.environ.get('LGDRY_WINE_PREFIX', None),
help='Set WINE prefix to use')
launch_minimal_parser.add_argument("--ask-alyways-sync", help="Ask for cloud saves",
action="store_true")
@ -80,10 +77,10 @@ def main():
if args.version:
from rare import __version__, code_name
print(f"Rare {__version__} Codename: {code_name}")
return
if args.subparser == "start":
if args.subparser == "start" or args.subparser == "launch":
from rare import game_launch_helper as helper
helper.start_game(args)
return
@ -99,16 +96,10 @@ def main():
from rare.utils.paths import data_dir
with open(os.path.join(data_dir, "lockfile"), "w") as file:
if args.subparser == "launch":
file.write(f"launch {args.app_name}")
else:
file.write("start")
file.write("show")
file.close()
return
if args.subparser == "launch":
args.silent = True
from rare.app import start
start(args)

View file

@ -1,4 +1,3 @@
import configparser
import logging
import os
import platform
@ -13,15 +12,18 @@ from typing import Optional
import legendary
import requests.exceptions
from PyQt5.QtCore import QThreadPool, QTimer, QT_VERSION_STR, PYQT_VERSION_STR
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMessageBox
from PyQt5.QtWidgets import QApplication, QMessageBox
from requests import HTTPError
import rare
from rare.components.dialogs.launch_dialog import LaunchDialog
from rare.components.main_window import MainWindow
from rare.components.tray_icon import TrayIcon
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.shared.image_manager import ImageManagerSingleton
from rare.shared import (
LegendaryCoreSingleton,
GlobalSignalsSingleton,
ArgumentsSingleton,
)
from rare.shared.rare_core import RareCore
from rare.utils import legendary_utils, config_helper
from rare.utils.paths import cache_dir, tmp_dir
from rare.widgets.rare_app import RareApp
@ -54,66 +56,24 @@ def excepthook(exc_type, exc_value, exc_tb):
class App(RareApp):
mainwindow: Optional[MainWindow] = None
tray_icon: Optional[QSystemTrayIcon] = None
def __init__(self, args: Namespace):
super(App, self).__init__()
self.args = ArgumentsSingleton(args) # add some options
self.window_launched = False
self.rare_core = RareCore(args=args)
self.args = ArgumentsSingleton()
self.signals = GlobalSignalsSingleton()
self.core = LegendaryCoreSingleton()
# init Legendary
try:
self.core = LegendaryCoreSingleton(init=True)
except configparser.MissingSectionHeaderError as e:
logger.warning(f"Config is corrupt: {e}")
if config_path := os.environ.get("XDG_CONFIG_HOME"):
path = os.path.join(config_path, "legendary")
else:
path = os.path.expanduser("~/.config/legendary")
with open(os.path.join(path, "config.ini"), "w") as config_file:
config_file.write("[Legendary]")
self.core = LegendaryCoreSingleton(init=True)
if "Legendary" not in self.core.lgd.config.sections():
self.core.lgd.config.add_section("Legendary")
self.core.lgd.save_config()
config_helper.init_config_handler(self.core)
lang = self.settings.value("language", self.core.language_code, type=str)
self.load_translator(lang)
config_helper.init_config_handler(self.core)
# 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.launch_dialog = None
self.signals = GlobalSignalsSingleton(init=True)
self.image_manager = ImageManagerSingleton(init=True)
self.signals.exit_app.connect(self.exit_app)
self.signals.send_notification.connect(
lambda title: self.tray_icon.showMessage(
self.tr("Download finished"),
self.tr("Download finished. {} is playable now").format(title),
QSystemTrayIcon.Information,
4000,
)
if self.settings.value("notification", True, bool)
else None
)
self.mainwindow: Optional[MainWindow] = None
self.launch_dialog: Optional[LaunchDialog] = None
# launch app
self.launch_dialog = LaunchDialog()
self.launch_dialog = LaunchDialog(parent=None)
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)
@ -140,12 +100,6 @@ class App(RareApp):
td = abs(dt_exp - dt_now)
self.timer.start(int(td.total_seconds() - 60) * 1000)
def show_mainwindow(self):
if self.window_launched:
self.mainwindow.show()
else:
self.mainwindow.show_window_centered()
def start_app(self):
for igame in self.core.get_installed_list():
if not os.path.exists(igame.install_path):
@ -167,78 +121,26 @@ class App(RareApp):
logger.info(f"{igame.title} needs verification")
self.mainwindow = MainWindow()
self.tray_icon: TrayIcon = TrayIcon(self)
self.tray_icon.exit_action.triggered.connect(self.exit_app)
self.tray_icon.start_rare.triggered.connect(self.show_mainwindow)
self.tray_icon.activated.connect(
lambda r: self.show_mainwindow()
if r == QSystemTrayIcon.DoubleClick
else None
)
self.mainwindow.exit_app.connect(self.exit_app)
if not self.args.silent:
self.mainwindow.show_window_centered()
self.window_launched = True
if self.args.subparser == "launch":
if self.args.app_name in [
i.app_name for i in self.core.get_installed_list()
]:
logger.info(
f"Launching {self.core.get_installed_game(self.args.app_name).title}"
)
self.mainwindow.tab_widget.games_tab.game_utils.prepare_launch(
self.args.app_name
)
else:
logger.error(
f"Could not find {self.args.app_name} in Games or it is not installed"
)
QMessageBox.warning(
self.mainwindow,
"Warning",
self.tr(
"Could not find {} in installed games. Did you modify the shortcut? "
).format(self.args.app_name),
)
self.mainwindow.show()
if self.args.test_start:
self.exit_app(0)
def tray(self, reason):
if reason == QSystemTrayIcon.DoubleClick:
self.mainwindow.show()
logger.info("Show App")
def exit_app(self, exit_code=0):
# FIXME: Fix this with the downlaod tab redesign
if self.mainwindow is not None:
if not self.args.offline and self.mainwindow.tab_widget.downloadTab.is_download_active:
question = QMessageBox.question(
self.mainwindow,
self.tr("Close"),
self.tr(
"There is a download active. Do you really want to exit app?"
),
QMessageBox.Yes,
QMessageBox.No,
)
if question == QMessageBox.No:
return
else:
# clear queue
self.mainwindow.tab_widget.downloadTab.queue_widget.update_queue([])
self.mainwindow.tab_widget.downloadTab.stop_download()
# FIXME: End of FIXME
self.mainwindow.timer.stop()
self.mainwindow.hide()
threadpool = QThreadPool.globalInstance()
threadpool.waitForDone()
self.core.exit()
if self.timer is not None:
self.timer.stop()
self.timer.deleteLater()
self.timer = None
if self.mainwindow is not None:
self.mainwindow.close()
if self.tray_icon is not None:
self.tray_icon.deleteLater()
self.mainwindow = None
self.rare_core.deleteLater()
del self.rare_core
self.processEvents()
shutil.rmtree(tmp_dir)
os.makedirs(tmp_dir)
@ -284,3 +186,5 @@ def start(args):
del app
if exit_code != -133742:
break

View file

@ -145,9 +145,9 @@ class InstallDialog(QDialog):
self.ui.shortcut_cb.setChecked(False)
self.ui.shortcut_cb.setToolTip(self.tr("Creating a shortcut is not supported on MacOS"))
self.ui.install_preqs_lbl.setVisible(False)
self.ui.install_preqs_check.setVisible(False)
self.ui.install_preqs_check.stateChanged.connect(lambda: self.non_reload_option_changed("install_preqs"))
self.ui.install_prereqs_lbl.setVisible(False)
self.ui.install_prereqs_check.setVisible(False)
self.ui.install_prereqs_check.stateChanged.connect(lambda: self.non_reload_option_changed("install_prereqs"))
self.non_reload_option_changed("shortcut")
@ -155,7 +155,7 @@ class InstallDialog(QDialog):
self.ui.verify_button.clicked.connect(self.verify_clicked)
self.ui.install_button.clicked.connect(self.install_clicked)
self.ui.install_preqs_check.setChecked(self.dl_item.options.install_preqs)
self.ui.install_prereqs_check.setChecked(self.dl_item.options.install_prereqs)
self.ui.install_dialog_layout.setSizeConstraint(QLayout.SetFixedSize)
@ -209,7 +209,7 @@ class InstallDialog(QDialog):
self.dl_item.options.ignore_space = self.ui.ignore_space_check.isChecked()
self.dl_item.options.no_install = self.ui.download_only_check.isChecked()
self.dl_item.options.platform = self.ui.platform_combo_box.currentText()
self.dl_item.options.install_preqs = self.ui.install_preqs_check.isChecked()
self.dl_item.options.install_prereqs = self.ui.install_prereqs_check.isChecked()
self.dl_item.options.create_shortcut = self.ui.shortcut_cb.isChecked()
if self.sdl_list_cbs:
self.dl_item.options.install_tag = [""]
@ -254,8 +254,8 @@ class InstallDialog(QDialog):
elif option == "shortcut":
QSettings().setValue("create_shortcut", self.ui.shortcut_cb.isChecked())
self.dl_item.options.create_shortcut = self.ui.shortcut_cb.isChecked()
elif option == "install_preqs":
self.dl_item.options.install_preqs = self.ui.install_preqs_check.isChecked()
elif option == "install_prereqs":
self.dl_item.options.install_prereqs = self.ui.install_prereqs_check.isChecked()
def cancel_clicked(self):
if self.config_tags:
@ -289,10 +289,10 @@ class InstallDialog(QDialog):
self.ui.cancel_button.setEnabled(True)
if pf.system() == "Windows" or ArgumentsSingleton().debug:
if dl_item.igame.prereq_info and not dl_item.igame.prereq_info.get("installed", False):
self.ui.install_preqs_check.setVisible(True)
self.ui.install_preqs_lbl.setVisible(True)
self.ui.install_preqs_check.setChecked(True)
self.ui.install_preqs_check.setText(
self.ui.install_prereqs_check.setVisible(True)
self.ui.install_prereqs_lbl.setVisible(True)
self.ui.install_prereqs_check.setChecked(True)
self.ui.install_prereqs_check.setText(
self.tr("Also install: {}").format(dl_item.igame.prereq_info.get("name", ""))
)
if self.silent:

View file

@ -2,17 +2,16 @@ import platform
from logging import getLogger
from PyQt5.QtCore import Qt, pyqtSignal, QRunnable, QObject, QThreadPool, QSettings
from PyQt5.QtWidgets import QDialog, qApp
from PyQt5.QtWidgets import QDialog, QApplication
from requests.exceptions import ConnectionError, HTTPError
from rare.components.dialogs.login import LoginDialog
from rare.models.apiresults import ApiResults
from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton, ApiResultsSingleton
from rare.shared.image_manager import ImageManagerSingleton
from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton, ApiResultsSingleton, ImageManagerSingleton
from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog
from rare.utils.misc import CloudWorker
logger = getLogger("Login")
logger = getLogger("LoginDialog")
class LaunchWorker(QRunnable):
@ -82,14 +81,13 @@ class ApiRequestWorker(LaunchWorker):
self.signals.result.emit(result, "32bit")
class LaunchDialog(QDialog, Ui_LaunchDialog):
class LaunchDialog(QDialog):
quit_app = pyqtSignal(int)
start_app = pyqtSignal()
completed = 0
def __init__(self, parent=None):
super(LaunchDialog, self).__init__(parent=parent)
self.setupUi(self)
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setWindowFlags(
Qt.Window
@ -101,19 +99,26 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
| Qt.MSWindowsFixedSizeDialogHint
)
self.setWindowModality(Qt.WindowModal)
self.ui = Ui_LaunchDialog()
self.ui.setupUi(self)
self.core = LegendaryCoreSingleton()
self.args = ArgumentsSingleton()
self.thread_pool = QThreadPool().globalInstance()
self.api_results = ApiResults()
self.login_dialog = LoginDialog(core=self.core, parent=self)
def login(self):
do_launch = True
try:
if self.args.offline:
pass
else:
qApp.processEvents()
QApplication.instance().processEvents()
# Force an update check and notice in case there are API changes
self.core.check_for_updates(force=True)
self.core.force_show_update = True
if self.core.login():
logger.info("You are logged in")
else:
@ -122,8 +127,8 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
logger.info(str(e))
# Do not set parent, because it won't show a task bar icon
# Update: Inherit the same parent as LaunchDialog
do_launch = LoginDialog(core=self.core, parent=self.parent()).login()
except ConnectionError as e:
do_launch = self.login_dialog.login()
except (HTTPError, ConnectionError) as e:
logger.warning(e)
self.args.offline = True
finally:
@ -143,7 +148,7 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
def launch(self):
if not self.args.offline:
self.image_info.setText(self.tr("Downloading Images"))
self.ui.image_info.setText(self.tr("Downloading Images"))
image_worker = ImageWorker()
image_worker.signals.result.connect(self.handle_api_worker_result)
image_worker.signals.progress.connect(self.update_image_progbar)
@ -202,13 +207,13 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
self.finish()
def update_image_progbar(self, i: int):
self.image_prog_bar.setValue(i)
self.ui.image_prog_bar.setValue(i)
def finish(self):
self.completed += 1
if self.completed == 2:
logger.info("App starting")
self.image_info.setText(self.tr("Starting..."))
self.ui.image_info.setText(self.tr("Starting..."))
ApiResultsSingleton(self.api_results)
self.completed += 1
self.start_app.emit()

View file

@ -36,8 +36,6 @@ class LoginDialog(QDialog):
def __init__(self, core: LegendaryCore, parent=None):
super(LoginDialog, self).__init__(parent=parent)
self.ui = Ui_LoginDialog()
self.ui.setupUi(self)
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setWindowFlags(
Qt.Window
@ -50,6 +48,8 @@ class LoginDialog(QDialog):
| Qt.MSWindowsFixedSizeDialogHint
)
self.setWindowModality(Qt.WindowModal)
self.ui = Ui_LoginDialog()
self.ui.setupUi(self)
self.core = core
self.args = ArgumentsSingleton()
@ -76,7 +76,10 @@ class LoginDialog(QDialog):
self.ui.back_button.setEnabled(False)
self.landing_page.ui.login_browser_radio.clicked.connect(lambda: self.ui.next_button.setEnabled(True))
self.landing_page.ui.login_browser_radio.clicked.connect(self.browser_radio_clicked)
self.landing_page.ui.login_import_radio.clicked.connect(lambda: self.ui.next_button.setEnabled(True))
self.landing_page.ui.login_import_radio.clicked.connect(self.import_radio_clicked)
self.ui.exit_button.clicked.connect(self.close)
self.ui.back_button.clicked.connect(self.back_clicked)
self.ui.next_button.clicked.connect(self.next_clicked)
@ -90,15 +93,22 @@ class LoginDialog(QDialog):
self.ui.next_button.setEnabled(True)
self.login_stack.slideInIndex(self.pages.landing)
def browser_radio_clicked(self):
self.login_stack.slideInIndex(self.pages.browser)
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.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.landing_page.ui.login_browser_radio.isChecked():
self.login_stack.slideInIndex(self.pages.browser)
self.ui.next_button.setEnabled(False)
self.browser_radio_clicked()
if self.landing_page.ui.login_import_radio.isChecked():
self.login_stack.slideInIndex(self.pages.import_egl)
self.ui.next_button.setEnabled(self.import_page.is_valid())
self.ui.back_button.setEnabled(True)
self.import_radio_clicked()
elif self.login_stack.currentIndex() == self.pages.browser:
self.browser_page.do_login()
elif self.login_stack.currentIndex() == self.pages.import_egl:

View file

@ -18,9 +18,6 @@ logger = getLogger("BrowserLogin")
class BrowserLogin(QFrame):
success = pyqtSignal()
changed = pyqtSignal()
login_url = (
"https://www.epicgames.com/id/login?redirectUrl=https%3A%2F%2Fwww.epicgames.com%2Fid%2Fapi%2Fredirect"
)
def __init__(self, core: LegendaryCore, parent=None):
super(BrowserLogin, self).__init__(parent=parent)
@ -29,9 +26,10 @@ class BrowserLogin(QFrame):
self.ui.setupUi(self)
self.core = core
self.login_url = self.core.egs.get_auth_url()
self.sid_edit = IndicatorLineEdit(
placeholder=self.tr("Insert SID here"), edit_func=self.text_changed, parent=self
placeholder=self.tr("Insert authorizationCode here"), edit_func=self.text_changed, parent=self
)
self.ui.link_text.setText(self.login_url)
self.ui.copy_button.setIcon(icon("mdi.content-copy", "fa.copy"))
@ -56,7 +54,7 @@ class BrowserLogin(QFrame):
text = text.strip()
if text.startswith("{") and text.endswith("}"):
try:
text = json.loads(text).get("sid")
text = json.loads(text).get("authorizationCode")
except json.JSONDecodeError:
return False, text, IndicatorLineEdit.reasons.wrong_format
elif '"' in text:
@ -67,10 +65,9 @@ class BrowserLogin(QFrame):
def do_login(self):
self.ui.status_label.setText(self.tr("Logging in..."))
sid = self.sid_edit.text()
auth_code = self.sid_edit.text()
try:
token = self.core.auth_sid(sid)
if self.core.auth_code(token):
if self.core.auth_code(auth_code):
logger.info(f"Successfully logged in as {self.core.lgd.userdata['displayName']}")
self.success.emit()
else:
@ -80,13 +77,11 @@ class BrowserLogin(QFrame):
logger.warning(e)
def open_browser(self):
if webview_login.webview_available is False:
logger.warning("You don't have webengine installed, " "you will need to manually copy the SID.")
if not webview_login.webview_available:
logger.warning("You don't have webengine installed, you will need to manually copy the authorizationCode.")
QDesktopServices.openUrl(QUrl(self.login_url))
else:
if webview_login.do_webview_login(
callback_sid=self.core.auth_sid, callback_code=self.core.auth_code
):
if webview_login.do_webview_login(callback_code=self.core.auth_ex_token):
logger.info("Successfully logged in as " f"{self.core.lgd.userdata['displayName']}")
self.success.emit()
else:

View file

@ -5,6 +5,7 @@ from logging import getLogger
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QFrame, QFileDialog
from legendary.core import LegendaryCore
from legendary.utils.wine_helpers import read_registry, get_shell_folders
from rare.ui.components.dialogs.login.import_login import Ui_ImportLogin
@ -18,7 +19,7 @@ class ImportLogin(QFrame):
localappdata = os.path.expandvars("%LOCALAPPDATA%")
else:
localappdata = os.path.join("drive_c/users", getuser(), "Local Settings/Application Data")
appdata_path = os.path.join(localappdata, "EpicGamesLauncher/Saved/Config/Windows")
egl_appdata = os.path.join(localappdata, "EpicGamesLauncher", "Saved", "Config", "Windows")
found = False
def __init__(self, core: LegendaryCore, parent=None):
@ -33,16 +34,19 @@ class ImportLogin(QFrame):
self.text_egl_notfound = self.tr("Could not find EGL Program Data. ")
if os.name == "nt":
if not self.core.egl.appdata_path and os.path.exists(self.appdata_path):
self.core.egl.appdata_path = self.appdata_path
if not self.core.egl.appdata_path and os.path.exists(self.egl_appdata):
self.core.egl.appdata_path = self.egl_appdata
if not self.core.egl.appdata_path:
self.ui.status_label.setText(self.text_egl_notfound)
else:
self.ui.status_label.setText(self.text_egl_found)
self.found = True
else:
if programdata_path := self.core.egl.programdata_path:
if wine_pfx := programdata_path.split("drive_c")[0]:
self.ui.prefix_combo.addItem(wine_pfx)
self.ui.info_label.setText(
self.tr("Please select the Wine prefix" " where Epic Games Launcher is installed. ")
self.tr("Please select the Wine prefix where Epic Games Launcher is installed. ")
+ self.ui.info_label.text()
)
prefixes = self.get_wine_prefixes()
@ -62,7 +66,7 @@ class ImportLogin(QFrame):
]
prefixes = []
for prefix in possible_prefixes:
if os.path.exists(os.path.join(prefix, self.appdata_path)):
if os.path.exists(os.path.join(prefix, self.egl_appdata)):
prefixes.append(prefix)
return prefixes
@ -73,18 +77,28 @@ class ImportLogin(QFrame):
names = prefix_dialog.selectedFiles()
self.ui.prefix_combo.setCurrentText(names[0])
def is_valid(self):
def is_valid(self) -> bool:
if os.name == "nt":
return self.found
else:
return os.path.exists(os.path.join(self.ui.prefix_combo.currentText(), self.appdata_path))
egl_wine_pfx = self.ui.prefix_combo.currentText()
try:
wine_folders = get_shell_folders(read_registry(egl_wine_pfx), egl_wine_pfx)
self.egl_appdata = os.path.realpath(
os.path.join(wine_folders['Local AppData'], 'EpicGamesLauncher', 'Saved', 'Config', 'Windows'))
if path_exists := os.path.exists(self.egl_appdata):
self.ui.status_label.setText(self.text_egl_found)
return path_exists
except KeyError:
return False
def do_login(self):
self.ui.status_label.setText(self.tr("Loading..."))
if os.name == "nt":
pass
else:
self.core.egl.appdata_path = os.path.join(self.ui.prefix_combo.currentText(), self.appdata_path)
logger.info(f'Using EGL appdata path at "{self.egl_appdata}"')
self.core.egl.appdata_path = self.egl_appdata
try:
if self.core.auth_import():
logger.info(f"Logged in as {self.core.lgd.userdata['displayName']}")

View file

@ -1,20 +1,24 @@
import os
from logging import getLogger
from PyQt5.QtCore import Qt, QSettings, QTimer, QSize
from PyQt5.QtCore import Qt, QSettings, QTimer, QSize, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QCursor
from PyQt5.QtWidgets import QMainWindow, QApplication, QStatusBar, QScrollArea, QScroller, QComboBox
from PyQt5.QtWidgets import QMainWindow, QApplication, QStatusBar, QScrollArea, QScroller, QComboBox, QMessageBox
from rare.components.tabs import TabWidget
from rare.components.tray_icon import TrayIcon
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.utils.paths import data_dir
logger = getLogger("Window")
logger = getLogger("MainWindow")
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
# int: exit code
exit_app: pyqtSignal = pyqtSignal(int)
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setAttribute(Qt.WA_DeleteOnClose)
self.core = LegendaryCoreSingleton()
self.signals = GlobalSignalsSingleton()
@ -46,8 +50,42 @@ class MainWindow(QMainWindow):
self.timer.timeout.connect(self.timer_finished)
self.timer.start(1000)
def show_window_centered(self):
self.show()
self.signals.exit_app.connect(self.on_exit_app)
self.exit_code = 0
self.accept_close = False
self.tray_icon: TrayIcon = TrayIcon(self)
self.tray_icon.exit_action.triggered.connect(self.on_exit_app)
self.tray_icon.start_rare.triggered.connect(self.show)
self.tray_icon.activated.connect(
lambda r: self.toggle()
if r == self.tray_icon.DoubleClick
else None
)
self.signals.send_notification.connect(
lambda title: self.tray_icon.showMessage(
self.tr("Download finished"),
self.tr("Download finished. {} is playable now").format(title),
self.tray_icon.Information,
4000,
)
if self.settings.value("notification", True, bool)
else None
)
self.window_launched = False
# enable kinetic scrolling
for scroll_area in self.findChildren(QScrollArea):
if not scroll_area.property("no_kinetic_scroll"):
QScroller.grabGesture(scroll_area.viewport(), QScroller.LeftMouseButtonGesture)
# fix scrolling
for combo_box in scroll_area.findChildren(QComboBox):
combo_box.wheelEvent = lambda e: e.ignore()
def center_window(self):
# get the margins of the decorated window
margins = self.windowHandle().frameMargins()
# get the screen the cursor is on
@ -68,14 +106,23 @@ class MainWindow(QMainWindow):
- self.rect().adjusted(0, 0, decor_width, decor_height).center()
)
# enable kinetic scrolling
for scroll_area in self.findChildren(QScrollArea):
if not scroll_area.property("no_kinetic_scroll"):
QScroller.grabGesture(scroll_area.viewport(), QScroller.LeftMouseButtonGesture)
def show(self) -> None:
super(MainWindow, self).show()
if not self.window_launched:
self.center_window()
self.window_launched = True
# fix scrolling
for combo_box in scroll_area.findChildren(QComboBox):
combo_box.wheelEvent = lambda e: e.ignore()
def hide(self) -> None:
if self.settings.value("save_size", False, bool):
size = self.size().width(), self.size().height()
self.settings.setValue("window_size", size)
super(MainWindow, self).hide()
def toggle(self):
if self.isHidden():
self.show()
else:
self.hide()
def timer_finished(self):
file_path = os.path.join(data_dir, "lockfile")
@ -83,30 +130,48 @@ class MainWindow(QMainWindow):
file = open(file_path, "r")
action = file.read()
file.close()
if action.startswith("launch"):
game = action.replace("launch ", "").replace("\n", "")
if game in [
i.app_name for i in self.tab_widget.games_tab.game_list
] and self.core.is_installed(game):
self.tab_widget.games_tab.game_utils.prepare_launch(
game, offline=self.args.offline
)
else:
logger.info(f"Could not find {game} in Games")
elif action.startswith("start"):
if action.startswith("show"):
self.show()
os.remove(file_path)
self.timer.start(1000)
@pyqtSlot()
@pyqtSlot(int)
def on_exit_app(self, exit_code=0) -> None:
# FIXME: Fix this with the download tab redesign
if not self.args.offline and self.tab_widget.downloadTab.is_download_active:
question = QMessageBox.question(
self,
self.tr("Close"),
self.tr(
"There is a download active. Do you really want to exit app?"
),
QMessageBox.Yes,
QMessageBox.No,
)
if question == QMessageBox.No:
return
else:
# clear queue
self.tab_widget.downloadTab.queue_widget.update_queue([])
self.tab_widget.downloadTab.stop_download()
# FIXME: End of FIXME
self.exit_code = exit_code
self.close()
def close(self) -> bool:
self.accept_close = True
return super(MainWindow, self).close()
def closeEvent(self, e: QCloseEvent):
if self.settings.value("save_size", False, bool):
size = self.size().width(), self.size().height()
self.settings.setValue("window_size", size)
if self.settings.value("sys_tray", True, bool):
self.hide()
e.ignore()
return
elif self.args.offline:
pass
self.signals.exit_app.emit(0)
e.ignore()
if not self.accept_close:
if self.settings.value("sys_tray", True, bool):
self.hide()
e.ignore()
return
self.timer.stop()
self.tray_icon.deleteLater()
self.hide()
self.exit_app.emit(self.exit_code)
super(MainWindow, self).closeEvent(e)
e.accept()

View file

@ -98,7 +98,7 @@ class DownloadThread(QThread):
# postinstall,
# self.item.download.igame,
# False,
# self.item.options.install_preqs,
# self.item.options.install_prereqs,
# )
self._handle_postinstall(postinstall, self.item.download.igame)
@ -140,7 +140,7 @@ class DownloadThread(QThread):
logger.info("This game lists the following prequisites to be installed:")
logger.info(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
if platform.system() == "Windows":
if not self.item.options.install_preqs:
if not self.item.options.install_prereqs:
logger.info("Marking prerequisites as installed...")
self.core.prereq_installed(self.item.download.igame.app_name)
else:

View file

@ -11,7 +11,7 @@ from rare.shared import (
ArgumentsSingleton,
ApiResultsSingleton,
)
from rare.shared.image_manager import ImageManagerSingleton
from rare.shared import ImageManagerSingleton
from rare.widgets.library_layout import LibraryLayout
from rare.widgets.sliding_stack import SlidingStackedWidget
from .cloud_save_utils import CloudSaveUtils

View file

@ -3,8 +3,8 @@ from PyQt5.QtWidgets import QFrame, QWidget, QMessageBox
from legendary.models.game import Game
from rare.components.tabs.games.game_utils import GameUtils
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ImageManagerSingleton
from rare.shared.image_manager import ImageSize
from rare.ui.components.tabs.games.game_info.game_dlc import Ui_GameDlc
from rare.ui.components.tabs.games.game_info.game_dlc_widget import Ui_GameDlcWidget
from rare.models.install import InstallOptionsModel

View file

@ -27,7 +27,8 @@ from rare.shared import (
GlobalSignalsSingleton,
ArgumentsSingleton,
)
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.shared import ImageManagerSingleton
from rare.shared.image_manager import ImageSize
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
from rare.utils.legendary_utils import VerifyWorker
from rare.utils.misc import get_size

View file

@ -11,7 +11,8 @@ from rare.shared import (
ArgumentsSingleton,
ApiResultsSingleton,
)
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.shared import ImageManagerSingleton
from rare.shared.image_manager import ImageSize
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
from rare.utils.extra_widgets import SideTabWidget
from rare.utils.json_formatter import QJsonModel

View file

@ -7,8 +7,8 @@ from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QFrame, QMessageBox, QAction
from rare.components.tabs.games.game_utils import GameUtils
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton, ImageManagerSingleton
from rare.shared.image_manager import ImageSize
from rare.utils.misc import create_desktop_link
from rare.widgets.image_widget import ImageWidget

View file

@ -4,7 +4,8 @@ from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import QFrame, QAction
from legendary.models.game import Game
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.shared import ImageManagerSingleton
from rare.shared.image_manager import ImageSize
from rare.widgets.image_widget import ImageWidget
logger = getLogger("Uninstalled")

View file

@ -3,8 +3,8 @@ from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QFrame
from legendary.models.game import Game
from rare.shared import LegendaryCoreSingleton
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
from rare.shared import LegendaryCoreSingleton, ImageManagerSingleton
from rare.shared.image_manager import ImageSize
from rare.widgets.elide_label import ElideLabel
from .library_widget import LibraryWidget

View file

@ -1,5 +1,7 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton
from rare.shared import GlobalSignalsSingleton
class DebugSettings(QWidget):
def __init__(self):
@ -9,6 +11,9 @@ class DebugSettings(QWidget):
self.raise_runtime_exception_button = QPushButton("Raise Exception")
self.layout().addWidget(self.raise_runtime_exception_button)
self.raise_runtime_exception_button.clicked.connect(self.raise_exception)
self.restart_button = QPushButton("Restart")
self.layout().addWidget(self.restart_button)
self.restart_button.clicked.connect(lambda: GlobalSignalsSingleton().exit_app.emit(-133742))
self.layout().addStretch(1)

View file

@ -13,7 +13,7 @@ logger = getLogger("TrayIcon")
class TrayIcon(QSystemTrayIcon):
def __init__(self, parent):
super(TrayIcon, self).__init__(parent)
super(TrayIcon, self).__init__(parent=parent)
self.core = LegendaryCoreSingleton()
self.setIcon(QIcon(":/images/Rare.png"))
@ -33,7 +33,7 @@ class TrayIcon(QSystemTrayIcon):
if len(installed := self.core.get_installed_list()) < 5:
last_played = [GameMeta(i.app_name) for i in sorted(installed, key=lambda x: x.title)]
elif games := sorted(
parent.mainwindow.tab_widget.games_tab.game_utils.game_meta.get_games(),
parent.tab_widget.games_tab.game_utils.game_meta.get_games(),
key=lambda x: x.last_played, reverse=True):
last_played: List[GameMeta] = games[0:5]
else:
@ -46,7 +46,7 @@ class TrayIcon(QSystemTrayIcon):
a.setProperty("app_name", game.app_name)
self.game_actions.append(a)
a.triggered.connect(
lambda: parent.mainwindow.tab_widget.games_tab.game_utils.prepare_launch(
lambda: parent.tab_widget.games_tab.game_utils.prepare_launch(
self.sender().property("app_name"))
)

View file

@ -11,11 +11,11 @@ from PyQt5.QtCore import QObject, QProcess, pyqtSignal, QUrl, QRunnable, QThread
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
from rare.lgndr.core import LegendaryCore
from rare.widgets.rare_app import RareApp
from .console import Console
from .lgd_helper import get_launch_args, InitArgs, get_configured_process, LaunchArgs, GameArgsError
from .message_models import ErrorModel, Actions, FinishedModel, BaseModel, StateChangedModel
from ..shared import LegendaryCoreSingleton
from ..widgets.rare_app import RareApp
class PreLaunchThread(QRunnable):
@ -25,9 +25,9 @@ class PreLaunchThread(QRunnable):
pre_launch_command_finished = pyqtSignal(int) # exit_code
error_occurred = pyqtSignal(str)
def __init__(self, args: InitArgs):
def __init__(self, core: LegendaryCore, args: InitArgs):
super(PreLaunchThread, self).__init__()
self.core = LegendaryCoreSingleton()
self.core = core
self.app_name = args.app_name
self.signals = self.Signals()
@ -69,7 +69,7 @@ class GameProcessApp(RareApp):
self.game_process = QProcess()
self.app_name = app_name
self.logger = getLogger(self.app_name)
self.core = LegendaryCoreSingleton(init=True)
self.core = LegendaryCore()
lang = self.settings.value("language", self.core.language_code, type=str)
self.load_translator(lang)
@ -187,7 +187,7 @@ class GameProcessApp(RareApp):
self.logger.error("Not logged in. Try to launch game offline")
args.offline = True
worker = PreLaunchThread(args)
worker = PreLaunchThread(self.core, args)
worker.signals.ready_to_launch.connect(self.launch_game)
worker.signals.error_occurred.connect(self.error_occurred)
# worker.signals.started_pre_launch_command(None)

View file

@ -6,9 +6,11 @@ from logging import getLogger
from typing import List
from PyQt5.QtCore import QProcess, QProcessEnvironment
from legendary.core import LegendaryCore
from legendary.models.game import InstalledGame, LaunchParameters
from rare.lgndr.core import LegendaryCore
logger = getLogger("Helper")

View file

@ -114,7 +114,7 @@ class LgndrInstallGameRealArgs:
dlm_debug: bool = False
yes: bool = False
# Rare: Extra arguments
install_preqs: bool = False
install_prereqs: bool = False
indirect_status: LgndrIndirectStatus = LgndrIndirectStatus()
ui_update: Callable[[UIUpdate], None] = lambda ui: None
dlm_signals: DLManagerSignals = DLManagerSignals()

View file

@ -248,7 +248,7 @@ class LegendaryCLI(LegendaryCLIReal):
postinstall = self.core.install_game(igame)
if postinstall:
self._handle_postinstall(postinstall, igame, yes=args.yes, choice=args.install_preqs)
self._handle_postinstall(postinstall, igame, skip_prereqs=args.yes, choice=args.install_prereqs)
dlcs = self.core.get_dlc_for_game(game.app_name)
if dlcs and not args.skip_dlcs:
@ -301,7 +301,7 @@ class LegendaryCLI(LegendaryCLIReal):
self.core.uninstall_tag(old_igame)
self.core.install_game(old_igame)
def _handle_postinstall(self, postinstall, igame, yes=False, choice=True):
def _handle_postinstall(self, postinstall, igame, skip_prereqs=False, choice=True):
# Override logger for the local context to use message as part of the indirect return value
logger = LgndrIndirectLogger(LgndrIndirectStatus(), self.logger)
# noinspection PyShadowingBuiltins
@ -309,12 +309,12 @@ class LegendaryCLI(LegendaryCLIReal):
# noinspection PyShadowingBuiltins
def input(x): return 'y' if choice else 'i'
print('\nThis game lists the following prequisites to be installed:')
print('\nThis game lists the following prerequisites to be installed:')
print(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
print('')
if os.name == 'nt':
if yes:
if skip_prereqs:
c = 'n' # we don't want to launch anything, just silent install.
else:
choice = input('Do you wish to install the prerequisites? ([y]es, [n]o, [i]gnore): ')

View file

@ -31,7 +31,7 @@ class InstallOptionsModel:
overlay: bool = False
update: bool = False
silent: bool = False
install_preqs: bool = pf.system() == "Windows"
install_prereqs: bool = pf.system() == "Windows"
def __post_init__(self):
self.sdl_prompt: Callable[[str, str], list] = \
@ -41,7 +41,7 @@ class InstallOptionsModel:
return {
k: getattr(self, k)
for k in self.__dict__
if k not in ["update", "silent", "create_shortcut", "overlay", "install_preqs"]
if k not in ["update", "silent", "create_shortcut", "overlay", "install_prereqs"]
}

View file

@ -5,52 +5,34 @@ Each of the objects in this module should be instantiated ONCE
and only ONCE!
"""
import logging
from argparse import Namespace
from typing import Optional
from rare.lgndr.core import LegendaryCore
from rare.models.apiresults import ApiResults
from rare.models.signals import GlobalSignals
from .image_manager import ImageManager
from .rare_core import RareCore
_legendary_core_singleton: Optional[LegendaryCore] = None
_global_signals_singleton: Optional[GlobalSignals] = None
_arguments_singleton: Optional[Namespace] = None
_api_results_singleton: Optional[ApiResults] = None
logger = logging.getLogger("Shared")
def LegendaryCoreSingleton(init: bool = False) -> LegendaryCore:
global _legendary_core_singleton
if _legendary_core_singleton is None and not init:
raise RuntimeError("Uninitialized use of LegendaryCoreSingleton")
if _legendary_core_singleton is None:
_legendary_core_singleton = LegendaryCore()
return _legendary_core_singleton
def ArgumentsSingleton() -> Optional[Namespace]:
return RareCore.instance().args()
def GlobalSignalsSingleton(init: bool = False) -> GlobalSignals:
global _global_signals_singleton
if _global_signals_singleton is None and not init:
raise RuntimeError("Uninitialized use of GlobalSignalsSingleton")
if _global_signals_singleton is None:
_global_signals_singleton = GlobalSignals()
return _global_signals_singleton
def GlobalSignalsSingleton() -> GlobalSignals:
return RareCore.instance().signals()
def ArgumentsSingleton(args: Namespace = None) -> Optional[Namespace]:
global _arguments_singleton
if _arguments_singleton is None and args is None:
raise RuntimeError("Uninitialized use of ArgumentsSingleton")
if _arguments_singleton is None:
_arguments_singleton = args
return _arguments_singleton
def LegendaryCoreSingleton() -> LegendaryCore:
return RareCore.instance().core()
def ImageManagerSingleton() -> ImageManager:
return RareCore.instance().image_manager()
def ApiResultsSingleton(res: ApiResults = None) -> Optional[ApiResults]:
global _api_results_singleton
if _api_results_singleton is None and res is None:
raise RuntimeError("Uninitialized use of ApiResultsSingleton")
if _api_results_singleton is None:
_api_results_singleton = res
return _api_results_singleton
return RareCore.instance().api_results(res)

View file

@ -7,7 +7,7 @@ import zlib
from logging import getLogger
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Tuple, Dict, Union, Type, List, Callable, Optional
from typing import Tuple, Dict, Union, Type, List, Callable
import requests
from PyQt5.QtCore import (
@ -26,7 +26,8 @@ from PyQt5.QtGui import (
from PyQt5.QtWidgets import QApplication
from legendary.models.game import Game
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton
from rare.lgndr.core import LegendaryCore
from rare.models.signals import GlobalSignals
from rare.utils.paths import image_dir, resources_path
if TYPE_CHECKING:
@ -111,10 +112,10 @@ class ImageManager(QObject):
__dl_retries = 1
__worker_app_names: List[str] = list()
def __init__(self):
def __init__(self, signals: GlobalSignals, core: LegendaryCore):
super(QObject, self).__init__()
self.core = LegendaryCoreSingleton()
self.signals = GlobalSignalsSingleton()
self.signals = signals
self.core = core
self.image_dir = Path(image_dir)
if not self.image_dir.is_dir():
@ -357,15 +358,3 @@ class ImageManager(QObject):
"""
image: QImage = self.__get_cover(QImage, app_name, color)
return image
_image_manager_singleton: Optional[ImageManager] = None
def ImageManagerSingleton(init: bool = False) -> ImageManager:
global _image_manager_singleton
if _image_manager_singleton is None and not init:
raise RuntimeError("Uninitialized use of ImageManagerSingleton")
if _image_manager_singleton is None:
_image_manager_singleton = ImageManager()
return _image_manager_singleton

133
rare/shared/rare_core.py Normal file
View file

@ -0,0 +1,133 @@
import configparser
import os
from argparse import Namespace
from logging import getLogger
from typing import Optional
from PyQt5.QtCore import QObject
from rare.lgndr.core import LegendaryCore
from rare.models.apiresults import ApiResults
from rare.models.signals import GlobalSignals
from .image_manager import ImageManager
logger = getLogger("RareCore")
class RareCore(QObject):
_instance: Optional['RareCore'] = None
def __init__(self, args: Namespace):
if self._instance is not None:
raise RuntimeError("RareCore already initialized")
super(RareCore, self).__init__()
self._args: Optional[Namespace] = None
self._signals: Optional[GlobalSignals] = None
self._core: Optional[LegendaryCore] = None
self._image_manager: Optional[ImageManager] = None
self._api_results: Optional[ApiResults] = None
self.args(args)
self.signals(init=True)
self.core(init=True)
self.image_manager(init=True)
RareCore._instance = self
@staticmethod
def instance() -> 'RareCore':
if RareCore._instance is None:
raise RuntimeError("Uninitialized use of RareCore")
return RareCore._instance
def signals(self, init: bool = False) -> GlobalSignals:
if self._signals is None and not init:
raise RuntimeError("Uninitialized use of GlobalSignalsSingleton")
if self._signals is not None and init:
raise RuntimeError("GlobalSignals already initialized")
if init:
self._signals = GlobalSignals()
return self._signals
def args(self, args: Namespace = None) -> Optional[Namespace]:
if self._args is None and args is None:
raise RuntimeError("Uninitialized use of ArgumentsSingleton")
if self._args is not None and args is not None:
raise RuntimeError("Arguments already initialized")
if args is not None:
self._args = args
return self._args
def core(self, init: bool = False) -> LegendaryCore:
if self._core is None and not init:
raise RuntimeError("Uninitialized use of LegendaryCoreSingleton")
if self._core is not None and init:
raise RuntimeError("LegendaryCore already initialized")
if init:
try:
self._core = LegendaryCore()
except configparser.MissingSectionHeaderError as e:
logger.warning(f"Config is corrupt: {e}")
if config_path := os.environ.get("XDG_CONFIG_HOME"):
path = os.path.join(config_path, "legendary")
else:
path = os.path.expanduser("~/.config/legendary")
with open(os.path.join(path, "config.ini"), "w") as config_file:
config_file.write("[Legendary]")
self._core = LegendaryCore()
if "Legendary" not in self._core.lgd.config.sections():
self._core.lgd.config.add_section("Legendary")
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()
return self._core
def image_manager(self, init: bool = False) -> ImageManager:
if self._image_manager is None and not init:
raise RuntimeError("Uninitialized use of ImageManagerSingleton")
if self._image_manager is not None and init:
raise RuntimeError("ImageManager already initialized")
if self._image_manager is None:
self._image_manager = ImageManager(self.signals(), self.core())
return self._image_manager
def api_results(self, res: ApiResults = None) -> Optional[ApiResults]:
if self._api_results is None and res is None:
raise RuntimeError("Uninitialized use of ApiResultsSingleton")
if self._api_results is not None and res is not None:
raise RuntimeError("ApiResults already initialized")
if res is not None:
self._api_results = res
return self._api_results
def deleteLater(self) -> None:
del self._api_results
self._api_results = None
self._image_manager.deleteLater()
del self._image_manager
self._image_manager = None
self._core.exit()
del self._core
self._core = None
self._signals.deleteLater()
del self._signals
self._signals = None
del self._args
self._args = None
RareCore._instance = None
super(RareCore, self).deleteLater()

View file

@ -109,17 +109,17 @@ class Ui_InstallDialog(object):
self.max_memory_info_label.setObjectName("max_memory_info_label")
self.max_memory_layout.addWidget(self.max_memory_info_label)
self.advanced_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.max_memory_layout)
self.install_preqs_lbl = QtWidgets.QLabel(InstallDialog)
self.install_preqs_lbl.setObjectName("install_preqs_lbl")
self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.install_preqs_lbl)
self.install_preqs_check = QtWidgets.QCheckBox(InstallDialog)
self.install_prereqs_lbl = QtWidgets.QLabel(InstallDialog)
self.install_prereqs_lbl.setObjectName("install_prereqs_lbl")
self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.install_prereqs_lbl)
self.install_prereqs_check = QtWidgets.QCheckBox(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.install_preqs_check.setFont(font)
self.install_preqs_check.setText("")
self.install_preqs_check.setChecked(False)
self.install_preqs_check.setObjectName("install_preqs_check")
self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.install_preqs_check)
self.install_prereqs_check.setFont(font)
self.install_prereqs_check.setText("")
self.install_prereqs_check.setChecked(False)
self.install_prereqs_check.setObjectName("install_prereqs_check")
self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.install_prereqs_check)
self.dl_optimizations_label = QtWidgets.QLabel(InstallDialog)
self.dl_optimizations_label.setObjectName("dl_optimizations_label")
self.advanced_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.dl_optimizations_label)
@ -216,7 +216,7 @@ class Ui_InstallDialog(object):
self.max_memory_label.setText(_translate("InstallDialog", "Max shared memory"))
self.max_memory_spin.setSuffix(_translate("InstallDialog", "MiB"))
self.max_memory_info_label.setText(_translate("InstallDialog", "Less is slower (0: Default)"))
self.install_preqs_lbl.setText(_translate("InstallDialog", "Install prerequisites"))
self.install_prereqs_lbl.setText(_translate("InstallDialog", "Install prerequisites"))
self.dl_optimizations_label.setText(_translate("InstallDialog", "Enable reordering"))
self.force_download_label.setText(_translate("InstallDialog", "Force redownload"))
self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space"))

View file

@ -185,14 +185,14 @@
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="install_preqs_lbl">
<widget class="QLabel" name="install_prereqs_lbl">
<property name="text">
<string>Install prerequisites</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="install_preqs_check">
<widget class="QCheckBox" name="install_prereqs_check">
<property name="font">
<font>
<italic>true</italic>

View file

@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_InstallDialogAdvanced(object):
def setupUi(self, InstallDialogAdvanced):
InstallDialogAdvanced.setObjectName("InstallDialogAdvanced")
InstallDialogAdvanced.resize(359, 208)
InstallDialogAdvanced.resize(379, 208)
self.install_dialog_advanced_layout = QtWidgets.QFormLayout(InstallDialogAdvanced)
self.install_dialog_advanced_layout.setObjectName("install_dialog_advanced_layout")
self.max_workers_label = QtWidgets.QLabel(InstallDialogAdvanced)
@ -63,17 +63,17 @@ class Ui_InstallDialogAdvanced(object):
self.max_memory_info_label.setObjectName("max_memory_info_label")
self.max_memory_layout.addWidget(self.max_memory_info_label)
self.install_dialog_advanced_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.max_memory_layout)
self.install_preqs_lbl = QtWidgets.QLabel(InstallDialogAdvanced)
self.install_preqs_lbl.setObjectName("install_preqs_lbl")
self.install_dialog_advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.install_preqs_lbl)
self.install_preqs_check = QtWidgets.QCheckBox(InstallDialogAdvanced)
self.install_prereqs_lbl = QtWidgets.QLabel(InstallDialogAdvanced)
self.install_prereqs_lbl.setObjectName("install_prereqs_lbl")
self.install_dialog_advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.install_prereqs_lbl)
self.install_prereqs_check = QtWidgets.QCheckBox(InstallDialogAdvanced)
font = QtGui.QFont()
font.setItalic(True)
self.install_preqs_check.setFont(font)
self.install_preqs_check.setText("")
self.install_preqs_check.setChecked(False)
self.install_preqs_check.setObjectName("install_preqs_check")
self.install_dialog_advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.install_preqs_check)
self.install_prereqs_check.setFont(font)
self.install_prereqs_check.setText("")
self.install_prereqs_check.setChecked(False)
self.install_prereqs_check.setObjectName("install_prereqs_check")
self.install_dialog_advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.install_prereqs_check)
self.dl_optimizations_label = QtWidgets.QLabel(InstallDialogAdvanced)
self.dl_optimizations_label.setObjectName("dl_optimizations_label")
self.install_dialog_advanced_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.dl_optimizations_label)
@ -119,7 +119,7 @@ class Ui_InstallDialogAdvanced(object):
self.max_memory_label.setText(_translate("InstallDialogAdvanced", "Max shared memory"))
self.max_memory_spin.setSuffix(_translate("InstallDialogAdvanced", "MiB"))
self.max_memory_info_label.setText(_translate("InstallDialogAdvanced", "Less is slower (0: Default)"))
self.install_preqs_lbl.setText(_translate("InstallDialogAdvanced", "Install prerequisites"))
self.install_prereqs_lbl.setText(_translate("InstallDialogAdvanced", "Install prerequisites"))
self.dl_optimizations_label.setText(_translate("InstallDialogAdvanced", "Enable reordering"))
self.force_download_label.setText(_translate("InstallDialogAdvanced", "Force redownload"))
self.ignore_space_label.setText(_translate("InstallDialogAdvanced", "Ignore free space"))

View file

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>359</width>
<width>379</width>
<height>208</height>
</rect>
</property>
@ -102,14 +102,14 @@
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="install_preqs_lbl">
<widget class="QLabel" name="install_prereqs_lbl">
<property name="text">
<string>Install prerequisites</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="install_preqs_check">
<widget class="QCheckBox" name="install_prereqs_check">
<property name="font">
<font>
<italic>true</italic>

View file

@ -14,12 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_BrowserLogin(object):
def setupUi(self, BrowserLogin):
BrowserLogin.setObjectName("BrowserLogin")
BrowserLogin.resize(400, 200)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(BrowserLogin.sizePolicy().hasHeightForWidth())
BrowserLogin.setSizePolicy(sizePolicy)
BrowserLogin.resize(182, 210)
BrowserLogin.setWindowTitle("BrowserLogin")
self.browser_layout = QtWidgets.QGridLayout(BrowserLogin)
self.browser_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
@ -78,7 +73,7 @@ class Ui_BrowserLogin(object):
_translate = QtCore.QCoreApplication.translate
self.open_button.setText(_translate("BrowserLogin", "Open Browser"))
self.title_label.setText(_translate("BrowserLogin", "Login through browser"))
self.info_label.setText(_translate("BrowserLogin", "Click the button to open the login page in a browser or copy the link and paste it in a browser. After logging in, copy the SID code in the input above."))
self.info_label.setText(_translate("BrowserLogin", "Click the button to open the login page in a browser or copy the link and paste it in a browser. After logging in, copy the <b><code>authorizationCode</code></b> in the input above."))
if __name__ == "__main__":

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>246</width>
<height>184</height>
<width>182</width>
<height>210</height>
</rect>
</property>
<property name="windowTitle">
@ -94,7 +94,7 @@
</font>
</property>
<property name="text">
<string>Click the button to open the login page in a browser or copy the link and paste it in a browser. After logging in, copy the SID code in the input above.</string>
<string>Click the button to open the login page in a browser or copy the link and paste it in a browser. After logging in, copy the &lt;b&gt;&lt;code&gt;authorizationCode&lt;/code&gt;&lt;/b&gt; in the input above.</string>
</property>
<property name="wordWrap">
<bool>true</bool>

View file

@ -14,12 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ImportLogin(object):
def setupUi(self, ImportLogin):
ImportLogin.setObjectName("ImportLogin")
ImportLogin.resize(400, 200)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(ImportLogin.sizePolicy().hasHeightForWidth())
ImportLogin.setSizePolicy(sizePolicy)
ImportLogin.resize(242, 120)
ImportLogin.setWindowTitle("ImportLogin")
self.import_layout = QtWidgets.QGridLayout(ImportLogin)
self.import_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
@ -51,6 +46,7 @@ class Ui_ImportLogin(object):
font.setItalic(True)
self.status_label.setFont(font)
self.status_label.setText("")
self.status_label.setWordWrap(True)
self.status_label.setObjectName("status_label")
self.import_layout.addWidget(self.status_label, 2, 1, 1, 2)
self.info_label = QtWidgets.QLabel(ImportLogin)
@ -68,7 +64,7 @@ class Ui_ImportLogin(object):
def retranslateUi(self, ImportLogin):
_translate = QtCore.QCoreApplication.translate
self.prefix_label.setText(_translate("ImportLogin", "Select path"))
self.prefix_label.setText(_translate("ImportLogin", "Select prefix"))
self.title_label.setText(_translate("ImportLogin", "Import existing session from EGL"))
self.prefix_tool.setText(_translate("ImportLogin", "Browse"))
self.info_label.setText(_translate("ImportLogin", "You will get logged out from EGL in the process."))

View file

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>235</width>
<width>242</width>
<height>120</height>
</rect>
</property>
@ -20,7 +20,7 @@
<item row="1" column="0">
<widget class="QLabel" name="prefix_label">
<property name="text">
<string>Select path</string>
<string>Select prefix</string>
</property>
</widget>
</item>

View file

@ -1,38 +1,38 @@
from typing import Callable
from typing import Callable, Optional
from legendary.core import LegendaryCore
from legendary.utils.config import LGDConf
config: LGDConf = None
save_config: Callable[[], None] = None
_config: Optional[LGDConf] = None
_save_config: Optional[Callable[[], None]] = None
def init_config_handler(core: LegendaryCore):
global config, save_config
config = core.lgd.config
save_config = core.lgd.save_config
global _config, _save_config
_config = core.lgd.config
_save_config = core.lgd.save_config
def add_option(app_name: str, option: str, value: str):
value = value.replace("%%", "%").replace("%", "%%")
if not config.has_section(app_name):
config[app_name] = {}
if not _config.has_section(app_name):
_config[app_name] = {}
config.set(app_name, option, value)
save_config()
_config.set(app_name, option, value)
_save_config()
def remove_option(app_name, option):
if config.has_option(app_name, option):
config.remove_option(app_name, option)
if _config.has_option(app_name, option):
_config.remove_option(app_name, option)
if config.has_section(app_name) and not config[app_name]:
config.remove_section(app_name)
if _config.has_section(app_name) and not _config[app_name]:
_config.remove_section(app_name)
save_config()
_save_config()
def remove_section(app_name):
if config.has_section(app_name):
config.remove_section(app_name)
save_config()
if _config.has_section(app_name):
_config.remove_section(app_name)
_save_config()

View file

@ -54,7 +54,6 @@ class IndicatorReasons:
class IndicatorLineEdit(QWidget):
textChanged = pyqtSignal(str)
is_valid = False
reasons = IndicatorReasons()
def __init__(
@ -97,9 +96,10 @@ class IndicatorLineEdit(QWidget):
layout.addWidget(self.indicator_label)
if not placeholder:
_translate = QCoreApplication.translate
_translate = QCoreApplication.instance().translate
self.line_edit.setPlaceholderText(_translate(self.__class__.__name__, "Default"))
self.is_valid = False
self.edit_func = edit_func
self.save_func = save_func
self.line_edit.textChanged.connect(self.__edit)
@ -107,7 +107,7 @@ class IndicatorLineEdit(QWidget):
self.line_edit.textChanged.connect(self.__save)
# lk: this can be placed here to trigger __edit
# lk: it going to save the input again if it is valid which
# lk: it is going to save the input again if it is valid which
# lk: is ok to do given the checks don't misbehave (they shouldn't)
# lk: however it is going to edit any "understood" bad input to good input
# lk: and we might not want that (but the validity check reports on the edited string)
@ -185,9 +185,6 @@ class PathEditIconProvider(QFileIconProvider):
class PathEdit(IndicatorLineEdit):
completer = QCompleter()
compl_model = QFileSystemModel()
def __init__(
self,
path: str = "",
@ -200,6 +197,9 @@ class PathEdit(IndicatorLineEdit):
horiz_policy: QSizePolicy = QSizePolicy.Expanding,
parent=None,
):
self.completer = QCompleter()
self.compl_model = QFileSystemModel()
try:
self.compl_model.setOptions(
QFileSystemModel.DontWatchForChanges
@ -230,7 +230,7 @@ class PathEdit(IndicatorLineEdit):
layout = self.layout()
layout.addWidget(self.path_select)
_translate = QCoreApplication.translate
_translate = QCoreApplication.instance().translate
self.path_select.setText(_translate("PathEdit", "Browse..."))
self.type_filter = type_filter
@ -414,7 +414,7 @@ class SelectViewWidget(QWidget):
class ImageLabel(QLabel):
image = None
img_size = None
name = str()
name = ""
def __init__(self):
super(ImageLabel, self).__init__()

View file

@ -20,7 +20,6 @@ class RareApp(QApplication):
def __init__(self):
super(RareApp, self).__init__(sys.argv)
self.setQuitOnLastWindowClosed(False)
self.core = LegendaryCore()
if hasattr(Qt, "AA_UseHighDpiPixmaps"):
self.setAttribute(Qt.AA_UseHighDpiPixmaps)
@ -36,7 +35,6 @@ class RareApp(QApplication):
# lk: this is a bit silly but serves well until we have a class
# lk: store the default qt style name from the system on startup as a property for later reference
self.setProperty("rareDefaultQtStyle", self.style().objectName())
if (
self.settings.value("color_scheme", None) is None
and self.settings.value("style_sheet", None) is None