diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index 3e77a46ae4..41aa2546d5 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -26,7 +26,7 @@ jobs:
fail-fast: false
matrix:
os: ['macos-latest', 'windows-latest', 'ubuntu-slim']
- version: ['3.9', '3.10', '3.11', '3.12', '3.13']
+ version: ['3.10', '3.11', '3.12', '3.13']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
diff --git a/misc/requirements.in b/misc/requirements.in
index 2049096040..bc5c919445 100644
--- a/misc/requirements.in
+++ b/misc/requirements.in
@@ -4,7 +4,7 @@ setuptools-scm
requests < 3.0
PySide6-Essentials >= 6.8.1
QtAwesome
-legendary-gl @ https://github.com/RareDevs/legendary/archive/5b3453c.zip
+legendary-gl @ https://github.com/RareDevs/legendary/archive/rare-next.zip
orjson
vdf @ https://github.com/solsticegamestudios/vdf/archive/be1f7220238022f8b29fe747f0b643f280bfdb6e.zip
pywin32 ; platform_system == "Windows"
diff --git a/pylintrc b/pylintrc
index 71caea947d..c9e02674e7 100644
--- a/pylintrc
+++ b/pylintrc
@@ -89,7 +89,7 @@ persistent=yes
# Minimum Python version to use for version dependent checks. Will default to
# the version used to run pylint.
-py-version=3.9
+py-version=3.10
# Discover python modules and packages in the file system subtree.
recursive=yes
diff --git a/pyproject.toml b/pyproject.toml
index 29a0cefd7b..685a236dd3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -24,7 +24,7 @@ dependencies = [
"requests < 3.0",
"PySide6-Essentials >= 6.8.1",
"QtAwesome",
- "legendary-gl @ git+https://github.com/RareDevs/legendary@5b3453c",
+ "legendary-gl @ git+https://github.com/RareDevs/legendary@rare-next",
"orjson",
"vdf @ https://github.com/solsticegamestudios/vdf/archive/be1f7220238022f8b29fe747f0b643f280bfdb6e.zip",
"pywin32 ; platform_system == 'Windows'",
@@ -134,15 +134,22 @@ force-exclude = '''
[tool.ruff]
include = ["pyproject.toml", "rare/*.py"]
-exclude = ["rare/ui", "rare/lgndr"]
+exclude = [
+ "rare/ui",
+ "rare/lgndr",
+ "rare/resources/static_css/__init__.py",
+ "rare/resources/stylesheets/*/__init__.py"
+]
+
line-length = 130
indent-width = 4
[tool.ruff.lint]
-extend-select = ["I"]
+extend-select = ["E", "F", "UP", "B", "SIM", "I"]
+extend-ignore = [ "UP008", "SIM102", "SIM108" ]
[tool.ruff.format]
-quote-style = "double"
+quote-style = "single"
indent-style = "space"
[tool.poetry]
diff --git a/rare/__init__.py b/rare/__init__.py
index 92a408daee..b32aa56d9f 100644
--- a/rare/__init__.py
+++ b/rare/__init__.py
@@ -1,13 +1,13 @@
from rare._version import __version__, __version_tuple__
-__codename__ = "Cobia Cormorant"
+__codename__ = 'Cobia Cormorant'
# For PyCharm profiler
-if __name__ == "__main__":
+if __name__ == '__main__':
import sys
from rare.main import main
sys.exit(main())
-__all__ = ["__version__", "__version_tuple__", "__codename__"]
+__all__ = ['__version__', '__version_tuple__', '__codename__']
diff --git a/rare/__main__.py b/rare/__main__.py
index 8a75aaf9c3..3a21060a94 100644
--- a/rare/__main__.py
+++ b/rare/__main__.py
@@ -1,4 +1,4 @@
-if __name__ == "__main__":
+if __name__ == '__main__':
import sys
from rare.main import main
diff --git a/rare/commands/launcher/__init__.py b/rare/commands/launcher/__init__.py
index 3c09eea1a5..a87a194f1c 100644
--- a/rare/commands/launcher/__init__.py
+++ b/rare/commands/launcher/__init__.py
@@ -1,3 +1,4 @@
+import contextlib
import json
import os
import platform
@@ -8,7 +9,6 @@
import traceback
from argparse import Namespace
from logging import getLogger
-from typing import Optional
import shiboken6
from legendary.models.game import SaveGameStatus
@@ -40,13 +40,13 @@
from .lgd_helper import InitParams, LaunchParams, dict_to_qprocenv, get_configured_qprocess, get_launch_params
DETACHED_APP_NAMES = {
- "0a2d9f6403244d12969e11da6713137b", # Fall Guys
- "Fortnite",
- "afdb5a85efcc45d8ae8e406e2121d81c", # Fortnite Battle Royale
- "09e442f830a341f698b4da42abd98c9b", # Fortnite Festival
- "d8f7763e07d74c209d760a679f9ed6ac", # Lego Fortnite
- "Fortnite_Studio", # Unreal Editor for Fortnite
- "dcfccf8d965a4f2281dddf9fead042de", # Homeworld Remastered Collection (issue#376)
+ '0a2d9f6403244d12969e11da6713137b', # Fall Guys
+ 'Fortnite',
+ 'afdb5a85efcc45d8ae8e406e2121d81c', # Fortnite Battle Royale
+ '09e442f830a341f698b4da42abd98c9b', # Fortnite Festival
+ 'd8f7763e07d74c209d760a679f9ed6ac', # Lego Fortnite
+ 'Fortnite_Studio', # Unreal Editor for Fortnite
+ 'dcfccf8d965a4f2281dddf9fead042de', # Homeworld Remastered Collection (issue#376)
}
@@ -67,20 +67,20 @@ def __init__(self, args: InitParams, rgame: RareGameSlim, sync_action=None):
self.sync_action = sync_action
def run(self) -> None:
- self.logger.info(f"Sync action: {self.sync_action}")
+ self.logger.info(f'Sync action: {self.sync_action}')
if self.sync_action == CloudSyncDialogResult.UPLOAD:
self.rgame.upload_saves()
elif self.sync_action == CloudSyncDialogResult.DOWNLOAD:
self.rgame.download_saves()
else:
- self.logger.info("No sync action")
+ self.logger.info('No sync action')
if args := self.prepare_launch(self.args):
self.signals.ready_to_launch.emit(args)
self.signals.disconnect(self.signals)
self.signals.deleteLater()
- def prepare_launch(self, args: InitParams) -> Optional[LaunchParams]:
+ def prepare_launch(self, args: InitParams) -> LaunchParams | None:
try:
launch = get_launch_params(self.rgame, args)
except Exception as e:
@@ -93,13 +93,13 @@ def prepare_launch(self, args: InitParams) -> Optional[LaunchParams]:
proc = get_configured_qprocess(shlex.split(launch.pre_launch_command), launch.environment)
proc.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels)
proc.readyReadStandardOutput.connect(
- (lambda obj: obj.logger.debug(str(proc.readAllStandardOutput().data(), "utf-8", "ignore"))).__get__(self)
+ (lambda obj: obj.logger.debug(str(proc.readAllStandardOutput().data(), 'utf-8', 'ignore'))).__get__(self)
)
self.signals.pre_launch_command_started.emit()
- self.logger.info("Running pre-launch command %s, %s", proc.program(), proc.arguments())
+ self.logger.info('Running pre-launch command %s, %s', proc.program(), proc.arguments())
if launch.pre_launch_wait:
proc.start()
- self.logger.info("Waiting for pre-launch command to finish")
+ self.logger.info('Waiting for pre-launch command to finish')
proc.waitForFinished(-1)
else:
proc.startDetached()
@@ -131,22 +131,20 @@ def run(self) -> None:
class RareLauncherException(RareAppException):
- def __init__(self, app: "RareLauncher", args: Namespace, parent=None):
+ def __init__(self, app: 'RareLauncher', args: Namespace, parent=None):
super(RareLauncherException, self).__init__(parent=parent)
self.__app = app
self.__args = args
def _handler(self, exc_type, exc_value, exc_tb) -> bool:
- try:
+ with contextlib.suppress(RuntimeError):
self.__app.send_message(
ErrorModel(
app_name=self.__args.app_name,
action=Actions.error,
- error_string="".join(traceback.format_exception(exc_type, exc_value, exc_tb)),
+ error_string=''.join(traceback.format_exception(exc_type, exc_value, exc_tb)),
)
)
- except RuntimeError:
- pass
return False
@@ -154,9 +152,9 @@ class RareLauncher(RareApp):
exit_app = Signal()
def __init__(self, args: InitParams):
- super(RareLauncher, self).__init__(args, f"{type(self).__name__}_{args.app_name}_{{0}}.log")
- self.socket: Optional[QLocalSocket] = None
- self.console: Optional[ConsoleDialog] = None
+ super(RareLauncher, self).__init__(args, f'{type(self).__name__}_{args.app_name}_{{0}}.log')
+ self.socket: QLocalSocket | None = None
+ self.console: ConsoleDialog | None = None
self.game_process: QProcess = QProcess(self)
self.server: QLocalServer = QLocalServer(self)
@@ -169,7 +167,7 @@ def __init__(self, args: InitParams):
self.core = LegendaryCore()
game = self.core.get_game(args.app_name)
if not game:
- self.logger.error(f"Game {args.app_name} not found. Exiting")
+ self.logger.error(f'Game {args.app_name} not found. Exiting')
return
self.rgame = RareGameSlim(self.settings, self.core, game)
@@ -178,7 +176,7 @@ def __init__(self, args: InitParams):
self.console.show()
self.game_process.stateChanged.connect(self._on_game_process_changed)
- self.sync_dialog: Optional[CloudSyncDialog] = None
+ self.sync_dialog: CloudSyncDialog | None = None
self.game_process.finished.connect(self.__process_finished)
self.game_process.errorOccurred.connect(self.__process_errored)
@@ -188,10 +186,10 @@ def __init__(self, args: InitParams):
self.console.term.connect(self._proc_term)
self.console.kill.connect(self._proc_kill)
- ret = self.server.listen(f"rare_{args.app_name}")
+ ret = self.server.listen(f'rare_{args.app_name}')
if not ret:
self.logger.error(self.server.errorString())
- self.logger.info("Server is running")
+ self.logger.info('Server is running')
self.server.close()
return
self.server.newConnection.connect(self.new_server_connection)
@@ -210,44 +208,42 @@ def _on_game_process_changed(self, state: QProcess.ProcessState):
@Slot()
def _proc_log_stdout(self):
- self.console.log_stdout(self.game_process.readAllStandardOutput().data().decode("utf-8", "ignore"))
+ self.console.log_stdout(self.game_process.readAllStandardOutput().data().decode('utf-8', 'ignore'))
@Slot()
def _proc_log_stderr(self):
- self.console.log_stderr(self.game_process.readAllStandardError().data().decode("utf-8", "ignore"))
+ self.console.log_stderr(self.game_process.readAllStandardError().data().decode('utf-8', 'ignore'))
@Slot()
def _proc_term(self):
- if platform.system() == "Windows":
+ if platform.system() == 'Windows':
self.game_process.terminate()
else:
os.kill(self.game_process.processId(), signal.SIGINT)
@Slot()
def _proc_kill(self):
- if platform.system() == "Windows":
+ if platform.system() == 'Windows':
self.game_process.kill()
else:
os.kill(self.game_process.processId(), signal.SIGINT)
def new_server_connection(self):
if self.socket is not None:
- try:
+ with contextlib.suppress(RuntimeError):
self.socket.disconnectFromServer()
- except RuntimeError:
- pass
- self.logger.info("New connection")
+ self.logger.info('New connection')
self.socket = self.server.nextPendingConnection()
self.socket.disconnected.connect(self.socket_disconnected)
self.socket.flush()
def socket_disconnected(self):
- self.logger.info("Server disconnected")
+ self.logger.info('Server disconnected')
self.socket = None
def send_message(self, message: BaseModel):
if self.socket:
- self.socket.write(json.dumps(vars(message)).encode("utf-8"))
+ self.socket.write(json.dumps(vars(message)).encode('utf-8'))
self.socket.flush()
else:
self.logger.error("Can't send message")
@@ -279,13 +275,13 @@ def check_saves_finished(self, exit_code, action):
action = CloudSyncDialogResult(action)
if action == CloudSyncDialogResult.UPLOAD:
if self.console:
- self.console.log("Uploading saves...")
+ self.console.log('Uploading saves...')
worker = CloudSyncWorker(self.rgame, CloudSyncWorker.Mode.UPLOAD)
QThreadPool.globalInstance().start(worker, priority=0)
QThreadPool.globalInstance().waitForDone()
elif action == CloudSyncDialogResult.DOWNLOAD:
if self.console:
- self.console.log("Downloading saves...")
+ self.console.log('Downloading saves...')
worker = CloudSyncWorker(self.rgame, CloudSyncWorker.Mode.DOWNLOAD)
QThreadPool.globalInstance().start(worker, priority=0)
QThreadPool.globalInstance().waitForDone()
@@ -294,7 +290,7 @@ def check_saves_finished(self, exit_code, action):
@Slot(int, QProcess.ExitStatus)
def __process_finished(self, exit_code: int, exit_status: QProcess.ExitStatus):
- self.logger.info("Game finished")
+ self.logger.info('Game finished')
if self.rgame.auto_sync_saves:
self.check_saves(exit_code)
@@ -330,28 +326,28 @@ def launch_game(self, params: LaunchParams):
self.start_time = time.time()
if self.args.dry_run:
- self.logger.info("Dry run %s (%s)", self.rgame.app_title, self.rgame.app_name)
- self.logger.info("Command: %s, %s", params.executable, " ".join(params.arguments))
+ self.logger.info('Dry run %s (%s)', self.rgame.app_title, self.rgame.app_name)
+ self.logger.info('Command: %s, %s', params.executable, ' '.join(params.arguments))
if self.console:
- self.console.log(f"Dry run {self.rgame.app_title} ({self.rgame.app_name})")
- self.console.log(f"{shlex.join((params.executable, *params.arguments))}")
+ self.console.log(f'Dry run {self.rgame.app_title} ({self.rgame.app_name})')
+ self.console.log(f'{shlex.join((params.executable, *params.arguments))}')
self.console.accept_close = True
self.stop()
return
- if platform.system() == "Windows" and params.is_origin_game:
+ if platform.system() == 'Windows' and params.is_origin_game:
# executable is a protocol link (link2ea://launchgame/...)
QDesktopServices.openUrl(QUrl(params.executable))
self.stop() # stop because it is not a subprocess
return
- self.logger.info("Starting %s (%s)", self.rgame.app_title, self.rgame.app_name)
- self.logger.info("Command: %s, %s", params.executable, " ".join(params.arguments))
- self.logger.debug("Working directory %s", params.working_directory)
+ self.logger.info('Starting %s (%s)', self.rgame.app_title, self.rgame.app_name)
+ self.logger.info('Command: %s, %s', params.executable, ' '.join(params.arguments))
+ self.logger.debug('Working directory %s', params.working_directory)
- if self.rgame.app_name in DETACHED_APP_NAMES and platform.system() == "Windows":
+ if self.rgame.app_name in DETACHED_APP_NAMES and platform.system() == 'Windows':
if self.console:
- self.console.log(f"Launching {params.executable} as a detached process")
+ self.console.log(f'Launching {params.executable} as a detached process')
subprocess.Popen(
(params.executable, *params.arguments),
stdin=None,
@@ -365,23 +361,21 @@ def launch_game(self, params: LaunchParams):
self.stop() # stop because we do not attach to the output
return
- if platform.system() in {"Linux", "FreeBSD"}:
+ if platform.system() in {'Linux', 'FreeBSD'}:
cmd_line = get_rare_executable()
executable, arguments = cmd_line[0], cmd_line[1:]
- if appid := os.environ.get("SteamGameId", False):
- params.environment["SteamGameId"] = appid
- elif params.environment.get("SteamGameId", False):
- appid = params.environment["SteamGameId"]
+ if appid := os.environ.get('SteamGameId', False): # noqa: SIM112
+ params.environment['SteamGameId'] = appid
+ elif params.environment.get('SteamGameId', False):
+ appid = params.environment['SteamGameId']
self.game_process.setProgram(executable)
# TODO: Add "SteamLauch" and "AppId=xxxxxx" here for steamdeck/gamescope
- try:
+ with contextlib.suppress(ValueError):
appid = int(appid) >> 32
- except ValueError:
- pass
self.game_process.setArguments(
- [*arguments, "subreaper", "SteamLaunch", f"AppId={appid}", "--", params.executable, *params.arguments]
+ [*arguments, 'subreaper', 'SteamLaunch', f'AppId={appid}', '--', params.executable, *params.arguments]
)
self.game_process.setUnixProcessParameters(
QProcess.UnixProcessFlag.ResetSignalHandlers | QProcess.UnixProcessFlag.CreateNewSession
@@ -428,7 +422,7 @@ def start_prepare(self, sync_action=None):
def sync_ready(self):
if self.rgame.is_save_up_to_date:
if self.console:
- self.console.log("Sync worker ready. Sync not required")
+ self.console.log('Sync worker ready. Sync not required')
self.start_prepare()
return
@@ -448,23 +442,23 @@ def __sync_ready(self, action: CloudSyncDialogResult):
self.no_sync_on_exit = True
if self.console:
if action == CloudSyncDialogResult.DOWNLOAD:
- self.console.log("Downloading saves...")
+ self.console.log('Downloading saves...')
elif action == CloudSyncDialogResult.UPLOAD:
- self.console.log("Uploading saves...")
+ self.console.log('Uploading saves...')
self.start_prepare(action)
def start(self):
if not self.args.offline:
try:
if not self.core.login():
- raise ValueError("You are not logged in")
+ raise ValueError('You are not logged in')
except ValueError:
# automatically launch offline if available
- self.logger.error("Not logged in. Trying to launch the game in offline mode")
+ self.logger.error('Not logged in. Trying to launch the game in offline mode')
self.args.offline = True
if not self.args.offline and self.rgame.auto_sync_saves:
- self.logger.info("Start sync worker")
+ self.logger.info('Start sync worker')
worker = SyncCheckWorker(self.core, self.rgame)
worker.signals.error_occurred.connect(self.error_occurred)
worker.signals.sync_state_ready.connect(self.sync_ready)
@@ -482,7 +476,7 @@ def stop(self, sig: int = signal.SIGINT):
if self.game_process.isSignalConnected(QMetaMethod.fromSignal(self.game_process.errorOccurred)):
self.game_process.errorOccurred.disconnect()
except (TypeError, RuntimeError) as e:
- self.logger.error("Failed to disconnect process signals: %s", e)
+ self.logger.error('Failed to disconnect process signals: %s', e)
# FIXME: refactor to avoid this
if shiboken6.isValid(self.game_process): # pylint: disable=E1101
@@ -499,12 +493,12 @@ def stop(self, sig: int = signal.SIGINT):
# FIXME: refactor to avoid this
if shiboken6.isValid(self.server): # pylint: disable=E1101
- self.logger.info("Stopping server %s", self.server.socketDescriptor())
+ self.logger.info('Stopping server %s', self.server.socketDescriptor())
try:
self.server.close()
self.server.deleteLater()
except RuntimeError as e:
- self.logger.error("Error occurred while stopping server: %s", e)
+ self.logger.error('Error occurred while stopping server: %s', e)
self.processEvents()
if not self.console:
@@ -525,7 +519,7 @@ def launcher(args: Namespace) -> int:
# This prevents ghost QLocalSockets, which block the name, which makes it unable to start
# No handling for SIGKILL
def signal_handler(sig, frame):
- app.logger.info("%s received. Stopping", signal.strsignal(sig))
+ app.logger.info('%s received. Stopping', signal.strsignal(sig))
app.stop(sig)
app.exit(1)
return 1
@@ -541,7 +535,7 @@ def signal_handler(sig, frame):
try:
exit_code = app.exec()
except Exception as e:
- app.logger.error("Unhandled error %s", e)
+ app.logger.error('Unhandled error %s', e)
exit_code = 1
finally:
pass
@@ -550,4 +544,4 @@ def signal_handler(sig, frame):
return exit_code
-__all__ = ["launcher"]
+__all__ = ['launcher']
diff --git a/rare/commands/launcher/cloud_sync_dialog.py b/rare/commands/launcher/cloud_sync_dialog.py
index 146868ddda..2a6d3510d6 100644
--- a/rare/commands/launcher/cloud_sync_dialog.py
+++ b/rare/commands/launcher/cloud_sync_dialog.py
@@ -12,7 +12,7 @@
from rare.utils.misc import qta_icon
from rare.widgets.dialogs import ButtonDialog, game_title
-logger = getLogger("CloudSyncDialog")
+logger = getLogger('CloudSyncDialog')
class CloudSyncDialogResult(IntEnum):
@@ -27,10 +27,10 @@ class CloudSyncDialog(ButtonDialog):
def __init__(self, igame: InstalledGame, dt_local: datetime, dt_remote: datetime, parent=None):
super(CloudSyncDialog, self).__init__(parent=parent)
- header = self.tr("Cloud saves for")
+ header = self.tr('Cloud saves for')
self.setWindowTitle(game_title(header, igame.title))
- title_label = QLabel(f"
{game_title(header, igame.title)}
", self)
+ title_label = QLabel(f'{game_title(header, igame.title)}
', self)
sync_widget = QWidget(self)
self.sync_ui = Ui_CloudSyncWidget()
@@ -40,17 +40,17 @@ def __init__(self, igame: InstalledGame, dt_local: datetime, dt_remote: datetime
layout.addWidget(title_label)
layout.addWidget(sync_widget)
- self.accept_button.setText(self.tr("Skip"))
- self.accept_button.setIcon(qta_icon("fa.chevron-right", "fa5s.chevron-right"))
+ self.accept_button.setText(self.tr('Skip'))
+ self.accept_button.setIcon(qta_icon('fa.chevron-right', 'fa5s.chevron-right'))
self.setCentralLayout(layout)
self.status = CloudSyncDialogResult.CANCEL
- newer = self.tr("Newer")
+ newer = self.tr('Newer')
if dt_remote and dt_local:
- self.sync_ui.age_label_local.setText(f"{newer}" if dt_remote < dt_local else " ")
- self.sync_ui.age_label_remote.setText(f"{newer}" if dt_remote > dt_local else " ")
+ self.sync_ui.age_label_local.setText(f'{newer}' if dt_remote < dt_local else ' ')
+ self.sync_ui.age_label_remote.setText(f'{newer}' if dt_remote > dt_local else ' ')
# Set status, if one of them is None
elif dt_remote and not dt_local:
self.status = CloudSyncDialogResult.DOWNLOAD
@@ -60,11 +60,11 @@ def __init__(self, igame: InstalledGame, dt_local: datetime, dt_remote: datetime
self.status = CloudSyncDialogResult.SKIP
local_tz = datetime.now().astimezone().tzinfo
- self.sync_ui.date_info_local.setText(dt_local.astimezone(local_tz).strftime("%A, %d %B %Y %X") if dt_local else "None")
- self.sync_ui.date_info_remote.setText(dt_remote.astimezone(local_tz).strftime("%A, %d %B %Y %X") if dt_remote else "None")
+ self.sync_ui.date_info_local.setText(dt_local.astimezone(local_tz).strftime('%A, %d %B %Y %X') if dt_local else 'None')
+ self.sync_ui.date_info_remote.setText(dt_remote.astimezone(local_tz).strftime('%A, %d %B %Y %X') if dt_remote else 'None')
- self.sync_ui.icon_local.setPixmap(qta_icon("mdi.harddisk", "fa5s.desktop").pixmap(128, 128))
- self.sync_ui.icon_remote.setPixmap(qta_icon("mdi.cloud-outline", "fa5s.cloud").pixmap(128, 128))
+ self.sync_ui.icon_local.setPixmap(qta_icon('mdi.harddisk', 'fa5s.desktop').pixmap(128, 128))
+ self.sync_ui.icon_remote.setPixmap(qta_icon('mdi.cloud-outline', 'fa5s.cloud').pixmap(128, 128))
self.sync_ui.upload_button.clicked.connect(self.__on_upload)
self.sync_ui.download_button.clicked.connect(self.__on_download)
@@ -90,7 +90,7 @@ def reject_handler(self):
self.status = CloudSyncDialogResult.CANCEL
-if __name__ == "__main__":
+if __name__ == '__main__':
app = QApplication(sys.argv)
core = LegendaryCore()
@@ -98,7 +98,7 @@ def reject_handler(self):
def __callback(status: int):
print(repr(CloudSyncDialogResult(status)))
- dlg = CloudSyncDialog(core.get_installed_list()[0], datetime.now(), datetime.strptime("2021,1", "%Y,%M"))
+ dlg = CloudSyncDialog(core.get_installed_list()[0], datetime.now(), datetime.strptime('2021,1', '%Y,%M'))
dlg.result_ready.connect(__callback)
dlg.open()
app.exec()
diff --git a/rare/commands/launcher/console_dialog.py b/rare/commands/launcher/console_dialog.py
index 587ff74056..432cb740ba 100644
--- a/rare/commands/launcher/console_dialog.py
+++ b/rare/commands/launcher/console_dialog.py
@@ -1,5 +1,3 @@
-from typing import Dict, Union
-
from PySide6.QtCore import QProcessEnvironment, QSize, Qt, Signal
from PySide6.QtGui import QCloseEvent, QCursor, QFont, QTextCursor
from PySide6.QtWidgets import (
@@ -29,7 +27,7 @@ class ConsoleDialog(QDialog):
def __init__(self, app_title: str, parent=None):
super(ConsoleDialog, self).__init__(parent=parent)
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
- self.setWindowTitle(dialog_title(game_title(self.tr("Console"), app_title)))
+ self.setWindowTitle(dialog_title(game_title(self.tr('Console'), app_title)))
self.setGeometry(0, 0, 640, 480)
layout = QVBoxLayout()
@@ -38,27 +36,27 @@ def __init__(self, app_title: str, parent=None):
button_layout = QHBoxLayout()
- self.env_button = QPushButton(self.tr("Show environment"))
+ self.env_button = QPushButton(self.tr('Show environment'))
button_layout.addWidget(self.env_button)
self.env_button.clicked.connect(self.show_env)
- self.save_button = QPushButton(self.tr("Save output to file"))
+ self.save_button = QPushButton(self.tr('Save output to file'))
button_layout.addWidget(self.save_button)
self.save_button.clicked.connect(self.save)
- self.clear_button = QPushButton(self.tr("Clear console"))
+ self.clear_button = QPushButton(self.tr('Clear console'))
button_layout.addWidget(self.clear_button)
self.clear_button.clicked.connect(self.console_edit.clear)
button_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed))
- self.terminate_button = QPushButton(self.tr("Terminate"))
+ self.terminate_button = QPushButton(self.tr('Terminate'))
# self.terminate_button.setVisible(platform.system() == "Windows")
button_layout.addWidget(self.terminate_button)
self.terminate_button.clicked.connect(self.term)
self.terminate_button.setEnabled(False)
- self.kill_button = QPushButton(self.tr("Kill"))
+ self.kill_button = QPushButton(self.tr('Kill'))
# self.kill_button.setVisible(platform.system() == "Windows")
button_layout.addWidget(self.kill_button)
self.kill_button.clicked.connect(self.kill)
@@ -95,16 +93,16 @@ def center_window(self):
self.move(screen_rect.center() - self.rect().adjusted(0, 0, decor_width, decor_height).center())
def save(self):
- file, ok = QFileDialog.getSaveFileName(self, "Save output", "", "Log Files (*.log);;All Files (*)")
+ file, ok = QFileDialog.getSaveFileName(self, 'Save output', '', 'Log Files (*.log);;All Files (*)')
if ok:
- if "." not in file:
- file += ".log"
- with open(file, "w", encoding="utf-8") as f:
+ if '.' not in file:
+ file += '.log'
+ with open(file, 'w', encoding='utf-8') as f:
f.write(self.console_edit.toPlainText())
f.close()
- self.save_button.setText(self.tr("Saved"))
+ self.save_button.setText(self.tr('Saved'))
- def set_env(self, env: Dict):
+ def set_env(self, env: dict):
self.env_variables = dict_to_qprocenv(env)
def show_env(self):
@@ -112,18 +110,18 @@ def show_env(self):
self.env_console.show()
def log(self, text: str):
- self.console_edit.log(f"Rare: {text}")
+ self.console_edit.log(f'Rare: {text}')
def log_stdout(self, text: str):
self.console_edit.log(text)
def error(self, text: str):
- self.console_edit.error(f"Rare: {text}")
+ self.console_edit.error(f'Rare: {text}')
def log_stderr(self, text: str):
self.console_edit.error(text)
- def on_process_exit(self, app_title: str, status: Union[int, str]):
+ def on_process_exit(self, app_title: str, status: int | str):
self.error(self.tr('Application finished with exit code "{}"').format(status))
self.accept_close = True
@@ -148,13 +146,13 @@ def __init__(self, app_title: str, parent=None):
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
self.ui = Ui_ConsoleEnv()
self.ui.setupUi(self)
- self.setWindowTitle(dialog_title(game_title(self.tr("Environment"), app_title)))
+ self.setWindowTitle(dialog_title(game_title(self.tr('Environment'), app_title)))
def setTable(self, env: QProcessEnvironment):
self.ui.table.clearContents()
self.ui.table.setRowCount(len(env.keys()))
- for idx, key in enumerate(env.keys()):
+ for idx, _ in enumerate(env.keys()):
self.ui.table.setItem(idx, 0, QTableWidgetItem(env.keys()[idx]))
self.ui.table.setItem(idx, 1, QTableWidgetItem(env.value(env.keys()[idx])))
@@ -165,7 +163,7 @@ class ConsoleEdit(QPlainTextEdit):
def __init__(self, parent=None):
super(ConsoleEdit, self).__init__(parent=parent)
self.setReadOnly(True)
- self.setFont(QFont("monospace"))
+ self.setFont(QFont('monospace'))
def scroll_to_last_line(self):
cursor = self.textCursor()
@@ -179,7 +177,7 @@ def print_to_console(self, text: str, color: str):
self.scroll_to_last_line()
def log(self, text):
- self.print_to_console(text, "#aaa")
+ self.print_to_console(text, '#aaa')
def error(self, text):
- self.print_to_console(text, "#a33")
+ self.print_to_console(text, '#a33')
diff --git a/rare/commands/launcher/lgd_helper.py b/rare/commands/launcher/lgd_helper.py
index 1ea5744992..721fb4703a 100644
--- a/rare/commands/launcher/lgd_helper.py
+++ b/rare/commands/launcher/lgd_helper.py
@@ -5,7 +5,6 @@
from argparse import Namespace
from dataclasses import dataclass, field
from logging import getLogger
-from typing import Dict, List, Tuple
from legendary.models.game import LaunchParameters
from PySide6.QtCore import QProcess, QProcessEnvironment
@@ -14,7 +13,7 @@
from rare.utils.compat.utils import create_compat_users
from rare.utils.paths import setup_compat_shaders_dir
-logger = getLogger("RareLauncherUtils")
+logger = getLogger('RareLauncherUtils')
class GameArgsError(Exception):
@@ -28,8 +27,8 @@ class InitParams(Namespace):
dry_run: bool = False
show_console: bool = False
skip_update_check: bool = False
- wine_prefix: str = ""
- wine_bin: str = ""
+ wine_prefix: str = ''
+ wine_bin: str = ''
@classmethod
def from_argparse(cls, args):
@@ -40,18 +39,18 @@ def from_argparse(cls, args):
dry_run=args.dry_run,
show_console=args.show_console,
skip_update_check=args.skip_update_check,
- wine_bin=args.wine_bin if hasattr(args, "wine_bin") else "",
- wine_prefix=args.wine_pfx if hasattr(args, "wine_prefix") else "",
+ wine_bin=args.wine_bin if hasattr(args, 'wine_bin') else '',
+ wine_prefix=args.wine_pfx if hasattr(args, 'wine_prefix') else '',
)
@dataclass
class LaunchParams:
- executable: str = ""
- arguments: List[str] = field(default_factory=list)
- working_directory: str = ""
- environment: Dict[str, str] = field(default_factory=dict)
- pre_launch_command: str = ""
+ executable: str = ''
+ arguments: list[str] = field(default_factory=list)
+ working_directory: str = ''
+ environment: dict[str, str] = field(default_factory=dict)
+ pre_launch_command: str = ''
pre_launch_wait: bool = False
is_origin_game: bool = False # only for windows to launch as url
@@ -64,7 +63,7 @@ def get_origin_params(rgame: RareGameSlim, init: InitParams, launch: LaunchParam
app_name = rgame.app_name
origin_uri = core.get_origin_uri(app_name, init.offline)
- if platform.system() == "Windows":
+ if platform.system() == 'Windows':
command = [origin_uri]
else:
command = core.get_app_launch_command(app_name)
@@ -87,16 +86,16 @@ def get_game_params(rgame: RareGameSlim, init: InitParams, launch: LaunchParams)
if not init.skip_update_check and not rgame.core.is_noupdate_game(rgame.app_name):
try:
latest = rgame.core.get_asset(rgame.app_name, rgame.igame.platform, update=False)
- except ValueError:
- raise GameArgsError("Metadata doesn't exist")
+ except ValueError as exc:
+ raise GameArgsError("Metadata doesn't exist") from exc
else:
if latest.build_version != rgame.igame.version:
- raise GameArgsError("Game is not up to date. Please update first")
+ raise GameArgsError('Game is not up to date. Please update first')
if (not rgame.igame or not rgame.igame.executable) and rgame.game is not None:
# override installed game with base title
if rgame.is_launchable_addon:
- app_name = rgame.game.metadata["mainGameItem"]["releaseInfo"][0]["appId"]
+ app_name = rgame.game.metadata['mainGameItem']['releaseInfo'][0]['appId']
rgame.igame = rgame.core.get_installed_game(app_name)
try:
@@ -104,13 +103,13 @@ def get_game_params(rgame: RareGameSlim, init: InitParams, launch: LaunchParams)
app_name=rgame.igame.app_name, offline=init.offline, addon_app_name=rgame.game.app_name
)
except TypeError:
- logger.warning("Using older get_launch_parameters due to legendary version")
+ logger.warning('Using older get_launch_parameters due to legendary version')
params: LaunchParameters = rgame.core.get_launch_parameters(app_name=rgame.igame.app_name, offline=init.offline)
full_params = []
full_params.extend(params.launch_command)
- if "LEGENDARY_WRAPPER_EXE" in params.environment:
- lgd_wrapper = params.environment.pop("LEGENDARY_WRAPPER_EXE").strip()
+ if 'LEGENDARY_WRAPPER_EXE' in params.environment:
+ lgd_wrapper = params.environment.pop('LEGENDARY_WRAPPER_EXE').strip()
if os.path.isfile(lgd_wrapper):
full_params.append(lgd_wrapper)
full_params.append(os.path.join(params.game_directory, params.game_executable))
@@ -132,18 +131,18 @@ def get_launch_params(rgame: RareGameSlim, init: InitParams = None) -> LaunchPar
resp = LaunchParams()
if not rgame.game:
- raise GameArgsError(f"Could not find metadata for {rgame.app_title}")
+ raise GameArgsError(f'Could not find metadata for {rgame.app_title}')
if rgame.is_origin:
init.offline = False
else:
if not rgame.is_installed:
- raise GameArgsError("Game is not installed or has unsupported format")
+ raise GameArgsError('Game is not installed or has unsupported format')
if rgame.is_dlc and not rgame.is_launchable_addon:
- raise GameArgsError("Game is a DLC")
+ raise GameArgsError('Game is a DLC')
if not os.path.exists(rgame.install_path):
- raise GameArgsError("Game path does not exist")
+ raise GameArgsError('Game path does not exist')
if rgame.is_origin:
resp = get_origin_params(rgame, init, resp)
@@ -155,36 +154,36 @@ def get_launch_params(rgame: RareGameSlim, init: InitParams = None) -> LaunchPar
return resp
-def prepare_process(command: List[str], environment: Dict) -> Tuple[str, List[str], Dict]:
- logger.debug("Preparing process: %s", command)
+def prepare_process(command: list[str], environment: dict) -> tuple[str, list[str], dict]:
+ logger.debug('Preparing process: %s', command)
_env = environment.copy()
# Sanity check environment (mostly for Linux)
# ensure shader compat dirs exist
- if platform.system() in {"Linux", "FreeBSD"}:
+ if platform.system() in {'Linux', 'FreeBSD'}:
_cmd_line = shlex.join(command)
- if os.environ.get("XDG_CURRENT_DESKTOP", None) == "gamescope" or "gamescope" in _cmd_line:
+ if os.environ.get('XDG_CURRENT_DESKTOP', None) == 'gamescope' or 'gamescope' in _cmd_line:
# disable mangohud in gamescope
- _env["MANGOHUD"] = "0"
- if "STEAM_COMPAT_CLIENT_INSTALL_PATH" not in _env:
- _env["STEAM_COMPAT_CLIENT_INSTALL_PATH"] = ""
- if "STEAM_COMPAT_DATA_PATH" in _env:
- compat_pfx = os.path.join(_env["STEAM_COMPAT_DATA_PATH"], "pfx")
+ _env['MANGOHUD'] = '0'
+ if 'STEAM_COMPAT_CLIENT_INSTALL_PATH' not in _env:
+ _env['STEAM_COMPAT_CLIENT_INSTALL_PATH'] = ''
+ if 'STEAM_COMPAT_DATA_PATH' in _env:
+ compat_pfx = os.path.join(_env['STEAM_COMPAT_DATA_PATH'], 'pfx')
os.makedirs(compat_pfx, exist_ok=True)
create_compat_users(compat_pfx)
- if "WINEPREFIX" in _env and not os.path.isdir(_env["WINEPREFIX"]):
- os.makedirs(_env["WINEPREFIX"], exist_ok=True)
- create_compat_users(_env["WINEPREFIX"])
- if "STEAM_COMPAT_SHADER_PATH" in _env:
- _env.update(setup_compat_shaders_dir(_env["STEAM_COMPAT_SHADER_PATH"]))
- _env["WINEDLLOVERRIDES"] = _env.get("WINEDLLOVERRIDES", "") + ";lsteamclient=d;"
+ if 'WINEPREFIX' in _env and not os.path.isdir(_env['WINEPREFIX']):
+ os.makedirs(_env['WINEPREFIX'], exist_ok=True)
+ create_compat_users(_env['WINEPREFIX'])
+ if 'STEAM_COMPAT_SHADER_PATH' in _env:
+ _env.update(setup_compat_shaders_dir(_env['STEAM_COMPAT_SHADER_PATH']))
+ _env['WINEDLLOVERRIDES'] = _env.get('WINEDLLOVERRIDES', '') + ';lsteamclient=d;'
final_env = os.environ.copy()
final_cmd = command.copy()
- if os.environ.get("container") == "flatpak":
- _flat_cmd = ["flatpak-spawn", "--host"]
- _flat_cmd.extend(f"--env={name}={value}" for name, value in _env.items())
+ if os.environ.get('container') == 'flatpak': # noqa: SIM112
+ _flat_cmd = ['flatpak-spawn', '--host']
+ _flat_cmd.extend(f'--env={name}={value}' for name, value in _env.items())
final_cmd = _flat_cmd + command
else:
final_env.update(_env)
@@ -192,14 +191,14 @@ def prepare_process(command: List[str], environment: Dict) -> Tuple[str, List[st
return final_cmd[0], final_cmd[1:] if len(final_cmd) > 1 else [], final_env
-def dict_to_qprocenv(env: Dict) -> QProcessEnvironment:
+def dict_to_qprocenv(env: dict) -> QProcessEnvironment:
_env = QProcessEnvironment()
for name, value in env.items():
_env.insert(name, value)
return _env
-def get_configured_qprocess(command: List[str], environment: Dict) -> QProcess:
+def get_configured_qprocess(command: list[str], environment: dict) -> QProcess:
cmd, args, env = prepare_process(command, environment)
proc = QProcess()
proc.setProcessChannelMode(QProcess.ProcessChannelMode.SeparateChannels)
diff --git a/rare/commands/subreaper/__init__.py b/rare/commands/subreaper/__init__.py
index 8212ea9568..8c1e67e33f 100644
--- a/rare/commands/subreaper/__init__.py
+++ b/rare/commands/subreaper/__init__.py
@@ -1,10 +1,10 @@
import platform
-if platform.system() == "FreeBSD":
+if platform.system() == 'FreeBSD':
from .subreaper_bsd import subreaper
-elif platform.system() == "Linux":
+elif platform.system() == 'Linux':
from .subreaper_linux import subreaper
else:
- raise RuntimeError(f"Unsupported subrepaer platform {platform.system()}")
+ raise RuntimeError(f'Unsupported subrepaer platform {platform.system()}')
-__all__ = ["subreaper"]
+__all__ = ['subreaper']
diff --git a/rare/commands/subreaper/subreaper_bsd.py b/rare/commands/subreaper/subreaper_bsd.py
index a3f22d26dd..bc78c81df0 100755
--- a/rare/commands/subreaper/subreaper_bsd.py
+++ b/rare/commands/subreaper/subreaper_bsd.py
@@ -8,11 +8,12 @@
import subprocess
import sys
from argparse import Namespace
+from collections.abc import Generator
from ctypes import CDLL, c_int, c_void_p
from ctypes.util import find_library
from logging import getLogger
from pathlib import Path
-from typing import Any, Generator, List
+from typing import Any
# Constants defined in sys/procctl.h
P_PID = 0
@@ -21,17 +22,17 @@
def get_libc() -> str:
"""Find libc.so from the user's system."""
- return find_library("c") or ""
+ return find_library('c') or ''
def _get_pids() -> Generator[int, Any, None]:
# FreeBSD's /proc is often not mounted. If it is, entries are just PIDs.
- proc_path = Path("/proc")
+ proc_path = Path('/proc')
if proc_path.exists():
- yield from (int(p.name) for p in proc_path.glob("*") if p.name.isdigit())
+ yield from (int(p.name) for p in proc_path.glob('*') if p.name.isdigit())
else:
# Fallback: Use ps to get PIDs if /proc isn't available
- out = subprocess.check_output(["ps", "-ax", "-o", "pid"])
+ out = subprocess.check_output(['ps', '-ax', '-o', 'pid'])
for line in out.splitlines()[1:]:
yield int(line.strip())
@@ -42,8 +43,8 @@ def get_pstree_from_pid(root_pid: int) -> set[int]:
try:
# -o ppid,pid gets parent and child PIDs
- out = subprocess.check_output(["ps", "-ax", "-o", "ppid,pid"], encoding="utf-8")
- lines = out.strip().split("\n")[1:]
+ out = subprocess.check_output(['ps', '-ax', '-o', 'ppid,pid'], encoding='utf-8')
+ lines = out.strip().split('\n')[1:]
pid_to_ppid = {}
for line in lines:
ppid, pid = map(int, line.split())
@@ -63,16 +64,16 @@ def get_pstree_from_pid(root_pid: int) -> set[int]:
return descendants
-def subreaper(args: Namespace, other: List[str]) -> int:
- logger = getLogger("subreaper")
+def subreaper(args: Namespace, other: list[str]) -> int:
+ logger = getLogger('subreaper')
logging.basicConfig(
- format="[%(name)s] %(levelname)s: %(message)s",
+ format='[%(name)s] %(levelname)s: %(message)s',
level=logging.DEBUG if args.debug else logging.INFO,
stream=sys.stderr,
)
- logger.debug("command: %s", args)
- logger.debug("arguments: %s", other)
+ logger.debug('command: %s', args)
+ logger.debug('arguments: %s', other)
def signal_handler(sig, frame):
logger.info("Caught '%s' signal.", signal.strsignal(sig))
@@ -83,13 +84,13 @@ def signal_handler(sig, frame):
except ProcessLookupError:
continue
- command: List[str] = [args.command, *other]
+ command: list[str] = [args.command, *other]
workdir: str = args.workdir
child_status: int = 0
libc_path: str = get_libc()
if not libc_path:
- logger.error("Could not find libc")
+ logger.error('Could not find libc')
return 1
libc: CDLL = CDLL(libc_path)
@@ -107,18 +108,18 @@ def signal_handler(sig, frame):
# Set Process Name (FreeBSD specific)
# FreeBSD prefers setproctitle over prctl for naming
- proc_name = b"reaper"
+ proc_name = b'reaper'
try:
libc.setproctitle(proc_name)
except AttributeError:
- logger.debug("setproctitle not found in libc")
+ logger.debug('setproctitle not found in libc')
procctl_res = procctl(P_PID, os.getpid(), PROC_REAP_ACQUIRE, None)
- logger.debug("procctl PROC_REAP_ACQUIRE exited with status: %s", procctl_res)
+ logger.debug('procctl PROC_REAP_ACQUIRE exited with status: %s', procctl_res)
pid = os.fork() # pylint: disable=E1101
if pid == -1:
- logger.error("Fork failed")
+ logger.error('Fork failed')
if pid == 0:
os.chdir(workdir)
@@ -130,7 +131,7 @@ def signal_handler(sig, frame):
while True:
try:
child_pid, child_status = os.wait() # pylint: disable=E1101
- logger.info("Child %s exited with status: %s", child_pid, child_status)
+ logger.info('Child %s exited with status: %s', child_pid, child_status)
except ChildProcessError as e:
logger.info(e)
break
@@ -138,11 +139,11 @@ def signal_handler(sig, frame):
return child_status
-if __name__ == "__main__":
- sep = sys.argv.index("--")
+if __name__ == '__main__':
+ sep = sys.argv.index('--')
argv = sys.argv[sep + 1 :]
args = Namespace(command=argv.pop(0), workdir=os.getcwd(), debug=True)
subreaper(args, argv)
-__all__ = ["subreaper"]
+__all__ = ['subreaper']
diff --git a/rare/commands/subreaper/subreaper_linux.py b/rare/commands/subreaper/subreaper_linux.py
index 3d2a4d415f..809638959c 100755
--- a/rare/commands/subreaper/subreaper_linux.py
+++ b/rare/commands/subreaper/subreaper_linux.py
@@ -5,11 +5,12 @@
import signal
import sys
from argparse import Namespace
+from collections.abc import Generator
from ctypes import CDLL, byref, c_int, create_string_buffer
from ctypes.util import find_library
from logging import getLogger
from pathlib import Path
-from typing import Any, Generator, List
+from typing import Any
from .util import find_mangohud_bin, find_mangohud_shim
@@ -21,11 +22,11 @@
def get_libc() -> str:
"""Find libc.so from the user's system."""
- return find_library("c") or ""
+ return find_library('c') or ''
def _get_pids() -> Generator[int, Any, None]:
- yield from (int(pid.name) for pid in Path("/proc").glob("*") if pid.name.isdigit())
+ yield from (int(pid.name) for pid in Path('/proc').glob('*') if pid.name.isdigit())
def get_pstree_from_pid(root_pid: int) -> set[int]:
@@ -35,10 +36,10 @@ def get_pstree_from_pid(root_pid: int) -> set[int]:
for pid in _get_pids():
try:
- path = Path(f"/proc/{pid}/status")
- with path.open(mode="r", encoding="utf-8") as file:
- st_ppid = next(line for line in file if line.startswith("PPid:"))
- st_ppid = st_ppid.removeprefix("PPid:").strip()
+ path = Path(f'/proc/{pid}/status')
+ with path.open(mode='r', encoding='utf-8') as file:
+ st_ppid = next(line for line in file if line.startswith('PPid:'))
+ st_ppid = st_ppid.removeprefix('PPid:').strip()
pid_to_ppid[pid] = int(st_ppid)
except (FileNotFoundError, ProcessLookupError, ValueError):
continue
@@ -55,16 +56,16 @@ def get_pstree_from_pid(root_pid: int) -> set[int]:
return descendants
-def subreaper(args: Namespace, other: List[str]) -> int:
- logger = getLogger("subreaper")
+def subreaper(args: Namespace, other: list[str]) -> int:
+ logger = getLogger('subreaper')
logging.basicConfig(
- format="[%(name)s] %(levelname)s: %(message)s",
+ format='[%(name)s] %(levelname)s: %(message)s',
level=logging.DEBUG if args.debug else logging.INFO,
stream=sys.stderr,
)
- logger.debug("command: %s", args)
- logger.debug("arguments: %s", other)
+ logger.debug('command: %s', args)
+ logger.debug('arguments: %s', other)
def signal_handler(sig, frame):
logger.info("Caught '%s' signal.", signal.strsignal(sig))
@@ -72,13 +73,13 @@ def signal_handler(sig, frame):
for p in pstree:
os.kill(p, sig)
- command: List[str] = [args.command, *other]
+ command: list[str] = [args.command, *other]
workdir: str = args.workdir
child_status: int = 0
libc_path: str = get_libc()
if not libc_path:
- logger.error("Could not find libc")
+ logger.error('Could not find libc')
return 1
libc: CDLL = CDLL(libc_path)
@@ -92,24 +93,24 @@ def signal_handler(sig, frame):
# c_ulong,
]
- proc_name = b"reaper"
+ proc_name = b'reaper'
buff = create_string_buffer(len(proc_name) + 1)
buff.value = proc_name
prctl_ret = prctl(PR_SET_NAME, byref(buff), 0, 0, 0)
- logger.debug("prctl PR_SET_NAME exited with status: %s", prctl_ret)
+ logger.debug('prctl PR_SET_NAME exited with status: %s', prctl_ret)
prctl_ret = prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0, 0)
- logger.debug("prctl PR_SET_CHILD_SUBREAPER exited with status: %s", prctl_ret)
+ logger.debug('prctl PR_SET_CHILD_SUBREAPER exited with status: %s', prctl_ret)
- if os.environ.get("MANGOHUD") == "1":
+ if os.environ.get('MANGOHUD') == '1':
if mangoshim := find_mangohud_shim():
- os.environ["LD_PRELOAD"] = f'{os.environ.get("LD_PRELOAD", "")}:{mangoshim}'
+ os.environ['LD_PRELOAD'] = f'{os.environ.get("LD_PRELOAD", "")}:{mangoshim}'
elif mangobin := find_mangohud_bin():
command.insert(0, mangobin)
pid = os.fork() # pylint: disable=E1101
if pid == -1:
- logger.error("Fork failed")
+ logger.error('Fork failed')
if pid == 0:
sys.stdout.flush()
@@ -123,7 +124,7 @@ def signal_handler(sig, frame):
while True:
try:
child_pid, child_status = os.wait() # pylint: disable=E1101
- logger.info("Child %s exited with status: %s", child_pid, child_status)
+ logger.info('Child %s exited with status: %s', child_pid, child_status)
except ChildProcessError as e:
logger.info(e)
break
@@ -131,11 +132,11 @@ def signal_handler(sig, frame):
return child_status
-if __name__ == "__main__":
- sep = sys.argv.index("--")
+if __name__ == '__main__':
+ sep = sys.argv.index('--')
argv = sys.argv[sep + 1 :]
args = Namespace(command=argv.pop(0), workdir=os.getcwd(), debug=True)
subreaper(args, argv)
-__all__ = ["subreaper"]
+__all__ = ['subreaper']
diff --git a/rare/commands/subreaper/util.py b/rare/commands/subreaper/util.py
index b89d800e3b..90b702af35 100644
--- a/rare/commands/subreaper/util.py
+++ b/rare/commands/subreaper/util.py
@@ -1,39 +1,38 @@
import os
import shutil
-from typing import List
-def find_all(name, path) -> List:
+def find_all(name, path) -> list:
result = []
- for root, dirs, files in os.walk(path):
+ for root, _, files in os.walk(path):
if name in files:
result.append(os.path.join(root, name))
return result
def find_mangohud_shim() -> str:
- libs = find_all("libMangoHud_shim.so", "/usr")
+ libs = find_all('libMangoHud_shim.so', '/usr')
if 1 > len(libs) > 2:
- return ""
+ return ''
if len(libs) == 1:
return libs[0]
ret = []
- for left, right in zip(*(lib.split("/") for lib in libs)):
+ for left, right in zip(*(lib.split('/') for lib in libs), strict=False):
if left == right:
ret.append(left)
- elif left.startswith("lib") and right.startswith("lib"):
- ret.append("$LIB")
+ elif left.startswith('lib') and right.startswith('lib'):
+ ret.append('$LIB')
else:
- return ""
- return "/".join(ret)
+ return ''
+ return '/'.join(ret)
def find_mangohud_bin() -> str:
- return shutil.which("mangohud")
+ return shutil.which('mangohud')
-if __name__ == "__main__":
+if __name__ == '__main__':
print(find_mangohud_shim())
-__all__ = ["find_mangohud_shim", "find_mangohud_bin"]
+__all__ = ['find_mangohud_shim', 'find_mangohud_bin']
diff --git a/rare/commands/webview.py b/rare/commands/webview.py
index 3b38495f15..78323edc09 100644
--- a/rare/commands/webview.py
+++ b/rare/commands/webview.py
@@ -5,10 +5,10 @@
def webview(args: Namespace) -> int:
- if webview_login.do_webview_login(callback_code=sys.stdout.write, user_agent=f"EpicGamesLauncher/{args.egl_version}"):
+ if webview_login.do_webview_login(callback_code=sys.stdout.write, user_agent=f'EpicGamesLauncher/{args.egl_version}'):
return 0
else:
return 1
-__all__ = ["webview"]
+__all__ = ['webview']
diff --git a/rare/components/__init__.py b/rare/components/__init__.py
index 9c460997c3..ac894e3669 100644
--- a/rare/components/__init__.py
+++ b/rare/components/__init__.py
@@ -2,7 +2,6 @@
import shutil
from argparse import Namespace
from datetime import datetime, timezone
-from typing import Optional
import requests.exceptions
from PySide6.QtCore import Qt, QThreadPool, QTimer, Slot
@@ -37,7 +36,7 @@ def _handler(self, exc_type, exc_value, exc_tb) -> bool:
class Rare(RareApp):
def __init__(self, args: Namespace):
- super(Rare, self).__init__(args, f"{type(self).__name__}_{{0}}.log")
+ super(Rare, self).__init__(args, f'{type(self).__name__}_{{0}}.log')
self._hook.deleteLater()
self._hook = RareException(self)
self.rcore = RareCore(self.settings, args=args)
@@ -46,23 +45,23 @@ def __init__(self, args: Namespace):
self.core = self.rcore.core()
# set Application name for settings
- self.main_window: Optional[RareWindow] = None
- self.launch_dialog: Optional[LaunchDialog] = None
- self.relogin_timer: Optional[QTimer] = None
+ self.main_window: RareWindow | None = None
+ self.launch_dialog: LaunchDialog | None = None
+ self.relogin_timer: QTimer | None = 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_exp = datetime.fromisoformat(self.core.lgd.userdata['expires_at'][:-1]).replace(tzinfo=timezone.utc)
dt_now = datetime.now(timezone.utc)
td = abs(dt_exp - dt_now)
self.relogin_timer.start(int(td.total_seconds() - 60) * 1000)
- self.logger.info(f"Renewed session expires at {self.core.lgd.userdata['expires_at']}")
+ self.logger.info(f'Renewed session expires at {self.core.lgd.userdata["expires_at"]}')
def relogin(self):
- self.logger.info("Session expires shortly. Renew session")
+ self.logger.info('Session expires shortly. Renew session')
try:
self.core.login(force_refresh=True)
except requests.exceptions.ConnectionError:
diff --git a/rare/components/dialogs/install/__init__.py b/rare/components/dialogs/install/__init__.py
index 34ac9a864a..c0cb9854d9 100644
--- a/rare/components/dialogs/install/__init__.py
+++ b/rare/components/dialogs/install/__init__.py
@@ -1,3 +1,3 @@
from .dialog import InstallDialog
-__all__ = ["InstallDialog"]
+__all__ = ['InstallDialog']
diff --git a/rare/components/dialogs/install/advanced.py b/rare/components/dialogs/install/advanced.py
index d2ba1267c0..9893d613c2 100644
--- a/rare/components/dialogs/install/advanced.py
+++ b/rare/components/dialogs/install/advanced.py
@@ -8,7 +8,7 @@ class InstallDialogAdvanced(CollapsibleFrame):
def __init__(self, parent=None):
super(InstallDialogAdvanced, self).__init__(parent=parent)
- title = self.tr("Advanced options")
+ title = self.tr('Advanced options')
self.setTitle(title)
self.widget = QWidget(parent=self)
@@ -17,4 +17,4 @@ def __init__(self, parent=None):
self.setWidget(self.widget)
-__all__ = ["InstallDialogAdvanced"]
+__all__ = ['InstallDialogAdvanced']
diff --git a/rare/components/dialogs/install/dialog.py b/rare/components/dialogs/install/dialog.py
index 7e69367f29..aa600af1b8 100644
--- a/rare/components/dialogs/install/dialog.py
+++ b/rare/components/dialogs/install/dialog.py
@@ -1,7 +1,6 @@
import os
import platform as pf
import shutil
-from typing import Optional, Tuple, Union
from PySide6.QtCore import Qt, QThreadPool, Signal, Slot
from PySide6.QtGui import QShowEvent
@@ -27,22 +26,22 @@
class InstallDialog(ActionDialog):
result_ready = Signal(InstallQueueItemModel)
- def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: InstallOptionsModel, parent=None):
+ def __init__(self, settings: RareAppSettings, rgame: 'RareGame', options: InstallOptionsModel, parent=None):
super(InstallDialog, self).__init__(parent=parent)
self.settings = settings
- header = self.tr("Install")
- bicon = qta_icon("ri.install-line")
+ header = self.tr('Install')
+ bicon = qta_icon('ri.install-line')
if options.repair_mode:
- header = self.tr("Repair")
- bicon = qta_icon("fa.wrench", "mdi.progress-wrench")
+ header = self.tr('Repair')
+ bicon = qta_icon('fa.wrench', 'mdi.progress-wrench')
if options.repair_and_update:
- header = self.tr("Repair and update")
+ header = self.tr('Repair and update')
elif options.update:
- header = self.tr("Update")
+ header = self.tr('Update')
elif options.reset_sdl:
- header = self.tr("Modify")
- bicon = qta_icon("fa.gear", "mdi.content-save-edit-outline")
+ header = self.tr('Modify')
+ bicon = qta_icon('fa.gear', 'mdi.content-save-edit-outline')
self.setWindowTitle(game_title(header, rgame.app_title))
self.setSubtitle(game_title(header, rgame.app_title))
@@ -53,8 +52,8 @@ def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: Instal
self.core = rgame.core
self.rgame = rgame
self._options: InstallOptionsModel = options
- self._download: Optional[InstallDownloadModel] = None
- self._queue_item: Optional[InstallQueueItemModel] = None
+ self._download: InstallDownloadModel | None = None
+ self._queue_item: InstallQueueItemModel | None = None
self.selectable = InstallDialogSelective(rgame, parent=self)
self.selectable.stateChanged.connect(self._on_option_changed)
@@ -122,10 +121,10 @@ def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: Instal
if options.repair_mode and not options.repair_and_update:
self.selectable.click()
- self.advanced.ui.max_workers_spin.setValue(self.core.lgd.config.getint("Legendary", "max_workers", fallback=0))
+ self.advanced.ui.max_workers_spin.setValue(self.core.lgd.config.getint('Legendary', 'max_workers', fallback=0))
self.advanced.ui.max_workers_spin.valueChanged.connect(self._on_option_changed)
- self.advanced.ui.max_memory_spin.setValue(self.core.lgd.config.getint("Legendary", "max_memory", fallback=0))
+ self.advanced.ui.max_memory_spin.setValue(self.core.lgd.config.getint('Legendary', 'max_memory', fallback=0))
self.advanced.ui.max_memory_spin.valueChanged.connect(self._on_option_changed)
self.advanced.ui.read_files_check.setChecked(options.read_files)
@@ -156,11 +155,11 @@ def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: Instal
self.ui.shortcut_check.setEnabled(False)
self.selectable.setEnabled(False)
- if pf.system() == "Darwin":
+ if pf.system() == 'Darwin':
self.ui.shortcut_label.setDisabled(True)
self.ui.shortcut_check.setDisabled(True)
self.ui.shortcut_check.setChecked(False)
- self.ui.shortcut_check.setToolTip(self.tr("Creating a shortcut is not supported on macOS"))
+ self.ui.shortcut_check.setToolTip(self.tr('Creating a shortcut is not supported on macOS'))
self.advanced.ui.install_prereqs_label.setEnabled(False)
self.advanced.ui.install_prereqs_check.setEnabled(False)
@@ -170,10 +169,10 @@ def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: Instal
# lk: set object names for CSS properties
self.accept_button.setText(header)
self.accept_button.setIcon(bicon)
- self.accept_button.setObjectName("InstallButton")
+ self.accept_button.setObjectName('InstallButton')
- self.action_button.setText(self.tr("Validate"))
- self.action_button.setIcon(qta_icon("fa.check", "fa5s.check"))
+ self.action_button.setText(self.tr('Validate'))
+ self.action_button.setIcon(qta_icon('fa.check', 'fa5s.check'))
self.setCentralWidget(install_widget)
@@ -204,19 +203,19 @@ def reset_install_dir(self, index: int):
@Slot(int)
def check_incompatible_platform(self, index: int):
platform = self.ui.platform_combo.itemText(index)
- if platform == "Mac" and pf.system() != "Darwin":
+ if platform == 'Mac' and pf.system() != 'Darwin':
self.set_error_labels(
- self.tr("Warning"),
- self.tr("You will not be able to run the game if you select {} as platform").format(platform),
+ self.tr('Warning'),
+ self.tr('You will not be able to run the game if you select {} as platform').format(platform),
)
else:
self.set_error_labels()
def get_options(self):
- base_path = os.path.join(self.install_dir_edit.text(), ".overlay" if self._options.overlay else "")
+ base_path = os.path.join(self.install_dir_edit.text(), '.overlay' if self._options.overlay else '')
# TODO: investigate if this check is needed
if self.rgame.is_installed or self.rgame.is_dlc:
- self._options.base_path = ""
+ self._options.base_path = ''
else:
self._options.base_path = base_path
self._options.platform = self.ui.platform_combo.currentText()
@@ -241,7 +240,7 @@ def get_download_info(self):
def action_handler(self):
self.set_error_labels()
- message = self.tr("Updating...")
+ message = self.tr('Updating...')
self.set_size_labels(message, message)
self.setActive(True)
self.options_changed = False
@@ -265,12 +264,12 @@ def _on_option_changed_no_reload(self, state: Qt.CheckState):
self._options.install_prereqs = state != Qt.CheckState.Unchecked
@staticmethod
- def _install_dir_edit_callback(path: str) -> Tuple[bool, str, int]:
+ def _install_dir_edit_callback(path: str) -> tuple[bool, str, int]:
if not path:
return False, path, IndicatorReasonsCommon.IS_EMPTY
try:
- perms_path = os.path.join(path, ".rare_perms")
- open(perms_path, "w").close()
+ perms_path = os.path.join(path, '.rare_perms')
+ open(perms_path, 'w').close()
os.unlink(perms_path)
except PermissionError:
return False, path, IndicatorReasonsCommon.PERM_NO_WRITE
@@ -289,17 +288,17 @@ def _on_install_dir_validation(self, is_valid: bool, reason: str):
self.accept_button.setEnabled(False)
self.action_button.setEnabled(is_valid and not self.active())
if not is_valid:
- self.set_error_labels(self.tr("Error"), reason)
+ self.set_error_labels(self.tr('Error'), reason)
else:
self.set_error_labels()
@staticmethod
def same_platform(download: InstallDownloadModel) -> bool:
platform = download.igame.platform
- if pf.system() == "Windows":
- return platform in {"Windows", "Win32"}
- elif pf.system() == "Darwin":
- return platform == "Mac"
+ if pf.system() == 'Windows':
+ return platform in {'Windows', 'Win32'}
+ elif pf.system() == 'Darwin':
+ return platform == 'Mac'
else:
return False
@@ -314,14 +313,14 @@ def _on_worker_result(self, download: InstallDownloadModel):
if download_size or (not download_size and (download.game.is_dlc or download.repair)):
self.accept_button.setEnabled(not self.options_changed)
self.action_button.setEnabled(self.options_changed)
- has_prereqs = bool(download.igame.prereq_info) and not download.igame.prereq_info.get("installed", False)
+ has_prereqs = bool(download.igame.prereq_info) and not download.igame.prereq_info.get('installed', False)
if has_prereqs:
- prereq_name = download.igame.prereq_info.get("name", "")
- prereq_path = os.path.split(download.igame.prereq_info.get("path", ""))[-1]
+ prereq_name = download.igame.prereq_info.get('name', '')
+ prereq_path = os.path.split(download.igame.prereq_info.get('path', ''))[-1]
prereq_desc = prereq_name if prereq_name else prereq_path
- self.advanced.ui.install_prereqs_check.setText(self.tr("Also install: {}").format(prereq_desc))
+ self.advanced.ui.install_prereqs_check.setText(self.tr('Also install: {}').format(prereq_desc))
else:
- self.advanced.ui.install_prereqs_check.setText("")
+ self.advanced.ui.install_prereqs_check.setText('')
# Offer to install prerequisites only on same platforms
self.advanced.ui.install_prereqs_label.setEnabled(has_prereqs)
self.advanced.ui.install_prereqs_check.setEnabled(has_prereqs)
@@ -341,7 +340,7 @@ def _on_worker_result(self, download: InstallDownloadModel):
@Slot(str)
def _on_worker_failed(self, message: str):
self.setActive(False)
- error_text = self.tr("Error")
+ error_text = self.tr('Error')
self.set_size_labels(error_text, error_text)
self.set_error_labels(error_text, message)
self.action_button.setEnabled(self.options_changed)
@@ -350,7 +349,7 @@ def _on_worker_failed(self, message: str):
self.open()
@staticmethod
- def _set_size_label(label: QLabel, value: Union[int, float, str]):
+ def _set_size_label(label: QLabel, value: int | float | str):
is_numeric = isinstance(value, (int, float))
font = label.font()
font.setBold(is_numeric)
@@ -359,11 +358,11 @@ def _set_size_label(label: QLabel, value: Union[int, float, str]):
text = format_size(value) if is_numeric else value
label.setText(text)
- def set_size_labels(self, download: Union[int, float, str], install: Union[int, float, str]):
+ def set_size_labels(self, download: int | float | str, install: int | float | str):
self._set_size_label(self.ui.download_size_text, download)
self._set_size_label(self.ui.install_size_text, install)
- def set_error_labels(self, label: str = "", message: str = ""):
+ def set_error_labels(self, label: str = '', message: str = ''):
self.ui.warning_label.setVisible(bool(label))
self.ui.warning_label.setText(label)
self.ui.warning_text.setVisible(bool(message))
diff --git a/rare/components/dialogs/install/file_filters.py b/rare/components/dialogs/install/file_filters.py
index 4ea756480f..e3104783ce 100644
--- a/rare/components/dialogs/install/file_filters.py
+++ b/rare/components/dialogs/install/file_filters.py
@@ -10,7 +10,7 @@ class InstallDialogFileFilters(CollapsibleFrame):
def __init__(self, parent=None):
super(InstallDialogFileFilters, self).__init__(parent=parent)
- title = self.tr("File filters")
+ title = self.tr('File filters')
self.setTitle(title)
self.widget = QWidget(parent=self)
@@ -27,9 +27,9 @@ def clear(self):
def add_item(self, data: str):
li = QListWidgetItem(data, self.ui.exclude_list)
- li.setFont(QFont("monospace"))
+ li.setFont(QFont('monospace'))
li.setCheckState(Qt.CheckState.Unchecked)
self.ui.exclude_list.addItem(li)
-__all__ = ["InstallDialogFileFilters"]
+__all__ = ['InstallDialogFileFilters']
diff --git a/rare/components/dialogs/install/selective.py b/rare/components/dialogs/install/selective.py
index 0a19162d69..51fe45c327 100644
--- a/rare/components/dialogs/install/selective.py
+++ b/rare/components/dialogs/install/selective.py
@@ -1,4 +1,4 @@
-from typing import List, Union
+from logging import getLogger
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QFont
@@ -9,14 +9,14 @@
class InstallTagCheckBox(QCheckBox):
- def __init__(self, text, desc, tags: List[str], parent=None):
+ def __init__(self, text, desc, tags: list[str], parent=None):
super(InstallTagCheckBox, self).__init__(parent)
- self.setFont(QFont("monospace"))
+ self.setFont(QFont('monospace'))
self.setText(text)
self.setToolTip(desc)
self.tags = tags
- def isChecked(self) -> Union[bool, List[str]]:
+ def isChecked(self) -> bool | list[str]:
return self.tags if super(InstallTagCheckBox, self).isChecked() else False
@@ -25,6 +25,7 @@ class SelectiveWidget(QWidget):
def __init__(self, rgame: RareGame, platform: str, parent=None):
super().__init__(parent=parent)
+ self.logger = getLogger(type(self).__name__)
self._has_tags = False
main_layout = QVBoxLayout(self)
@@ -32,18 +33,18 @@ def __init__(self, rgame: RareGame, platform: str, parent=None):
core = rgame.core
- config_tags = core.lgd.config.get(rgame.app_name, "install_tags", fallback=None)
- config_disable_sdl = core.lgd.config.getboolean(rgame.app_name, "disable_sdl", fallback=False)
+ config_tags = core.lgd.config.get(rgame.app_name, 'install_tags', fallback=None)
+ config_disable_sdl = core.lgd.config.getboolean(rgame.app_name, 'disable_sdl', fallback=False)
sdl_data = rgame.sdl_data(platform)
if not config_disable_sdl and sdl_data:
for group, info in sdl_data.items():
- cb = InstallTagCheckBox(info["name"].strip(), info["description"].strip(), info["tags"])
- if group == "__required":
+ cb = InstallTagCheckBox(info['name'].strip(), info['description'].strip(), info['tags'])
+ if group == '__required':
cb.setChecked(True)
cb.setDisabled(True)
if config_tags is not None:
- if all(tag in config_tags for tag in info["tags"]):
+ if all(tag in config_tags for tag in info['tags']):
cb.setChecked(True)
cb.stateChanged.connect(self.stateChanged)
main_layout.addWidget(cb)
@@ -51,13 +52,13 @@ def __init__(self, rgame: RareGame, platform: str, parent=None):
else:
self._has_tags = False
- def enabled_tags(self) -> List[str]:
+ def enabled_tags(self) -> list[str]:
install_tags = set()
for cb in self.findChildren(InstallTagCheckBox, options=Qt.FindChildOption.FindDirectChildrenOnly):
if data := cb.isChecked():
# noinspection PyTypeChecker
install_tags.update(data)
- install_tags = ["", *install_tags]
+ install_tags = ['', *install_tags]
return install_tags
def supports_tags(self) -> bool:
@@ -69,7 +70,7 @@ class InstallDialogSelective(CollapsibleFrame):
def __init__(self, rgame: RareGame, parent=None):
super(InstallDialogSelective, self).__init__(parent=parent)
- title = self.tr("Optional downloads")
+ title = self.tr('Optional downloads')
self.setTitle(title)
self.setEnabled(False)
@@ -85,8 +86,8 @@ def update_list(self, platform: str):
self.widget.stateChanged.connect(self.stateChanged)
self.setWidget(self.widget)
- def enabled_tags(self) -> List[str]:
+ def enabled_tags(self) -> list[str]:
return self.widget.enabled_tags()
-__all__ = ["InstallDialogSelective", "SelectiveWidget"]
+__all__ = ['InstallDialogSelective', 'SelectiveWidget']
diff --git a/rare/components/dialogs/launch.py b/rare/components/dialogs/launch.py
index 30a3a2db14..63119f50ca 100644
--- a/rare/components/dialogs/launch.py
+++ b/rare/components/dialogs/launch.py
@@ -51,8 +51,8 @@ def login(self):
# self.core.check_for_updates(force=True)
# self.core.force_show_update = True
if not self.core.login(force_refresh=True):
- raise ValueError("You are not logged in. Opening login window.")
- self.logger.info("You are logged in")
+ raise ValueError('You are not logged in. Opening login window.')
+ self.logger.info('You are logged in')
self.login_dialog.close()
except ValueError as e:
self.logger.info(str(e))
@@ -74,7 +74,7 @@ def do_launch(self):
self.launch()
def launch(self):
- self.progress_info.setText(self.tr("Preparing Rare"))
+ self.progress_info.setText(self.tr('Preparing Rare'))
self.rcore.fetch()
@Slot(int, str)
@@ -83,5 +83,5 @@ def __on_progress(self, i: int, m: str):
self.progress_info.setText(m)
def __on_completed(self):
- self.logger.info("Application starting")
+ self.logger.info('Application starting')
self.start_app.emit()
diff --git a/rare/components/dialogs/login/__init__.py b/rare/components/dialogs/login/__init__.py
index 111efc5e3e..d75a5d2342 100644
--- a/rare/components/dialogs/login/__init__.py
+++ b/rare/components/dialogs/login/__init__.py
@@ -60,19 +60,19 @@ def __init__(self, args: Namespace, core: LegendaryCore, parent=None):
self.import_page.validated.connect(self._on_page_validated)
self.info_message = {
- self.landing_index: self.tr("Select log-in method."),
+ self.landing_index: self.tr('Select log-in method.'),
self.browser_index: self.tr(
- "Click the Open Browser button to open the "
- "login page in your web browser or copy the link and paste it "
- "in any web browser. After logging in using the browser, copy "
- "the text in the quotes after authorizationCode "
- "in the same line into the empty input above."
- "
DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH "
- "ANYONE IN ANY FORM (TEXT OR SCREENSHOT)!"
+ 'Click the Open Browser button to open the '
+ 'login page in your web browser or copy the link and paste it '
+ 'in any web browser. After logging in using the browser, copy '
+ 'the text in the quotes after authorizationCode '
+ 'in the same line into the empty input above.'
+ '
DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH '
+ 'ANYONE IN ANY FORM (TEXT OR SCREENSHOT)!'
),
self.import_index: self.tr(
- "Select the Wine prefix where Epic Games Launcher is installed. "
- "You will get logged out from EGL in the process."
+ 'Select the Wine prefix where Epic Games Launcher is installed. '
+ 'You will get logged out from EGL in the process.'
),
}
self.ui.info_label.setText(self.info_message[self.landing_index])
@@ -96,9 +96,9 @@ def __init__(self, args: Namespace, core: LegendaryCore, parent=None):
self.login_stack.setCurrentWidget(self.landing_page)
- self.ui.exit_button.setIcon(qta_icon("fa.remove", "fa5s.times"))
- self.ui.back_button.setIcon(qta_icon("fa.chevron-left", "fa5s.chevron-left"))
- self.ui.next_button.setIcon(qta_icon("fa.chevron-right", "fa5s.chevron-right"))
+ self.ui.exit_button.setIcon(qta_icon('fa.remove', 'fa5s.times'))
+ self.ui.back_button.setIcon(qta_icon('fa.chevron-left', 'fa5s.chevron-left'))
+ self.ui.next_button.setIcon(qta_icon('fa.chevron-right', 'fa5s.chevron-right'))
# lk: Set next as the default button only to stop closing the dialog when pressing enter
self.ui.exit_button.setAutoDefault(False)
@@ -157,7 +157,7 @@ def login(self):
def _on_login_successful(self):
try:
if not self.core.login():
- raise ValueError("Login failed.")
+ raise ValueError('Login failed.')
self.logged_in = True
self.accept()
except Exception as e:
@@ -165,4 +165,4 @@ def _on_login_successful(self):
self.core.lgd.invalidate_userdata()
self.ui.next_button.setEnabled(False)
self.logged_in = False
- QMessageBox.warning(None, self.tr("Login error"), str(e))
+ QMessageBox.warning(None, self.tr('Login error'), str(e))
diff --git a/rare/components/dialogs/login/browser_login.py b/rare/components/dialogs/login/browser_login.py
index 45861a370f..bc64a9f5d9 100644
--- a/rare/components/dialogs/login/browser_login.py
+++ b/rare/components/dialogs/login/browser_login.py
@@ -1,5 +1,4 @@
import json
-from typing import Tuple
from legendary.utils import webview_login
from PySide6.QtCore import QProcess, QUrl, Slot
@@ -26,11 +25,11 @@ def __init__(self, core: LegendaryCore, parent=None):
self.login_url = self.core.egs.get_auth_url()
self.auth_edit = IndicatorLineEdit(
- placeholder=self.tr("Insert authorizationCode here"), edit_func=self.sid_edit_callback, parent=self
+ placeholder=self.tr('Insert authorizationCode here'), edit_func=self.sid_edit_callback, parent=self
)
self.auth_edit.line_edit.setEchoMode(QLineEdit.EchoMode.Password)
self.ui.link_text.setText(self.login_url)
- self.ui.copy_button.setIcon(qta_icon("mdi.content-copy", "fa5.copy"))
+ self.ui.copy_button.setIcon(qta_icon('mdi.content-copy', 'fa5.copy'))
self.ui.copy_button.clicked.connect(self._on_copy_link)
self.ui.form_layout.setWidget(
self.ui.form_layout.getWidgetPosition(self.ui.sid_label)[0], QFormLayout.ItemRole.FieldRole, self.auth_edit
@@ -43,18 +42,18 @@ def __init__(self, core: LegendaryCore, parent=None):
def _on_copy_link(self):
clipboard = QApplication.instance().clipboard()
clipboard.setText(self.login_url)
- self.ui.status_field.setText(self.tr("Copied to clipboard"))
+ self.ui.status_field.setText(self.tr('Copied to clipboard'))
def is_valid(self) -> bool:
return self.auth_edit.is_valid
@staticmethod
- def sid_edit_callback(text) -> Tuple[bool, str, int]:
+ def sid_edit_callback(text) -> tuple[bool, str, int]:
if text:
text = text.strip()
- if text.startswith("{") and text.endswith("}"):
+ if text.startswith('{') and text.endswith('}'):
try:
- text = json.loads(text).get("authorizationCode")
+ text = json.loads(text).get('authorizationCode')
except json.JSONDecodeError:
return False, text, IndicatorReasonsCommon.WRONG_FORMAT
elif '"' in text:
@@ -64,16 +63,16 @@ def sid_edit_callback(text) -> Tuple[bool, str, int]:
return False, text, IndicatorReasonsCommon.VALID
def do_login(self) -> None:
- self.ui.status_field.setText(self.tr("Logging in..."))
+ self.ui.status_field.setText(self.tr('Logging in...'))
auth_code = self.auth_edit.text()
try:
if self.core.auth_code(auth_code):
- self.logger.info("Successfully logged in as %s", self.core.lgd.userdata["displayName"])
+ self.logger.info('Successfully logged in as %s', self.core.lgd.userdata['displayName'])
self.success.emit()
except Exception as e:
msg = e.message if isinstance(e, LgndrException) else str(e)
- self.ui.status_field.setText(self.tr("Login failed: {}").format(msg))
- self.logger.error("Failed to login through browser")
+ self.ui.status_field.setText(self.tr('Login failed: {}').format(msg))
+ self.logger.error('Failed to login through browser')
self.logger.error(e)
@Slot()
@@ -82,25 +81,25 @@ def _on_open_browser(self):
self.logger.warning("You don't have webengine installed, you will need to manually copy the authorizationCode.")
QDesktopServices.openUrl(QUrl(self.login_url))
else:
- cmd = get_rare_executable() + ["login", self.core.get_egl_version()]
+ cmd = get_rare_executable() + ['login', self.core.get_egl_version()]
proc = QProcess(self)
proc.start(cmd[0], cmd[1:])
proc.waitForFinished(-1)
out, _ = (
- proc.readAllStandardOutput().data().decode("utf-8", "ignore"),
- proc.readAllStandardError().data().decode("utf-8", "ignore"),
+ proc.readAllStandardOutput().data().decode('utf-8', 'ignore'),
+ proc.readAllStandardError().data().decode('utf-8', 'ignore'),
)
proc.deleteLater()
if out:
try:
self.core.auth_ex_token(out)
- self.logger.info("Successfully logged in as %s", {self.core.lgd.userdata["displayName"]})
+ self.logger.info('Successfully logged in as %s', {self.core.lgd.userdata['displayName']})
self.success.emit()
except Exception as e:
msg = e.message if isinstance(e, LgndrException) else str(e)
- self.ui.status_field.setText(self.tr("Login failed: {}").format(msg))
- self.logger.error("Failed to login through browser")
+ self.ui.status_field.setText(self.tr('Login failed: {}').format(msg))
+ self.logger.error('Failed to login through browser')
self.logger.error(e)
else:
- self.logger.error("Failed to login through browser")
+ self.logger.error('Failed to login through browser')
diff --git a/rare/components/dialogs/login/import_login.py b/rare/components/dialogs/login/import_login.py
index 249932da8d..dc13abb848 100644
--- a/rare/components/dialogs/login/import_login.py
+++ b/rare/components/dialogs/login/import_login.py
@@ -15,11 +15,11 @@
class ImportLogin(LoginFrame):
# FIXME: Use pathspec instead of duplicated code
- if platform.system() == "Windows":
- localappdata = os.path.expandvars("%LOCALAPPDATA%")
+ if platform.system() == 'Windows':
+ localappdata = os.path.expandvars('%LOCALAPPDATA%')
else:
- localappdata = os.path.join("drive_c/users", getuser(), "Local Settings/Application Data")
- egl_appdata = os.path.join(localappdata, "EpicGamesLauncher", "Saved", "Config", "Windows")
+ localappdata = os.path.join('drive_c/users', getuser(), 'Local Settings/Application Data')
+ egl_appdata = os.path.join(localappdata, 'EpicGamesLauncher', 'Saved', 'Config', 'Windows')
found = False
def __init__(self, core: LegendaryCore, parent=None):
@@ -29,9 +29,9 @@ def __init__(self, core: LegendaryCore, parent=None):
self.ui.setupUi(self)
self.text_egl_found = self.tr("Found EGL Program Data. Click 'Next' to import them.")
- self.text_egl_notfound = self.tr("Could not find EGL Program Data. ")
+ self.text_egl_notfound = self.tr('Could not find EGL Program Data. ')
- if platform.system() == "Windows":
+ if platform.system() == 'Windows':
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:
@@ -42,12 +42,12 @@ def __init__(self, core: LegendaryCore, parent=None):
self.ui.prefix_combo.setCurrentText(self.egl_appdata)
else:
if programdata_path := self.core.egl.programdata_path:
- if wine_pfx := programdata_path.split("drive_c")[0]:
+ if wine_pfx := programdata_path.split('drive_c')[0]:
self.ui.prefix_combo.addItem(wine_pfx)
prefixes = self.get_wine_prefixes()
if len(prefixes):
self.ui.prefix_combo.addItems(prefixes)
- self.ui.status_field.setText(self.tr("Select the Wine prefix you want to import."))
+ self.ui.status_field.setText(self.tr('Select the Wine prefix you want to import.'))
else:
self.ui.status_field.setText(self.text_egl_notfound)
@@ -56,8 +56,8 @@ def __init__(self, core: LegendaryCore, parent=None):
def get_wine_prefixes(self):
possible_prefixes = [
- os.path.expanduser("~/.wine"),
- os.path.expanduser("~/Games/epic-games-store"),
+ os.path.expanduser('~/.wine'),
+ os.path.expanduser('~/Games/epic-games-store'),
]
prefixes = []
for prefix in possible_prefixes:
@@ -67,7 +67,7 @@ def get_wine_prefixes(self):
@Slot()
def _on_prefix_path(self):
- prefix_dialog = QFileDialog(self, self.tr("Choose path"), os.path.expanduser("~/"))
+ prefix_dialog = QFileDialog(self, self.tr('Choose path'), os.path.expanduser('~/'))
prefix_dialog.setFileMode(QFileDialog.FileMode.Directory)
prefix_dialog.setOption(QFileDialog.Option.ShowDirsOnly)
if prefix_dialog.exec_():
@@ -75,14 +75,14 @@ def _on_prefix_path(self):
self.ui.prefix_combo.setCurrentText(names[0])
def is_valid(self) -> bool:
- if platform.system() == "Windows":
+ if platform.system() == 'Windows':
return self.found
else:
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")
+ os.path.join(wine_folders['Local AppData'], 'EpicGamesLauncher', 'Saved', 'Config', 'Windows')
)
if path_exists := os.path.exists(self.egl_appdata):
self.ui.status_field.setText(self.text_egl_found)
@@ -91,16 +91,16 @@ def is_valid(self) -> bool:
return False
def do_login(self) -> None:
- self.ui.status_field.setText(self.tr("Loading..."))
- if os.name != "nt":
- self.logger.info("Using EGL appdata path at %s", {self.egl_appdata})
+ self.ui.status_field.setText(self.tr('Loading...'))
+ if os.name != 'nt':
+ self.logger.info('Using EGL appdata path at %s', {self.egl_appdata})
self.core.egl.appdata_path = self.egl_appdata
try:
if self.core.auth_import():
- self.logger.info("Logged in as %s", {self.core.lgd.userdata["displayName"]})
+ self.logger.info('Logged in as %s', {self.core.lgd.userdata['displayName']})
self.success.emit()
except Exception as e:
msg = e.message if isinstance(e, LgndrException) else str(e)
- self.ui.status_field.setText(self.tr("Login failed: {}").format(msg))
- self.logger.warning("Failed to import existing session")
+ self.ui.status_field.setText(self.tr('Login failed: {}').format(msg))
+ self.logger.warning('Failed to import existing session')
self.logger.error(e)
diff --git a/rare/components/dialogs/move.py b/rare/components/dialogs/move.py
index e1b216678f..32924fbee5 100644
--- a/rare/components/dialogs/move.py
+++ b/rare/components/dialogs/move.py
@@ -1,5 +1,4 @@
import os
-from typing import Optional, Tuple, Union
from PySide6.QtCore import QSignalBlocker, QThreadPool, Signal, Slot
from PySide6.QtGui import QFont, QShowEvent, Qt
@@ -21,7 +20,7 @@ class MoveDialog(ActionDialog):
def __init__(self, rcore: RareCore, rgame: RareGame, parent=None):
super(MoveDialog, self).__init__(parent=parent)
- header = self.tr("Move")
+ header = self.tr('Move')
self.setWindowTitle(game_title(header, rgame.app_title))
self.setSubtitle(game_title(header, rgame.app_title))
@@ -34,7 +33,7 @@ def __init__(self, rcore: RareCore, rgame: RareGame, parent=None):
self.rcore = rcore
self.core = rcore.core()
- self.rgame: Optional[RareGame] = rgame
+ self.rgame: RareGame | None = rgame
self.options: MoveGameModel = MoveGameModel(rgame.app_name, rgame.install_path, rgame.folder_name)
self.target_path_edit = PathEdit(
@@ -45,12 +44,12 @@ def __init__(self, rcore: RareCore, rgame: RareGame, parent=None):
)
self.target_path_edit.setReadOnly(True)
self.target_path_edit.reasons = {
- MovePathEditReasons.MOVEDIALOG_DST_MISSING: self.tr("You need to provide the destination directory."),
- MovePathEditReasons.MOVEDIALOG_NO_WRITE: self.tr("No write permission on destination."),
- MovePathEditReasons.MOVEDIALOG_SAME_DIR: self.tr("Same directory or subdirectory selected."),
- MovePathEditReasons.MOVEDIALOG_DST_IN_SRC: self.tr("Destination is inside source directory"),
- MovePathEditReasons.MOVEDIALOG_NESTED_DIR: self.tr("Game install directories cannot be nested."),
- MovePathEditReasons.MOVEDIALOG_NO_SPACE: self.tr("Not enough space available on drive."),
+ MovePathEditReasons.MOVEDIALOG_DST_MISSING: self.tr('You need to provide the destination directory.'),
+ MovePathEditReasons.MOVEDIALOG_NO_WRITE: self.tr('No write permission on destination.'),
+ MovePathEditReasons.MOVEDIALOG_SAME_DIR: self.tr('Same directory or subdirectory selected.'),
+ MovePathEditReasons.MOVEDIALOG_DST_IN_SRC: self.tr('Destination is inside source directory'),
+ MovePathEditReasons.MOVEDIALOG_NESTED_DIR: self.tr('Game install directories cannot be nested.'),
+ MovePathEditReasons.MOVEDIALOG_NO_SPACE: self.tr('Not enough space available on drive.'),
}
self.target_path_edit.validationFinished.connect(self.__on_target_path_validation)
self.ui.main_layout.setWidget(
@@ -60,7 +59,7 @@ def __init__(self, rcore: RareCore, rgame: RareGame, parent=None):
)
self.full_path_info = ElideLabel(parent=self)
- self.full_path_info.setFont(QFont("monospace"))
+ self.full_path_info.setFont(QFont('monospace'))
self.ui.main_layout.setWidget(
self.ui.main_layout.getWidgetPosition(self.ui.full_path_label)[0],
QFormLayout.ItemRole.FieldRole,
@@ -73,12 +72,12 @@ def __init__(self, rcore: RareCore, rgame: RareGame, parent=None):
self.ui.reset_name_check.setChecked(self.options.reset_name)
self.ui.reset_name_check.checkStateChanged.connect(self.__on_reset_name_changed)
- self.accept_button.setText(self.tr("Move"))
- self.accept_button.setIcon(qta_icon("mdi.folder-move-outline"))
- self.accept_button.setObjectName("MoveButton")
+ self.accept_button.setText(self.tr('Move'))
+ self.accept_button.setIcon(qta_icon('mdi.folder-move-outline'))
+ self.accept_button.setObjectName('MoveButton')
- self.action_button.setText(self.tr("Validate"))
- self.action_button.setIcon(qta_icon("fa.check", "fa5s.check"))
+ self.action_button.setText(self.tr('Validate'))
+ self.action_button.setIcon(qta_icon('fa.check', 'fa5s.check'))
self.setCentralWidget(move_widget)
@@ -90,7 +89,7 @@ def showEvent(self, a0: QShowEvent) -> None:
def action_handler(self):
self.set_error_labels()
- message = self.tr("Updating...")
+ message = self.tr('Updating...')
self.set_size_labels(message, message)
self.setActive(True, disable=False)
self.options.target_path = self.target_path_edit.text()
@@ -109,7 +108,7 @@ def accept_handler(self):
def reject_handler(self):
self.options.accepted = False
- self.options.target_path = ""
+ self.options.target_path = ''
@Slot(Qt.CheckState)
def __on_rename_path_changed(self, state: Qt.CheckState):
@@ -128,12 +127,12 @@ def __on_reset_name_changed(self, state: Qt.CheckState):
self.action_button.setEnabled(True)
@staticmethod
- def __target_path_edit_callback(path: str) -> Tuple[bool, str, int]:
+ def __target_path_edit_callback(path: str) -> tuple[bool, str, int]:
if not path:
return False, path, IndicatorReasonsCommon.IS_EMPTY
try:
- perms_path = os.path.join(path, ".rare_perms")
- open(perms_path, "w").close()
+ perms_path = os.path.join(path, '.rare_perms')
+ open(perms_path, 'w').close()
os.unlink(perms_path)
except PermissionError:
return False, path, IndicatorReasonsCommon.PERM_NO_WRITE
@@ -142,14 +141,12 @@ def __target_path_edit_callback(path: str) -> Tuple[bool, str, int]:
return True, path, IndicatorReasonsCommon.VALID
@Slot(bool, object, object, MovePathEditReasons)
- def __on_worker_result(
- self, is_valid: bool, src_size: Union[int, float], dst_size: Union[int, float], reason: MovePathEditReasons
- ):
+ def __on_worker_result(self, is_valid: bool, src_size: int | float, dst_size: int | float, reason: MovePathEditReasons):
self.setActive(False, disable=False)
self.set_size_labels(src_size, dst_size)
self.action_button.setEnabled(False)
self.accept_button.setEnabled(is_valid)
- error, reason = (self.tr("Error"), self.target_path_edit.reasons[reason]) if not is_valid else ("", "")
+ error, reason = (self.tr('Error'), self.target_path_edit.reasons[reason]) if not is_valid else ('', '')
self.set_error_labels(error, reason)
@Slot(bool, str)
@@ -158,11 +155,11 @@ def __on_target_path_validation(self, is_valid: bool, reason: str):
self.full_path_info.setText(self.options.full_path)
self.action_button.setEnabled(is_valid and not self.active())
self.accept_button.setEnabled(False)
- error, reason = (self.tr("Error"), reason) if not is_valid else ("", "")
+ error, reason = (self.tr('Error'), reason) if not is_valid else ('', '')
self.set_error_labels(error, reason)
@staticmethod
- def __set_size_label(label: QLabel, value: Union[int, float, str]):
+ def __set_size_label(label: QLabel, value: int | float | str):
is_numeric = isinstance(value, (int, float))
font = label.font()
font.setBold(is_numeric)
@@ -171,11 +168,11 @@ def __set_size_label(label: QLabel, value: Union[int, float, str]):
text = format_size(value) if is_numeric else value
label.setText(text)
- def set_size_labels(self, required: Union[int, float, str], available: Union[int, float, str]):
+ def set_size_labels(self, required: int | float | str, available: int | float | str):
self.__set_size_label(self.ui.required_space_text, required)
self.__set_size_label(self.ui.available_space_text, available)
- def set_error_labels(self, label: str = "", message: str = ""):
+ def set_error_labels(self, label: str = '', message: str = ''):
self.ui.warning_label.setVisible(bool(label))
self.ui.warning_label.setText(label)
self.ui.warning_text.setVisible(bool(message))
diff --git a/rare/components/dialogs/selective.py b/rare/components/dialogs/selective.py
index 8581937398..bf6a3d439d 100644
--- a/rare/components/dialogs/selective.py
+++ b/rare/components/dialogs/selective.py
@@ -14,14 +14,14 @@ class SelectiveDialog(ButtonDialog):
def __init__(self, rgame: RareGame, parent=None):
super(SelectiveDialog, self).__init__(parent=parent)
- header = self.tr("Optional downloads for")
+ header = self.tr('Optional downloads for')
self.setWindowTitle(game_title(header, rgame.app_title))
self.setSubtitle(game_title(header, rgame.app_title))
self.rgame = rgame
self.selective_widget = SelectiveWidget(rgame, rgame.igame.platform, self)
- container = QGroupBox(self.tr("Optional downloads"), self)
+ container = QGroupBox(self.tr('Optional downloads'), self)
container_layout = QVBoxLayout(container)
container_layout.setContentsMargins(0, 0, 0, 0)
container_layout.addWidget(self.selective_widget)
@@ -31,8 +31,8 @@ def __init__(self, rgame: RareGame, parent=None):
self.setCentralLayout(layout)
- self.accept_button.setText(self.tr("Verify"))
- self.accept_button.setIcon(qta_icon("fa.check", "fa5s.check"))
+ self.accept_button.setText(self.tr('Verify'))
+ self.accept_button.setIcon(qta_icon('fa.check', 'fa5s.check'))
self.options: SelectiveDownloadsModel = SelectiveDownloadsModel(rgame.app_name)
diff --git a/rare/components/dialogs/uninstall.py b/rare/components/dialogs/uninstall.py
index f2327f068a..468cfd9d12 100644
--- a/rare/components/dialogs/uninstall.py
+++ b/rare/components/dialogs/uninstall.py
@@ -1,5 +1,3 @@
-from typing import Union
-
from PySide6.QtCore import Qt, Signal, Slot
from PySide6.QtWidgets import (
QCheckBox,
@@ -15,25 +13,25 @@
class UninstallDialog(ButtonDialog):
result_ready = Signal(UninstallOptionsModel)
- def __init__(self, rgame: Union[RareGame, RareEosOverlay], options: UninstallOptionsModel, parent=None):
+ def __init__(self, rgame: RareGame | RareEosOverlay, options: UninstallOptionsModel, parent=None):
super(UninstallDialog, self).__init__(parent=parent)
- header = self.tr("Uninstall")
+ header = self.tr('Uninstall')
self.setWindowTitle(game_title(header, rgame.app_title))
self.setSubtitle(game_title(header, rgame.app_title))
- self.keep_files = QCheckBox(self.tr("Keep files"))
+ self.keep_files = QCheckBox(self.tr('Keep files'))
self.keep_files.setChecked(bool(options.keep_files))
self.keep_files.setEnabled(not rgame.is_overlay)
- self.keep_folder = QCheckBox(self.tr("Keep game folder"))
+ self.keep_folder = QCheckBox(self.tr('Keep game folder'))
self.keep_folder.setChecked(bool(options.keep_folder))
self.keep_folder.setEnabled(not rgame.is_overlay and not rgame.is_dlc)
- self.keep_config = QCheckBox(self.tr("Keep configuation"))
+ self.keep_config = QCheckBox(self.tr('Keep configuation'))
self.keep_config.setChecked(bool(options.keep_config))
self.keep_config.setEnabled(not rgame.is_overlay and not rgame.is_dlc)
- self.keep_overlay_keys = QCheckBox(self.tr("Keep EOS Overlay registry keys"))
+ self.keep_overlay_keys = QCheckBox(self.tr('Keep EOS Overlay registry keys'))
self.keep_overlay_keys.setChecked(bool(options.keep_overlay_keys))
self.keep_overlay_keys.setEnabled(rgame.is_overlay)
@@ -45,9 +43,9 @@ def __init__(self, rgame: Union[RareGame, RareEosOverlay], options: UninstallOpt
self.setCentralLayout(layout)
- self.accept_button.setText(self.tr("Uninstall"))
- self.accept_button.setIcon(qta_icon("ri.uninstall-line"))
- self.accept_button.setObjectName("UninstallButton")
+ self.accept_button.setText(self.tr('Uninstall'))
+ self.accept_button.setIcon(qta_icon('ri.uninstall-line'))
+ self.accept_button.setObjectName('UninstallButton')
self.keep_files.checkStateChanged.connect(self.__on_keep_files_changed)
diff --git a/rare/components/main_window.py b/rare/components/main_window.py
index 2f9a85c5a5..fca75ad6a3 100644
--- a/rare/components/main_window.py
+++ b/rare/components/main_window.py
@@ -26,7 +26,7 @@
from .tabs import MainTabWidget
from .tray_icon import TrayIcon
-logger = getLogger("MainWindow")
+logger = getLogger('MainWindow')
class RareWindow(QMainWindow):
@@ -55,7 +55,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.status_bar = QStatusBar(self)
self.setStatusBar(self.status_bar)
- self.active_label = QLabel(self.tr("Active:"), self.status_bar)
+ self.active_label = QLabel(self.tr('Active:'), self.status_bar)
# lk: set top and botton margins to accommodate border for scroll area labels
self.active_label.setContentsMargins(5, 1, 0, 1)
self.status_bar.addWidget(self.active_label)
@@ -66,7 +66,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
active_layout.setSizeConstraint(QHBoxLayout.SizeConstraint.SetFixedSize)
self.status_bar.addWidget(self.active_container, stretch=0)
- self.queued_label = QLabel(self.tr("Queued:"), self.status_bar)
+ self.queued_label = QLabel(self.tr('Queued:'), self.status_bar)
# lk: set top and botton margins to accommodate border for scroll area labels
self.queued_label.setContentsMargins(5, 1, 0, 1)
self.status_bar.addPermanentWidget(self.queued_label)
@@ -108,7 +108,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.discord_rpc = DiscordRPC(settings, rcore, parent=self)
except ModuleNotFoundError:
- logger.warning("Discord RPC module not found")
+ logger.warning('Discord RPC module not found')
self.singleton_timer = QTimer(self)
self.singleton_timer.setInterval(1000)
@@ -122,7 +122,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
# enable kinetic scrolling
for scroll_area in self.findChildren(QScrollArea):
- if not scroll_area.property("no_kinetic_scroll"):
+ if not scroll_area.property('no_kinetic_scroll'):
QScroller.grabGesture(
scroll_area.viewport(),
QScroller.ScrollerGestureType.LeftMouseButtonGesture,
@@ -185,10 +185,10 @@ def update_statusbar(self):
self.queued_container.layout().removeWidget(label)
label.deleteLater()
for info in self.rcore.queue_info():
- label = ElideLabel(f"{info.prefix}: {info.app_title}")
- label.setObjectName("QueueWorkerLabel")
- label.setToolTip(f"{info.prefix}: {info.app_title}")
- label.setProperty("workertype", info.type)
+ label = ElideLabel(f'{info.prefix}: {info.app_title}')
+ label.setObjectName('QueueWorkerLabel')
+ label.setToolTip(f'{info.prefix}: {info.app_title}')
+ label.setProperty('workertype', info.type)
label.setFixedWidth(160)
label.setContentsMargins(3, 0, 3, 0)
if info.state == QueueWorkerState.ACTIVE:
@@ -203,9 +203,9 @@ def update_statusbar(self):
def timer_finished(self):
file_path = lock_file()
if os.path.exists(file_path):
- with open(file_path, "r") as file:
+ with open(file_path) as file:
action = file.read()
- if action.startswith("show"):
+ if action.startswith('show'):
self.show()
os.remove(file_path)
self.singleton_timer.start()
@@ -233,9 +233,10 @@ def closeEvent(self, e: QCloseEvent) -> None:
if self.rcore.threadpool_disk.activeThreadCount() or self.rcore.threadpool_net.activeThreadCount():
reply = QMessageBox.question(
self,
- self.tr("Quit {}?").format(QApplication.applicationName()),
+ self.tr('Quit {}?').format(QApplication.applicationName()),
self.tr(
- "There are currently running operations. Rare cannot exit until they are completed.\n\nDo you want to clear the queue?"
+ 'There are currently running operations. Rare cannot exit until they are completed.\n\n'
+ 'Do you want to clear the queue?'
),
buttons=(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No),
defaultButton=QMessageBox.StandardButton.No,
@@ -251,9 +252,9 @@ def closeEvent(self, e: QCloseEvent) -> None:
elif self.tab_widget.downloads_tab.is_download_active:
reply = QMessageBox.question(
self,
- self.tr("Quit {}?").format(QApplication.applicationName()),
+ self.tr('Quit {}?').format(QApplication.applicationName()),
self.tr(
- "There is an active download. Quitting Rare now will stop the download.\n\nAre you sure you want to quit?"
+ 'There is an active download. Quitting Rare now will stop the download.\n\nAre you sure you want to quit?'
),
buttons=(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No),
defaultButton=QMessageBox.StandardButton.No,
diff --git a/rare/components/tabs/__init__.py b/rare/components/tabs/__init__.py
index 73403e2fee..6c93545117 100644
--- a/rare/components/tabs/__init__.py
+++ b/rare/components/tabs/__init__.py
@@ -36,32 +36,32 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent):
# Generate Tabs
self.games_tab = GamesLibrary(self.settings, self.rcore, self)
self.games_tab.import_clicked.connect(self.show_import)
- self.games_index = self.addTab(self.games_tab, self.tr("Games"))
+ self.games_index = self.addTab(self.games_tab, self.tr('Games'))
# Downloads Tab after Games Tab to use populated RareCore games list
self.downloads_tab = DownloadsTab(self.settings, self.rcore, self)
- self.downloads_index = self.addTab(self.downloads_tab, "")
+ self.downloads_index = self.addTab(self.downloads_tab, '')
self.downloads_tab.update_title.connect(self.__on_downloads_update_title)
self.downloads_tab.update_queues_count()
self.setTabEnabled(self.downloads_index, not self.args.offline)
if not self.args.offline:
self.store_tab = StoreTab(self.core, parent=self)
- self.store_index = self.addTab(self.store_tab, self.tr("Store (Beta)"))
+ self.store_index = self.addTab(self.store_tab, self.tr('Store (Under Construction)'))
self.setTabEnabled(self.store_index, not self.args.offline)
# Space Tab
- space_index = self.addTab(QWidget(self), "Rare")
+ space_index = self.addTab(QWidget(self), 'Rare')
self.setTabEnabled(space_index, False)
self.main_bar.expanded_idx = space_index
# Integrations Tab
self.integrations_tab = IntegrationsTab(self.rcore, self)
- self.integrations_index = self.addTab(self.integrations_tab, self.tr("Integrations"))
+ self.integrations_index = self.addTab(self.integrations_tab, self.tr('Integrations'))
# Settings Tab
self.settings_tab = SettingsTab(settings, rcore, self)
- self.settings_index = self.addTab(self.settings_tab, qta_icon("fa.gear", "fa6s.gear"), self.tr("Settings"))
+ self.settings_index = self.addTab(self.settings_tab, qta_icon('fa.gear', 'fa6s.gear'), self.tr('Settings'))
self.settings_tab.update_available.connect(self._on_update_available)
# Account Tab
@@ -73,19 +73,19 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent):
self.account_menu.addAction(account_action)
self.account_tab = QWidget(self)
self.account_index = self.addTab(
- self.account_tab, qta_icon("mdi.account-circle", "fa5s.user"), self.core.lgd.userdata.get("displayName")
+ self.account_tab, qta_icon('mdi.account-circle', 'fa5s.user'), self.core.lgd.userdata.get('displayName')
)
# Open game list on click on Games tab button
self.tabBarClicked.connect(self.mouse_clicked)
# shortcuts
- QShortcut("Alt+1", self).activated.connect(self._on_shortcut_activated_games)
+ QShortcut('Alt+1', self).activated.connect(self._on_shortcut_activated_games)
if not self.args.offline:
- QShortcut("Alt+2", self).activated.connect(self._on_shortcut_activated_downloads)
- QShortcut("Alt+3", self).activated.connect(self._on_shortcut_activated_store)
- QShortcut("Alt+4", self).activated.connect(self._on_shortcut_activated_integrations)
- QShortcut("Alt+5", self).activated.connect(self._on_shortcut_activated_settings)
+ QShortcut('Alt+2', self).activated.connect(self._on_shortcut_activated_downloads)
+ QShortcut('Alt+3', self).activated.connect(self._on_shortcut_activated_store)
+ QShortcut('Alt+4', self).activated.connect(self._on_shortcut_activated_integrations)
+ QShortcut('Alt+5', self).activated.connect(self._on_shortcut_activated_settings)
self.setCurrentIndex(self.games_index)
@@ -126,7 +126,7 @@ def _on_shortcut_activated_settings(self):
@Slot()
def _on_update_available(self):
- self.main_bar.setTabText(self.settings_index, self.tr("Settings (!)"))
+ self.main_bar.setTabText(self.settings_index, self.tr('Settings (!)'))
@Slot()
@Slot(str)
@@ -151,10 +151,10 @@ def show_ubisoft(self):
@Slot(int)
def __on_downloads_update_title(self, num_downloads: int):
- suffix = "" if not num_downloads else f" ({num_downloads})"
+ suffix = '' if not num_downloads else f' ({num_downloads})'
self.setTabText(
self.indexOf(self.downloads_tab),
- self.tr("Downloads") + suffix,
+ self.tr('Downloads') + suffix,
)
def mouse_clicked(self, index):
@@ -173,16 +173,16 @@ def _on_exit_app(self, exit_code: int):
if self.downloads_tab.is_download_active:
QMessageBox.warning(
self,
- self.tr("Quit") if exit_code == ExitCodes.EXIT else self.tr("Logout"),
- self.tr("There are active downloads. Stop them before trying to quit."),
+ self.tr('Quit') if exit_code == ExitCodes.EXIT else self.tr('Logout'),
+ self.tr('There are active downloads. Stop them before trying to quit.'),
)
return
# FIXME: End of FIXME
if exit_code == 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")),
+ self.tr('Logout'),
+ self.tr('Do you really want to logout {}?').format(self.core.lgd.userdata.get('display_name')),
buttons=(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No),
defaultButton=QMessageBox.StandardButton.No,
)
diff --git a/rare/components/tabs/account/__init__.py b/rare/components/tabs/account/__init__.py
index 94326552b4..a3fe30a86a 100644
--- a/rare/components/tabs/account/__init__.py
+++ b/rare/components/tabs/account/__init__.py
@@ -18,23 +18,23 @@ def __init__(self, signals: GlobalSignals, core: LegendaryCore, parent):
self.signals = signals
self.core = core
- username = self.core.lgd.userdata.get("displayName")
+ username = self.core.lgd.userdata.get('displayName')
if not username:
- username = "Offline"
+ username = 'Offline'
self.open_browser = QPushButton(
- qta_icon("fa.external-link", "fa5s.external-link-alt"),
- self.tr("Account settings"),
+ qta_icon('fa.external-link', 'fa5s.external-link-alt'),
+ self.tr('Account settings'),
)
self.open_browser.clicked.connect(self._on_browser_clicked)
- self.logout_button = QPushButton(self.tr("Logout"), parent=self)
+ 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 = QPushButton(self.tr('Quit'), parent=self)
self.quit_button.clicked.connect(self._on_quit)
layout = QVBoxLayout(self)
- layout.addWidget(QLabel(self.tr("Logged in as {}").format(username)))
+ layout.addWidget(QLabel(self.tr('Logged in as {}').format(username)))
vspacer = QSpacerItem(10, 10, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
layout.addSpacerItem(vspacer)
layout.addWidget(self.open_browser)
@@ -43,7 +43,7 @@ def __init__(self, signals: GlobalSignals, core: LegendaryCore, parent):
@Slot()
def _on_browser_clicked(self):
- webbrowser.open("https://www.epicgames.com/account/personal?productName=epicgames")
+ webbrowser.open('https://www.epicgames.com/account/personal?productName=epicgames')
@Slot()
def _on_quit(self):
diff --git a/rare/components/tabs/downloads/__init__.py b/rare/components/tabs/downloads/__init__.py
index 42fb2ee7fc..b445599ea4 100644
--- a/rare/components/tabs/downloads/__init__.py
+++ b/rare/components/tabs/downloads/__init__.py
@@ -2,7 +2,6 @@
import platform
from copy import deepcopy
from logging import getLogger
-from typing import Optional, Union
from PySide6.QtCore import Qt, QThreadPool, Signal, Slot
from PySide6.QtGui import QPixmap
@@ -35,7 +34,7 @@
from .thread import DlResultCode, DlResultModel, DlThread
-def get_time(seconds: Union[int, float]) -> str:
+def get_time(seconds: int | float) -> str:
return str(datetime.timedelta(seconds=seconds))
@@ -53,7 +52,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.signals = rcore.signals()
self.args = rcore.args()
- self.__thread: Optional[DlThread] = None
+ self.__thread: DlThread | None = None
layout = QVBoxLayout(self)
@@ -95,7 +94,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.signals.download.enqueue.connect(self.__add_update)
self.signals.download.dequeue.connect(self.__remove_update)
- self.__forced_item: Optional[InstallQueueItemModel] = None
+ self.__forced_item: InstallQueueItemModel | None = None
self.__omit_requeue = False
@Slot()
@@ -115,7 +114,7 @@ def __check_updates(self):
@Slot(str)
@Slot(RareGame)
@Slot(RareEosOverlay)
- def __add_update(self, update: Union[str, RareGame, RareEosOverlay]):
+ def __add_update(self, update: str | RareGame | RareEosOverlay):
if isinstance(update, str):
update = self.rcore.get_game(update)
@@ -186,19 +185,19 @@ def __refresh_download(self, item: InstallQueueItemModel):
(lambda obj, d: obj.start_download(InstallQueueItemModel(options=item.options, download=d))).__get__(self)
)
worker.signals.failed.connect(
- (lambda obj, m: obj.logger.error(f"Failed to refresh download for {item.options.app_name} with error: {m}")).__get__(
+ (lambda obj, m: obj.logger.error(f'Failed to refresh download for {item.options.app_name} with error: {m}')).__get__(
self
)
)
worker.signals.finished.connect(
- (lambda obj: obj.logger.info(f"Download refresh worker finished for {item.options.app_name}")).__get__(self)
+ (lambda obj: obj.logger.info(f'Download refresh worker finished for {item.options.app_name}')).__get__(self)
)
QThreadPool.globalInstance().start(worker)
def start_download(self, item: InstallQueueItemModel):
rgame = self.rcore.get_game(item.options.app_name)
- if not rgame.state == RareGame.State.DOWNLOADING:
+ if rgame.state != RareGame.State.DOWNLOADING:
self.logger.error(
f"Can't start download {item.options.app_name} due to incompatible state {RareGame.State(rgame.state).name}"
)
@@ -218,25 +217,25 @@ def start_download(self, item: InstallQueueItemModel):
self.download_widget.setPixmap(self.rcore.image_manager().get_pixmap(rgame.app_name, ImageSize.Wide, True))
self.signals.application.notify.emit(
- self.tr("Downloads"),
+ self.tr('Downloads'),
self.tr('Starting: "{}" is now downloading.').format(rgame.app_title),
)
@Slot(UIUpdate, object)
def __on_download_progress(self, ui_update: UIUpdate, dl_size: int):
self.download_widget.ui.progress_bar.setValue(int(ui_update.progress))
- self.download_widget.ui.dl_speed.setText(f"{format_size(ui_update.download_compressed_speed)}/s")
+ self.download_widget.ui.dl_speed.setText(f'{format_size(ui_update.download_compressed_speed)}/s')
self.download_widget.ui.cache_used.setText(
- f"{format_size(ui_update.cache_usage) if ui_update.cache_usage > 1023 else '0KB'}"
+ f'{format_size(ui_update.cache_usage) if ui_update.cache_usage > 1023 else "0KB"}'
)
- self.download_widget.ui.downloaded.setText(f"{format_size(ui_update.total_downloaded)} / {format_size(dl_size)}")
+ self.download_widget.ui.downloaded.setText(f'{format_size(ui_update.total_downloaded)} / {format_size(dl_size)}')
self.download_widget.ui.time_left.setText(get_time(ui_update.estimated_time_left))
def __requeue_download(self, item: InstallQueueItemModel):
rgame = self.rcore.get_game(item.options.app_name)
rgame.state = RareGame.State.DOWNLOADING
self.queue_group.push_front(item, rgame.igame)
- self.logger.info(f"Re-queued download for {rgame.app_name} ({rgame.app_title})")
+ self.logger.info(f'Re-queued download for {rgame.app_name} ({rgame.app_title})')
@Slot(DlResultModel)
def __on_download_result(self, result: DlResultModel):
@@ -245,21 +244,21 @@ def __on_download_result(self, result: DlResultModel):
self.__thread.deleteLater()
if result.code == DlResultCode.FINISHED:
- self.logger.info(f"Download finished: {result.options.app_name}")
+ self.logger.info(f'Download finished: {result.options.app_name}')
if result.shortcut and desktop_links_supported():
if not create_desktop_link(
app_name=result.options.app_name,
app_title=result.app_title,
link_name=result.folder_name,
- link_type="desktop",
+ link_type='desktop',
):
# maybe add it to download summary, to show in finished downloads
- self.logger.error(f"Failed to create desktop link on {platform.system()}")
+ self.logger.error(f'Failed to create desktop link on {platform.system()}')
else:
- self.logger.info(f"Created desktop link {result.folder_name} for {result.app_title}")
+ self.logger.info(f'Created desktop link {result.folder_name} for {result.app_title}')
self.signals.application.notify.emit(
- self.tr("Downloads"),
+ self.tr('Downloads'),
self.tr('Finished: "{}" is now playable.').format(result.app_title),
)
@@ -267,16 +266,16 @@ def __on_download_result(self, result: DlResultModel):
self.updates_group.set_widget_enabled(result.options.app_name, True)
elif result.code == DlResultCode.ERROR:
- self.logger.error(f"Download error: {result.options.app_name} ({result.message})")
+ self.logger.error(f'Download error: {result.options.app_name} ({result.message})')
QMessageBox.warning(
self,
- self.tr("Error - {}").format(result.app_title),
- self.tr("Download error: {}").format(result.message),
+ self.tr('Error - {}').format(result.app_title),
+ self.tr('Download error: {}').format(result.message),
QMessageBox.StandardButton.Close,
)
elif result.code == DlResultCode.STOPPED:
- self.logger.info(f"Download stopped: {result.options.app_name}")
+ self.logger.info(f'Download stopped: {result.options.app_name}')
if not self.__omit_requeue:
self.__requeue_download(InstallQueueItemModel(options=result.options))
else:
@@ -298,12 +297,12 @@ def __reset_download(self):
self.__thread = None
self.download_widget.setPixmap(QPixmap())
self.download_widget.ui.kill_button.setDisabled(True)
- self.download_widget.ui.dl_name.setText(self.tr("No active download"))
+ self.download_widget.ui.dl_name.setText(self.tr('No active download'))
self.download_widget.ui.progress_bar.setValue(0)
- self.download_widget.ui.dl_speed.setText("...")
- self.download_widget.ui.time_left.setText("...")
- self.download_widget.ui.cache_used.setText("...")
- self.download_widget.ui.downloaded.setText("...")
+ self.download_widget.ui.dl_speed.setText('...')
+ self.download_widget.ui.time_left.setText('...')
+ self.download_widget.ui.cache_used.setText('...')
+ self.download_widget.ui.downloaded.setText('...')
self.update_queues_count()
@Slot(InstallOptionsModel)
@@ -377,7 +376,7 @@ def __on_uninstall_worker_result(self, rgame: RareGame, success: bool, message:
else:
QMessageBox.warning(
None,
- self.tr("Uninstall - {}").format(rgame.app_title),
+ self.tr('Uninstall - {}').format(rgame.app_title),
message,
QMessageBox.StandardButton.Close,
)
diff --git a/rare/components/tabs/downloads/groups.py b/rare/components/tabs/downloads/groups.py
index 6fd185e993..247a361283 100644
--- a/rare/components/tabs/downloads/groups.py
+++ b/rare/components/tabs/downloads/groups.py
@@ -1,7 +1,6 @@
from collections import deque
from enum import IntEnum
from logging import getLogger
-from typing import Deque, Optional
from legendary.models.game import Game, InstalledGame
from PySide6.QtCore import Qt, Signal, Slot
@@ -19,7 +18,7 @@
from rare.shared.image_manager import ImageManager
from rare.utils.misc import widget_object_name
-logger = getLogger("QueueGroup")
+logger = getLogger('QueueGroup')
class UpdateGroup(QGroupBox):
@@ -29,8 +28,8 @@ class UpdateGroup(QGroupBox):
def __init__(self, imgmgr: ImageManager, parent=None):
super(UpdateGroup, self).__init__(parent=parent)
self.setObjectName(type(self).__name__)
- self.setTitle(self.tr("Updates"))
- self.__text = QLabel(self.tr("No updates available"))
+ self.setTitle(self.tr('Updates'))
+ self.__text = QLabel(self.tr('No updates available'))
self.__text.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
# lk: For findChildren to work, the update's layout has to be in a widget
@@ -45,7 +44,7 @@ def __init__(self, imgmgr: ImageManager, parent=None):
self.imgmgr = imgmgr
- def __find_widget(self, app_name: str) -> Optional[UpdateWidget]:
+ def __find_widget(self, app_name: str) -> UpdateWidget | None:
return self.__container.findChild(UpdateWidget, name=widget_object_name(UpdateWidget, app_name))
def count(self) -> int:
@@ -96,8 +95,8 @@ class QueueGroup(QGroupBox):
def __init__(self, core: LegendaryCore, imgmgr: ImageManager, parent=None):
super(QueueGroup, self).__init__(parent=parent)
self.setObjectName(type(self).__name__)
- self.setTitle(self.tr("Queue"))
- self.__text = QLabel(self.tr("No downloads in queue"), self)
+ self.setTitle(self.tr('Queue'))
+ self.__text = QLabel(self.tr('No downloads in queue'), self)
self.__text.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
# lk: For findChildren to work, the queue's layout has to be in a widget
@@ -112,9 +111,9 @@ def __init__(self, core: LegendaryCore, imgmgr: ImageManager, parent=None):
self.core = core
self.imgmgr = imgmgr
- self.__queue: Deque[str] = deque()
+ self.__queue: deque[str] = deque()
- def __find_widget(self, app_name: str) -> Optional[QueueWidget]:
+ def __find_widget(self, app_name: str) -> QueueWidget | None:
return self.__container.findChild(QueueWidget, name=widget_object_name(QueueWidget, app_name))
def count(self) -> int:
diff --git a/rare/components/tabs/downloads/thread.py b/rare/components/tabs/downloads/thread.py
index 85353de2c3..25a669f144 100644
--- a/rare/components/tabs/downloads/thread.py
+++ b/rare/components/tabs/downloads/thread.py
@@ -1,13 +1,11 @@
-import multiprocessing
+import contextlib
import os
import platform
import queue
-import threading
import time
from dataclasses import dataclass
from enum import IntEnum
from logging import getLogger
-from typing import Dict, List, Optional
from PySide6.QtCore import QProcess, QThread, Signal
@@ -29,13 +27,13 @@ class DlResultCode(IntEnum):
class DlResultModel:
options: InstallOptionsModel
code: DlResultCode = DlResultCode.ERROR
- message: str = ""
- dlcs: Optional[List[Dict]] = None
+ message: str = ''
+ dlcs: list[dict] | None = None
sync_saves: bool = False
- tip_url: str = ""
+ tip_url: str = ''
shortcut: bool = False
- folder_name: str = ""
- app_title: str = ""
+ folder_name: str = ''
+ app_title: str = ''
class DlThread(QThread):
@@ -85,33 +83,29 @@ def run(self):
self.rgame.signals.progress.start.emit()
time.sleep(1)
while self.item.download.dlm.is_alive():
- try:
+ with contextlib.suppress(queue.Empty):
self._status_callback(self.item.download.dlm.status_queue.get(timeout=1.0))
- except queue.Empty:
- pass
if self.dlm_signals.update:
- try:
+ with contextlib.suppress(queue.Full):
self.item.download.dlm.signals_queue.put(self.dlm_signals, block=False, timeout=1.0)
- except queue.Full:
- pass
time.sleep(self.item.download.dlm.update_interval / 10)
self.item.download.dlm.join()
except Exception as e:
self.kill()
self.item.download.dlm.join()
end_t = time.time()
- self.logger.error(f"Installation failed after {end_t - start_t:.02f} seconds.")
- self.logger.warning(f"The following exception occurred while waiting for the downloader to finish: {e!r}.")
+ self.logger.error(f'Installation failed after {end_t - start_t:.02f} seconds.')
+ self.logger.warning(f'The following exception occurred while waiting for the downloader to finish: {e!r}.')
result.code = DlResultCode.ERROR
- result.message = f"{e!r}"
+ result.message = f'{e!r}'
return
else:
end_t = time.time()
if self.dlm_signals.kill:
- self.logger.info(f"Download stopped after {end_t - start_t:.02f} seconds.")
+ self.logger.info(f'Download stopped after {end_t - start_t:.02f} seconds.')
result.code = DlResultCode.STOPPED
return
- self.logger.info(f"Download finished in {end_t - start_t:.02f} seconds.")
+ self.logger.info(f'Download finished in {end_t - start_t:.02f} seconds.')
result.code = DlResultCode.FINISHED
@@ -135,9 +129,9 @@ def run(self):
result.dlcs = []
result.dlcs.extend(
{
- "app_name": dlc.app_name,
- "app_title": dlc.app_title,
- "app_version": dlc.app_version(self.item.options.platform),
+ 'app_name': dlc.app_name,
+ 'app_title': dlc.app_title,
+ 'app_version': dlc.app_version(self.item.options.platform),
}
for dlc in dlcs
)
@@ -165,27 +159,27 @@ def run(self):
self._finish(result)
def _handle_postinstall(self, postinstall, igame):
- self.logger.info("This game lists the following prerequisites to be installed:")
- self.logger.info(f"- {postinstall['name']}: {' '.join((postinstall['path'], postinstall['args']))}")
- if platform.system() == "Windows":
+ self.logger.info('This game lists the following prerequisites to be installed:')
+ self.logger.info(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
+ if platform.system() == 'Windows':
if self.item.options.install_prereqs:
- self.logger.info("Launching prerequisite executable..")
+ self.logger.info('Launching prerequisite executable..')
self.core.prereq_installed(igame.app_name)
- req_path, req_exec = os.path.split(postinstall["path"])
+ req_path, req_exec = os.path.split(postinstall['path'])
work_dir = os.path.join(igame.install_path, req_path)
fullpath = os.path.join(work_dir, req_exec)
proc = QProcess(self)
proc.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels)
proc.readyReadStandardOutput.connect(
- (lambda obj: obj.logger.debug(str(proc.readAllStandardOutput().data(), "utf-8", "ignore"))).__get__(self)
+ (lambda obj: obj.logger.debug(str(proc.readAllStandardOutput().data(), 'utf-8', 'ignore'))).__get__(self)
)
proc.setProgram(fullpath)
- proc.setArguments(postinstall.get("args", "").split(" "))
+ proc.setArguments(postinstall.get('args', '').split(' '))
proc.setWorkingDirectory(work_dir)
proc.start()
proc.waitForFinished() # wait, because it is inside the thread
else:
- self.logger.info("Automatic installation not available on Linux.")
+ self.logger.info('Automatic installation not available on Linux.')
def kill(self):
self.dlm_signals.kill = True
diff --git a/rare/components/tabs/downloads/widgets.py b/rare/components/tabs/downloads/widgets.py
index afc7322394..2c042d9ee4 100644
--- a/rare/components/tabs/downloads/widgets.py
+++ b/rare/components/tabs/downloads/widgets.py
@@ -1,5 +1,4 @@
from logging import getLogger
-from typing import Optional
from legendary.models.downloading import AnalysisResult
from legendary.models.game import Game, InstalledGame
@@ -19,17 +18,17 @@
from rare.utils.misc import elide_text, format_size, qta_icon, widget_object_name
from rare.widgets.image_widget import ImageSize, ImageWidget
-logger = getLogger("DownloadWidgets")
+logger = getLogger('DownloadWidgets')
class QueueInfoWidget(QWidget):
def __init__(
self,
imgmgr: ImageManager,
- game: Optional[Game],
- igame: Optional[InstalledGame],
- analysis: Optional[AnalysisResult] = None,
- old_igame: Optional[InstalledGame] = None,
+ game: Game | None,
+ igame: InstalledGame | None,
+ analysis: AnalysisResult | None = None,
+ old_igame: InstalledGame | None = None,
parent=None,
):
super(QueueInfoWidget, self).__init__(parent=parent)
@@ -47,11 +46,11 @@ def __init__(
if game and igame:
self.update_information(game, igame, analysis, old_igame)
else:
- self.ui.title.setText("...")
- self.ui.remote_version.setText("...")
- self.ui.local_version.setText("...")
- self.ui.dl_size.setText("...")
- self.ui.install_size.setText("...")
+ self.ui.title.setText('...')
+ self.ui.remote_version.setText('...')
+ self.ui.local_version.setText('...')
+ self.ui.dl_size.setText('...')
+ self.ui.install_size.setText('...')
if old_igame:
self.ui.title.setText(old_igame.title)
@@ -66,8 +65,8 @@ def update_information(self, game, igame, analysis, old_igame):
)
)
self.ui.local_version.setText(elide_text(self.ui.local_version, igame.version))
- self.ui.dl_size.setText(format_size(analysis.dl_size) if analysis else "")
- self.ui.install_size.setText(format_size(analysis.install_size) if analysis else "")
+ self.ui.dl_size.setText(format_size(analysis.dl_size) if analysis else '')
+ self.ui.install_size.setText(format_size(analysis.install_size) if analysis else '')
self.image.setPixmap(self.image_manager.get_pixmap(game.app_name, ImageSize.LibraryIcon))
@@ -148,12 +147,12 @@ def __init__(
worker.signals.result.connect(self.__update_info)
worker.signals.failed.connect(
(
- lambda obj, m: obj.logger.error(f"Failed to requeue download for {item.options.app_name} with error: {m}")
+ lambda obj, m: obj.logger.error(f'Failed to requeue download for {item.options.app_name} with error: {m}')
).__get__(self)
)
worker.signals.failed.connect((lambda obj, m: obj.remove.emit(item.options.app_name)).__get__(self))
worker.signals.finished.connect(
- (lambda obj: obj.logger.error(f"Download requeue worker finished for {item.options.app_name}")).__get__(self)
+ (lambda obj: obj.logger.error(f'Download requeue worker finished for {item.options.app_name}')).__get__(self)
)
QThreadPool.globalInstance().start(worker)
self.info_widget = QueueInfoWidget(imgmgr, None, None, None, old_igame, parent=self)
@@ -172,10 +171,10 @@ def __init__(
self.old_igame = old_igame
self.item = item
- self.ui.move_up_button.setIcon(qta_icon("fa.arrow-up", "fa5s.arrow-up"))
+ self.ui.move_up_button.setIcon(qta_icon('fa.arrow-up', 'fa5s.arrow-up'))
self.ui.move_up_button.clicked.connect(self._on_move_up)
- self.ui.move_down_button.setIcon(qta_icon("fa.arrow-down", "fa5s.arrow-down"))
+ self.ui.move_down_button.setIcon(qta_icon('fa.arrow-down', 'fa5s.arrow-down'))
self.ui.move_down_button.clicked.connect(self._on_move_down)
self.ui.remove_button.clicked.connect(self._on_remove)
diff --git a/rare/components/tabs/integrations/__init__.py b/rare/components/tabs/integrations/__init__.py
index db3b020a81..45b7ad115d 100644
--- a/rare/components/tabs/integrations/__init__.py
+++ b/rare/components/tabs/integrations/__init__.py
@@ -1,5 +1,3 @@
-from typing import Optional
-
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QLabel, QSizePolicy, QVBoxLayout, QWidget
@@ -18,34 +16,34 @@ def __init__(self, rcore: RareCore, parent=None):
self.import_group = ImportGroup(rcore, self)
self.import_widget = IntegrationsWidget(
self.import_group,
- self.tr("To import games from Epic Games Store, please enable EGL Sync."),
+ self.tr('To import games from Epic Games Store, please enable EGL Sync.'),
self,
)
- self.import_index = self.addTab(self.import_widget, self.tr("Import Games"))
+ self.import_index = self.addTab(self.import_widget, self.tr('Import Games'))
self.egl_sync_group = EGLSyncGroup(rcore, self)
self.egl_sync_widget = IntegrationsWidget(
self.egl_sync_group,
- self.tr("To import EGL games from directories, please use Import Game."),
+ self.tr('To import EGL games from directories, please use Import Game.'),
self,
)
- self.egl_sync_index = self.addTab(self.egl_sync_widget, self.tr("Sync with EGL"))
+ self.egl_sync_index = self.addTab(self.egl_sync_widget, self.tr('Sync with EGL'))
self.eos_group = EosGroup(rcore, self)
self.eos_widget = IntegrationsWidget(
self.eos_group,
- self.tr(""),
+ self.tr(''),
self,
)
- self.eos_index = self.addTab(self.eos_widget, self.tr("Epic Overlay"))
+ self.eos_index = self.addTab(self.eos_widget, self.tr('Epic Overlay'))
self.ubisoft_group = UbisoftGroup(rcore, self)
self.ubisoft_widget = IntegrationsWidget(
self.ubisoft_group,
- self.tr(""),
+ self.tr(''),
self,
)
- self.ubisoft_index = self.addTab(self.ubisoft_widget, self.tr("Ubisoft Link"))
+ self.ubisoft_index = self.addTab(self.ubisoft_widget, self.tr('Ubisoft Link'))
self.setCurrentIndex(self.import_index)
@@ -64,9 +62,9 @@ def show_ubisoft(self):
class IntegrationsWidget(QWidget):
- def __init__(self, widget: Optional[QWidget], info: str, parent=None):
+ def __init__(self, widget: QWidget | None, info: str, parent=None):
super(IntegrationsWidget, self).__init__(parent=parent)
- self.info = QLabel(f"{info}")
+ self.info = QLabel(f'{info}')
self.__layout = QVBoxLayout(self)
if widget is not None:
diff --git a/rare/components/tabs/integrations/egl_sync_group.py b/rare/components/tabs/integrations/egl_sync_group.py
index 341ab08cf5..516a4b71a7 100644
--- a/rare/components/tabs/integrations/egl_sync_group.py
+++ b/rare/components/tabs/integrations/egl_sync_group.py
@@ -1,8 +1,8 @@
import os
import platform
from abc import abstractmethod
+from collections.abc import Iterable
from logging import getLogger
-from typing import Iterable, List, Tuple, Union
from legendary.models.egl import EGLManifest
from legendary.models.game import InstalledGame
@@ -29,7 +29,7 @@
from rare.widgets.elide_label import ElideLabel
from rare.widgets.indicator_edit import IndicatorReasonsCommon, PathEdit
-logger = getLogger("EGLSync")
+logger = getLogger('EGLSync')
class EGLSyncGroup(QGroupBox):
@@ -42,7 +42,7 @@ def __init__(self, rcore: RareCore, parent=None):
self.egl_path_edit = PathEdit(
path=self.core.egl.programdata_path,
- placeholder=self.tr("Path to the Wine prefix where EGL is installed, or the Manifests folder"),
+ placeholder=self.tr('Path to the Wine prefix where EGL is installed, or the Manifests folder'),
file_mode=QFileDialog.FileMode.Directory,
edit_func=self.egl_path_edit_edit_cb,
save_func=self.egl_path_edit_save_cb,
@@ -62,7 +62,7 @@ def __init__(self, rcore: RareCore, parent=None):
self.egl_path_info,
)
- if platform.system() == "Windows":
+ if platform.system() == 'Windows':
self.ui.egl_path_edit_label.setEnabled(False)
self.egl_path_edit.setEnabled(False)
self.ui.egl_path_info_label.setEnabled(False)
@@ -97,8 +97,8 @@ def showEvent(self, a0: QShowEvent) -> None:
super().showEvent(a0)
def __run_wine_resolver(self):
- self.egl_path_info.setText(self.tr("Updating..."))
- wine_resolver = WinePathResolver(self.core, "default", str(PathSpec.egl_programdata()))
+ self.egl_path_info.setText(self.tr('Updating...'))
+ wine_resolver = WinePathResolver(self.core, 'default', str(PathSpec.egl_programdata()))
wine_resolver.signals.result_ready.connect(self.__on_wine_resolver_result)
QThreadPool.globalInstance().start(wine_resolver)
@@ -106,26 +106,26 @@ def __on_wine_resolver_result(self, path):
self.egl_path_info.setText(path)
if not path:
self.egl_path_info.setText(
- self.tr("Default Wine prefix is unset, or path does not exist. Create it or configure it in Settings -> Linux.")
+ self.tr('Default Wine prefix is unset, or path does not exist. Create it or configure it in Settings -> Linux.')
)
elif not os.path.exists(path):
self.egl_path_info.setText(
self.tr(
- "Default Wine prefix is set but EGL manifests path does not exist. "
- "Your configured default Wine prefix might not be where EGL is installed."
+ 'Default Wine prefix is set but EGL manifests path does not exist. '
+ 'Your configured default Wine prefix might not be where EGL is installed.'
)
)
else:
self.egl_path_edit.setText(path)
@staticmethod
- def egl_path_edit_edit_cb(path) -> Tuple[bool, str, int]:
+ def egl_path_edit_edit_cb(path) -> tuple[bool, str, int]:
if not path:
return True, path, IndicatorReasonsCommon.VALID
- if os.path.exists(os.path.join(path, "system.reg")) and os.path.exists(os.path.join(path, "dosdevices/c:")):
+ if os.path.exists(os.path.join(path, 'system.reg')) and os.path.exists(os.path.join(path, 'dosdevices/c:')):
# path is a wine prefix
path = PathSpec.prefix_egl_programdata(path)
- elif not path.rstrip("/").endswith(PathSpec.wine_egl_programdata()):
+ elif not path.rstrip('/').endswith(PathSpec.wine_egl_programdata()):
# lower() might or might not be needed in the check
return False, path, IndicatorReasonsCommon.WRONG_FORMAT
if os.path.exists(path):
@@ -136,15 +136,15 @@ def egl_path_edit_save_cb(self, path):
if not path or not os.path.exists(path):
# This is the same as "--unlink"
self.core.egl.programdata_path = None
- self.core.lgd.config.remove_option("Legendary", "egl_programdata")
- self.core.lgd.config.remove_option("Legendary", "egl_sync")
+ self.core.lgd.config.remove_option('Legendary', 'egl_programdata')
+ self.core.lgd.config.remove_option('Legendary', 'egl_sync')
# remove EGL GUIDs from all games, DO NOT remove .egstore folders because that would fuck things up.
for igame in self.core.get_installed_list():
- igame.egl_guid = ""
+ igame.egl_guid = ''
self.core.install_game(igame)
else:
self.core.egl.programdata_path = path
- self.core.lgd.config.set("Legendary", "egl_programdata", path)
+ self.core.lgd.config.set('Legendary', 'egl_programdata', path)
self.core.lgd.save_config()
@@ -160,9 +160,9 @@ def egl_sync_changed(self, state):
if state == Qt.CheckState.Unchecked:
self.import_list.setEnabled(bool(self.import_list.items))
self.export_list.setEnabled(bool(self.export_list.items))
- self.core.lgd.config.remove_option("Legendary", "egl_sync")
+ self.core.lgd.config.remove_option('Legendary', 'egl_sync')
else:
- self.core.lgd.config.set("Legendary", "egl_sync", str(True))
+ self.core.lgd.config.set('Legendary', 'egl_sync', str(True))
# lk: do import/export here since automatic sync was selected
self.import_list.mark(Qt.CheckState.Checked)
self.export_list.mark(Qt.CheckState.Checked)
@@ -194,7 +194,7 @@ def update_lists(self):
class EGLSyncListItem(QListWidgetItem):
- def __init__(self, core: LegendaryCore, game: Union[EGLManifest, InstalledGame]):
+ def __init__(self, core: LegendaryCore, game: EGLManifest | InstalledGame):
super(EGLSyncListItem, self).__init__()
self.core = core
self.game = game
@@ -207,7 +207,7 @@ def is_checked(self) -> bool:
return self.checkState() == Qt.CheckState.Checked
@abstractmethod
- def action(self) -> Union[str, bool]:
+ def action(self) -> str | bool:
pass
@property
@@ -224,7 +224,7 @@ class EGLSyncExportItem(EGLSyncListItem):
def __init__(self, core: LegendaryCore, game: InstalledGame):
super(EGLSyncExportItem, self).__init__(core, game=game)
- def action(self) -> Union[str, bool]:
+ def action(self) -> str | bool:
error = False
try:
self.core.egl_export(self.game.app_name)
@@ -241,7 +241,7 @@ class EGLSyncImportItem(EGLSyncListItem):
def __init__(self, core: LegendaryCore, game: EGLManifest):
super(EGLSyncImportItem, self).__init__(core, game=game)
- def action(self) -> Union[str, bool]:
+ def action(self) -> str | bool:
error = False
try:
self.core.egl_import(self.game.app_name)
@@ -313,7 +313,7 @@ def action(self):
@Slot(list)
@abstractmethod
- def show_errors(self, errors: List):
+ def show_errors(self, errors: list):
pass
@property
@@ -327,9 +327,9 @@ def items(self) -> Iterable[EGLSyncListItem]:
class EGLSyncExportGroup(EGLSyncListGroup):
def __init__(self, rcore: RareCore, parent=None):
super(EGLSyncExportGroup, self).__init__(rcore, parent=parent)
- self.setTitle(self.tr("Exportable games"))
- self.ui.label.setText(self.tr("No games to export to EGL"))
- self.ui.action_button.setText(self.tr("Export"))
+ self.setTitle(self.tr('Exportable games'))
+ self.ui.label.setText(self.tr('No games to export to EGL'))
+ self.ui.action_button.setText(self.tr('Export'))
def populate(self, enabled: bool):
if enabled:
@@ -339,7 +339,7 @@ def populate(self, enabled: bool):
i = EGLSyncExportItem(self.core, item)
except AttributeError as e:
logger.error(
- "%s(%s) constructor failed with %s",
+ '%s(%s) constructor failed with %s',
type(self).__name__,
item.app_name,
e,
@@ -349,15 +349,15 @@ def populate(self, enabled: bool):
super(EGLSyncExportGroup, self).populate(enabled)
@Slot(list)
- def show_errors(self, errors: List):
+ def show_errors(self, errors: list):
QMessageBox.warning(
self.parent(),
- self.tr("The following errors occurred while exporting."),
- "\n".join(errors),
+ self.tr('The following errors occurred while exporting.'),
+ '\n'.join(errors),
)
def action(self):
- errors: List = []
+ errors: list = []
for item in self.items:
if item.is_checked():
if e := item.action():
@@ -370,9 +370,9 @@ def action(self):
class EGLSyncImportGroup(EGLSyncListGroup):
def __init__(self, rcore: RareCore, parent=None):
super(EGLSyncImportGroup, self).__init__(rcore, parent=parent)
- self.setTitle(self.tr("Importable games"))
- self.ui.label.setText(self.tr("No games to import from EGL"))
- self.ui.action_button.setText(self.tr("Import"))
+ self.setTitle(self.tr('Importable games'))
+ self.ui.label.setText(self.tr('No games to import from EGL'))
+ self.ui.action_button.setText(self.tr('Import'))
def populate(self, enabled: bool):
if enabled:
@@ -382,7 +382,7 @@ def populate(self, enabled: bool):
i = EGLSyncImportItem(self.core, item)
except AttributeError as e:
logger.error(
- "%s(%s) constructor failed with %s",
+ '%s(%s) constructor failed with %s',
type(self).__name__,
item.app_name,
e,
@@ -392,15 +392,15 @@ def populate(self, enabled: bool):
super(EGLSyncImportGroup, self).populate(enabled)
@Slot(list)
- def show_errors(self, errors: List):
+ def show_errors(self, errors: list):
QMessageBox.warning(
self.parent(),
- self.tr("The following errors occurred while importing."),
- "\n".join(errors),
+ self.tr('The following errors occurred while importing.'),
+ '\n'.join(errors),
)
def action(self):
- errors: List = []
+ errors: list = []
for item in self.items:
if item.is_checked():
if e := item.action():
diff --git a/rare/components/tabs/integrations/eos_group.py b/rare/components/tabs/integrations/eos_group.py
index e8c3b2993c..95e7e1814a 100644
--- a/rare/components/tabs/integrations/eos_group.py
+++ b/rare/components/tabs/integrations/eos_group.py
@@ -1,7 +1,6 @@
import os
import platform
from logging import getLogger
-from typing import Optional
from PySide6.QtCore import (
QObject,
@@ -34,7 +33,7 @@
from rare.utils.misc import qta_icon, style_hyperlink
from rare.widgets.elide_label import ElideLabel
-logger = getLogger("EpicOverlay")
+logger = getLogger('EpicOverlay')
class CheckForUpdateWorkerSignals(QObject):
@@ -56,7 +55,7 @@ def run(self) -> None:
class EosPrefixWidget(QFrame):
- def __init__(self, overlay: RareEosOverlay, prefix: Optional[str], parent=None):
+ def __init__(self, overlay: RareEosOverlay, prefix: str | None, parent=None):
super(EosPrefixWidget, self).__init__(parent=parent)
self.setFrameShape(QFrame.Shape.StyledPanel)
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
@@ -65,7 +64,7 @@ def __init__(self, overlay: RareEosOverlay, prefix: Optional[str], parent=None):
self.indicator.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Preferred)
self.prefix_label = ElideLabel(
- prefix.replace(os.path.expanduser("~"), "~") if prefix is not None else overlay.app_title,
+ prefix.replace(os.path.expanduser('~'), '~') if prefix is not None else overlay.app_title,
parent=self,
)
self.prefix_label.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
@@ -102,41 +101,41 @@ def __init__(self, overlay: RareEosOverlay, prefix: Optional[str], parent=None):
@Slot(int)
def path_changed(self, index: int) -> None:
path = self.path_select.itemData(index, Qt.ItemDataRole.UserRole)
- active_path = os.path.normpath(p) if (p := self.overlay.active_path(self.prefix)) else ""
+ active_path = os.path.normpath(p) if (p := self.overlay.active_path(self.prefix)) else ''
if self.overlay.is_enabled(self.prefix) and (path == active_path):
- self.button.setText(self.tr("Disable overlay"))
+ self.button.setText(self.tr('Disable overlay'))
else:
- self.button.setText(self.tr("Enable overlay"))
+ self.button.setText(self.tr('Enable overlay'))
@Slot()
def update_state(self) -> None:
- active_path = os.path.normpath(p) if (p := self.overlay.active_path(self.prefix)) else ""
+ active_path = os.path.normpath(p) if (p := self.overlay.active_path(self.prefix)) else ''
- self.overlay_label.setText(f"{active_path}")
+ self.overlay_label.setText(f'{active_path}')
self.overlay_label.setVisible(bool(active_path))
self.path_select.clear()
if not self.overlay.is_installed and not self.overlay.available_paths(self.prefix):
self.setDisabled(True)
- self.indicator.setPixmap(qta_icon("fa.circle-o", "fa5.circle", color="grey").pixmap(20, 20))
+ self.indicator.setPixmap(qta_icon('fa.circle-o', 'fa5.circle', color='grey').pixmap(20, 20))
active_path = self.overlay.active_path(self.prefix)
- self.overlay_label.setText(f"{active_path}")
+ self.overlay_label.setText(f'{active_path}')
self.overlay_label.setVisible(bool(active_path))
- self.button.setText(self.tr("Unavailable"))
+ self.button.setText(self.tr('Unavailable'))
return
if self.overlay.is_enabled(self.prefix):
- self.indicator.setPixmap(qta_icon("fa.check-circle-o", "fa5.check-circle", color="green").pixmap(QSize(20, 20)))
+ self.indicator.setPixmap(qta_icon('fa.check-circle-o', 'fa5.check-circle', color='green').pixmap(QSize(20, 20)))
else:
- self.indicator.setPixmap(qta_icon("fa.times-circle-o", "fa5.times-circle", color="red").pixmap(QSize(20, 20)))
+ self.indicator.setPixmap(qta_icon('fa.times-circle-o', 'fa5.times-circle', color='red').pixmap(QSize(20, 20)))
- install_path = os.path.normpath(p) if (p := self.overlay.install_path) else ""
+ install_path = os.path.normpath(p) if (p := self.overlay.install_path) else ''
- self.path_select.addItem("Auto-detect", "")
- self.path_select.setItemData(0, "Auto-detect", Qt.ItemDataRole.ToolTipRole)
+ self.path_select.addItem('Auto-detect', '')
+ self.path_select.setItemData(0, 'Auto-detect', Qt.ItemDataRole.ToolTipRole)
for path in self.overlay.available_paths(self.prefix):
path = os.path.normpath(path)
- self.path_select.addItem("Legendary-managed" if path == install_path else "EGL-managed", path)
+ self.path_select.addItem('Legendary-managed' if path == install_path else 'EGL-managed', path)
self.path_select.setItemData(self.path_select.findData(path), path, Qt.ItemDataRole.ToolTipRole)
self.path_select.setCurrentIndex(self.path_select.findData(active_path))
@@ -145,17 +144,17 @@ def update_state(self) -> None:
@Slot()
def action(self) -> None:
path = self.path_select.currentData(Qt.ItemDataRole.UserRole)
- active_path = os.path.normpath(p) if (p := self.overlay.active_path(self.prefix)) else ""
- install_path = os.path.normpath(p) if (p := self.overlay.install_path) else ""
+ active_path = os.path.normpath(p) if (p := self.overlay.active_path(self.prefix)) else ''
+ install_path = os.path.normpath(p) if (p := self.overlay.install_path) else ''
if self.overlay.is_enabled(self.prefix) and (path == active_path):
if not self.overlay.disable(prefix=self.prefix):
QMessageBox.warning(
self,
- "Warning",
- self.tr("Failed to completely disable the active EOS Overlay.{}").format(
- self.tr(" Since the previous overlay was managed by EGL you can safely ignore this is.")
+ 'Warning',
+ self.tr('Failed to completely disable the active EOS Overlay.{}').format(
+ self.tr(' Since the previous overlay was managed by EGL you can safely ignore this is.')
if active_path != install_path
- else ""
+ else ''
),
)
else:
@@ -163,11 +162,11 @@ def action(self) -> None:
if not self.overlay.enable(prefix=self.prefix, path=path):
QMessageBox.warning(
self,
- "Warning",
- self.tr("Failed to completely enable EOS overlay.{}").format(
- self.tr(" Since the previous overlay was managed by EGL you can safely ignore this is.")
+ 'Warning',
+ self.tr('Failed to completely enable EOS overlay.{}').format(
+ self.tr(' Since the previous overlay was managed by EGL you can safely ignore this is.')
if active_path != install_path
- else ""
+ else ''
),
)
self.update_state()
@@ -184,19 +183,19 @@ def __init__(self, rcore: RareCore, parent=None):
self.ui = Ui_EosWidget()
self.ui.setupUi(self)
# lk: set object names for CSS properties
- self.ui.install_button.setObjectName("InstallButton")
- self.ui.uninstall_button.setObjectName("UninstallButton")
+ self.ui.install_button.setObjectName('InstallButton')
+ self.ui.uninstall_button.setObjectName('UninstallButton')
self.ui.install_page_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
self.ui.update_page_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
- self.ui.install_button.setIcon(qta_icon("ri.install-line"))
- self.ui.update_button.setIcon(qta_icon("ri.arrow-up-circle-line"))
- self.ui.uninstall_button.setIcon(qta_icon("ri.uninstall-line"))
+ self.ui.install_button.setIcon(qta_icon('ri.install-line'))
+ self.ui.update_button.setIcon(qta_icon('ri.arrow-up-circle-line'))
+ self.ui.uninstall_button.setIcon(qta_icon('ri.uninstall-line'))
self.version = ElideLabel(parent=self)
self.install_path = QLabel(parent=self)
- self.install_path.setObjectName("LinkLabel")
+ self.install_path.setObjectName('LinkLabel')
self.install_path.setOpenExternalLinks(True)
self.ui.info_layout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.version)
@@ -211,7 +210,7 @@ def __init__(self, rcore: RareCore, parent=None):
self.ui.uninstall_button.clicked.connect(self.uninstall_overlay)
self.threadpool = QThreadPool.globalInstance()
- self.worker: Optional[CheckForUpdateWorker] = None
+ self.worker: CheckForUpdateWorker | None = None
def showEvent(self, e: QShowEvent) -> None:
if e.spontaneous():
@@ -232,10 +231,10 @@ def hideEvent(self, e: QHideEvent, /):
@Slot()
def update_state(self):
if self.overlay.is_installed: # installed
- self.version.setText(f"{self.overlay.version}")
+ self.version.setText(f'{self.overlay.version}')
self.ui.button_stack.setCurrentWidget(self.ui.update_page)
else:
- self.version.setText(self.tr("Epic Online Services Overlay is not installed"))
+ self.version.setText(self.tr('Epic Online Services Overlay is not installed'))
self.ui.button_stack.setCurrentWidget(self.ui.install_page)
self.install_path.setEnabled(self.overlay.is_installed)
self.install_path.setText(
@@ -244,7 +243,7 @@ def update_state(self):
self.overlay.install_path,
)
if self.overlay.is_installed
- else "N/A"
+ else 'N/A'
)
self.ui.install_button.setEnabled(self.overlay.state == RareEosOverlay.State.IDLE)
@@ -256,16 +255,16 @@ def update_prefixes(self):
widget.disconnect(widget)
widget.deleteLater()
- if platform.system() != "Windows":
+ if platform.system() != 'Windows':
prefixes = config.get_prefixes()
prefixes = sorted({prefix for prefix, _ in prefixes})
- if platform.system() == "Darwin":
+ if platform.system() == 'Darwin':
# TODO: add crossover support
pass
for prefix in prefixes:
widget = EosPrefixWidget(self.overlay, prefix)
self.ui.eos_layout.addWidget(widget)
- logger.debug("Updated prefixes")
+ logger.debug('Updated prefixes')
else:
widget = EosPrefixWidget(self.overlay, None)
self.ui.eos_layout.addWidget(widget)
@@ -290,11 +289,11 @@ def check_for_update(self):
@Slot()
def install_finished(self):
if not self.overlay.is_installed:
- logger.error("Something went wrong while installing EOS Overlay")
+ logger.error('Something went wrong while installing EOS Overlay')
QMessageBox.warning(
self,
- "Error",
- self.tr("Something went wrong while installing EOS Overlay"),
+ 'Error',
+ self.tr('Something went wrong while installing EOS Overlay'),
QMessageBox.StandardButton.Close,
)
return
@@ -310,7 +309,7 @@ def install_overlay(self):
def uninstall_overlay(self):
if not self.overlay.is_installed:
- logger.error("No Legendary-managed EOS Overlay installation found.")
+ logger.error('No Legendary-managed EOS Overlay installation found.')
self.ui.button_stack.setCurrentWidget(self.ui.install_page)
return
self.overlay.uninstall()
diff --git a/rare/components/tabs/integrations/import_group.py b/rare/components/tabs/integrations/import_group.py
index e8cb9689e8..3e3b0301f2 100644
--- a/rare/components/tabs/integrations/import_group.py
+++ b/rare/components/tabs/integrations/import_group.py
@@ -4,7 +4,6 @@
from enum import IntEnum
from logging import getLogger
from pathlib import Path
-from typing import Dict, List, Optional, Set, Tuple
from PySide6.QtCore import QObject, QRunnable, Qt, QThreadPool, Signal, Slot
from PySide6.QtGui import QShowEvent
@@ -32,15 +31,15 @@
PathEdit,
)
-logger = getLogger("Import")
+logger = getLogger('Import')
-def find_app_name(core, path: str) -> Optional[str]:
- if os.path.exists(os.path.join(path, ".egstore")):
- for i in os.listdir(os.path.join(path, ".egstore")):
- if i.endswith(".mancpn"):
- with open(os.path.join(path, ".egstore", i)) as file:
- app_name = json.load(file).get("AppName")
+def find_app_name(core, path: str) -> str | None:
+ if os.path.exists(os.path.join(path, '.egstore')):
+ for i in os.listdir(os.path.join(path, '.egstore')):
+ if i.endswith('.mancpn'):
+ with open(os.path.join(path, '.egstore', i)) as file:
+ app_name = json.load(file).get('AppName')
return app_name
elif app_name := LegendaryCLI(core)._resolve_aliases(os.path.basename(os.path.normpath(path))):
# return None if game does not exist (Workaround for overlay)
@@ -48,7 +47,7 @@ def find_app_name(core, path: str) -> Optional[str]:
return None
return app_name
else:
- logger.warning(f"Could not find AppName for {path}")
+ logger.warning(f'Could not find AppName for {path}')
return None
@@ -61,10 +60,10 @@ class ImportResult(IntEnum):
@dataclass
class ImportedGame:
result: ImportResult
- path: Optional[str] = None
- app_name: Optional[str] = None
- app_title: Optional[str] = None
- message: Optional[str] = None
+ path: str | None = None
+ app_name: str | None = None
+ app_title: str | None = None
+ message: str | None = None
class ImportWorkerSignals(QObject):
@@ -78,7 +77,7 @@ def __init__(
core: LegendaryCore,
path: str,
app_name: str = None,
- platform: Optional[str] = None,
+ platform: str | None = None,
import_folder: bool = False,
import_dlcs: bool = False,
import_force: bool = False,
@@ -96,7 +95,7 @@ def __init__(
self.import_force = import_force
def run(self) -> None:
- result_list: List = []
+ result_list: list = []
if self.import_folder:
folders = [i for i in self.path.iterdir() if i.is_dir()]
number_of_folders = len(folders)
@@ -123,7 +122,7 @@ def _try_import(self, path: Path, app_name: str = None) -> ImportedGame:
result.app_title = game.app_title
platform = self.platform
if platform not in self.core.get_game(app_name, update_meta=False).asset_infos:
- platform = "Windows"
+ platform = 'Windows'
success, message = self._import_game(path, app_name, platform)
if not success:
result.result = ImportResult.FAILED
@@ -158,12 +157,12 @@ def __init__(self, rcore: RareCore, parent=None):
self.ui = Ui_ImportGroup()
self.ui.setupUi(self)
- self.worker: Optional[ImportWorker] = None
+ self.worker: ImportWorker | None = None
self.threadpool = QThreadPool.globalInstance()
- self._app_names: Dict[str, str] = {}
- self._app_titles: Dict[str, str] = {}
- self._install_dirs: Set[str] = set()
+ self._app_names: dict[str, str] = {}
+ self._app_titles: dict[str, str] = {}
+ self._install_dirs: set[str] = set()
self.import_path_edit = PathEdit(
path=self.core.get_default_install_dir(self.core.default_platform),
@@ -180,7 +179,7 @@ def __init__(self, rcore: RareCore, parent=None):
)
self.app_name_edit = IndicatorLineEdit(
- placeholder=self.tr("Use in case the app name was not found automatically"),
+ placeholder=self.tr('Use in case the app name was not found automatically'),
edit_func=self._app_name_edit_callback,
save_func=self._app_name_save_callback,
parent=self,
@@ -196,14 +195,14 @@ def __init__(self, rcore: RareCore, parent=None):
self.ui.import_dlcs_check.setEnabled(False)
self.ui.import_dlcs_check.checkStateChanged.connect(self._on_import_dlcs_changed)
- self.ui.import_button_label.setText("")
+ self.ui.import_button_label.setText('')
self.ui.import_button.setEnabled(False)
self.ui.import_button.clicked.connect(self._on_import_clicked)
self.button_info_stack = QStackedWidget(self)
self.button_info_stack.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.button_info_stack.setFixedHeight(self.ui.import_button.sizeHint().height())
- self.info_label = ElideLabel(text="", parent=self.button_info_stack)
+ self.info_label = ElideLabel(text='', parent=self.button_info_stack)
self.info_label.setFixedHeight(False)
self.info_label.setAlignment(Qt.AlignmentFlag.AlignVCenter)
self.info_progress = QProgressBar(self.button_info_stack)
@@ -234,32 +233,30 @@ def set_game(self, app_name: str):
)
self.app_name_edit.setText(app_name)
- def _path_edit_callback(self, path) -> Tuple[bool, str, int]:
+ def _path_edit_callback(self, path) -> tuple[bool, str, int]:
if not os.path.exists(path):
return False, path, IndicatorReasonsCommon.DIR_NOT_EXISTS
- if os.path.exists(os.path.join(path, ".egstore")):
- return True, path, IndicatorReasonsCommon.VALID
- elif os.path.basename(path) in self._install_dirs:
+ if os.path.exists(os.path.join(path, '.egstore')) or os.path.basename(path) in self._install_dirs:
return True, path, IndicatorReasonsCommon.VALID
return False, path, IndicatorReasonsCommon.INVALID
@Slot(str)
def _path_changed(self, path: str):
- self.info_label.setText("")
+ self.info_label.setText('')
self.ui.import_folder_check.setCheckState(Qt.CheckState.Unchecked)
self.ui.import_force_check.setCheckState(Qt.CheckState.Unchecked)
if self.import_path_edit.is_valid:
self.app_name_edit.setText(find_app_name(self.core, path))
else:
- self.app_name_edit.setText("")
+ self.app_name_edit.setText('')
- def _app_name_edit_callback(self, text) -> Tuple[bool, str, int]:
- self.app_name_edit.setInfo("")
+ def _app_name_edit_callback(self, text) -> tuple[bool, str, int]:
+ self.app_name_edit.setInfo('')
if not text:
return False, text, IndicatorReasonsCommon.UNDEFINED
- if text in self._app_names.keys():
+ if text in self._app_names:
return True, self._app_names[text], IndicatorReasonsCommon.VALID
- if text in self._app_titles.keys():
+ if text in self._app_titles:
return True, text, IndicatorReasonsCommon.VALID
else:
return False, text, IndicatorReasonsCommon.GAME_NOT_EXISTS
@@ -273,7 +270,7 @@ def _app_name_save_callback(self, text) -> None:
@Slot(str)
def _app_name_changed(self, app_name: str):
- self.info_label.setText("")
+ self.info_label.setText('')
self.ui.import_dlcs_check.setCheckState(Qt.CheckState.Unchecked)
self.ui.import_force_check.setCheckState(Qt.CheckState.Unchecked)
self.ui.import_dlcs_check.setEnabled(self.app_name_edit.is_valid and bool(self.core.get_dlc_for_game(app_name)))
@@ -287,12 +284,12 @@ def _on_import_folder_changed(self, state: Qt.CheckState):
self.ui.platform_combo.setEnabled(not state)
self.ui.platform_combo.setToolTip(
self.tr(
- "When importing multiple games, the current OS will be used at the"
- " platform for the games that support it, otherwise the Windows version"
- " will be imported."
+ 'When importing multiple games, the current OS will be used at the'
+ ' platform for the games that support it, otherwise the Windows version'
+ ' will be imported.'
)
if state != Qt.CheckState.Unchecked
- else ""
+ else ''
)
self.ui.import_dlcs_check.setCheckState(Qt.CheckState.Unchecked)
self.ui.import_force_check.setCheckState(Qt.CheckState.Unchecked)
@@ -316,9 +313,9 @@ def _on_import_clicked(self):
self._import(self.import_path_edit.text())
@Slot(str)
- def _import(self, path: Optional[str] = None):
+ def _import(self, path: str | None = None):
self.ui.import_button.setDisabled(True)
- self.info_label.setText(self.tr("Status: Importing games"))
+ self.info_label.setText(self.tr('Status: Importing games'))
self.info_progress.setValue(0)
self.button_info_stack.setCurrentWidget(self.info_progress)
@@ -342,32 +339,33 @@ def _on_import_progress(self, imported: ImportedGame, progress: int):
self.info_progress.setValue(progress)
if imported.result == ImportResult.SUCCESS:
self.rcore.get_game(imported.app_name).set_installed(True)
- status = "error" if not imported.result else ("failed" if imported.result == ImportResult.FAILED else "successful")
- logger.info(f"Import {status}: {imported.app_title}: {imported.path} ({imported.message})")
+ status = 'error' if not imported.result else ('failed' if imported.result == ImportResult.FAILED else 'successful')
+ logger.info(f'Import {status}: {imported.app_title}: {imported.path} ({imported.message})')
@Slot(list)
- def _on_import_result(self, result: List[ImportedGame]):
+ def _on_import_result(self, result: list[ImportedGame]):
self.worker = None
self.button_info_stack.setCurrentWidget(self.info_label)
if len(result) == 1:
res = result[0]
if res.result == ImportResult.SUCCESS:
- self.info_label.setText(self.tr("Success: {} imported").format(res.app_title))
+ self.info_label.setText(self.tr('Success: {} imported').format(res.app_title))
elif res.result == ImportResult.FAILED:
- self.info_label.setText(self.tr("Failed: {} - {}").format(res.app_title, res.message))
+ self.info_label.setText(self.tr('Failed: {} - {}').format(res.app_title, res.message))
else:
- self.info_label.setText(self.tr("Error: Could not find AppName for {}").format(res.path))
+ self.info_label.setText(self.tr('Error: Could not find AppName for {}').format(res.path))
else:
- self.info_label.setText(self.tr("Status: Finished importing games"))
+ self.info_label.setText(self.tr('Status: Finished importing games'))
success = [r for r in result if r.result == ImportResult.SUCCESS]
failure = [r for r in result if r.result == ImportResult.FAILED]
errored = [r for r in result if r.result == ImportResult.ERROR]
# pylint: disable=E1101
messagebox = QMessageBox(
QMessageBox.Icon.Information,
- self.tr("Import summary"),
+ self.tr('Import summary'),
self.tr(
- "Tried to import {} folders.\n\nSuccessfully imported {} games, failed to import {} games and {} errors occurred"
+ 'Tried to import {} folders.\n\n'
+ 'Successfully imported {} games, failed to import {} games and {} errors occurred'
).format(
len(success) + len(failure) + len(errored),
len(success),
@@ -378,12 +376,12 @@ def _on_import_result(self, result: List[ImportedGame]):
parent=self,
)
messagebox.setWindowModality(Qt.WindowModality.NonModal)
- details: List = []
+ details: list = []
for res in success:
- details.append(self.tr("Success: {} imported").format(res.app_title))
+ details.append(self.tr('Success: {} imported').format(res.app_title))
for res in failure:
- details.append(self.tr("Failed: {} - {}").format(res.app_title, res.message))
+ details.append(self.tr('Failed: {} - {}').format(res.app_title, res.message))
for res in errored:
- details.append(self.tr("Error: Could not find AppName for {}").format(res.path))
- messagebox.setDetailedText("\n".join(details))
+ details.append(self.tr('Error: Could not find AppName for {}').format(res.path))
+ messagebox.setDetailedText('\n'.join(details))
messagebox.show()
diff --git a/rare/components/tabs/integrations/ubisoft_group.py b/rare/components/tabs/integrations/ubisoft_group.py
index fd772dd26d..773e3b1f57 100644
--- a/rare/components/tabs/integrations/ubisoft_group.py
+++ b/rare/components/tabs/integrations/ubisoft_group.py
@@ -37,35 +37,35 @@ def __init__(self, core: LegendaryCore):
def run_real(self) -> None:
try:
- with timelogger(self.logger, "Request external auths"):
+ with timelogger(self.logger, 'Request external auths'):
external_auths = self.core.egs.get_external_auths()
for ext_auth in external_auths:
- if ext_auth["type"] != "ubisoft":
+ if ext_auth['type'] != 'ubisoft':
continue
- ubi_account_id = ext_auth["externalAuthId"]
+ ubi_account_id = ext_auth['externalAuthId']
break
else:
- self.signals.result.emit(set(), set(), "")
+ self.signals.result.emit(set(), set(), '')
return
- with timelogger(self.logger, "Request uplay codes"):
+ with timelogger(self.logger, 'Request uplay codes'):
uplay_keys = self.core.egs.store_get_uplay_codes()
- key_list = uplay_keys["data"]["PartnerIntegration"]["accountUplayCodes"]
- redeemed = {k["gameId"] for k in key_list if k["redeemedOnUplay"]}
+ key_list = uplay_keys['data']['PartnerIntegration']['accountUplayCodes']
+ redeemed = {k['gameId'] for k in key_list if k['redeemedOnUplay']}
if (entitlements := self.core.lgd.entitlements) is None:
- with timelogger(self.logger, "Request entitlements"):
+ with timelogger(self.logger, 'Request entitlements'):
try:
entitlements = self.core.egs.get_user_entitlements_full()
except Exception as e:
self.logger.warning(e)
self.core.lgd.entitlements = entitlements
- entitlements = {i["entitlementName"] for i in entitlements}
+ entitlements = {i['entitlementName'] for i in entitlements}
self.signals.result.emit(redeemed, entitlements, ubi_account_id)
except Exception as e:
self.logger.error(e)
- self.signals.result.emit(set(), set(), "error")
+ self.signals.result.emit(set(), set(), 'error')
class UbiConnectWorkerSignals(QObject):
@@ -83,7 +83,7 @@ def __init__(self, core: LegendaryCore, ubi_account_id, partner_link_id):
def run_real(self) -> None:
if not self.ubi_account_id: # debug
time.sleep(2)
- self.signals.linked.emit("")
+ self.signals.linked.emit('')
return
try:
self.core.egs.store_claim_uplay_code(self.ubi_account_id, self.partner_link_id)
@@ -92,7 +92,7 @@ def run_real(self) -> None:
self.signals.linked.emit(str(e))
return
else:
- self.signals.linked.emit("")
+ self.signals.linked.emit('')
class UbiLinkWidget(QFrame):
@@ -107,20 +107,20 @@ def __init__(self, rcore: RareCore, game: Game, ubi_account_id, activated: bool
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.redeem_indicator = QLabel(parent=self)
- self.redeem_indicator.setPixmap(qta_icon("fa.circle-o", "fa5.circle", color="grey").pixmap(20, 20))
+ self.redeem_indicator.setPixmap(qta_icon('fa.circle-o', 'fa5.circle', color='grey').pixmap(20, 20))
self.redeem_indicator.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Preferred)
self.title_label = ElideLabel(game.app_title, parent=self)
- self.redeem_button = QPushButton(self.tr("Redeem in Ubisoft"), parent=self)
+ self.redeem_button = QPushButton(self.tr('Redeem in Ubisoft'), parent=self)
self.redeem_button.setMinimumWidth(150)
self.redeem_button.clicked.connect(self.activate)
if activated:
- self.redeem_button.setText(self.tr("Already activated"))
+ self.redeem_button.setText(self.tr('Already activated'))
self.redeem_button.setDisabled(True)
self.redeem_indicator.setPixmap(
- qta_icon("fa.check-circle-o", "fa5.check-circle", color="green").pixmap(QSize(20, 20))
+ qta_icon('fa.check-circle-o', 'fa5.check-circle', color='green').pixmap(QSize(20, 20))
)
layout = QHBoxLayout(self)
@@ -132,7 +132,7 @@ def __init__(self, rcore: RareCore, game: Game, ubi_account_id, activated: bool
def activate(self):
self.redeem_button.setDisabled(True)
# self.ok_indicator.setPixmap(icon("mdi.loading", color="grey").pixmap(20, 20))
- self.redeem_indicator.setPixmap(qta_icon("mdi.transit-connection-horizontal", color="grey").pixmap(20, 20))
+ self.redeem_indicator.setPixmap(qta_icon('mdi.transit-connection-horizontal', color='grey').pixmap(20, 20))
if self.args.debug:
worker = UbiConnectWorker(self.core, None, None)
@@ -144,14 +144,14 @@ def activate(self):
def _on_linked(self, error):
if not error:
self.redeem_indicator.setPixmap(
- qta_icon("fa.check-circle-o", "fa5.check-circle", color="green").pixmap(QSize(20, 20))
+ qta_icon('fa.check-circle-o', 'fa5.check-circle', color='green').pixmap(QSize(20, 20))
)
self.redeem_button.setDisabled(True)
- self.redeem_button.setText(self.tr("Already activated"))
+ self.redeem_button.setText(self.tr('Already activated'))
else:
- self.redeem_indicator.setPixmap(qta_icon("fa.times-circle-o", "fa5.times-circle", color="red").pixmap(QSize(20, 20)))
+ self.redeem_indicator.setPixmap(qta_icon('fa.times-circle-o', 'fa5.times-circle', color='red').pixmap(QSize(20, 20)))
self.redeem_indicator.setToolTip(error)
- self.redeem_button.setText(self.tr("Try again"))
+ self.redeem_button.setText(self.tr('Try again'))
self.redeem_button.setDisabled(False)
@@ -164,14 +164,14 @@ def __init__(self, rcore: RareCore, parent=None):
self.core = rcore.core()
self.args = rcore.args()
- self.setTitle(self.tr("Link Ubisoft Games"))
+ self.setTitle(self.tr('Link Ubisoft Games'))
self.thread_pool = QThreadPool.globalInstance()
self.worker: UbiGetInfoWorker = None
self.info_label = QLabel(parent=self)
- self.info_label.setText(self.tr("Getting information about your redeemable Ubisoft games."))
- self.link_button = QPushButton(self.tr("Link Ubisoft acccount"), parent=self)
+ self.info_label.setText(self.tr('Getting information about your redeemable Ubisoft games.'))
+ self.link_button = QPushButton(self.tr('Link Ubisoft acccount'), parent=self)
self.link_button.setMinimumWidth(140)
self.link_button.clicked.connect(self._on_link_clicked)
self.link_button.setEnabled(False)
@@ -189,7 +189,7 @@ def __init__(self, rcore: RareCore, parent=None):
@Slot()
def _on_link_clicked(self):
- webbrowser.open("https://www.epicgames.com/id/link/ubisoft")
+ webbrowser.open('https://www.epicgames.com/id/link/ubisoft')
def showEvent(self, a0: QShowEvent) -> None:
if a0.spontaneous():
@@ -212,11 +212,11 @@ def showEvent(self, a0: QShowEvent) -> None:
def _on_result(self, redeemed: set, entitlements: set, ubi_account_id: str):
self.worker = None
self.loading_widget.stop()
- if not redeemed and ubi_account_id != "error":
- self.logger.error("No linked ubisoft account found! Link your accounts via your browser and try again.")
- self.info_label.setText(self.tr("Your account is not linked with Ubisoft. Please link your account and try again."))
+ if not redeemed and ubi_account_id != 'error':
+ self.logger.error('No linked ubisoft account found! Link your accounts via your browser and try again.')
+ self.info_label.setText(self.tr('Your account is not linked with Ubisoft. Please link your account and try again.'))
self.link_button.setEnabled(True)
- elif ubi_account_id == "error":
+ elif ubi_account_id == 'error':
self.info_label.setText(self.tr("An error has occurred while requesting your account's Ubisoft information."))
self.link_button.setEnabled(True)
else:
@@ -226,26 +226,26 @@ def _on_result(self, redeemed: set, entitlements: set, ubi_account_id: str):
activated = 0
for rgame in self.rcore.ubisoft_games:
game = rgame.game
- for dlc_data in game.metadata.get("dlcItemList", []):
- if dlc_data["entitlementName"] not in entitlements:
+ for dlc_data in game.metadata.get('dlcItemList', []):
+ if dlc_data['entitlementName'] not in entitlements:
continue
try:
- app_name = dlc_data["releaseInfo"][0]["appId"]
+ app_name = dlc_data['releaseInfo'][0]['appId']
except (IndexError, KeyError):
- app_name = "unknown"
+ app_name = 'unknown'
dlc_game = Game(
app_name=app_name,
- app_title=dlc_data["title"],
+ app_title=dlc_data['title'],
metadata=dlc_data,
)
- if dlc_game.partner_link_type != "ubisoft":
+ if dlc_game.partner_link_type != 'ubisoft':
continue
if dlc_game.partner_link_id in redeemed:
continue
uplay_games.append(dlc_game)
- if game.partner_link_type != "ubisoft":
+ if game.partner_link_type != 'ubisoft':
continue
if game.partner_link_id in redeemed:
activated += 1
@@ -254,12 +254,12 @@ def _on_result(self, redeemed: set, entitlements: set, ubi_account_id: str):
if not uplay_games:
self.info_label.setText(self.tr("You don't own any Ubisoft games."))
elif activated == len(uplay_games):
- self.info_label.setText(self.tr("All your Ubisoft games have already been activated."))
+ self.info_label.setText(self.tr('All your Ubisoft games have already been activated.'))
else:
self.info_label.setText(
- self.tr("You have {} games available to redeem.").format(len(uplay_games) - activated)
+ self.tr('You have {} games available to redeem.').format(len(uplay_games) - activated)
)
- self.logger.info(f"Found {len(uplay_games) - activated} game(s) to redeem.")
+ self.logger.info(f'Found {len(uplay_games) - activated} game(s) to redeem.')
for game in uplay_games:
widget = UbiLinkWidget(
@@ -275,8 +275,8 @@ def _on_result(self, redeemed: set, entitlements: set, ubi_account_id: str):
widget = UbiLinkWidget(
self.rcore,
Game(
- app_name="RareTestGame",
- app_title="Super Fake Super Rare Super Test (Super?) Game",
+ app_name='RareTestGame',
+ app_title='Super Fake Super Rare Super Test (Super?) Game',
),
ubi_account_id,
activated=False,
diff --git a/rare/components/tabs/library/__init__.py b/rare/components/tabs/library/__init__.py
index 5d515d1a41..582e8f6778 100644
--- a/rare/components/tabs/library/__init__.py
+++ b/rare/components/tabs/library/__init__.py
@@ -19,7 +19,7 @@
from .head_bar import LibraryHeadBar
from .widgets import LibraryFilter, LibraryOrder, LibraryView, LibraryWidgetController
-logger = getLogger("GamesLibrary")
+logger = getLogger('GamesLibrary')
class GamesLibrary(QStackedWidget):
@@ -105,7 +105,7 @@ def setup_game_list(self):
for rgame in self.rcore.games:
widget = self.add_library_widget(rgame)
if not widget:
- logger.warning("Excluding %s from the game list", rgame.app_title)
+ logger.warning('Excluding %s from the game list', rgame.app_title)
continue
self.filter_games(self.head_bar.current_filter())
self.order_games(self.head_bar.current_order())
@@ -115,18 +115,18 @@ def add_library_widget(self, rgame: RareGame):
try:
widget = self.library_controller.add_game(rgame)
except Exception as e:
- logger.error("Could not add widget for %s to library: %s", rgame.app_name, e)
+ logger.error('Could not add widget for %s to library: %s', rgame.app_name, e)
return None
widget.show_info.connect(self.show_game_info)
return widget
@Slot(str)
- def search_games(self, search_text: str = ""):
+ def search_games(self, search_text: str = ''):
self.filter_games(self.head_bar.current_filter(), search_text)
@Slot(object)
@Slot(object, str)
- def filter_games(self, library_filter: LibraryFilter = None, search_text: str = ""):
+ def filter_games(self, library_filter: LibraryFilter = None, search_text: str = ''):
if not search_text and (t := self.head_bar.search_bar.text()):
search_text = t
@@ -134,7 +134,7 @@ def filter_games(self, library_filter: LibraryFilter = None, search_text: str =
@Slot(object)
@Slot(object, str)
- def order_games(self, library_order: LibraryOrder = None, search_text: str = ""):
+ def order_games(self, library_order: LibraryOrder = None, search_text: str = ''):
if not search_text and (t := self.head_bar.search_bar.text()):
search_text = t
diff --git a/rare/components/tabs/library/details/__init__.py b/rare/components/tabs/library/details/__init__.py
index f89ef8b54c..2cef232d1f 100644
--- a/rare/components/tabs/library/details/__init__.py
+++ b/rare/components/tabs/library/details/__init__.py
@@ -1,5 +1,5 @@
+import contextlib
import platform as pf
-from typing import Optional
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QKeyEvent, QShowEvent
@@ -15,6 +15,7 @@
from .compat import LocalCompatSettings
from .details import GameDetails
from .dlcs import GameDlcs
+from .environ import LocalEnvironSettings
from .game import LocalGameSettings
@@ -31,29 +32,32 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.details_tab = GameDetails(self.rcore, self)
self.details_tab.import_clicked.connect(self.import_clicked)
- self.details_index = self.addTab(self.details_tab, self.tr("Information"))
+ self.details_index = self.addTab(self.details_tab, self.tr('Information'))
self.game_settings_tab = LocalGameSettings(settings, rcore, self)
- self.game_settings_index = self.addTab(self.game_settings_tab, self.tr("Settings"))
+ self.game_settings_index = self.addTab(self.game_settings_tab, self.tr('Settings'))
- if pf.system() != "Windows":
+ if pf.system() != 'Windows':
self.compat_settings_tab = LocalCompatSettings(settings, rcore, self)
- self.compat_settings_index = self.addTab(self.compat_settings_tab, self.tr("Compatibility"))
+ self.compat_settings_index = self.addTab(self.compat_settings_tab, self.tr('Compatibility'))
+
+ self.environ_tab = LocalEnvironSettings(rcore, self)
+ self.environ_index = self.addTab(self.environ_tab, self.tr('Environment'))
self.cloud_saves_tab = CloudSaves(settings, rcore, self)
- self.cloud_saves_index = self.addTab(self.cloud_saves_tab, self.tr("Cloud Saves"))
+ self.cloud_saves_index = self.addTab(self.cloud_saves_tab, self.tr('Cloud Saves'))
self.dlcs_tab = GameDlcs(rcore, self)
- self.dlcs_index = self.addTab(self.dlcs_tab, self.tr("Downloadable Content"))
+ self.dlcs_index = self.addTab(self.dlcs_tab, self.tr('Downloadable Content'))
# FIXME: Hiding didn't work, so don't add these tabs in normal mode. Fix this properly later
if self.args.debug:
self.game_meta_view = GameMetadataView(self)
- self.game_meta_index = self.addTab(self.game_meta_view, self.tr("Game Metadata"))
+ self.game_meta_index = self.addTab(self.game_meta_view, self.tr('Game Metadata'))
self.igame_meta_view = GameMetadataView(self)
- self.igame_meta_index = self.addTab(self.igame_meta_view, self.tr("InstalledGame Metadata"))
+ self.igame_meta_index = self.addTab(self.igame_meta_view, self.tr('InstalledGame Metadata'))
self.rgame_meta_view = GameMetadataView(self)
- self.rgame_meta_index = self.addTab(self.rgame_meta_view, self.tr("RareGame Metadata"))
+ self.rgame_meta_index = self.addTab(self.rgame_meta_view, self.tr('RareGame Metadata'))
self.setCurrentIndex(self.details_index)
@@ -62,12 +66,14 @@ def update_game(self, rgame: RareGame):
self.game_settings_tab.load_settings(rgame)
self.game_settings_tab.launch.setEnabled(rgame.is_installed or rgame.is_origin)
- self.game_settings_tab.env_vars.setEnabled(rgame.is_installed or rgame.is_origin)
- if pf.system() != "Windows":
+ if pf.system() != 'Windows':
self.compat_settings_tab.load_settings(rgame)
self.compat_settings_tab.setEnabled(rgame.is_installed or rgame.is_origin)
+ self.environ_tab.load_settings(rgame)
+ self.environ_tab.setEnabled(rgame.is_installed or rgame.is_origin)
+
self.dlcs_tab.update_dlcs(rgame)
self.dlcs_tab.setEnabled(rgame.is_installed and bool(rgame.owned_dlcs))
@@ -95,7 +101,7 @@ def __init__(self, parent=None):
self.setEditTriggers(QTreeView.EditTrigger.NoEditTriggers)
self.model = QJsonModel()
self.setModel(self.model)
- self.rgame: Optional[RareGame] = None
+ self.rgame: RareGame | None = None
def showEvent(self, event: QShowEvent):
if event.spontaneous():
@@ -106,8 +112,6 @@ def update_game(self, rgame: RareGame, view):
self.rgame = rgame
self.set_title.emit(self.rgame.app_title)
self.model.clear()
- try:
+ with contextlib.suppress(Exception):
self.model.load(vars(view))
- except Exception:
- pass
self.resizeColumnToContents(0)
diff --git a/rare/components/tabs/library/details/cloud_saves.py b/rare/components/tabs/library/details/cloud_saves.py
index cc98e73c8f..340fe8d3a0 100644
--- a/rare/components/tabs/library/details/cloud_saves.py
+++ b/rare/components/tabs/library/details/cloud_saves.py
@@ -2,7 +2,6 @@
import platform
from datetime import datetime
from logging import getLogger
-from typing import Tuple
from legendary.models.game import SaveGameStatus
from PySide6.QtCore import Qt, QThreadPool, Slot
@@ -49,8 +48,8 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.rcore = rcore
self.core = rcore.core()
- self.sync_ui.icon_local.setPixmap(qta_icon("mdi.harddisk", "fa5s.desktop").pixmap(128, 128))
- self.sync_ui.icon_remote.setPixmap(qta_icon("mdi.cloud-outline", "fa5s.cloud").pixmap(128, 128))
+ self.sync_ui.icon_local.setPixmap(qta_icon('mdi.harddisk', 'fa5s.desktop').pixmap(128, 128))
+ self.sync_ui.icon_remote.setPixmap(qta_icon('mdi.cloud-outline', 'fa5s.cloud').pixmap(128, 128))
self.sync_ui.upload_button.clicked.connect(self.upload)
self.sync_ui.download_button.clicked.connect(self.download)
@@ -66,7 +65,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.cloud_ui.setupUi(self.cloud_widget)
self.cloud_save_path_edit = PathEdit(
- path="",
+ path='',
file_mode=QFileDialog.FileMode.Directory,
placeholder=self.tr('Use "Resolve path" or "Browse" ...'),
edit_func=self.edit_save_path,
@@ -79,7 +78,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.cloud_save_path_edit,
)
- self.compute_save_path_button = QPushButton(qta_icon("fa.magic", "fa5s.magic"), self.tr("Resolve path"))
+ self.compute_save_path_button = QPushButton(qta_icon('fa.magic', 'fa5s.magic'), self.tr('Resolve path'))
self.compute_save_path_button.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Fixed)
self.compute_save_path_button.clicked.connect(self.__compute_save_path)
self.cloud_ui.main_layout.addRow(None, self.compute_save_path_button)
@@ -94,7 +93,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
layout.addWidget(self.info_label)
layout.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding))
- def edit_save_path(self, text: str) -> Tuple[bool, str, int]:
+ def edit_save_path(self, text: str) -> tuple[bool, str, int]:
# Validate against raw_save_path (usefull when the user types the path manually)
# path = os.path.normpath(text.lower()).split("/")
# spec = os.path.normpath(self.save_path_spec.lower()).split("/")
@@ -102,7 +101,7 @@ def edit_save_path(self, text: str) -> Tuple[bool, str, int]:
# depth = depth if depth > 0 else 1
# if path[-depth:] != spec[-depth:]:
# return False, text, IndicatorReasonsCommon.INVALID
- if platform.system() != "Windows":
+ if platform.system() != 'Windows':
if os.path.exists(text):
return True, text, IndicatorReasonsCommon.VALID
else:
@@ -128,9 +127,9 @@ def download(self):
def __compute_save_path(self):
if self.rgame.is_installed and self.rgame.game.supports_cloud_saves:
try:
- with timelogger(self.logger, "Detecting save path"):
+ with timelogger(self.logger, 'Detecting save path'):
new_path = self.core.get_save_path(self.rgame.app_name)
- if platform.system() != "Windows" and not os.path.exists(new_path):
+ if platform.system() != 'Windows' and not os.path.exists(new_path):
raise ValueError(f'Path "{new_path}" does not exist.')
except Exception as e:
self.logger.warning(str(e))
@@ -140,7 +139,7 @@ def __compute_save_path(self):
# self.cloud_save_path_edit.setText("")
# QMessageBox.warning(self, "Warning", "No wine prefix selected. Please set it in settings")
# return
- self.cloud_save_path_edit.setText(self.tr("Loading..."))
+ self.cloud_save_path_edit.setText(self.tr('Loading...'))
self.cloud_save_path_edit.setDisabled(True)
self.compute_save_path_button.setDisabled(True)
@@ -156,8 +155,8 @@ def __sync_check_changed(self, state: Qt.CheckState):
@Slot(str, str)
def __on_wine_resolver_result(self, path, app_name):
- self.logger.info("Wine resolver finished for %s", app_name)
- self.logger.info("Computed save path: %s", path)
+ self.logger.info('Wine resolver finished for %s', app_name)
+ self.logger.info('Computed save path: %s', path)
if app_name == self.rgame.app_name:
self.cloud_save_path_edit.setDisabled(False)
self.compute_save_path_button.setDisabled(False)
@@ -165,17 +164,17 @@ def __on_wine_resolver_result(self, path, app_name):
try:
os.makedirs(path, exist_ok=True)
except PermissionError:
- self.cloud_save_path_edit.setText("")
+ self.cloud_save_path_edit.setText('')
QMessageBox.warning(
self,
- self.tr("Error - {}").format(self.rgame.app_title),
+ self.tr('Error - {}').format(self.rgame.app_title),
self.tr(
- "Error while calculating path for {}. Insufficient permissions to create {}"
+ 'Error while calculating path for {}. Insufficient permissions to create {}'
).format(self.rgame.app_title, path),
)
return
if not path:
- self.cloud_save_path_edit.setText("")
+ self.cloud_save_path_edit.setText('')
return
self.cloud_save_path_edit.setText(path)
@@ -189,28 +188,28 @@ def __update_widget(self):
info_text = (
self.tr("This game doesn't support cloud saves")
if not supports_saves
- else (self.tr("This game supports cloud saves, but it's not installed") if self.rgame.igame is None else "")
+ else (self.tr("This game supports cloud saves, but it's not installed") if self.rgame.igame is None else '')
)
self.info_label.setText(info_text)
self.info_label.setVisible(bool(info_text))
if not saves_ready:
- self.sync_ui.date_info_local.setText("None")
- self.sync_ui.age_label_local.setText("None")
- self.sync_ui.date_info_remote.setText("None")
- self.sync_ui.age_label_remote.setText("None")
+ self.sync_ui.date_info_local.setText('None')
+ self.sync_ui.age_label_local.setText('None')
+ self.sync_ui.date_info_remote.setText('None')
+ self.sync_ui.age_label_remote.setText('None')
self.cloud_ui.sync_check.setChecked(False)
- self.cloud_save_path_edit.setText("")
+ self.cloud_save_path_edit.setText('')
return
status, (dt_local, dt_remote) = self.rgame.save_game_state
local_tz = datetime.now().astimezone().tzinfo
- self.sync_ui.date_info_local.setText(dt_local.astimezone(local_tz).strftime("%A, %d %B %Y %X") if dt_local else "None")
- self.sync_ui.date_info_remote.setText(dt_remote.astimezone(local_tz).strftime("%A, %d %B %Y %X") if dt_remote else "None")
+ self.sync_ui.date_info_local.setText(dt_local.astimezone(local_tz).strftime('%A, %d %B %Y %X') if dt_local else 'None')
+ self.sync_ui.date_info_remote.setText(dt_remote.astimezone(local_tz).strftime('%A, %d %B %Y %X') if dt_remote else 'None')
- newer = self.tr("Newer")
- self.sync_ui.age_label_local.setText(f"{newer}" if status == SaveGameStatus.LOCAL_NEWER else " ")
- self.sync_ui.age_label_remote.setText(f"{newer}" if status == SaveGameStatus.REMOTE_NEWER else " ")
+ newer = self.tr('Newer')
+ self.sync_ui.age_label_local.setText(f'{newer}' if status == SaveGameStatus.LOCAL_NEWER else ' ')
+ self.sync_ui.age_label_remote.setText(f'{newer}' if status == SaveGameStatus.REMOTE_NEWER else ' ')
button_disabled = self.rgame.state in [
RareGame.State.RUNNING,
@@ -229,8 +228,8 @@ def __update_widget(self):
self.cloud_ui.sync_check.setChecked(self.rgame.auto_sync_saves)
self.cloud_ui.sync_check.blockSignals(False)
- self.cloud_save_path_edit.setText(self.rgame.save_path if self.rgame.save_path else "")
- if platform.system() == "Windows" and not self.rgame.save_path:
+ self.cloud_save_path_edit.setText(self.rgame.save_path if self.rgame.save_path else '')
+ if platform.system() == 'Windows' and not self.rgame.save_path:
self.__compute_save_path()
def update_game(self, rgame: RareGame):
diff --git a/rare/components/tabs/library/details/compat.py b/rare/components/tabs/library/details/compat.py
index b406cb20ad..9a6012ca94 100644
--- a/rare/components/tabs/library/details/compat.py
+++ b/rare/components/tabs/library/details/compat.py
@@ -1,6 +1,5 @@
import platform as pf
from logging import getLogger
-from typing import Tuple
from PySide6.QtCore import QSignalBlocker, Qt, Slot
from PySide6.QtGui import QHideEvent, QShowEvent
@@ -18,18 +17,18 @@
from rare.shared import RareCore
from rare.utils import config_helper as config
from rare.utils.paths import compat_shaders_dir, proton_compat_dir, wine_prefix_dir
-from rare.utils.steam_grades import SteamGrades
+from rare.utils.steam_grades import steam_grades
from rare.widgets.indicator_edit import (
ColumnCompleter,
IndicatorLineEdit,
IndicatorReasonsCommon,
)
-if pf.system() in {"Linux", "FreeBSD"}:
+if pf.system() in {'Linux', 'FreeBSD'}:
from rare.components.tabs.settings.widgets.overlay import MangoHudSettings
from rare.components.tabs.settings.widgets.proton import ProtonSettings
-logger = getLogger("LocalCompatSettings")
+logger = getLogger('LocalCompatSettings')
class LocalWineSettings(WineSettings):
@@ -37,17 +36,17 @@ def load_settings(self, app_name):
self.app_name = app_name
-if pf.system() in {"Linux", "FreeBSD"}:
+if pf.system() in {'Linux', 'FreeBSD'}:
class LocalProtonSettings(ProtonSettings):
def load_settings(self, app_name: str):
self.app_name = app_name
def _get_compat_path(self, compat_location: ProtonSettings.CompatLocation):
- folder_name = "default"
+ folder_name = 'default'
local_folder_name = self.rcore.get_game(self.app_name).folder_name
if compat_location == ProtonSettings.CompatLocation.NONE:
- if wine_prefix_dir(local_folder_name).joinpath("system.reg").is_file():
+ if wine_prefix_dir(local_folder_name).joinpath('system.reg').is_file():
compat_location = ProtonSettings.CompatLocation.ISOLATED
if compat_location == ProtonSettings.CompatLocation.ISOLATED:
folder_name = local_folder_name
@@ -61,7 +60,7 @@ def load_settings(self, app_name: str):
class LocalRunnerSettings(RunnerSettingsBase):
def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
- if pf.system() in {"Linux", "FreeBSD"}:
+ if pf.system() in {'Linux', 'FreeBSD'}:
super(LocalRunnerSettings, self).__init__(settings, rcore, LocalWineSettings, LocalProtonSettings, parent=parent)
else:
super(LocalRunnerSettings, self).__init__(settings, rcore, LocalWineSettings, parent=parent)
@@ -69,28 +68,28 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.rgame: RareGame = None
self.steam_appid_edit = IndicatorLineEdit(
- placeholder=self.tr("Use in case the SteamAppID was not found automatically"),
+ placeholder=self.tr('Use in case the SteamAppID was not found automatically'),
edit_func=self.__steam_appid_edit_callback,
save_func=self.__steam_appid_save_callback,
parent=self,
)
- self.form_layout.addRow(self.tr("Steam AppID"), self.steam_appid_edit)
+ self.form_layout.addRow(self.tr('Steam AppID'), self.steam_appid_edit)
- self.__grades = SteamGrades()
+ self.__grades = steam_grades
def showEvent(self, e: QShowEvent):
if e.spontaneous():
return super().showEvent(e)
_ = QSignalBlocker(self.shader_cache_check)
is_local_cache_enabled = self.settings.get_with_global(app_settings.local_shader_cache, self.rgame.app_name)
- has_local_cache_path = bool(config.get_envvar(self.app_name, "STEAM_COMPAT_SHADER_PATH", False))
+ has_local_cache_path = bool(config.get_envvar(self.app_name, 'STEAM_COMPAT_SHADER_PATH', False))
self.shader_cache_check.setChecked(is_local_cache_enabled or has_local_cache_path)
self.shader_cache_check.setChecked(is_local_cache_enabled or has_local_cache_path)
_ = QSignalBlocker(self.steam_appid_edit)
items = {k: v for k, v in self.__grades.steam_appids.items() if self.rgame.app_title.lower()[0:4] in k.lower()[0:4]}
self.steam_appid_edit.setCompleter(ColumnCompleter(items=items))
- self.steam_appid_edit.setText(self.rgame.steam_appid if self.rgame.steam_appid else "")
- self.steam_appid_edit.setInfo(self.__grades.steam_titles.get(self.rgame.steam_appid, ""))
+ self.steam_appid_edit.setText(self.rgame.steam_appid if self.rgame.steam_appid else '')
+ self.steam_appid_edit.setInfo(self.__grades.steam_titles.get(self.rgame.steam_appid, ''))
return super().showEvent(e)
def hideEvent(self, e: QHideEvent):
@@ -99,19 +98,19 @@ def hideEvent(self, e: QHideEvent):
self.steam_appid_edit.setCompleter(None)
return super().hideEvent(e)
- def __steam_appid_edit_callback(self, text: str) -> Tuple[bool, str, int]:
- self.steam_appid_edit.setInfo("")
+ def __steam_appid_edit_callback(self, text: str) -> tuple[bool, str, int]:
+ self.steam_appid_edit.setInfo('')
if not text or len(text) < 3:
return True, text, IndicatorReasonsCommon.UNDEFINED
- if text in self.__grades.steam_appids.keys():
+ if text in self.__grades.steam_appids:
return True, self.__grades.steam_appids[text], IndicatorReasonsCommon.VALID
- if text in self.__grades.steam_titles.keys():
+ if text in self.__grades.steam_titles:
return True, text, IndicatorReasonsCommon.VALID
else:
return False, text, IndicatorReasonsCommon.GAME_NOT_EXISTS
def __steam_appid_save_callback(self, text: str) -> None:
- self.steam_appid_edit.setInfo(self.__grades.steam_titles.get(text, ""))
+ self.steam_appid_edit.setInfo(self.__grades.steam_titles.get(text, ''))
if text == self.rgame.steam_appid:
return
self.rgame.steam_appid = text
@@ -122,13 +121,13 @@ def _shader_cache_check_changed(self, state: Qt.CheckState):
if checked := (state != Qt.CheckState.Unchecked):
config.set_envvar(
self.rgame.app_name,
- "STEAM_COMPAT_SHADER_PATH",
+ 'STEAM_COMPAT_SHADER_PATH',
compat_shaders_dir(self.rgame.folder_name).as_posix(),
)
else:
- config.remove_envvar(self.rgame.app_name, "STEAM_COMPAT_SHADER_PATH")
+ config.remove_envvar(self.rgame.app_name, 'STEAM_COMPAT_SHADER_PATH')
self.settings.set_with_global(app_settings.local_shader_cache, checked, self.rgame.app_name)
- self.environ_changed.emit("STEAM_COMPAT_SHADER_PATH")
+ self.environ_changed.emit('STEAM_COMPAT_SHADER_PATH')
def load_settings(self, rgame: RareGame):
self.rgame = rgame
@@ -155,7 +154,7 @@ def load_settings(self, app_name: str):
class LocalCompatSettings(CompatSettingsBase):
def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
- if pf.system() in {"Linux", "FreeBSD"}:
+ if pf.system() in {'Linux', 'FreeBSD'}:
super(LocalCompatSettings, self).__init__(
settings,
rcore,
diff --git a/rare/components/tabs/library/details/details.py b/rare/components/tabs/library/details/details.py
index a15b7bd8a1..ef63856bb7 100644
--- a/rare/components/tabs/library/details/details.py
+++ b/rare/components/tabs/library/details/details.py
@@ -2,7 +2,6 @@
import platform
from hashlib import sha1
from logging import getLogger
-from typing import Dict, Optional, Tuple
from PySide6.QtCore import (
QCoreApplication,
@@ -33,12 +32,12 @@
from rare.ui.components.tabs.library.details.details import Ui_GameDetails
from rare.utils.misc import format_size, qta_icon, relative_date, style_hyperlink
from rare.utils.paths import cache_dir
-from rare.utils.qt_requests import QtRequests
+from rare.utils.qrequests import QRequests
from rare.widgets.dialogs import ButtonDialog, game_title
from rare.widgets.image_widget import ImageSize, ImageWidget, LoadingImageWidget
from rare.widgets.side_tab import SideTabContents
-logger = getLogger("GameInfo")
+logger = getLogger('GameInfo')
class GameDetails(QWidget, SideTabContents):
@@ -51,21 +50,21 @@ def __init__(self, rcore: RareCore, parent=None):
self.ui = Ui_GameDetails()
self.ui.setupUi(self)
# lk: set object names for CSS properties
- self.ui.install_path.setObjectName("LinkLabel")
- self.ui.install_button.setObjectName("InstallButton")
- self.ui.modify_button.setObjectName("InstallButton")
- self.ui.verify_button.setObjectName("VerifyButton")
- self.ui.move_button.setObjectName("MoveButton")
- self.ui.uninstall_button.setObjectName("UninstallButton")
-
- self.ui.install_button.setIcon(qta_icon("ri.install-line"))
- self.ui.import_button.setIcon(qta_icon("mdi.application-import"))
-
- self.ui.modify_button.setIcon(qta_icon("mdi.content-save-edit-outline"))
- self.ui.verify_button.setIcon(qta_icon("mdi.check-underline"))
- self.ui.repair_button.setIcon(qta_icon("mdi.progress-wrench"))
- self.ui.move_button.setIcon(qta_icon("mdi.folder-move-outline"))
- self.ui.uninstall_button.setIcon(qta_icon("ri.uninstall-line"))
+ self.ui.install_path.setObjectName('LinkLabel')
+ self.ui.install_button.setObjectName('InstallButton')
+ self.ui.modify_button.setObjectName('InstallButton')
+ self.ui.verify_button.setObjectName('VerifyButton')
+ self.ui.move_button.setObjectName('MoveButton')
+ self.ui.uninstall_button.setObjectName('UninstallButton')
+
+ self.ui.install_button.setIcon(qta_icon('ri.install-line'))
+ self.ui.import_button.setIcon(qta_icon('mdi.application-import'))
+
+ self.ui.modify_button.setIcon(qta_icon('mdi.content-save-edit-outline'))
+ self.ui.verify_button.setIcon(qta_icon('mdi.check-underline'))
+ self.ui.repair_button.setIcon(qta_icon('mdi.progress-wrench'))
+ self.ui.move_button.setIcon(qta_icon('mdi.folder-move-outline'))
+ self.ui.uninstall_button.setIcon(qta_icon('ri.uninstall-line'))
self.ui.grade.setOpenExternalLinks(True)
self.ui.install_path.setOpenExternalLinks(True)
@@ -73,9 +72,9 @@ def __init__(self, rcore: RareCore, parent=None):
self.rcore = rcore
self.core = rcore.core()
self.args = rcore.args()
- self.net_manager = QtRequests(cache=str(cache_dir().joinpath("achievements")), parent=self)
+ self.net_manager = QRequests(cache=str(cache_dir().joinpath('achievements')), parent=self)
- self.rgame: Optional[RareGame] = None
+ self.rgame: RareGame | None = None
self.image = ImageWidget(self)
self.image.setFixedSize(ImageSize.DisplayTall)
@@ -91,17 +90,17 @@ def __init__(self, rcore: RareCore, parent=None):
self.ui.uninstall_button.clicked.connect(self.__on_uninstall)
self.steam_grade_ratings = {
- "platinum": self.tr("Platinum"),
- "gold": self.tr("Gold"),
- "silver": self.tr("Silver"),
- "bronze": self.tr("Bronze"),
- "borked": self.tr("Borked"),
- "fail": self.tr("Failed to get rating"),
- "pending": self.tr("Loading..."),
- "na": self.tr("Not applicable"),
+ 'platinum': self.tr('Platinum'),
+ 'gold': self.tr('Gold'),
+ 'silver': self.tr('Silver'),
+ 'bronze': self.tr('Bronze'),
+ 'borked': self.tr('Borked'),
+ 'fail': self.tr('Failed to get rating'),
+ 'pending': self.tr('Loading...'),
+ 'na': self.tr('Not applicable'),
}
- self.ui.add_tag_button.setIcon(qta_icon("mdi.plus"))
+ self.ui.add_tag_button.setIcon(qta_icon('mdi.plus'))
self.ui.add_tag_button.clicked.connect(self.__on_tag_add)
ach_progress_layout = QVBoxLayout(self.ui.ach_progress_page)
@@ -145,12 +144,12 @@ def __on_modify(self):
@Slot()
def __on_repair(self):
"""This method is to be called from the button only"""
- repair_file = os.path.join(self.core.lgd.get_tmp_path(), f"{self.rgame.app_name}.repair")
+ repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{self.rgame.app_name}.repair')
if not os.path.exists(repair_file):
QMessageBox.warning(
self,
- self.tr("Error - {}").format(self.rgame.app_title),
- self.tr("Repair file does not exist or game does not need a repair. Please verify game first"),
+ self.tr('Error - {}').format(self.rgame.app_title),
+ self.tr('Repair file does not exist or game does not need a repair. Please verify game first'),
)
return
self.repair_game(self.rgame)
@@ -161,9 +160,10 @@ def repair_game(self, rgame: RareGame):
if rgame.has_update:
mbox = QMessageBox.question(
self,
- self.tr("Repair and update? - {}").format(self.rgame.app_title),
+ self.tr('Repair and update? - {}').format(self.rgame.app_title),
self.tr(
- "There is an update for {} from {} to {}. Do you want to update the game while repairing it?"
+ 'There is an update for {} from {} to {}. '
+ 'Do you want to update the game while repairing it?'
).format(rgame.app_title, rgame.version, rgame.remote_version),
)
ans = mbox == QMessageBox.StandardButton.Yes
@@ -171,17 +171,17 @@ def repair_game(self, rgame: RareGame):
@Slot(RareGame, str)
def __on_worker_error(self, rgame: RareGame, message: str):
- QMessageBox.warning(self, self.tr("Error - {}").format(rgame.app_title), message)
+ QMessageBox.warning(self, self.tr('Error - {}').format(rgame.app_title), message)
@Slot()
def __on_verify(self):
"""This method is to be called from the button only"""
if not os.path.exists(self.rgame.igame.install_path):
- logger.error(f"Installation path {self.rgame.igame.install_path} for {self.rgame.app_title} does not exist")
+ logger.error(f'Installation path {self.rgame.igame.install_path} for {self.rgame.app_title} does not exist')
QMessageBox.warning(
self,
- self.tr("Error - {}").format(self.rgame.app_title),
- self.tr("Installation path for {} does not exist. Cannot continue.").format(self.rgame.app_title),
+ self.tr('Error - {}').format(self.rgame.app_title),
+ self.tr('Installation path for {} does not exist. Cannot continue.').format(self.rgame.app_title),
)
return
if self.rgame.sdl_available:
@@ -196,7 +196,7 @@ def verify_game(self, rgame: RareGame, sdl_model: SelectiveDownloadsModel = None
if sdl_model is not None:
if not sdl_model.accepted or sdl_model.install_tag is None:
return
- self.core.lgd.config.set(rgame.app_name, "install_tags", ",".join(sdl_model.install_tag))
+ self.core.lgd.config.set(rgame.app_name, 'install_tags', ','.join(sdl_model.install_tag))
self.core.lgd.save_config()
worker = VerifyWorker(self.core, self.args, rgame)
worker.signals.progress.connect(self.__on_verify_progress)
@@ -217,15 +217,16 @@ def __on_verify_result(self, rgame: RareGame, success, failed, missing):
if success:
QMessageBox.information(
self,
- self.tr("Summary - {}").format(rgame.app_title),
- self.tr("{} has been verified successfully. No missing or corrupt files found").format(rgame.app_title),
+ self.tr('Summary - {}').format(rgame.app_title),
+ self.tr('{} has been verified successfully. No missing or corrupt files found').format(rgame.app_title),
)
else:
ans = QMessageBox.question(
self,
- self.tr("Summary - {}").format(rgame.app_title),
+ self.tr('Summary - {}').format(rgame.app_title),
self.tr(
- "{} failed verification, {} file(s) corrupted, {} file(s) are missing. Do you want to repair them?"
+ '{} failed verification, {} file(s) corrupted, {} file(s) are missing. '
+ 'Do you want to repair them?'
).format(rgame.app_title, failed, missing),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.Yes,
@@ -251,8 +252,8 @@ def move_game(self, rgame: RareGame, options: MoveGameModel):
if not options.dst_exists and options.target_name in os.listdir(options.target_path):
ans = QMessageBox.question(
self,
- self.tr("Move game? - {}").format(self.rgame.app_title),
- self.tr("Destination {} already exists. Are you sure you want to overwrite it?").format(new_install_path),
+ self.tr('Move game? - {}').format(self.rgame.app_title),
+ self.tr('Destination {} already exists. Are you sure you want to overwrite it?').format(new_install_path),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.Yes,
)
@@ -279,8 +280,8 @@ def __on_move_progress(self, rgame: RareGame, progress: int, total_size: int, co
def __on_move_result(self, rgame: RareGame, dst_path: str):
QMessageBox.information(
self,
- self.tr("Summary - {}").format(rgame.app_title),
- self.tr("{} successfully moved to {}.").format(rgame.app_title, dst_path),
+ self.tr('Summary - {}').format(rgame.app_title),
+ self.tr('{} successfully moved to {}.').format(rgame.app_title, dst_path),
)
@Slot(Qt.CheckState, str)
@@ -325,11 +326,11 @@ def __update_widget(self):
self.ui.version_label.setDisabled(self.rgame.is_non_asset)
self.ui.version.setDisabled(self.rgame.is_non_asset)
- self.ui.version.setText(self.rgame.version if not self.rgame.is_non_asset else "N/A")
+ self.ui.version.setText(self.rgame.version if not self.rgame.is_non_asset else 'N/A')
self.ui.install_size_label.setEnabled(bool(self.rgame.install_size))
self.ui.install_size.setEnabled(bool(self.rgame.install_size))
- self.ui.install_size.setText(format_size(self.rgame.install_size) if self.rgame.install_size else "N/A")
+ self.ui.install_size.setText(format_size(self.rgame.install_size) if self.rgame.install_size else 'N/A')
self.ui.install_path_label.setEnabled(bool(self.rgame.install_path))
self.ui.install_path.setEnabled(bool(self.rgame.install_path))
@@ -339,19 +340,19 @@ def __update_widget(self):
self.rgame.install_path,
)
if self.rgame.install_path
- else "N/A"
+ else 'N/A'
)
self.ui.platform.setText(
self.rgame.igame.platform if self.rgame.is_installed and not self.rgame.is_non_asset else self.rgame.default_platform
)
- self.ui.grade_label.setDisabled(self.rgame.is_unreal or platform.system() == "Windows")
- self.ui.grade.setDisabled(self.rgame.is_unreal or platform.system() == "Windows")
+ self.ui.grade_label.setDisabled(self.rgame.is_unreal or platform.system() == 'Windows')
+ self.ui.grade.setDisabled(self.rgame.is_unreal or platform.system() == 'Windows')
self.ui.grade.setText(
style_hyperlink(
- f"https://www.protondb.com/app/{self.rgame.steam_appid}",
- f"{self.steam_grade_ratings[self.rgame.get_steam_grade()]} ({self.rgame.steam_appid})",
+ f'https://www.protondb.com/app/{self.rgame.steam_appid}',
+ f'{self.steam_grade_ratings[self.rgame.get_steam_grade()]} ({self.rgame.steam_appid})',
)
)
@@ -410,12 +411,12 @@ def update_game(self, rgame: RareGame):
try:
worker.signals.progress.disconnect(self.__on_verify_progress)
except TypeError as e:
- logger.warning(f"{self.rgame.app_name} verify worker: {e}")
+ logger.warning(f'{self.rgame.app_name} verify worker: {e}')
if isinstance(worker, MoveWorker):
try:
worker.signals.progress.disconnect(self.__on_move_progress)
except TypeError as e:
- logger.warning(f"{self.rgame.app_name} move worker: {e}")
+ logger.warning(f'{self.rgame.app_name} move worker: {e}')
self.rgame.signals.widget.refresh.disconnect(self.__update_widget)
self.rgame = None
@@ -432,12 +433,12 @@ def update_game(self, rgame: RareGame):
self.ui.dev.setText(rgame.developer)
if rgame.is_non_asset:
- self.ui.install_button.setText(self.tr("Link/Launch"))
+ self.ui.install_button.setText(self.tr('Link/Launch'))
self.ui.actions_stack.setCurrentWidget(self.ui.uninstalled_page)
else:
- self.ui.install_button.setText(self.tr("Install"))
+ self.ui.install_button.setText(self.tr('Install'))
- self.ui.description_field.setText(rgame.game.metadata["description"])
+ self.ui.description_field.setText(rgame.game.metadata['description'])
for page in (
self.ui.ach_progress_page,
@@ -451,15 +452,15 @@ def update_game(self, rgame: RareGame):
w.deleteLater()
if ach := rgame.achievements:
- self.ui.progress_field.setText(f"{ach.user_unlocked}/{ach.total_achievements}")
- self.ui.exp_field.setText(f"{ach.user_xp}/{ach.total_product_xp}")
+ self.ui.progress_field.setText(f'{ach.user_unlocked}/{ach.total_achievements}')
+ self.ui.exp_field.setText(f'{ach.user_xp}/{ach.total_product_xp}')
for group, page in zip(
(
- sorted(ach.hidden, key=lambda a: a["xp"], reverse=False),
- sorted(ach.uninitiated, key=lambda a: a["xp"], reverse=False),
- sorted(ach.completed, key=lambda a: a["unlock_date"], reverse=True),
- sorted(ach.in_progress, key=lambda a: a["progress"], reverse=True),
+ sorted(ach.hidden, key=lambda a: a['xp'], reverse=False),
+ sorted(ach.uninitiated, key=lambda a: a['xp'], reverse=False),
+ sorted(ach.completed, key=lambda a: a['unlock_date'], reverse=True),
+ sorted(ach.in_progress, key=lambda a: a['progress'], reverse=True),
),
(
self.ui.ach_hidden_page,
@@ -467,6 +468,7 @@ def update_game(self, rgame: RareGame):
self.ui.ach_completed_page,
self.ui.ach_progress_page,
),
+ strict=False,
):
self.ui.achievements_toolbox.setItemEnabled(self.ui.achievements_toolbox.indexOf(page), bool(group))
if bool(group):
@@ -474,33 +476,33 @@ def update_game(self, rgame: RareGame):
for item in group:
page.layout().addWidget(AchievementWidget(self.net_manager, item), alignment=Qt.AlignmentFlag.AlignTop)
else:
- self.ui.progress_field.setText(self.tr("No data"))
- self.ui.exp_field.setText(self.tr("No data"))
+ self.ui.progress_field.setText(self.tr('No data'))
+ self.ui.exp_field.setText(self.tr('No data'))
self.ui.achievements_group.setVisible(bool(ach))
self.rgame = rgame
class AchievementWidget(QFrame):
- def __init__(self, manager: QtRequests, achievement: Dict, parent=None):
+ def __init__(self, manager: QRequests, achievement: dict, parent=None):
super().__init__(parent=parent)
self.setFrameShape(QFrame.Shape.StyledPanel)
self.setFrameShadow(QFrame.Shadow.Sunken)
image = LoadingImageWidget(manager, parent=self)
image.setFixedSize(ImageSize.LibraryIcon)
- image.fetchPixmap(achievement["icon_link"])
+ image.fetchPixmap(achievement['icon_link'])
title = QLabel(
- f"{achievement['display_name']} ({achievement['xp']} XP)",
+ f'{achievement["display_name"]} ({achievement["xp"]} XP)',
parent=self,
)
title.setWordWrap(True)
- description = QLabel(achievement["description"], parent=self)
+ description = QLabel(achievement['description'], parent=self)
description.setWordWrap(True)
- unlock_date = achievement["unlock_date"].astimezone() if achievement["unlock_date"] else None
- unlock_date_str = f" ( On: {relative_date(unlock_date)} )" if unlock_date else ""
- progress = QLabel(f"Progress: {achievement['progress'] * 100:,.2f}% {unlock_date_str}", parent=self)
+ unlock_date = achievement['unlock_date'].astimezone() if achievement['unlock_date'] else None
+ unlock_date_str = f' ( On: {relative_date(unlock_date)} )' if unlock_date else ''
+ progress = QLabel(f'Progress: {achievement["progress"] * 100:,.2f}% {unlock_date_str}', parent=self)
if unlock_date:
progress.setToolTip(str(unlock_date))
@@ -519,10 +521,10 @@ class GameTagCheckBox(QCheckBox):
checkStateChangedData = Signal(Qt.CheckState, str)
tag_translations = {
- "backlog": QCoreApplication.translate("GameTagCheckBox", "Backlog", None),
- "completed": QCoreApplication.translate("GameTagCheckBox", "Completed", None),
- "favorite": QCoreApplication.translate("GameTagCheckBox", "Favorite", None),
- "hidden": QCoreApplication.translate("GameTagCheckBox", "Hidden", None),
+ 'backlog': QCoreApplication.translate('GameTagCheckBox', 'Backlog', None),
+ 'completed': QCoreApplication.translate('GameTagCheckBox', 'Completed', None),
+ 'favorite': QCoreApplication.translate('GameTagCheckBox', 'Favorite', None),
+ 'hidden': QCoreApplication.translate('GameTagCheckBox', 'Hidden', None),
}
def __init__(self, tag: str, parent=None):
@@ -531,14 +533,15 @@ def __init__(self, tag: str, parent=None):
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.setText(self.tag_translations.get(tag, tag))
self.tag = tag
- base_color = (int(sha1(tag.encode("utf-8")).hexdigest()[0:6], base=16) & 0x707070) | 0x0C0C0C
+ base_color = (int(sha1(tag.encode('utf-8')).hexdigest()[0:6], base=16) & 0x707070) | 0x0C0C0C
border_color = base_color | 0x3F3F3F
luminance = (
((base_color & 0xFF0000) >> 16) * 0.2126 + ((base_color & 0x00FF00) >> 8) * 0.7152 + (base_color & 0x0000FF) * 0.0722
)
- font_color = "white" if luminance < 140 else "black"
- style = "QCheckBox#{0}{{color: {1};border-color: #{2:x};background-color: #{3:x};}}".format(
- self.objectName(), font_color, border_color, base_color
+ font_color = 'white' if luminance < 140 else 'black'
+ style = (
+ f'QCheckBox#{self.objectName()}'
+ f'{{color: {font_color};border-color: #{border_color:x};background-color: #{base_color:x};}}'
)
self.setStyleSheet(style)
self.checkStateChanged.connect(self._on_state_changed)
@@ -561,9 +564,9 @@ def setText(self, text, /):
class GameTagAddDialog(ButtonDialog):
result_ready = Signal(bool, str)
- def __init__(self, rgame: RareGame, tags: Tuple[str, ...], parent=None):
+ def __init__(self, rgame: RareGame, tags: tuple[str, ...], parent=None):
super(GameTagAddDialog, self).__init__(parent=parent)
- header = self.tr("Add tag")
+ header = self.tr('Add tag')
self.setWindowTitle(header)
self.setSubtitle(game_title(header, rgame.app_title))
@@ -575,12 +578,12 @@ def __init__(self, rgame: RareGame, tags: Tuple[str, ...], parent=None):
self.setCentralLayout(self.widget_layout)
- self.accept_button.setText(self.tr("Save"))
- self.accept_button.setIcon(qta_icon("fa.edit", "fa5s.edit"))
+ self.accept_button.setText(self.tr('Save'))
+ self.accept_button.setIcon(qta_icon('fa.edit', 'fa5s.edit'))
self.accept_button.setEnabled(False)
self.tags = tags
- self.result: Tuple = (False, "")
+ self.result: tuple = (False, '')
@Slot(str)
def __on_text_changed(self, text: str):
diff --git a/rare/components/tabs/library/details/dlcs.py b/rare/components/tabs/library/details/dlcs.py
index 12a6f1bb40..e02abd858e 100644
--- a/rare/components/tabs/library/details/dlcs.py
+++ b/rare/components/tabs/library/details/dlcs.py
@@ -1,5 +1,3 @@
-from typing import List, Optional
-
from PySide6.QtCore import Qt, Signal, Slot
from PySide6.QtGui import QShowEvent
from PySide6.QtWidgets import QFrame, QMessageBox, QToolBox
@@ -54,10 +52,10 @@ class InstalledGameDlcWidget(GameDlcWidget):
def __init__(self, rgame: RareGame, rdlc: RareGame, parent=None):
super(InstalledGameDlcWidget, self).__init__(rgame=rgame, rdlc=rdlc, parent=parent)
# lk: set object names for CSS properties
- self.ui.action_button.setObjectName("UninstallButton")
+ self.ui.action_button.setObjectName('UninstallButton')
self.ui.action_button.clicked.connect(self.uninstall_dlc)
- self.ui.action_button.setText(self.tr("Uninstall DLC"))
- self.ui.action_button.setIcon(qta_icon("ri.uninstall-line"))
+ self.ui.action_button.setText(self.tr('Uninstall DLC'))
+ self.ui.action_button.setIcon(qta_icon('ri.uninstall-line'))
# lk: don't reference `self.rdlc` here because the object has been deleted
rdlc.signals.game.uninstalled.connect(self.__uninstalled)
@@ -75,10 +73,10 @@ class AvailableGameDlcWidget(GameDlcWidget):
def __init__(self, rgame: RareGame, rdlc: RareGame, parent=None):
super(AvailableGameDlcWidget, self).__init__(rgame=rgame, rdlc=rdlc, parent=parent)
# lk: set object names for CSS properties
- self.ui.action_button.setObjectName("InstallButton")
+ self.ui.action_button.setObjectName('InstallButton')
self.ui.action_button.clicked.connect(self.install_dlc)
- self.ui.action_button.setText(self.tr("Install DLC"))
- self.ui.action_button.setIcon(qta_icon("ri.install-line"))
+ self.ui.action_button.setText(self.tr('Install DLC'))
+ self.ui.action_button.setIcon(qta_icon('ri.install-line'))
# lk: don't reference `self.rdlc` here because the object has been deleted
rdlc.signals.game.installed.connect(self.__installed)
@@ -91,8 +89,8 @@ def install_dlc(self):
if not self.rgame.is_installed:
QMessageBox.warning(
self,
- self.tr("Error"),
- self.tr("Base Game is not installed. Please install {} first").format(self.rgame.app_title),
+ self.tr('Error'),
+ self.tr('Base Game is not installed. Please install {} first').format(self.rgame.app_title),
)
return
self.rdlc.install()
@@ -107,26 +105,26 @@ def __init__(self, rcore: RareCore, parent=None):
self.core = rcore.core()
self.signals = rcore.signals()
- self.rgame: Optional[RareGame] = None
+ self.rgame: RareGame | None = None
- def list_installed(self) -> List[InstalledGameDlcWidget]:
+ def list_installed(self) -> list[InstalledGameDlcWidget]:
return self.ui.installed_dlc_container.findChildren(
InstalledGameDlcWidget, options=Qt.FindChildOption.FindDirectChildrenOnly
)
- def list_available(self) -> List[AvailableGameDlcWidget]:
+ def list_available(self) -> list[AvailableGameDlcWidget]:
return self.ui.available_dlc_container.findChildren(
AvailableGameDlcWidget, options=Qt.FindChildOption.FindDirectChildrenOnly
)
- def get_installed(self, app_name: str) -> Optional[InstalledGameDlcWidget]:
+ def get_installed(self, app_name: str) -> InstalledGameDlcWidget | None:
return self.ui.installed_dlc_container.findChild(
InstalledGameDlcWidget,
name=widget_object_name(InstalledGameDlcWidget, app_name),
options=Qt.FindChildOption.FindDirectChildrenOnly,
)
- def get_available(self, app_name: str) -> Optional[AvailableGameDlcWidget]:
+ def get_available(self, app_name: str) -> AvailableGameDlcWidget | None:
return self.ui.available_dlc_container.findChild(
AvailableGameDlcWidget,
name=widget_object_name(AvailableGameDlcWidget, app_name),
diff --git a/rare/components/tabs/library/details/environ.py b/rare/components/tabs/library/details/environ.py
new file mode 100644
index 0000000000..362d6d21f3
--- /dev/null
+++ b/rare/components/tabs/library/details/environ.py
@@ -0,0 +1,12 @@
+from rare.components.tabs.settings.environ import EnvironSettingsBase
+from rare.models.game import RareGame
+from rare.shared import RareCore
+
+
+class LocalEnvironSettings(EnvironSettingsBase):
+ def __init__(self, rcore: RareCore, parent=None):
+ super(LocalEnvironSettings, self).__init__(rcore, parent=parent)
+
+ def load_settings(self, rgame: RareGame):
+ self.set_title.emit(rgame.app_title)
+ self.app_name = rgame.app_name
diff --git a/rare/components/tabs/library/details/game.py b/rare/components/tabs/library/details/game.py
index 09ada8b422..f4f8a57ca5 100644
--- a/rare/components/tabs/library/details/game.py
+++ b/rare/components/tabs/library/details/game.py
@@ -1,6 +1,5 @@
import os
from logging import getLogger
-from typing import Tuple
from legendary.models.game import Game, InstalledGame
from PySide6.QtCore import Qt, Slot
@@ -8,7 +7,6 @@
from PySide6.QtWidgets import QComboBox, QFileDialog, QLineEdit
from rare.components.tabs.settings.game import GameSettingsBase
-from rare.components.tabs.settings.widgets.env_vars import EnvVars
from rare.components.tabs.settings.widgets.launch import LaunchSettingsBase
from rare.components.tabs.settings.widgets.wrappers import WrapperSettings
from rare.models.game import RareGame
@@ -17,7 +15,7 @@
from rare.utils import config_helper as config
from rare.widgets.indicator_edit import IndicatorReasonsCommon, PathEdit
-logger = getLogger("LocalGameSettings")
+logger = getLogger('LocalGameSettings')
class LocalWrapperSettings(WrapperSettings):
@@ -33,51 +31,51 @@ def __init__(self, rcore: RareCore, parent=None):
self.igame: InstalledGame = None
self.skip_update_combo = QComboBox(self)
- self.skip_update_combo.addItem(self.tr("Default"), None)
- self.skip_update_combo.addItem(self.tr("No"), "false")
- self.skip_update_combo.addItem(self.tr("Yes"), "true")
+ self.skip_update_combo.addItem(self.tr('Default'), None)
+ self.skip_update_combo.addItem(self.tr('No'), 'false')
+ self.skip_update_combo.addItem(self.tr('Yes'), 'true')
self.skip_update_combo.currentIndexChanged.connect(self.__skip_update_changed)
self.offline_combo = QComboBox(self)
- self.offline_combo.addItem(self.tr("Default"), None)
- self.offline_combo.addItem(self.tr("No"), "false")
- self.offline_combo.addItem(self.tr("Yes"), "true")
+ self.offline_combo.addItem(self.tr('Default'), None)
+ self.offline_combo.addItem(self.tr('No'), 'false')
+ self.offline_combo.addItem(self.tr('Yes'), 'true')
self.offline_combo.currentIndexChanged.connect(self.__offline_changed)
- self.override_exe_name_filters: Tuple[str, ...] = (
- "*.exe",
- "*.app",
- "*.bat",
- "*.ps1",
- "*.sh",
+ self.override_exe_name_filters: tuple[str, ...] = (
+ '*.exe',
+ '*.app',
+ '*.bat',
+ '*.ps1',
+ '*.sh',
)
self.override_exe_edit = PathEdit(
file_mode=QFileDialog.FileMode.ExistingFile,
name_filters=self.override_exe_name_filters,
- placeholder=self.tr("Relative path to the replacement executable"),
+ placeholder=self.tr('Relative path to the replacement executable'),
edit_func=self.__override_exe_edit_callback,
save_func=self.__override_exe_save_callback,
parent=self,
)
self.launch_params_edit = QLineEdit(self)
- self.launch_params_edit.setPlaceholderText(self.tr("Game specific command line arguments"))
+ self.launch_params_edit.setPlaceholderText(self.tr('Game specific command line arguments'))
self.launch_params_edit.textChanged.connect(self.__launch_params_changed)
- self.main_layout.insertRow(0, self.tr("Skip update check"), self.skip_update_combo)
- self.main_layout.insertRow(1, self.tr("Offline mode"), self.offline_combo)
- self.main_layout.insertRow(2, self.tr("Launch parameters"), self.launch_params_edit)
- self.main_layout.insertRow(3, self.tr("Override executable"), self.override_exe_edit)
+ self.main_layout.insertRow(0, self.tr('Skip update check'), self.skip_update_combo)
+ self.main_layout.insertRow(1, self.tr('Offline mode'), self.offline_combo)
+ self.main_layout.insertRow(2, self.tr('Launch parameters'), self.launch_params_edit)
+ self.main_layout.insertRow(3, self.tr('Override executable'), self.override_exe_edit)
def showEvent(self, a0: QShowEvent):
if a0.spontaneous():
return super().showEvent(a0)
- skip_update = config.get_option(self.app_name, "skip_update_check", fallback=None)
+ skip_update = config.get_option(self.app_name, 'skip_update_check', fallback=None)
self.skip_update_combo.setCurrentIndex(self.offline_combo.findData(skip_update, Qt.ItemDataRole.UserRole))
- offline = config.get_option(self.app_name, "offline", fallback=None)
+ offline = config.get_option(self.app_name, 'offline', fallback=None)
self.offline_combo.setCurrentIndex(self.offline_combo.findData(offline, Qt.ItemDataRole.UserRole))
if self.igame:
@@ -85,12 +83,12 @@ def showEvent(self, a0: QShowEvent):
self.override_exe_edit.setRootPath(self.igame.install_path)
else:
self.offline_combo.setEnabled(False)
- self.override_exe_edit.setRootPath(os.path.expanduser("~/"))
+ self.override_exe_edit.setRootPath(os.path.expanduser('~/'))
- launch_params = config.get_option(self.app_name, "start_params", "")
+ launch_params = config.get_option(self.app_name, 'start_params', '')
self.launch_params_edit.setText(launch_params)
- override_exe = config.get_option(self.app_name, "override_exe", fallback="")
+ override_exe = config.get_option(self.app_name, 'override_exe', fallback='')
self.override_exe_edit.setText(override_exe)
return super().showEvent(a0)
@@ -98,9 +96,9 @@ def showEvent(self, a0: QShowEvent):
@Slot(int)
def __skip_update_changed(self, index):
data = self.skip_update_combo.itemData(index, Qt.ItemDataRole.UserRole)
- config.adjust_option(self.app_name, "skip_update_check", data)
+ config.adjust_option(self.app_name, 'skip_update_check', data)
- def __override_exe_edit_callback(self, path: str) -> Tuple[bool, str, int]:
+ def __override_exe_edit_callback(self, path: str) -> tuple[bool, str, int]:
if not path or self.igame is None:
return True, path, IndicatorReasonsCommon.VALID
if not os.path.isabs(path):
@@ -113,21 +111,21 @@ def __override_exe_edit_callback(self, path: str) -> Tuple[bool, str, int]:
if not os.path.exists(path):
return False, path, IndicatorReasonsCommon.WRONG_PATH
- if not path.endswith(tuple(map(lambda s: s.replace("*", ""), self.override_exe_name_filters))):
+ if not path.endswith(tuple(map(lambda s: s.replace('*', ''), self.override_exe_name_filters))):
return False, path, IndicatorReasonsCommon.WRONG_PATH
path = os.path.relpath(path, self.igame.install_path)
return True, path, IndicatorReasonsCommon.VALID
def __override_exe_save_callback(self, path: str):
- config.adjust_option(self.app_name, "override_exe", path)
+ config.adjust_option(self.app_name, 'override_exe', path)
@Slot(int)
def __offline_changed(self, index):
data = self.skip_update_combo.itemData(index, Qt.ItemDataRole.UserRole)
- config.adjust_option(self.app_name, "offline", data)
+ config.adjust_option(self.app_name, 'offline', data)
def __launch_params_changed(self, value) -> None:
- config.adjust_option(self.app_name, "start_params", value)
+ config.adjust_option(self.app_name, 'start_params', value)
def load_settings(self, rgame: RareGame):
self.game = rgame.game
@@ -136,19 +134,11 @@ def load_settings(self, rgame: RareGame):
self.wrappers_widget.load_settings(rgame.app_name)
-class LocalEnvVars(EnvVars):
- def load_settings(self, app_name):
- self.app_name = app_name
-
-
class LocalGameSettings(GameSettingsBase):
def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
- super(LocalGameSettings, self).__init__(
- settings, rcore, launch_widget=LocalLaunchSettings, envvar_widget=LocalEnvVars, parent=parent
- )
+ super(LocalGameSettings, self).__init__(settings, rcore, launch_widget=LocalLaunchSettings, parent=parent)
def load_settings(self, rgame: RareGame):
self.set_title.emit(rgame.app_title)
self.app_name = rgame.app_name
self.launch.load_settings(rgame)
- self.env_vars.load_settings(rgame.app_name)
diff --git a/rare/components/tabs/library/head_bar.py b/rare/components/tabs/library/head_bar.py
index 9618d12b40..6482f47dda 100644
--- a/rare/components/tabs/library/head_bar.py
+++ b/rare/components/tabs/library/head_bar.py
@@ -35,23 +35,23 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.filter = QComboBox(self)
filters = {
- LibraryFilter.ALL: self.tr("All games"),
- LibraryFilter.INSTALLED: self.tr("Installed"),
- LibraryFilter.OFFLINE: self.tr("Offline"),
- LibraryFilter.HIDDEN: self.tr("Hidden"),
- LibraryFilter.FAVORITES: self.tr("Favorites"),
+ LibraryFilter.ALL: self.tr('All games'),
+ LibraryFilter.INSTALLED: self.tr('Installed'),
+ LibraryFilter.OFFLINE: self.tr('Offline'),
+ LibraryFilter.HIDDEN: self.tr('Hidden'),
+ LibraryFilter.FAVORITES: self.tr('Favorites'),
}
for data, text in filters.items():
self.filter.addItem(text, data)
if self.rcore.bit32_games:
- self.filter.addItem(self.tr("Only 32bit"), LibraryFilter.WIN32)
+ self.filter.addItem(self.tr('Only 32bit'), LibraryFilter.WIN32)
if self.rcore.mac_games:
- self.filter.addItem(self.tr("Only macOS"), LibraryFilter.MAC)
+ self.filter.addItem(self.tr('Only macOS'), LibraryFilter.MAC)
if self.rcore.non_asset_games:
- self.filter.addItem(self.tr("Exclude non-asset"), LibraryFilter.INSTALLABLE)
- self.filter.addItem(self.tr("Include Unreal"), LibraryFilter.INCLUDE_UE)
- self.filter.addItem(self.tr("Android"), LibraryFilter.ANDROID)
+ self.filter.addItem(self.tr('Exclude non-asset'), LibraryFilter.INSTALLABLE)
+ self.filter.addItem(self.tr('Include Unreal'), LibraryFilter.INCLUDE_UE)
+ self.filter.addItem(self.tr('Android'), LibraryFilter.ANDROID)
try:
_filter = LibraryFilter(self.settings.get_value(app_settings.library_filter))
@@ -60,7 +60,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
else:
self.filter.setCurrentIndex(index)
except (TypeError, ValueError) as e:
- self.logger.error("Error while loading library: %s", e)
+ self.logger.error('Error while loading library: %s', e)
self.settings.set_value(app_settings.library_filter, app_settings.library_filter.default)
_filter = LibraryFilter(app_settings.library_filter.default)
self.filter.setCurrentIndex(self.filter.findData(_filter, Qt.ItemDataRole.UserRole))
@@ -68,10 +68,10 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.order = QComboBox(parent=self)
sortings = {
- LibraryOrder.TITLE: self.tr("Title"),
- LibraryOrder.RECENT: self.tr("Recently played"),
- LibraryOrder.NEWEST: self.tr("Newest"),
- LibraryOrder.OLDEST: self.tr("Oldest"),
+ LibraryOrder.TITLE: self.tr('Title'),
+ LibraryOrder.RECENT: self.tr('Recently played'),
+ LibraryOrder.NEWEST: self.tr('Newest'),
+ LibraryOrder.OLDEST: self.tr('Oldest'),
}
for data, text in sortings.items():
self.order.addItem(text, data)
@@ -83,35 +83,35 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
else:
self.order.setCurrentIndex(index)
except (TypeError, ValueError) as e:
- self.logger.error("Error while loading library: %s", e)
+ self.logger.error('Error while loading library: %s', e)
self.settings.set_value(app_settings.library_order, app_settings.library_order.default)
_order = LibraryOrder(app_settings.library_order.default)
self.order.setCurrentIndex(self.order.findData(_order, Qt.ItemDataRole.UserRole))
self.order.currentIndexChanged.connect(self.__order_changed)
- self.search_bar = ButtonLineEdit("fa5s.search", placeholder_text=self.tr("Search (use :: to filter by tag)"))
+ self.search_bar = ButtonLineEdit('fa5s.search', placeholder_text=self.tr('Search (use :: to filter by tag)'))
self.search_bar.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Preferred)
- self.search_bar.setObjectName("SearchBar")
+ self.search_bar.setObjectName('SearchBar')
self.search_bar.setMinimumWidth(250)
- installed_tooltip = self.tr("Installed games")
+ installed_tooltip = self.tr('Installed games')
self.installed_icon = QLabel(parent=self)
- self.installed_icon.setPixmap(qta_icon("ph.floppy-disk-back-fill").pixmap(QSize(16, 16)))
+ self.installed_icon.setPixmap(qta_icon('ph.floppy-disk-back-fill').pixmap(QSize(16, 16)))
self.installed_icon.setToolTip(installed_tooltip)
self.installed_label = QLabel(parent=self)
font = self.installed_label.font()
font.setBold(True)
self.installed_label.setFont(font)
self.installed_label.setToolTip(installed_tooltip)
- available_tooltip = self.tr("Available games")
+ available_tooltip = self.tr('Available games')
self.available_icon = QLabel(parent=self)
- self.available_icon.setPixmap(qta_icon("ph.floppy-disk-back-light").pixmap(QSize(16, 16)))
+ self.available_icon.setPixmap(qta_icon('ph.floppy-disk-back-light').pixmap(QSize(16, 16)))
self.available_icon.setToolTip(available_tooltip)
self.available_label = QLabel(parent=self)
self.available_label.setToolTip(available_tooltip)
self.refresh_list = QPushButton(parent=self)
- self.refresh_list.setIcon(qta_icon("fa.refresh", "fa5s.sync")) # Reload icon
+ self.refresh_list.setIcon(qta_icon('fa.refresh', 'fa5s.sync')) # Reload icon
self.refresh_list.clicked.connect(self.__refresh_clicked)
left_layout = QHBoxLayout()
@@ -141,7 +141,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
def __game_tags_updated(self):
if self.search_bar.completer():
self.search_bar.completer().deleteLater()
- wordlist = tuple(map(lambda x: "::" + x, self.rcore.game_tags))
+ wordlist = tuple(map(lambda x: '::' + x, self.rcore.game_tags))
completer = QCompleter(wordlist, self.search_bar)
completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
self.search_bar.setCompleter(completer)
@@ -179,16 +179,16 @@ class SelectViewWidget(QWidget):
def __init__(self, icon_view: bool, parent=None):
super(SelectViewWidget, self).__init__(parent=parent)
self.icon_button = QPushButton(self)
- self.icon_button.setObjectName(f"{type(self).__name__}Button")
+ self.icon_button.setObjectName(f'{type(self).__name__}Button')
self.list_button = QPushButton(self)
- self.list_button.setObjectName(f"{type(self).__name__}Button")
+ self.list_button.setObjectName(f'{type(self).__name__}Button')
if icon_view:
- self.icon_button.setIcon(qta_icon("mdi.view-grid-outline", "ei.th-large", color="orange"))
- self.list_button.setIcon(qta_icon("fa5s.list", "ei.th-list", color="#eee"))
+ self.icon_button.setIcon(qta_icon('mdi.view-grid-outline', 'ei.th-large', color='orange'))
+ self.list_button.setIcon(qta_icon('fa5s.list', 'ei.th-list', color='#eee'))
else:
- self.icon_button.setIcon(qta_icon("mdi.view-grid-outline", "ei.th-large", color="#eee"))
- self.list_button.setIcon(qta_icon("fa5s.list", "ei.th-list", color="orange"))
+ self.icon_button.setIcon(qta_icon('mdi.view-grid-outline', 'ei.th-large', color='#eee'))
+ self.list_button.setIcon(qta_icon('fa5s.list', 'ei.th-list', color='orange'))
self.icon_button.clicked.connect(self.icon)
self.list_button.clicked.connect(self.list)
@@ -201,11 +201,11 @@ def __init__(self, icon_view: bool, parent=None):
self.setLayout(layout)
def icon(self):
- self.icon_button.setIcon(qta_icon("mdi.view-grid-outline", "ei.th-large", color="orange"))
- self.list_button.setIcon(qta_icon("fa5s.list", "ei.th-list", color="#eee"))
+ self.icon_button.setIcon(qta_icon('mdi.view-grid-outline', 'ei.th-large', color='orange'))
+ self.list_button.setIcon(qta_icon('fa5s.list', 'ei.th-list', color='#eee'))
self.toggled.emit(True)
def list(self):
- self.icon_button.setIcon(qta_icon("mdi.view-grid-outline", "ei.th-large", color="#eee"))
- self.list_button.setIcon(qta_icon("fa5s.list", "ei.th-list", color="orange"))
+ self.icon_button.setIcon(qta_icon('mdi.view-grid-outline', 'ei.th-large', color='#eee'))
+ self.list_button.setIcon(qta_icon('fa5s.list', 'ei.th-list', color='orange'))
self.toggled.emit(False)
diff --git a/rare/components/tabs/library/widgets/__init__.py b/rare/components/tabs/library/widgets/__init__.py
index e9d37cffa5..0af343c954 100644
--- a/rare/components/tabs/library/widgets/__init__.py
+++ b/rare/components/tabs/library/widgets/__init__.py
@@ -1,5 +1,5 @@
from abc import abstractmethod
-from typing import Tuple, Type, TypeVar, Union
+from typing import TypeVar
from PySide6.QtCore import QObject, Qt, Slot
from PySide6.QtWidgets import QScrollArea, QVBoxLayout, QWidget
@@ -12,7 +12,7 @@
from .icon_game_widget import IconGameWidget
from .list_game_widget import ListGameWidget
-ViewWidget = TypeVar("ViewWidget", IconGameWidget, ListGameWidget)
+ViewWidget = TypeVar('ViewWidget', IconGameWidget, ListGameWidget)
class ViewContainer(QWidget):
@@ -20,29 +20,29 @@ def __init__(self, rcore: RareCore, parent=None):
super().__init__(parent=parent)
self.rcore: RareCore = rcore
- def _add_widget(self, widget_type: Type[ViewWidget], rgame: RareGame) -> ViewWidget:
+ def _add_widget(self, widget_type: type[ViewWidget], rgame: RareGame) -> ViewWidget:
widget = widget_type(rgame, self)
self.layout().addWidget(widget)
return widget
__is_visible = {
- LibraryFilter.HIDDEN: lambda x: "hidden" in x.metadata.tags,
- LibraryFilter.FAVORITES: lambda x: "favorite" in x.metadata.tags,
- LibraryFilter.INSTALLED: lambda x: x.is_installed and not x.is_unreal and "hidden" not in x.metadata.tags,
- LibraryFilter.OFFLINE: lambda x: x.can_run_offline and not x.is_unreal and "hidden" not in x.metadata.tags,
- LibraryFilter.WIN32: lambda x: x.is_win32 and not x.is_unreal and "hidden" not in x.metadata.tags,
- LibraryFilter.MAC: lambda x: x.is_mac and not x.is_unreal and "hidden" not in x.metadata.tags,
- LibraryFilter.INSTALLABLE: lambda x: not x.is_non_asset and not x.is_unreal and "hidden" not in x.metadata.tags,
- LibraryFilter.INCLUDE_UE: lambda x: not x.is_android_only and "hidden" not in x.metadata.tags,
+ LibraryFilter.HIDDEN: lambda x: 'hidden' in x.metadata.tags,
+ LibraryFilter.FAVORITES: lambda x: 'favorite' in x.metadata.tags,
+ LibraryFilter.INSTALLED: lambda x: x.is_installed and not x.is_unreal and 'hidden' not in x.metadata.tags,
+ LibraryFilter.OFFLINE: lambda x: x.can_run_offline and not x.is_unreal and 'hidden' not in x.metadata.tags,
+ LibraryFilter.WIN32: lambda x: x.is_win32 and not x.is_unreal and 'hidden' not in x.metadata.tags,
+ LibraryFilter.MAC: lambda x: x.is_mac and not x.is_unreal and 'hidden' not in x.metadata.tags,
+ LibraryFilter.INSTALLABLE: lambda x: not x.is_non_asset and not x.is_unreal and 'hidden' not in x.metadata.tags,
+ LibraryFilter.INCLUDE_UE: lambda x: not x.is_android_only and 'hidden' not in x.metadata.tags,
LibraryFilter.ANDROID: lambda x: x.is_android_only,
- LibraryFilter.ALL: lambda x: not x.is_unreal and not x.is_android_only and "hidden" not in x.metadata.tags,
+ LibraryFilter.ALL: lambda x: not x.is_unreal and not x.is_android_only and 'hidden' not in x.metadata.tags,
}
- def __visibility(self, widget: ViewWidget, library_filter, search_text) -> Tuple[bool, float]:
+ def __visibility(self, widget: ViewWidget, library_filter, search_text) -> tuple[bool, float]:
name_search = True
tag_search = False
- if search_text.startswith("::"):
- search_text = search_text.removeprefix("::")
+ if search_text.startswith('::'):
+ search_text = search_text.removeprefix('::')
tag_search = True
name_search = False
visible = True
@@ -64,9 +64,9 @@ def __visibility(self, widget: ViewWidget, library_filter, search_text) -> Tuple
def _filter_view(
self,
- widget_type: Type[ViewWidget],
+ widget_type: type[ViewWidget],
filter_by: LibraryFilter = LibraryFilter.ALL,
- search_text: str = "",
+ search_text: str = '',
):
widgets = self.findChildren(widget_type)
for iw in widgets:
@@ -74,7 +74,7 @@ def _filter_view(
iw.setOpacity(opacity)
iw.setVisible(visibility)
- def _update_view(self, widget_type: Type[ViewWidget]):
+ def _update_view(self, widget_type: type[ViewWidget]):
widgets = self.findChildren(widget_type)
app_names = {iw.rgame.app_name for iw in widgets}
games = list(self.rcore.games)
@@ -85,7 +85,7 @@ def _update_view(self, widget_type: Type[ViewWidget]):
w = widget_type(game, self)
self.layout().addWidget(w)
- def _find_widget(self, widget_type: Type[ViewWidget], app_name: str) -> ViewWidget:
+ def _find_widget(self, widget_type: type[ViewWidget], app_name: str) -> ViewWidget:
w = self.findChild(widget_type, app_name)
return w
@@ -116,13 +116,13 @@ def update_view(self):
def find_widget(self, app_name: str) -> ViewWidget:
return self._find_widget(IconGameWidget, app_name)
- def filter_view(self, filter_by: LibraryFilter, search_text: str = ""):
+ def filter_view(self, filter_by: LibraryFilter, search_text: str = ''):
self._filter_view(IconGameWidget, filter_by, search_text)
- def order_view(self, order_by: LibraryOrder, search_text: str = ""):
+ def order_view(self, order_by: LibraryOrder, search_text: str = ''):
if search_text:
- if search_text.startswith("::"):
- self.layout().sort(lambda x: search_text.removeprefix("::") not in x.widget().rgame.metadata.tags)
+ if search_text.startswith('::'):
+ self.layout().sort(lambda x: search_text.removeprefix('::') not in x.widget().rgame.metadata.tags)
else:
self.layout().sort(
lambda x: search_text not in x.widget().rgame.app_title.lower()
@@ -177,14 +177,14 @@ def update_view(self):
def find_widget(self, app_name: str) -> ViewWidget:
return self._find_widget(ListGameWidget, app_name)
- def filter_view(self, filter_by: LibraryFilter, search_text: str = ""):
+ def filter_view(self, filter_by: LibraryFilter, search_text: str = ''):
self._filter_view(ListGameWidget, filter_by, search_text)
- def order_view(self, order_by: LibraryOrder, search_text: str = ""):
+ def order_view(self, order_by: LibraryOrder, search_text: str = ''):
list_widgets = self.findChildren(ListGameWidget)
if search_text:
- if search_text.startswith("::"):
- list_widgets.sort(key=lambda x: search_text.removeprefix("::") not in x.rgame.metadata.tags)
+ if search_text.startswith('::'):
+ list_widgets.sort(key=lambda x: search_text.removeprefix('::') not in x.rgame.metadata.tags)
else:
list_widgets.sort(
key=lambda x: search_text not in x.rgame.app_title.lower() and search_text not in x.rgame.app_name.lower()
@@ -246,13 +246,13 @@ def add_game(self, rgame: RareGame):
def add_widget(self, rgame: RareGame) -> ViewWidget:
return self._container.add_widget(rgame)
- def filter_game_view(self, filter_by: LibraryFilter = None, search_text: str = ""):
+ def filter_game_view(self, filter_by: LibraryFilter = None, search_text: str = ''):
self._current_filter = filter_by if filter_by is not None else self._current_filter
self._container.filter_view(self._current_filter, search_text)
self.order_game_view(self._current_order, search_text=search_text)
@Slot()
- def order_game_view(self, order_by: LibraryOrder = None, search_text: str = ""):
+ def order_game_view(self, order_by: LibraryOrder = None, search_text: str = ''):
self._current_order = order_by if order_by is not None else self._current_order
self._container.order_view(self._current_order, search_text)
@@ -265,5 +265,5 @@ def refresh_game_view(self):
self._container.update_view()
self.order_game_view(self._current_order)
- def __find_widget(self, app_name: str) -> Union[ViewWidget, None]:
+ def __find_widget(self, app_name: str) -> ViewWidget | None:
return self._container.find_widget(app_name)
diff --git a/rare/components/tabs/library/widgets/game_widget.py b/rare/components/tabs/library/widgets/game_widget.py
index 36e23e5d2e..995831470a 100644
--- a/rare/components/tabs/library/widgets/game_widget.py
+++ b/rare/components/tabs/library/widgets/game_widget.py
@@ -25,7 +25,7 @@
from .library_widget import LibraryWidget
-logger = getLogger("GameWidget")
+logger = getLogger('GameWidget')
class GameWidget(LibraryWidget):
@@ -38,10 +38,10 @@ def __init__(self, rgame: RareGame, parent=None):
self.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)
- self.launch_action = QAction(self.tr("Launch"), self)
+ self.launch_action = QAction(self.tr('Launch'), self)
self.launch_action.triggered.connect(self._launch)
- self.install_action = QAction(self.tr("Install"), self)
+ self.install_action = QAction(self.tr('Install'), self)
self.install_action.triggered.connect(self._install)
self.desktop_link_action = QAction(self)
@@ -53,10 +53,10 @@ def __init__(self, rgame: RareGame, parent=None):
self.steam_shortcut_action = QAction(self)
self.steam_shortcut_action.triggered.connect(self._create_steam_shortcut)
- self.reload_action = QAction(self.tr("Reload Image"), self)
+ self.reload_action = QAction(self.tr('Reload Image'), self)
self.reload_action.triggered.connect(self._on_reload_image)
- self.uninstall_action = QAction(self.tr("Uninstall"), self)
+ self.uninstall_action = QAction(self.tr('Uninstall'), self)
self.uninstall_action.triggered.connect(self._uninstall)
self.update_actions()
@@ -73,27 +73,27 @@ def __init__(self, rgame: RareGame, parent=None):
self.rgame.signals.progress.finish.connect(self.hideProgress)
self.state_strings = {
- RareGame.State.IDLE: "",
- RareGame.State.RUNNING: self.tr("Running..."),
- RareGame.State.DOWNLOADING: self.tr("Downloading..."),
- RareGame.State.VERIFYING: self.tr("Verifying..."),
- RareGame.State.MOVING: self.tr("Moving..."),
- RareGame.State.UNINSTALLING: self.tr("Uninstalling..."),
- RareGame.State.SYNCING: self.tr("Syncing saves..."),
- "has_update": self.tr("Update available"),
- "needs_verification": self.tr("Needs verification"),
- "not_can_launch": self.tr("Can't launch"),
- "save_not_up_to_date": self.tr("Save is not up-to-date"),
+ RareGame.State.IDLE: '',
+ RareGame.State.RUNNING: self.tr('Running...'),
+ RareGame.State.DOWNLOADING: self.tr('Downloading...'),
+ RareGame.State.VERIFYING: self.tr('Verifying...'),
+ RareGame.State.MOVING: self.tr('Moving...'),
+ RareGame.State.UNINSTALLING: self.tr('Uninstalling...'),
+ RareGame.State.SYNCING: self.tr('Syncing saves...'),
+ 'has_update': self.tr('Update available'),
+ 'needs_verification': self.tr('Needs verification'),
+ 'not_can_launch': self.tr("Can't launch"),
+ 'save_not_up_to_date': self.tr('Save is not up-to-date'),
}
self.hover_strings = {
- "info": self.tr("Show information"),
- "install": self.tr("Install game"),
- "can_launch": self.tr("Launch game"),
- "is_foreign": self.tr("Launch offline"),
- "has_update": self.tr("Launch without version check"),
- "is_origin": self.tr("Launch/Link"),
- "not_can_launch": self.tr("Can't launch"),
+ 'info': self.tr('Show information'),
+ 'install': self.tr('Install game'),
+ 'can_launch': self.tr('Launch game'),
+ 'is_foreign': self.tr('Launch offline'),
+ 'has_update': self.tr('Launch without version check'),
+ 'is_origin': self.tr('Launch/Link'),
+ 'not_can_launch': self.tr("Can't launch"),
}
self._ui = None
@@ -138,17 +138,17 @@ def showEvent(self, a0: QShowEvent) -> None:
def update_state(self):
if self.rgame.is_idle:
if self.rgame.has_update:
- self.ui.status_label.setText(self.state_strings["has_update"])
+ self.ui.status_label.setText(self.state_strings['has_update'])
elif self.rgame.needs_verification:
- self.ui.status_label.setText(self.state_strings["needs_verification"])
+ self.ui.status_label.setText(self.state_strings['needs_verification'])
elif not self.rgame.can_launch and self.rgame.is_installed:
- self.ui.status_label.setText(self.state_strings["not_can_launch"])
+ self.ui.status_label.setText(self.state_strings['not_can_launch'])
elif (
self.rgame.igame
and (self.rgame.game.supports_cloud_saves or self.rgame.game.supports_mac_cloud_saves)
and not self.rgame.is_save_up_to_date
):
- self.ui.status_label.setText(self.state_strings["save_not_up_to_date"])
+ self.ui.status_label.setText(self.state_strings['save_not_up_to_date'])
else:
self.ui.status_label.setText(self.state_strings[self.rgame.state])
else:
@@ -175,22 +175,22 @@ def update_actions(self):
self.addAction(self.install_action)
if desktop_links_supported() and self.rgame.is_installed:
- if desktop_link_path(self.rgame.folder_name, "desktop").exists():
- self.desktop_link_action.setText(self.tr("Remove Desktop link"))
+ if desktop_link_path(self.rgame.folder_name, 'desktop').exists():
+ self.desktop_link_action.setText(self.tr('Remove Desktop link'))
else:
- self.desktop_link_action.setText(self.tr("Create Desktop link"))
+ self.desktop_link_action.setText(self.tr('Create Desktop link'))
self.addAction(self.desktop_link_action)
- if desktop_link_path(self.rgame.folder_name, "start_menu").exists():
- self.menu_link_action.setText(self.tr("Remove Start Menu link"))
+ if desktop_link_path(self.rgame.folder_name, 'start_menu').exists():
+ self.menu_link_action.setText(self.tr('Remove Start Menu link'))
else:
- self.menu_link_action.setText(self.tr("Create Start Menu link"))
+ self.menu_link_action.setText(self.tr('Create Start Menu link'))
self.addAction(self.menu_link_action)
if steam_shortcuts_supported() and self.rgame.is_installed:
if steam_shortcut_exists(self.rgame.app_name):
- self.steam_shortcut_action.setText(self.tr("Remove Steam shortcut"))
+ self.steam_shortcut_action.setText(self.tr('Remove Steam shortcut'))
else:
- self.steam_shortcut_action.setText(self.tr("Create Steam shortcut"))
+ self.steam_shortcut_action.setText(self.tr('Create Steam shortcut'))
self.addAction(self.steam_shortcut_action)
self.addAction(self.reload_action)
@@ -205,7 +205,7 @@ def eventFilter(self, a0: QObject, a1: QEvent) -> bool:
# \
# is not a QEvent object
logger.error(
- "Supplied arg1 %s with target %s is not a QEvent object",
+ 'Supplied arg1 %s with target %s is not a QEvent object',
type(a1),
type(a0),
)
@@ -213,29 +213,29 @@ def eventFilter(self, a0: QObject, a1: QEvent) -> bool:
if a0 is self.ui.launch_btn:
if a1.type() == QEvent.Type.Enter:
if not self.rgame.can_launch:
- self.ui.tooltip_label.setText(self.hover_strings["not_can_launch"])
+ self.ui.tooltip_label.setText(self.hover_strings['not_can_launch'])
elif self.rgame.is_origin:
- self.ui.tooltip_label.setText(self.hover_strings["is_origin"])
+ self.ui.tooltip_label.setText(self.hover_strings['is_origin'])
elif self.rgame.has_update:
- self.ui.tooltip_label.setText(self.hover_strings["has_update"])
+ self.ui.tooltip_label.setText(self.hover_strings['has_update'])
elif self.rgame.is_foreign and self.rgame.can_run_offline:
- self.ui.tooltip_label.setText(self.hover_strings["is_foreign"])
+ self.ui.tooltip_label.setText(self.hover_strings['is_foreign'])
elif self.rgame.can_launch:
- self.ui.tooltip_label.setText(self.hover_strings["can_launch"])
+ self.ui.tooltip_label.setText(self.hover_strings['can_launch'])
return True
if a1.type() == QEvent.Type.Leave:
- self.ui.tooltip_label.setText(self.hover_strings["info"])
+ self.ui.tooltip_label.setText(self.hover_strings['info'])
# return True
if a0 is self.ui.install_btn:
if a1.type() == QEvent.Type.Enter:
- self.ui.tooltip_label.setText(self.hover_strings["install"])
+ self.ui.tooltip_label.setText(self.hover_strings['install'])
return True
if a1.type() == QEvent.Type.Leave:
- self.ui.tooltip_label.setText(self.hover_strings["info"])
+ self.ui.tooltip_label.setText(self.hover_strings['info'])
# return True
if a0 is self:
if a1.type() == QEvent.Type.Enter:
- self.ui.tooltip_label.setText(self.hover_strings["info"])
+ self.ui.tooltip_label.setText(self.hover_strings['info'])
return super(GameWidget, self).eventFilter(a0, a1)
def mousePressEvent(self, e: QMouseEvent) -> None:
@@ -270,19 +270,19 @@ def _uninstall(self):
@Slot()
def _create_link_desktop(self):
- self._create_link(self.rgame.folder_name, "desktop")
+ self._create_link(self.rgame.folder_name, 'desktop')
@Slot()
def _create_link_start_menu(self):
- self._create_link(self.rgame.folder_name, "start_menu")
+ self._create_link(self.rgame.folder_name, 'start_menu')
@Slot(str, str)
def _create_link(self, name: str, link_type: str):
if not desktop_links_supported():
QMessageBox.warning(
self,
- self.tr("Warning"),
- self.tr("Creating shortcuts is currently unsupported on {}").format(platform.system()),
+ self.tr('Warning'),
+ self.tr('Creating shortcuts is currently unsupported on {}').format(platform.system()),
)
return
@@ -298,7 +298,7 @@ def _create_link(self, name: str, link_type: str):
):
raise PermissionError
except PermissionError:
- QMessageBox.warning(self, "Error", "Could not create shortcut.")
+ QMessageBox.warning(self, 'Error', 'Could not create shortcut.')
return
else:
if shortcut_path.exists():
diff --git a/rare/components/tabs/library/widgets/icon_game_widget.py b/rare/components/tabs/library/widgets/icon_game_widget.py
index 8330f34b84..a93c2a7355 100644
--- a/rare/components/tabs/library/widgets/icon_game_widget.py
+++ b/rare/components/tabs/library/widgets/icon_game_widget.py
@@ -1,5 +1,4 @@
from logging import getLogger
-from typing import Optional
from PySide6.QtCore import QEvent, Slot
@@ -9,13 +8,13 @@
from .game_widget import GameWidget
from .icon_widget import IconWidget
-logger = getLogger("IconGameWidget")
+logger = getLogger('IconGameWidget')
class IconGameWidget(GameWidget):
def __init__(self, rgame: RareGame, parent=None):
super().__init__(rgame, parent)
- self.setObjectName(f"{rgame.app_name}")
+ self.setObjectName(f'{rgame.app_name}')
self.setFixedSize(ImageSize.LibraryTall)
self.ui = IconWidget()
self.ui.setupUi(self)
@@ -46,12 +45,12 @@ def start_progress(self):
self.rgame.get_pixmap(ImageSize.LibraryTall, False),
)
- def enterEvent(self, a0: Optional[QEvent] = None) -> None:
+ def enterEvent(self, a0: QEvent | None = None) -> None:
if a0 is not None:
a0.accept()
self.ui.enterAnimation(self)
- def leaveEvent(self, a0: Optional[QEvent] = None) -> None:
+ def leaveEvent(self, a0: QEvent | None = None) -> None:
if a0 is not None:
a0.accept()
self.ui.leaveAnimation(self)
diff --git a/rare/components/tabs/library/widgets/icon_widget.py b/rare/components/tabs/library/widgets/icon_widget.py
index 103c3a92af..19c5766149 100644
--- a/rare/components/tabs/library/widgets/icon_widget.py
+++ b/rare/components/tabs/library/widgets/icon_widget.py
@@ -14,7 +14,7 @@
from rare.widgets.elide_label import ElideLabel
-class IconWidget(object):
+class IconWidget:
def __init__(self):
self._effect = None
self._animation: QPropertyAnimation = None
@@ -30,14 +30,14 @@ def __init__(self):
def setupUi(self, widget: QWidget):
# information at top
self.status_label = ElideLabel(parent=widget)
- self.status_label.setObjectName(f"{type(self).__name__}StatusLabel")
+ self.status_label.setObjectName(f'{type(self).__name__}StatusLabel')
self.status_label.setFixedHeight(False)
self.status_label.setContentsMargins(6, 6, 6, 6)
self.status_label.setAutoFillBackground(False)
# on-hover popup
self.mini_widget = QWidget(parent=widget)
- self.mini_widget.setObjectName(f"{type(self).__name__}MiniWidget")
+ self.mini_widget.setObjectName(f'{type(self).__name__}MiniWidget')
self.mini_widget.setFixedHeight(widget.height() // 3)
self.mini_effect = QGraphicsOpacityEffect(self.mini_widget)
@@ -45,7 +45,7 @@ def setupUi(self, widget: QWidget):
# game title
self.title_label = QLabel(parent=self.mini_widget)
- self.title_label.setObjectName(f"{type(self).__name__}TitleLabel")
+ self.title_label.setObjectName(f'{type(self).__name__}TitleLabel')
self.title_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.title_label.setAlignment(Qt.AlignmentFlag.AlignVCenter)
self.title_label.setAutoFillBackground(False)
@@ -53,19 +53,19 @@ def setupUi(self, widget: QWidget):
# information below title
self.tooltip_label = ElideLabel(parent=self.mini_widget)
- self.tooltip_label.setObjectName(f"{type(self).__name__}TooltipLabel")
+ self.tooltip_label.setObjectName(f'{type(self).__name__}TooltipLabel')
self.tooltip_label.setAutoFillBackground(False)
# play button
self.launch_btn = QPushButton(parent=self.mini_widget)
- self.launch_btn.setObjectName(f"{type(self).__name__}Button")
- self.launch_btn.setIcon(qta_icon("ei.play-alt", color="white"))
+ self.launch_btn.setObjectName(f'{type(self).__name__}Button')
+ self.launch_btn.setIcon(qta_icon('ei.play-alt', color='white'))
self.launch_btn.setIconSize(QSize(20, 20))
self.launch_btn.setFixedSize(QSize(widget.width() // 4, widget.width() // 4))
self.install_btn = QPushButton(parent=self.mini_widget)
- self.install_btn.setObjectName(f"{type(self).__name__}Button")
- self.install_btn.setIcon(qta_icon("ri.install-fill", color="white"))
+ self.install_btn.setObjectName(f'{type(self).__name__}Button')
+ self.install_btn.setIcon(qta_icon('ri.install-fill', color='white'))
self.install_btn.setIconSize(QSize(20, 20))
self.install_btn.setFixedSize(QSize(widget.width() // 4, widget.width() // 4))
@@ -113,7 +113,7 @@ def translateUi(self, widget: QWidget):
pass
def enterAnimation(self, widget: QWidget):
- self._animation = QPropertyAnimation(self.mini_effect, b"opacity")
+ self._animation = QPropertyAnimation(self.mini_effect, b'opacity')
self._animation.setDuration(250)
self._animation.setStartValue(0)
self._animation.setEndValue(1)
@@ -121,7 +121,7 @@ def enterAnimation(self, widget: QWidget):
self._animation.start(QPropertyAnimation.DeletionPolicy.DeleteWhenStopped)
def leaveAnimation(self, widget: QWidget):
- self._animation = QPropertyAnimation(self.mini_effect, b"opacity")
+ self._animation = QPropertyAnimation(self.mini_effect, b'opacity')
self._animation.setDuration(150)
self._animation.setStartValue(1)
self._animation.setEndValue(0)
diff --git a/rare/components/tabs/library/widgets/library_widget.py b/rare/components/tabs/library/widgets/library_widget.py
index f1a43ff34a..2229ba8015 100644
--- a/rare/components/tabs/library/widgets/library_widget.py
+++ b/rare/components/tabs/library/widgets/library_widget.py
@@ -1,5 +1,3 @@
-from typing import List, Optional, Tuple
-
from PySide6.QtCore import QEvent, QObject, Qt, Slot
from PySide6.QtGui import (
QBrush,
@@ -26,7 +24,7 @@ def __init__(self, parent=None):
def __center_on_parent(self):
fm = QFontMetrics(self.font())
- rect = fm.boundingRect(" 100% ")
+ rect = fm.boundingRect(' 100% ')
rect.moveCenter(self.parent().contentsRect().center())
self.setGeometry(rect)
@@ -52,15 +50,15 @@ def eventFilter(self, a0: QObject, a1: QEvent) -> bool:
return False
@staticmethod
- def calculateColors(image: QImage) -> Tuple[QColor, QColor]:
- color: List[int] = [0, 0, 0]
+ def calculateColors(image: QImage) -> tuple[QColor, QColor]:
+ color: list[int] = [0, 0, 0]
# take the two diagonals of the center square section
min_d = min(image.width(), image.height())
origin_w = (image.width() - min_d) // 2
origin_h = (image.height() - min_d) // 2
- for x, y in zip(range(origin_w, min_d), range(origin_h, min_d)):
+ for x, y in zip(range(origin_w, min_d), range(origin_h, min_d), strict=False):
pixel = image.pixelColor(x, y).getRgb()
- color = list(map(lambda t: sum(t) // 2, zip(pixel[:3], color)))
+ color = list(map(lambda t: sum(t) // 2, zip(pixel[:3], color, strict=False)))
# take the V component of the HSV color
fg_color = QColor(0, 0, 0) if QColor(*color).value() < 127 else QColor(255, 255, 255)
bg_color = QColor(*map(lambda c: 255 - c, color))
@@ -68,11 +66,11 @@ def calculateColors(image: QImage) -> Tuple[QColor, QColor]:
def setStyleSheetColors(self, bg: QColor, fg: QColor, brd: QColor):
sheet = (
- f"QLabel#{type(self).__name__} {{"
- f"background-color: rgba({bg.red()}, {bg.green()}, {bg.blue()}, 65%);"
- f"color: rgb({fg.red()}, {fg.green()}, {fg.blue()});"
- f"border-color: rgb({brd.red()}, {brd.green()}, {brd.blue()});"
- f"}}"
+ f'QLabel#{type(self).__name__} {{'
+ f'background-color: rgba({bg.red()}, {bg.green()}, {bg.blue()}, 65%);'
+ f'color: rgb({fg.red()}, {fg.green()}, {fg.blue()});'
+ f'border-color: rgb({brd.red()}, {brd.green()}, {brd.blue()});'
+ f'}}'
)
self.setStyleSheet(sheet)
@@ -84,8 +82,8 @@ def __init__(self, parent=None) -> None:
self.progress_label.setVisible(False)
self.progressPixmap = self.horizontalProgressPixmap
- self._color_pixmap: Optional[QPixmap] = None
- self._gray_pixmap: Optional[QPixmap] = None
+ self._color_pixmap: QPixmap | None = None
+ self._gray_pixmap: QPixmap | None = None
# lk: keep percentage to not over-generate the image
self._progress: int = -1
@@ -151,7 +149,7 @@ def showProgress(self, color_pm: QPixmap, gray_pm: QPixmap) -> None:
@Slot(int)
def updateProgress(self, progress: int):
- self.progress_label.setText(f"{progress:02}%")
+ self.progress_label.setText(f'{progress:02}%')
if progress > self._progress:
self._progress = progress
self.setPixmap(self.progressPixmap(self._color_pixmap, self._gray_pixmap, progress))
diff --git a/rare/components/tabs/library/widgets/list_game_widget.py b/rare/components/tabs/library/widgets/list_game_widget.py
index 39d08dcbcc..8a197d76b9 100644
--- a/rare/components/tabs/library/widgets/list_game_widget.py
+++ b/rare/components/tabs/library/widgets/list_game_widget.py
@@ -18,7 +18,7 @@
from .game_widget import GameWidget
from .list_widget import ListWidget
-logger = getLogger("ListGameWidget")
+logger = getLogger('ListGameWidget')
class ListGameWidget(GameWidget):
@@ -26,7 +26,7 @@ def __init__(self, rgame: RareGame, parent=None):
super().__init__(rgame, parent)
self.progressPixmap = self.verticalProgressPixmap
- self.setObjectName(f"{rgame.app_name}")
+ self.setObjectName(f'{rgame.app_name}')
self.ui = ListWidget()
self.ui.setupUi(self)
@@ -38,12 +38,12 @@ def __init__(self, rgame: RareGame, parent=None):
self.ui.launch_btn.setEnabled(self.rgame.can_launch)
- self.ui.launch_btn.setText(self.tr("Launch") if not self.rgame.is_origin else self.tr("Link/Play"))
+ self.ui.launch_btn.setText(self.tr('Launch') if not self.rgame.is_origin else self.tr('Link/Play'))
self.ui.developer_label.setText(self.rgame.developer)
# self.version_label.setVisible(self.is_installed)
if self.rgame.igame:
self.ui.version_label.setText(self.rgame.version)
- self.ui.size_label.setText(format_size(self.rgame.install_size) if self.rgame.install_size else "")
+ self.ui.size_label.setText(format_size(self.rgame.install_size) if self.rgame.install_size else '')
self.update_state()
diff --git a/rare/components/tabs/library/widgets/list_widget.py b/rare/components/tabs/library/widgets/list_widget.py
index 337f964607..4034b3331b 100644
--- a/rare/components/tabs/library/widgets/list_widget.py
+++ b/rare/components/tabs/library/widgets/list_widget.py
@@ -13,7 +13,7 @@
from rare.widgets.elide_label import ElideLabel
-class ListWidget(object):
+class ListWidget:
def __init__(self):
self.title_label = None
self.status_label = None
@@ -27,26 +27,26 @@ def __init__(self):
def setupUi(self, widget: QWidget):
self.title_label = ElideLabel(parent=widget)
- self.title_label.setObjectName(f"{type(self).__name__}TitleLabel")
+ self.title_label.setObjectName(f'{type(self).__name__}TitleLabel')
self.title_label.setWordWrap(False)
self.status_label = QLabel(parent=widget)
- self.status_label.setObjectName(f"{type(self).__name__}StatusLabel")
+ self.status_label.setObjectName(f'{type(self).__name__}StatusLabel')
self.status_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
self.tooltip_label = QLabel(parent=widget)
- self.tooltip_label.setObjectName(f"{type(self).__name__}TooltipLabel")
+ self.tooltip_label.setObjectName(f'{type(self).__name__}TooltipLabel')
self.tooltip_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
self.install_btn = QPushButton(parent=widget)
- self.install_btn.setObjectName(f"{type(self).__name__}Button")
- self.install_btn.setIcon(qta_icon("ri.install-line"))
+ self.install_btn.setObjectName(f'{type(self).__name__}Button')
+ self.install_btn.setIcon(qta_icon('ri.install-line'))
self.install_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.install_btn.setFixedWidth(120)
self.launch_btn = QPushButton(parent=widget)
- self.launch_btn.setObjectName(f"{type(self).__name__}Button")
- self.launch_btn.setIcon(qta_icon("ei.play-alt"))
+ self.launch_btn.setObjectName(f'{type(self).__name__}Button')
+ self.launch_btn.setIcon(qta_icon('ei.play-alt'))
self.launch_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.launch_btn.setFixedWidth(120)
@@ -58,15 +58,15 @@ def setupUi(self, widget: QWidget):
self.install_btn.setFocusPolicy(Qt.FocusPolicy.NoFocus)
self.developer_label = ElideLabel(parent=widget)
- self.developer_label.setObjectName(f"{type(self).__name__}InfoLabel")
+ self.developer_label.setObjectName(f'{type(self).__name__}InfoLabel')
self.developer_label.setFixedWidth(120)
self.version_label = ElideLabel(parent=widget)
- self.version_label.setObjectName(f"{type(self).__name__}InfoLabel")
+ self.version_label.setObjectName(f'{type(self).__name__}InfoLabel')
self.version_label.setFixedWidth(120)
self.size_label = ElideLabel(parent=widget)
- self.size_label.setObjectName(f"{type(self).__name__}InfoLabel")
+ self.size_label.setObjectName(f'{type(self).__name__}InfoLabel')
self.size_label.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
self.size_label.setFixedWidth(60)
@@ -110,4 +110,4 @@ def setupUi(self, widget: QWidget):
self.translateUi(widget)
def translateUi(self, widget: QWidget):
- self.install_btn.setText(widget.tr("Install"))
+ self.install_btn.setText(widget.tr('Install'))
diff --git a/rare/components/tabs/settings/__init__.py b/rare/components/tabs/settings/__init__.py
index 3bb248221f..299764c5c6 100644
--- a/rare/components/tabs/settings/__init__.py
+++ b/rare/components/tabs/settings/__init__.py
@@ -9,6 +9,7 @@
from .about import About
from .compat import GlobalCompatSettings
from .debug import DebugSettings
+from .environ import GlobalEnvironSettings
from .game import GlobalGameSettings
from .legendary import LegendarySettings
from .rare import RareSettings
@@ -21,30 +22,33 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
super(SettingsTab, self).__init__(parent=parent)
rare_settings = RareSettings(settings, rcore, self)
- self.rare_index = self.addTab(rare_settings, "Rare")
+ self.rare_index = self.addTab(rare_settings, 'Rare')
legendary_settings = LegendarySettings(settings, rcore, self)
- self.legendary_index = self.addTab(legendary_settings, "Legendary")
+ self.legendary_index = self.addTab(legendary_settings, 'Legendary')
game_settings = GlobalGameSettings(settings, rcore, self)
- self.game_index = self.addTab(game_settings, self.tr("Defaults"))
+ self.game_index = self.addTab(game_settings, self.tr('Defaults'))
- if pf.system() != "Windows":
+ if pf.system() != 'Windows':
compat_settings = GlobalCompatSettings(settings, rcore, self)
- self.compat_index = self.addTab(compat_settings, self.tr("Compatibility"))
+ self.compat_index = self.addTab(compat_settings, self.tr('Compatibility'))
+
+ environ_settings = GlobalEnvironSettings(rcore, self)
+ self.environ_index = self.addTab(environ_settings, self.tr('Environment'))
self.about = About(self)
- title = self.tr("About")
+ title = self.tr('About')
self.about_index = self.addTab(self.about, title, title)
self.about.update_available.connect(self._on_update_available)
self.about.update_available.connect(self.update_available)
if rcore.args().debug:
- title = self.tr("Debug")
+ title = self.tr('Debug')
self.debug_index = self.addTab(DebugSettings(rcore.signals(), self), title, title)
self.setCurrentIndex(self.rare_index)
@Slot()
def _on_update_available(self):
- self.tabBar().setTabText(self.about_index, "About (!)")
+ self.tabBar().setTabText(self.about_index, 'About (!)')
diff --git a/rare/components/tabs/settings/about.py b/rare/components/tabs/settings/about.py
index 6dc16fe18a..5c316ded32 100644
--- a/rare/components/tabs/settings/about.py
+++ b/rare/components/tabs/settings/about.py
@@ -1,6 +1,5 @@
import webbrowser
from logging import getLogger
-from typing import Tuple
from PySide6.QtCore import Signal, Slot
from PySide6.QtGui import QShowEvent
@@ -8,16 +7,16 @@
from rare import __codename__, __version__
from rare.ui.components.tabs.settings.about import Ui_About
-from rare.utils.qt_requests import QtRequests
+from rare.utils.qrequests import QRequests
-logger = getLogger("About")
+logger = getLogger('About')
-def versiontuple(v) -> Tuple[int, ...]:
+def versiontuple(v) -> tuple[int, ...]:
try:
- return tuple(map(int, (v.split("."))))
+ return tuple(map(int, (v.split('.'))))
except Exception as e:
- logger.error("Error while parsing version %s", v)
+ logger.error('Error while parsing version %s', v)
logger.error(e)
return 99, 99, 99, 999
@@ -27,19 +26,20 @@ class About(QWidget):
def __init__(self, parent=None):
super(About, self).__init__(parent=parent)
+
self.ui = Ui_About()
self.ui.setupUi(self)
- self.ui.version.setText(f"{__version__} {__codename__}")
+ self.ui.version.setText(f'{__version__} {__codename__}')
self.ui.update_label.setEnabled(False)
self.ui.update_field.setEnabled(False)
self.ui.open_browser.setVisible(False)
self.ui.open_browser.setEnabled(False)
- self.releases_url = "https://api.github.com/repos/RareDevs/Rare/releases/latest"
+ self.releases_url = 'https://api.github.com/repos/RareDevs/Rare/releases/latest'
- self.manager = QtRequests(parent=self)
+ self.manager = QRequests(parent=self)
self.manager.get(self.releases_url, self._on_update_check_finished)
self.ui.open_browser.clicked.connect(self._on_browser_clicked)
@@ -54,21 +54,21 @@ def showEvent(self, a0: QShowEvent) -> None:
@Slot()
def _on_browser_clicked(self):
- webbrowser.open("https://github.com/RareDevs/Rare/releases/latest")
+ webbrowser.open('https://github.com/RareDevs/Rare/releases/latest')
@Slot(dict)
def _on_update_check_finished(self, data: dict):
- if latest_tag := data.get("tag_name"):
+ if latest_tag := data.get('tag_name'):
self._update_available = versiontuple(latest_tag) > versiontuple(__version__)
else:
self._update_available = False
if self._update_available:
- logger.info(f"Update available: {__version__} -> {latest_tag}")
- self.ui.update_field.setText(f"{__version__} -> {latest_tag}")
+ logger.info(f'Update available: {__version__} -> {latest_tag}')
+ self.ui.update_field.setText(f'{__version__} -> {latest_tag}')
self.update_available.emit()
else:
- self.ui.update_field.setText(self.tr("You have the latest version"))
+ self.ui.update_field.setText(self.tr('You have the latest version'))
self.ui.update_label.setEnabled(self._update_available)
self.ui.update_field.setEnabled(self._update_available)
self.ui.open_browser.setVisible(self._update_available)
diff --git a/rare/components/tabs/settings/compat.py b/rare/components/tabs/settings/compat.py
index 3566fd8a95..f2e69a3515 100644
--- a/rare/components/tabs/settings/compat.py
+++ b/rare/components/tabs/settings/compat.py
@@ -1,6 +1,4 @@
import platform as pf
-from logging import getLogger
-from typing import Type
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QHideEvent
@@ -15,12 +13,10 @@
from .widgets.runner import RunnerSettingsBase, RunnerSettingsType
from .widgets.wine import WineSettings
-if pf.system() in {"Linux", "FreeBSD"}:
+if pf.system() in {'Linux', 'FreeBSD'}:
from .widgets.overlay import MangoHudSettings
from .widgets.proton import ProtonSettings
-logger = getLogger("GlobalCompatSettings")
-
class CompatSettingsBase(QWidget, SideTabContents):
# str: option key
@@ -32,18 +28,18 @@ def __init__(
self,
settings: RareAppSettings,
rcore: RareCore,
- dxvk_hud_widget: Type[DxvkHudSettings],
- dxvk_config_widget: Type[DxvkConfigSettings],
- dxvk_nvapi_drs_widget: Type[DxvkNvapiDrsSettings],
- runner_widget: Type["RunnerSettingsType"],
- mangohud_widget: Type["MangoHudSettings"] = None,
+ dxvk_hud_widget: type[DxvkHudSettings],
+ dxvk_config_widget: type[DxvkConfigSettings],
+ dxvk_nvapi_drs_widget: type[DxvkNvapiDrsSettings],
+ runner_widget: type['RunnerSettingsType'],
+ mangohud_widget: type['MangoHudSettings'] = None,
parent=None,
):
super(CompatSettingsBase, self).__init__(parent=parent)
self.settings = settings
self.core = rcore.core()
- self.app_name: str = "default"
+ self.app_name: str = 'default'
self.runner = runner_widget(settings, rcore, self)
self.runner.environ_changed.connect(self.environ_changed)
@@ -81,7 +77,7 @@ def hideEvent(self, a0: QHideEvent):
class GlobalRunnerSettings(RunnerSettingsBase):
def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
- if pf.system() in {"Linux", "FreeBSD"}:
+ if pf.system() in {'Linux', 'FreeBSD'}:
super(GlobalRunnerSettings, self).__init__(settings, rcore, WineSettings, ProtonSettings, parent=parent)
else:
super(GlobalRunnerSettings, self).__init__(settings, rcore, WineSettings, parent=parent)
@@ -89,7 +85,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
class GlobalCompatSettings(CompatSettingsBase):
def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
- if pf.system() in {"Linux", "FreeBSD"}:
+ if pf.system() in {'Linux', 'FreeBSD'}:
super(GlobalCompatSettings, self).__init__(
settings,
rcore,
diff --git a/rare/components/tabs/settings/debug.py b/rare/components/tabs/settings/debug.py
index 08b02856e0..1c5f508dcb 100644
--- a/rare/components/tabs/settings/debug.py
+++ b/rare/components/tabs/settings/debug.py
@@ -10,11 +10,11 @@ def __init__(self, signals: GlobalSignals, parent=None):
super(DebugSettings, self).__init__(parent=parent)
self.signals = signals
- self.raise_runtime_exception_button = QPushButton("Raise Exception", self)
+ self.raise_runtime_exception_button = QPushButton('Raise Exception', self)
self.raise_runtime_exception_button.clicked.connect(self.raise_exception)
- self.restart_button = QPushButton("Restart", self)
+ self.restart_button = QPushButton('Restart', self)
self.restart_button.clicked.connect(self._on_restart_clicked)
- self.send_notification_button = QPushButton("Notify", self)
+ self.send_notification_button = QPushButton('Notify', self)
self.send_notification_button.clicked.connect(self.send_notification)
layout = QVBoxLayout(self)
@@ -28,7 +28,7 @@ def _on_restart_clicked(self):
self.signals.application.quit.emit(ExitCodes.LOGOUT)
def raise_exception(self):
- raise RuntimeError("Debug Crash")
+ raise RuntimeError('Debug Crash')
def send_notification(self):
- self.signals.application.notify.emit("Debug", "Test notification")
+ self.signals.application.notify.emit('Debug', 'Test notification')
diff --git a/rare/components/tabs/settings/environ.py b/rare/components/tabs/settings/environ.py
new file mode 100644
index 0000000000..32cbac24d7
--- /dev/null
+++ b/rare/components/tabs/settings/environ.py
@@ -0,0 +1,19 @@
+from rare.shared import RareCore
+from rare.widgets.side_tab import SideTabContents
+
+from .widgets.envvars import EnvVars
+
+
+class EnvironSettingsBase(EnvVars, SideTabContents):
+ def __init__(
+ self,
+ rcore: RareCore,
+ parent=None,
+ ):
+ super(EnvironSettingsBase, self).__init__(rcore.core(), parent=parent)
+ self.implements_scrollarea = True
+
+
+class GlobalEnvironSettings(EnvironSettingsBase):
+ def __init__(self, rcore: RareCore, parent=None):
+ super(GlobalEnvironSettings, self).__init__(rcore, parent=parent)
diff --git a/rare/components/tabs/settings/game.py b/rare/components/tabs/settings/game.py
index 538353140f..93d55a59cc 100644
--- a/rare/components/tabs/settings/game.py
+++ b/rare/components/tabs/settings/game.py
@@ -1,6 +1,3 @@
-from logging import getLogger
-from typing import Type
-
from PySide6.QtCore import Qt
from PySide6.QtGui import QHideEvent
from PySide6.QtWidgets import QVBoxLayout, QWidget
@@ -10,35 +7,28 @@
from rare.utils import config_helper as config
from rare.widgets.side_tab import SideTabContents
-from .widgets.env_vars import EnvVars
from .widgets.launch import LaunchSettingsBase, LaunchSettingsType
from .widgets.wrappers import WrapperSettings
-logger = getLogger("GlobalGameSettings")
-
class GameSettingsBase(QWidget, SideTabContents):
def __init__(
self,
settings: RareAppSettings,
rcore: RareCore,
- launch_widget: Type[LaunchSettingsType],
- envvar_widget: Type[EnvVars],
+ launch_widget: type[LaunchSettingsType],
parent=None,
):
super(GameSettingsBase, self).__init__(parent=parent)
self.implements_scrollarea = True
self.settings = settings
- self.core = rcore.core()
- self.app_name: str = "default"
+ self.app_name: str = 'default'
self.launch = launch_widget(rcore, self)
- self.env_vars = envvar_widget(self.core, self)
self.main_layout = QVBoxLayout(self)
self.main_layout.addWidget(self.launch, stretch=0)
- self.main_layout.addWidget(self.env_vars, stretch=2)
self.main_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
def hideEvent(self, a0: QHideEvent):
@@ -55,6 +45,4 @@ def __init__(self, rcore: RareCore, parent=None):
class GlobalGameSettings(GameSettingsBase):
def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
- super(GlobalGameSettings, self).__init__(
- settings, rcore, launch_widget=GlobalLaunchSettings, envvar_widget=EnvVars, parent=parent
- )
+ super(GlobalGameSettings, self).__init__(settings, rcore, launch_widget=GlobalLaunchSettings, parent=parent)
diff --git a/rare/components/tabs/settings/legendary.py b/rare/components/tabs/settings/legendary.py
index c7a5e274ac..6c01ab9420 100644
--- a/rare/components/tabs/settings/legendary.py
+++ b/rare/components/tabs/settings/legendary.py
@@ -2,7 +2,6 @@
import platform as pf
import re
from logging import getLogger
-from typing import Set, Tuple
from PySide6.QtCore import QObject, Qt, QThreadPool, Signal, Slot
from PySide6.QtGui import QHideEvent, QShowEvent
@@ -20,19 +19,17 @@
PathEdit,
)
-logger = getLogger("LegendarySettings")
-
class RefreshGameMetaWorkerSignals(QObject):
finished = Signal()
class RefreshGameMetaWorker(Worker):
- def __init__(self, core: LegendaryCore, platforms: Set[str], include_unreal: bool):
+ def __init__(self, core: LegendaryCore, platforms: set[str], include_unreal: bool):
super(RefreshGameMetaWorker, self).__init__()
self.core = core
self.signals = RefreshGameMetaWorkerSignals()
- self.platforms = platforms if platforms else {"Windows"}
+ self.platforms = platforms if platforms else {'Windows'}
self.skip_ue = not include_unreal
def run_real(self) -> None:
@@ -44,6 +41,8 @@ def run_real(self) -> None:
class LegendarySettings(QWidget):
def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
super(LegendarySettings, self).__init__(parent=parent)
+ self.logger = getLogger(type(self).__name__)
+
self.ui = Ui_LegendarySettings()
self.ui.setupUi(self)
@@ -52,10 +51,10 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.core = rcore.core()
# Platform specific installation directory for macOS games
- if pf.system() == "Darwin":
+ if pf.system() == 'Darwin':
self.mac_install_dir_edit = PathEdit(
- path=self.core.get_default_install_dir("Mac"),
- placeholder=self.tr("Default installation folder for macOS games"),
+ path=self.core.get_default_install_dir('Mac'),
+ placeholder=self.tr('Default installation folder for macOS games'),
file_mode=QFileDialog.FileMode.Directory,
edit_func=self.__path_edit_callback,
save_func=self._path_save_callback_mac,
@@ -66,7 +65,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
# Platform-independent installation directory
self.install_dir_edit = PathEdit(
path=self.core.get_default_install_dir(),
- placeholder=self.tr("Default installation folder for Windows games"),
+ placeholder=self.tr('Default installation folder for Windows games'),
file_mode=QFileDialog.FileMode.Directory,
edit_func=self.__path_edit_callback,
save_func=self._path_save_callback_win,
@@ -75,19 +74,19 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.ui.install_dir_layout.addWidget(self.install_dir_edit)
# Max Workers
- self.ui.max_worker_spin.setValue(self.core.lgd.config["Legendary"].getint("max_workers", fallback=0))
+ self.ui.max_worker_spin.setValue(self.core.lgd.config['Legendary'].getint('max_workers', fallback=0))
self.ui.max_worker_spin.valueChanged.connect(self.max_worker_save)
# Max memory
- self.ui.max_memory_spin.setValue(self.core.lgd.config["Legendary"].getint("max_memory", fallback=0))
+ self.ui.max_memory_spin.setValue(self.core.lgd.config['Legendary'].getint('max_memory', fallback=0))
self.ui.max_memory_spin.valueChanged.connect(self.max_memory_save)
# Preferred CDN
- self.ui.preferred_cdn_line.setText(self.core.lgd.config["Legendary"].get("preferred_cdn", fallback=""))
+ self.ui.preferred_cdn_line.setText(self.core.lgd.config['Legendary'].get('preferred_cdn', fallback=''))
self.ui.preferred_cdn_line.textChanged.connect(self.preferred_cdn_save)
# Disable HTTPS
- self.ui.disable_https_check.setChecked(self.core.lgd.config["Legendary"].getboolean("disable_https", fallback=False))
+ self.ui.disable_https_check.setChecked(self.core.lgd.config['Legendary'].getboolean('disable_https', fallback=False))
self.ui.disable_https_check.checkStateChanged.connect(self.disable_https_save)
# Clean metadata
@@ -95,7 +94,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.ui.clean_keep_manifests_button.clicked.connect(self._on_clean_keep_manifests_clicked)
self.locale_edit = IndicatorLineEdit(
- f"{self.core.language_code}-{self.core.country_code}",
+ f'{self.core.language_code}-{self.core.country_code}',
edit_func=self.__locale_edit_callback,
save_func=self.__locale_save_callback,
horiz_policy=QSizePolicy.Policy.Minimum,
@@ -108,7 +107,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.ui.fetch_macos_check.setChecked(self.settings.get_value(app_settings.macos_meta))
self.ui.fetch_macos_check.checkStateChanged.connect(self._on_fetch_macos_changed)
- self.ui.fetch_macos_check.setDisabled(pf.system() == "Darwin")
+ self.ui.fetch_macos_check.setDisabled(pf.system() == 'Darwin')
self.ui.fetch_unreal_check.setChecked(self.settings.get_value(app_settings.unreal_meta))
self.ui.fetch_unreal_check.checkStateChanged.connect(self._on_fetch_unreal_changed)
@@ -127,7 +126,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
def showEvent(self, a0: QShowEvent):
if a0.spontaneous():
return super().showEvent(a0)
- if pf.system() == "Darwin":
+ if pf.system() == 'Darwin':
self.mac_install_dir_edit.refresh()
self.install_dir_edit.refresh()
return super().showEvent(a0)
@@ -146,20 +145,20 @@ def _refresh_metadata(self):
self.ui.refresh_metadata_button.setDisabled(True)
platforms = set()
if self.ui.fetch_win32_check.isChecked():
- platforms.add("Win32")
+ platforms.add('Win32')
if self.ui.fetch_macos_check.isChecked():
- platforms.add("Mac")
+ platforms.add('Mac')
worker = RefreshGameMetaWorker(self.core, platforms, self.ui.fetch_unreal_check.isChecked())
worker.signals.finished.connect(self._on_refresh_worker_finished)
QThreadPool.globalInstance().start(worker)
@staticmethod
- def __locale_edit_callback(text: str) -> Tuple[bool, str, int]:
+ def __locale_edit_callback(text: str) -> tuple[bool, str, int]:
if text:
- if re.match("^[a-zA-Z]{2,3}[-_][a-zA-Z]{2,3}$", text):
- language, country = text.split("-" if "-" in text else "_")
- text = "-".join([language.lower(), country.upper()])
- if bool(re.match("^[a-z]{2,3}-[A-Z]{2,3}$", text)):
+ if re.match('^[a-zA-Z]{2,3}[-_][a-zA-Z]{2,3}$', text):
+ language, country = text.split('-' if '-' in text else '_')
+ text = '-'.join([language.lower(), country.upper()])
+ if bool(re.match('^[a-z]{2,3}-[A-Z]{2,3}$', text)):
return True, text, IndicatorReasonsCommon.VALID
else:
return False, text, IndicatorReasonsCommon.WRONG_FORMAT
@@ -168,18 +167,18 @@ def __locale_edit_callback(text: str) -> Tuple[bool, str, int]:
def __locale_save_callback(self, text: str):
if text:
- self.core.egs.language_code, self.core.egs.country_code = text.split("-")
- self.core.lgd.config.set("Legendary", "locale", text)
+ self.core.egs.language_code, self.core.egs.country_code = text.split('-')
+ self.core.lgd.config.set('Legendary', 'locale', text)
else:
- self.core.lgd.config.remove_option("Legendary", "locale")
+ self.core.lgd.config.remove_option('Legendary', 'locale')
@staticmethod
- def __path_edit_callback(path: str) -> Tuple[bool, str, int]:
+ def __path_edit_callback(path: str) -> tuple[bool, str, int]:
if not path:
return False, path, IndicatorReasonsCommon.IS_EMPTY
try:
- perms_path = os.path.join(path, ".rare_perms")
- open(perms_path, "w").close()
+ perms_path = os.path.join(path, '.rare_perms')
+ open(perms_path, 'w').close()
os.unlink(perms_path)
except PermissionError:
return False, path, IndicatorReasonsCommon.PERM_NO_WRITE
@@ -189,70 +188,70 @@ def __path_edit_callback(path: str) -> Tuple[bool, str, int]:
@Slot(str)
def _path_save_callback_mac(self, text: str) -> None:
- self._path_save(text, "mac_install_dir")
+ self._path_save(text, 'mac_install_dir')
@Slot(str)
def _path_save_callback_win(self, text: str) -> None:
- self._path_save(text, "install_dir")
- if pf.system() != "Darwin":
+ self._path_save(text, 'install_dir')
+ if pf.system() != 'Darwin':
self._path_save_callback_mac(text)
def _path_save(self, text: str, option: str):
if text:
- self.core.lgd.config.set("Legendary", option, text)
+ self.core.lgd.config.set('Legendary', option, text)
else:
- self.core.lgd.config.remove_option("Legendary", option)
+ self.core.lgd.config.remove_option('Legendary', option)
@Slot(int)
def max_worker_save(self, workers: int):
if workers:
- self.core.lgd.config.set("Legendary", "max_workers", str(workers))
+ self.core.lgd.config.set('Legendary', 'max_workers', str(workers))
else:
- self.core.lgd.config.remove_option("Legendary", "max_workers")
+ self.core.lgd.config.remove_option('Legendary', 'max_workers')
@Slot(int)
def max_memory_save(self, memory: int):
if memory:
- self.core.lgd.config.set("Legendary", "max_memory", str(memory))
+ self.core.lgd.config.set('Legendary', 'max_memory', str(memory))
else:
- self.core.lgd.config.remove_option("Legendary", "max_memory")
+ self.core.lgd.config.remove_option('Legendary', 'max_memory')
@Slot(str)
def preferred_cdn_save(self, cdn: str):
if cdn:
- self.core.lgd.config.set("Legendary", "preferred_cdn", cdn.strip())
+ self.core.lgd.config.set('Legendary', 'preferred_cdn', cdn.strip())
else:
- self.core.lgd.config.remove_option("Legendary", "preferred_cdn")
+ self.core.lgd.config.remove_option('Legendary', 'preferred_cdn')
@Slot(Qt.CheckState)
def disable_https_save(self, state: Qt.CheckState):
- self.core.lgd.config.set("Legendary", "disable_https", str(state != Qt.CheckState.Unchecked).lower())
+ self.core.lgd.config.set('Legendary', 'disable_https', str(state != Qt.CheckState.Unchecked).lower())
def clean_metadata(self, keep_manifests: bool):
before = self.core.lgd.get_dir_size()
- logger.debug("Removing app metadata...")
+ self.logger.debug('Removing app metadata...')
app_names = {g.app_name for g in self.core.get_assets(update_assets=False)}
self.core.lgd.clean_metadata(app_names)
if not keep_manifests:
- logger.debug("Removing manifests...")
+ self.logger.debug('Removing manifests...')
installed = [(ig.app_name, ig.version, ig.platform) for ig in self.core.get_installed_list()]
installed.extend((ig.app_name, ig.version, ig.platform) for ig in self.core.get_installed_dlc_list())
self.core.lgd.clean_manifests(installed)
- logger.debug("Removing tmp data")
+ self.logger.debug('Removing tmp data')
self.core.lgd.clean_tmp_data()
after = self.core.lgd.get_dir_size()
- logger.info(f"Cleanup complete! Removed {(before - after) / 1024 / 1024:.02f} MiB.")
+ self.logger.info(f'Cleanup complete! Removed {(before - after) / 1024 / 1024:.02f} MiB.')
if (before - after) > 0:
QMessageBox.information(
self,
- self.tr("Cleanup"),
- self.tr("Cleanup complete! Successfully removed {}").format(format_size(before - after)),
+ self.tr('Cleanup'),
+ self.tr('Cleanup complete! Successfully removed {}').format(format_size(before - after)),
)
else:
- QMessageBox.information(self, self.tr("Cleanup"), self.tr("Nothing to clean"))
+ QMessageBox.information(self, self.tr('Cleanup'), self.tr('Nothing to clean'))
@Slot()
def _on_clean_clicked(self):
diff --git a/rare/components/tabs/settings/rare.py b/rare/components/tabs/settings/rare.py
index c5923c998f..fd75774324 100644
--- a/rare/components/tabs/settings/rare.py
+++ b/rare/components/tabs/settings/rare.py
@@ -25,12 +25,12 @@
log_dir,
)
-logger = getLogger("RareSettings")
-
class RareSettings(QWidget):
def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
super(RareSettings, self).__init__(parent=parent)
+ self.logger = getLogger(type(self).__name__)
+
self.settings = settings
self.rcore = rcore
self.core = rcore.core()
@@ -39,7 +39,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.ui.setupUi(self)
# Select lang
- self.ui.lang_select.addItem(self.tr("System default"), app_settings.language.default)
+ self.ui.lang_select.addItem(self.tr('System default'), app_settings.language.default)
for lang_code, title in get_translations():
self.ui.lang_select.addItem(title, lang_code)
language = self.settings.get_value(app_settings.language)
@@ -49,7 +49,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.ui.lang_select.setCurrentIndex(0)
self.ui.lang_select.currentIndexChanged.connect(self._on_lang_changed)
- self.ui.color_select.addItem(self.tr("None"), "")
+ self.ui.color_select.addItem(self.tr('None'), '')
for item in get_color_schemes():
self.ui.color_select.addItem(item, item)
color = self.settings.get_value(app_settings.color_scheme)
@@ -61,7 +61,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.ui.color_select.setCurrentIndex(0)
self.ui.color_select.currentIndexChanged.connect(self._on_color_select_changed)
- self.ui.style_select.addItem(self.tr("None"), "")
+ self.ui.style_select.addItem(self.tr('None'), '')
for item in get_style_sheets():
self.ui.style_select.addItem(item, item)
style = self.settings.get_value(app_settings.style_sheet)
@@ -73,8 +73,8 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.ui.style_select.setCurrentIndex(0)
self.ui.style_select.currentIndexChanged.connect(self._on_style_select_changed)
- self.ui.view_combo.addItem(self.tr("Game covers"), LibraryView.COVER)
- self.ui.view_combo.addItem(self.tr("Vertical list"), LibraryView.VLIST)
+ self.ui.view_combo.addItem(self.tr('Game covers'), LibraryView.COVER)
+ self.ui.view_combo.addItem(self.tr('Vertical list'), LibraryView.VLIST)
view = LibraryView(self.settings.get_value(app_settings.library_view))
if (index := self.ui.view_combo.findData(view)) > -1:
self.ui.view_combo.setCurrentIndex(index)
@@ -112,21 +112,21 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.ui.log_games.checkStateChanged.connect(self._on_log_games_changed)
if desktop_links_supported():
- self.desktop_link = desktop_link_path("Rare", "desktop")
- self.start_menu_link = desktop_link_path("Rare", "start_menu")
+ self.desktop_link = desktop_link_path('Rare', 'desktop')
+ self.start_menu_link = desktop_link_path('Rare', 'start_menu')
else:
- self.ui.desktop_link_btn.setToolTip(self.tr("Not supported"))
+ self.ui.desktop_link_btn.setToolTip(self.tr('Not supported'))
self.ui.desktop_link_btn.setDisabled(True)
- self.ui.startmenu_link_btn.setToolTip(self.tr("Not supported"))
+ self.ui.startmenu_link_btn.setToolTip(self.tr('Not supported'))
self.ui.startmenu_link_btn.setDisabled(True)
- self.desktop_link = ""
- self.start_menu_link = ""
+ self.desktop_link = ''
+ self.start_menu_link = ''
if self.desktop_link and self.desktop_link.exists():
- self.ui.desktop_link_btn.setText(self.tr("Remove desktop link"))
+ self.ui.desktop_link_btn.setText(self.tr('Remove desktop link'))
if self.start_menu_link and self.start_menu_link.exists():
- self.ui.startmenu_link_btn.setText(self.tr("Remove start menu link"))
+ self.ui.startmenu_link_btn.setText(self.tr('Remove start menu link'))
self.ui.desktop_link_btn.clicked.connect(self._create_desktop_link)
self.ui.startmenu_link_btn.clicked.connect(self._create_start_menu_link)
@@ -175,7 +175,7 @@ def _clean_logdir(self):
if log_dir().joinpath(f).is_file():
log_dir().joinpath(f).unlink()
except PermissionError as e:
- logger.error(e)
+ self.logger.error(e)
size = sum(log_dir().joinpath(f).stat().st_size for f in log_dir().iterdir() if log_dir().joinpath(f).is_file())
self.ui.log_dir_size_label.setText(format_size(size))
@@ -183,36 +183,36 @@ def _clean_logdir(self):
def _create_start_menu_link(self):
try:
if not os.path.exists(self.start_menu_link):
- if not create_desktop_link(app_name="rare_shortcut", link_type="start_menu"):
+ if not create_desktop_link(app_name='rare_shortcut', link_type='start_menu'):
return
- self.ui.startmenu_link_btn.setText(self.tr("Remove start menu link"))
+ self.ui.startmenu_link_btn.setText(self.tr('Remove start menu link'))
else:
os.remove(self.start_menu_link)
- self.ui.startmenu_link_btn.setText(self.tr("Create start menu link"))
+ self.ui.startmenu_link_btn.setText(self.tr('Create start menu link'))
except PermissionError as e:
- logger.error(str(e))
+ self.logger.error(str(e))
QMessageBox.warning(
self,
- self.tr("Error"),
- self.tr("Permission error, cannot remove {}").format(self.start_menu_link),
+ self.tr('Error'),
+ self.tr('Permission error, cannot remove {}').format(self.start_menu_link),
)
@Slot()
def _create_desktop_link(self):
try:
if not os.path.exists(self.desktop_link):
- if not create_desktop_link(app_name="rare_shortcut", link_type="desktop"):
+ if not create_desktop_link(app_name='rare_shortcut', link_type='desktop'):
return
- self.ui.desktop_link_btn.setText(self.tr("Remove Desktop link"))
+ self.ui.desktop_link_btn.setText(self.tr('Remove Desktop link'))
else:
os.remove(self.desktop_link)
- self.ui.desktop_link_btn.setText(self.tr("Create desktop link"))
+ self.ui.desktop_link_btn.setText(self.tr('Create desktop link'))
except PermissionError as e:
- logger.error(str(e))
- logger.warning(
+ self.logger.error(str(e))
+ QMessageBox.warning(
self,
- self.tr("Error"),
- self.tr("Permission error, cannot remove {}").format(self.start_menu_link),
+ self.tr('Error'),
+ self.tr('Permission error, cannot remove {}').format(self.start_menu_link),
)
@Slot(int)
diff --git a/rare/components/tabs/settings/widgets/discord_rpc.py b/rare/components/tabs/settings/widgets/discord_rpc.py
index 088a19c183..b3e41b09bb 100644
--- a/rare/components/tabs/settings/widgets/discord_rpc.py
+++ b/rare/components/tabs/settings/widgets/discord_rpc.py
@@ -17,9 +17,9 @@ def __init__(self, settings: RareAppSettings, signals: GlobalSignals, parent):
self.ui = Ui_DiscordRPCSettings()
self.ui.setupUi(self)
- self.ui.mode_combo.addItem(self.tr("When playing"), DiscordRPCMode.GAME_ONLY)
- self.ui.mode_combo.addItem(self.tr("Always"), DiscordRPCMode.ALWAYS)
- self.ui.mode_combo.addItem(self.tr("Never"), DiscordRPCMode.NEVER)
+ self.ui.mode_combo.addItem(self.tr('When playing'), DiscordRPCMode.GAME_ONLY)
+ self.ui.mode_combo.addItem(self.tr('Always'), DiscordRPCMode.ALWAYS)
+ self.ui.mode_combo.addItem(self.tr('Never'), DiscordRPCMode.NEVER)
rpc_mode = DiscordRPCMode(self.settings.get_value(app_settings.discord_rpc_mode))
if (index := self.ui.mode_combo.findData(rpc_mode, Qt.ItemDataRole.UserRole)) < 0:
@@ -39,9 +39,9 @@ def __init__(self, settings: RareAppSettings, signals: GlobalSignals, parent):
self.ui.time_check.setChecked(self.settings.get_value(app_settings.discord_rpc_time))
self.ui.time_check.checkStateChanged.connect(self._on_time_changed)
- if not importlib.util.find_spec("pypresence"):
+ if not importlib.util.find_spec('pypresence'):
self.setDisabled(True)
- self.setToolTip(self.tr("Pypresence is not installed"))
+ self.setToolTip(self.tr('Pypresence is not installed'))
@Slot(Qt.CheckState)
def _on_game_changed(self, state: Qt.CheckState):
diff --git a/rare/components/tabs/settings/widgets/env_vars.py b/rare/components/tabs/settings/widgets/env_vars.py
deleted file mode 100644
index 1e48aea4a0..0000000000
--- a/rare/components/tabs/settings/widgets/env_vars.py
+++ /dev/null
@@ -1,70 +0,0 @@
-from logging import getLogger
-
-from PySide6.QtCore import Qt
-from PySide6.QtGui import QShowEvent
-from PySide6.QtWidgets import (
- QGroupBox,
- QHeaderView,
- QTableView,
- QVBoxLayout,
-)
-
-from rare.lgndr.core import LegendaryCore
-
-from .env_vars_model import EnvVarsTableModel
-
-logger = getLogger("EnvVars")
-
-
-class EnvVars(QGroupBox):
- def __init__(self, core: LegendaryCore, parent):
- super(EnvVars, self).__init__(parent=parent)
- self.setTitle(self.tr("Environment"))
-
- self.core = core
- self.app_name: str = "default"
-
- self.table_model = EnvVarsTableModel(self.core)
- self.table_view = QTableView(self)
- self.table_view.setModel(self.table_model)
- self.table_view.verticalHeader().sectionPressed.disconnect()
- self.table_view.horizontalHeader().sectionPressed.disconnect()
- self.table_view.verticalHeader().sectionClicked.connect(self.table_model.removeRow)
- self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
- self.table_view.horizontalHeader().setStretchLastSection(True)
- self.table_view.setCornerButtonEnabled(False)
-
- # FIXME: investigate signaling between widgets
- # We use this function to keep an eye on the config.
- # When the user uses for example the wineprefix settings, we need to update the table.
- # With this function, when the config file changes, we update the table.
- # self.config_file_watcher = QFileSystemWatcher([str(self.core.lgd.config_path)], self)
- # self.config_file_watcher.fileChanged.connect(self.table_model.reset)
-
- row_height = self.table_view.rowHeight(0)
- self.table_view.setMinimumHeight(row_height * 7)
-
- layout = QVBoxLayout(self)
- layout.addWidget(self.table_view)
-
- def showEvent(self, a0: QShowEvent):
- if a0.spontaneous():
- return super().showEvent(a0)
- self.table_model.load(self.app_name)
- return super().showEvent(a0)
-
- def keyPressEvent(self, a0):
- if a0.key() in {Qt.Key.Key_Delete, Qt.Key.Key_Backspace}:
- indexes = self.table_view.selectedIndexes()
- if not len(indexes):
- return
- for idx in indexes:
- if idx.column() == 0:
- self.table_view.model().removeRow(idx.row())
- elif idx.column() == 1:
- self.table_view.model().setData(idx, "", Qt.ItemDataRole.EditRole)
- elif a0.key() == Qt.Key.Key_Escape:
- a0.ignore()
-
- def reset_model(self):
- self.table_model.reset()
diff --git a/rare/components/tabs/settings/widgets/env_vars_model.py b/rare/components/tabs/settings/widgets/envvars.py
similarity index 67%
rename from rare/components/tabs/settings/widgets/env_vars_model.py
rename to rare/components/tabs/settings/widgets/envvars.py
index 3a8a07c1f3..2f3a1f3101 100644
--- a/rare/components/tabs/settings/widgets/env_vars_model.py
+++ b/rare/components/tabs/settings/widgets/envvars.py
@@ -1,19 +1,24 @@
import platform
import re
-import sys
from collections import ChainMap
-from typing import Any, Union
+from typing import Any
from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt, Slot
-from PySide6.QtGui import QFont
+from PySide6.QtGui import QFont, QShowEvent
+from PySide6.QtWidgets import (
+ QGroupBox,
+ QHeaderView,
+ QTableView,
+ QVBoxLayout,
+)
from rare.lgndr.core import LegendaryCore
from rare.utils.misc import qta_icon
-if platform.system() != "Windows":
+if platform.system() != 'Windows':
from rare.utils.compat.wine import get_wine_environment
-if platform.system() in {"Linux", "FreeBSD"}:
+if platform.system() in {'Linux', 'FreeBSD'}:
from rare.utils.compat.steam import get_steam_environment
@@ -25,26 +30,27 @@ def __init__(self, core: LegendaryCore, parent=None):
# lk: validator matches anything starting with a letter or underscore
# lk: and containing letters, numbers or underscores.
# lk: Empty strings are considered invalid.
- self.__validator = re.compile(r"(^[A-Za-z_][A-Za-z0-9_]*)")
+ self.__validator = re.compile(r'(^[A-Za-z_][A-Za-z0-9_]*)')
self.__data_map: ChainMap = ChainMap()
self.__readonly = set()
- if platform.system() != "Windows":
+ if platform.system() != 'Windows':
self.__readonly.update(
{
- "DXVK_HUD",
- "DXVK_CONFIG",
- "DXVK_NVAPI_DRS_SETTINGS",
- "MANGOHUD",
- "MANGOHUD_CONFIG",
+ 'DXVK_HUD',
+ 'DXVK_CONFIG',
+ 'DXVK_NVAPI_DRS_SETTINGS',
+ 'MANGOHUD',
+ 'MANGOHUD_CONFIG',
+ 'LEGENDARY_WRAPPER_EXE',
}
)
self.__readonly.update(get_wine_environment().keys())
- if platform.system() in {"Linux", "FreeBSD"}:
+ if platform.system() in {'Linux', 'FreeBSD'}:
self.__readonly.update(get_steam_environment().keys())
- self.__readonly.add("STEAM_COMPAT_SHADER_PATH")
- self.__default: str = "default"
- self.__appname: str = None
+ self.__readonly.add('STEAM_COMPAT_SHADER_PATH')
+ self.__default: str = 'default'
+ self.__appname: str | None = None
@Slot(str)
def reset(self):
@@ -55,44 +61,44 @@ def reset(self):
def load(self, app_name: str):
self.__appname = app_name
self.beginResetModel()
- if not self.core.lgd.config.has_section(f"{self.__appname}.env"):
- self.core.lgd.config[f"{self.__appname}.env"] = {}
+ if not self.core.lgd.config.has_section(f'{self.__appname}.env'):
+ self.core.lgd.config[f'{self.__appname}.env'] = {}
self.__data_map = ChainMap(
- self.core.lgd.config[f"{self.__appname}.env"],
- self.core.lgd.config[f"{self.__default}.env"] if self.__appname != self.__default else {},
+ self.core.lgd.config[f'{self.__appname}.env'],
+ self.core.lgd.config[f'{self.__default}.env'] if self.__appname != self.__default else {},
)
self.endResetModel()
- def __key(self, index: Union[QModelIndex, int]):
+ def __key(self, index: QModelIndex | int):
if isinstance(index, QModelIndex):
index = index.row()
try:
return list(self.__data_map)[index]
except Exception:
- return ""
+ return ''
- def __is_local(self, index: Union[QModelIndex, int]):
+ def __is_local(self, index: QModelIndex | int):
key = self.__key(index)
- return key in self.__data_map.maps[0].keys()
+ return key in self.__data_map.maps[0]
- def __is_global(self, index: Union[QModelIndex, int]):
+ def __is_global(self, index: QModelIndex | int):
key = self.__key(index)
- return key in self.__data_map.maps[1].keys()
+ return key in self.__data_map.maps[1]
- def __is_readonly(self, index: Union[QModelIndex, int]):
+ def __is_readonly(self, index: QModelIndex | int):
key = self.__key(index)
return key in self.__readonly
- def __value(self, index: Union[QModelIndex, int]):
+ def __value(self, index: QModelIndex | int):
if isinstance(index, QModelIndex):
index = index.row()
return self.__data_map[self.__key(index)]
def __title(self, section: int):
if section == 0:
- return self.tr("Key")
+ return self.tr('Key')
elif section == 1:
- return self.tr("Value")
+ return self.tr('Value')
def __data_length(self):
return len(self.__data_map)
@@ -106,7 +112,7 @@ def __is_key_valid(self, value: str):
def data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole) -> Any:
if role in {Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.EditRole}:
if index.row() == self.__data_length():
- return ""
+ return ''
if index.column() == 0:
return self.__key(index)
else:
@@ -119,7 +125,7 @@ def data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole) -> A
return Qt.AlignmentFlag.AlignVCenter + Qt.AlignmentFlag.AlignLeft
if role == Qt.ItemDataRole.FontRole:
- font = QFont("Monospace")
+ font = QFont('Monospace')
font.setStyleHint(QFont.StyleHint.Monospace)
if index.row() < self.__data_length() and not self.__is_local(index):
font.setWeight(QFont.Weight.Bold)
@@ -130,12 +136,12 @@ def data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole) -> A
if role == Qt.ItemDataRole.ToolTipRole:
if index.row() == self.__data_length():
if index.column() == 1:
- return self.tr("Disabled, please set the variable name first.")
+ return self.tr('Disabled, please set the variable name first.')
return None
if self.__key(index) in self.__readonly:
if index.column() == 1:
- return self.tr("Value: {}").format(self.__value(index))
- return self.tr("Readonly, please edit this via setting the appropriate setting.")
+ return self.tr('Value: {}').format(self.__value(index))
+ return self.tr('Readonly, please edit this via setting the appropriate setting.')
def headerData(
self,
@@ -150,12 +156,12 @@ def headerData(
if orientation == Qt.Orientation.Vertical:
if section < self.__data_length():
if self.__is_readonly(section) or not self.__is_local(section):
- return qta_icon("mdi.lock", "ei.lock")
+ return qta_icon('mdi.lock', 'ei.lock')
if self.__is_global(section) and self.__is_local(section):
- return qta_icon("mdi.refresh", "ei.refresh")
+ return qta_icon('mdi.refresh', 'ei.refresh')
if self.__is_local(section):
- return qta_icon("mdi.delete", "ei.remove-sign")
- return qta_icon("mdi.plus", "ei.plus-sign")
+ return qta_icon('mdi.delete', 'ei.remove-sign')
+ return qta_icon('mdi.plus', 'ei.plus-sign')
if role == Qt.ItemDataRole.TextAlignmentRole:
return Qt.AlignmentFlag.AlignVCenter + Qt.AlignmentFlag.AlignHCenter
return None
@@ -192,13 +198,13 @@ def setData(self, index: QModelIndex, value: Any, role: int = Qt.ItemDataRole.Di
if (not self.__is_key_valid(value)) or value in self.__readonly:
return False
# Do not accept existing variable names (this also protects against unchanged contents)
- if value in self.__data_map.keys():
+ if value in self.__data_map:
return False
if index.row() == self.__data_length():
self.beginInsertRows(QModelIndex(), self.rowCount(index), self.rowCount(index))
self.endInsertRows()
- self.__data_map[value] = ""
+ self.__data_map[value] = ''
self.core.lgd.save_config()
self.dataChanged.emit(index, index, [])
self.headerDataChanged.emit(Qt.Orientation.Vertical, index.row(), index.row())
@@ -216,11 +222,11 @@ def setData(self, index: QModelIndex, value: Any, role: int = Qt.ItemDataRole.Di
# old key remains, new key added -> insert row for new key, update from index to end
# new key masking global key:
# can't happen because we do not accept existing keys
- if old_key in self.__data_map.maps[0].keys():
+ if old_key in self.__data_map.maps[0]:
# delete the old key if it is a local one, replacing a local key
del self.__data_map[old_key]
self.core.lgd.save_config()
- if old_key in self.__data_map.maps[1].keys():
+ if old_key in self.__data_map.maps[1]:
self.beginInsertRows(QModelIndex(), self.__data_length(), self.__data_length())
self.endInsertRows()
self.dataChanged.emit(index, self.index(index.row(), 1), [])
@@ -274,42 +280,58 @@ def removeRow(self, row: int, parent: QModelIndex = None) -> bool:
return True
-if __name__ == "__main__":
- from PySide6.QtWidgets import (
- QApplication,
- QDialog,
- QHeaderView,
- QTableView,
- QVBoxLayout,
- )
+class EnvVars(QGroupBox):
+ def __init__(self, core: LegendaryCore, parent):
+ super(EnvVars, self).__init__(parent=parent)
+ self.setTitle(self.tr('Environment'))
- from rare.lgndr.core import LegendaryCore
- from rare.utils.misc import set_style_sheet
-
- class MainDialog(QDialog):
- def __init__(self):
- super().__init__()
-
- self.table = QTableView()
- self.model = EnvVarsTableModel(LegendaryCore())
- self.model.load("Tamarind")
-
- self.table.setModel(self.model)
- self.table.verticalHeader().sectionPressed.disconnect()
- self.table.horizontalHeader().sectionPressed.disconnect()
- self.table.verticalHeader().sectionClicked.connect(self.model.removeRow)
- self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
- self.table.horizontalHeader().setStretchLastSection(True)
- self.table.setCornerButtonEnabled(False)
-
- self.setLayout(QVBoxLayout(self))
- self.layout().addWidget(self.table)
-
- app = QApplication(sys.argv)
-
- set_style_sheet("RareStyle")
-
- window = MainDialog()
- window.setFixedSize(800, 600)
- window.show()
- app.exec()
+ self.core = core
+ self.app_name: str = 'default'
+
+ self.table_model = EnvVarsTableModel(self.core)
+ self.table_view = QTableView(self)
+ self.table_view.setModel(self.table_model)
+ self.table_view.verticalHeader().sectionPressed.disconnect()
+ self.table_view.horizontalHeader().sectionPressed.disconnect()
+ self.table_view.verticalHeader().sectionClicked.connect(self.table_model.removeRow)
+ self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
+ self.table_view.horizontalHeader().setStretchLastSection(True)
+ self.table_view.setCornerButtonEnabled(False)
+
+ # FIXME: investigate signaling between widgets
+ # We use this function to keep an eye on the config.
+ # When the user uses for example the wineprefix settings, we need to update the table.
+ # With this function, when the config file changes, we update the table.
+ # self.config_file_watcher = QFileSystemWatcher([str(self.core.lgd.config_path)], self)
+ # self.config_file_watcher.fileChanged.connect(self.table_model.reset)
+
+ row_height = self.table_view.rowHeight(0)
+ self.table_view.setMinimumHeight(row_height * 7)
+
+ layout = QVBoxLayout(self)
+ layout.addWidget(self.table_view)
+
+ def showEvent(self, a0: QShowEvent):
+ if a0.spontaneous():
+ return super().showEvent(a0)
+ self.table_model.load(self.app_name)
+ return super().showEvent(a0)
+
+ def keyPressEvent(self, a0):
+ if a0.key() in {Qt.Key.Key_Delete, Qt.Key.Key_Backspace}:
+ indexes = self.table_view.selectedIndexes()
+ if not len(indexes):
+ return
+ for idx in indexes:
+ if idx.column() == 0:
+ self.table_view.model().removeRow(idx.row())
+ elif idx.column() == 1:
+ self.table_view.model().setData(idx, '', Qt.ItemDataRole.EditRole)
+ elif a0.key() == Qt.Key.Key_Escape:
+ a0.ignore()
+
+ def reset_model(self):
+ self.table_model.reset()
+
+
+__all__ = ['EnvVars']
diff --git a/rare/components/tabs/settings/widgets/launch.py b/rare/components/tabs/settings/widgets/launch.py
index 7d971545b9..6141dbd135 100644
--- a/rare/components/tabs/settings/widgets/launch.py
+++ b/rare/components/tabs/settings/widgets/launch.py
@@ -1,7 +1,7 @@
import os
import shlex
import shutil
-from typing import Tuple, Type, TypeVar
+from typing import TypeVar
from PySide6.QtCore import Qt, Slot
from PySide6.QtGui import QShowEvent
@@ -22,30 +22,30 @@
class LaunchSettingsBase(QGroupBox):
- def __init__(self, rcore: RareCore, wrapper_widget: Type[WrapperSettings], parent=None):
+ def __init__(self, rcore: RareCore, wrapper_widget: type[WrapperSettings], parent=None):
super(LaunchSettingsBase, self).__init__(parent=parent)
- self.setTitle(self.tr("Launch"))
+ self.setTitle(self.tr('Launch'))
self.core = rcore.core()
- self.app_name: str = "default"
+ self.app_name: str = 'default'
self.prelaunch_cmd = PathEdit(
- path="",
- placeholder=self.tr("Path to a script or program to run before the game"),
+ path='',
+ placeholder=self.tr('Path to a script or program to run before the game'),
file_mode=QFileDialog.FileMode.ExistingFile,
edit_func=self.__prelaunch_cmd_edit_callback,
save_func=self.__prelaunch_cmd_save_callback,
)
- self.prelaunch_args = QLineEdit("")
- self.prelaunch_args.setPlaceholderText(self.tr("Arguments to the script or program to run before the game"))
+ self.prelaunch_args = QLineEdit('')
+ self.prelaunch_args.setPlaceholderText(self.tr('Arguments to the script or program to run before the game'))
self.prelaunch_args.setToolTip(self.prelaunch_args.placeholderText())
self.prelaunch_args.textChanged.connect(self.__prelaunch_changed)
font = self.font()
font.setItalic(True)
- self.prelaunch_check = QCheckBox(self.tr("Wait for the pre-launch command to finish before launching the game"))
+ self.prelaunch_check = QCheckBox(self.tr('Wait for the pre-launch command to finish before launching the game'))
self.prelaunch_check.setFont(font)
self.prelaunch_check.checkStateChanged.connect(self.__prelauch_check_changed)
@@ -61,16 +61,16 @@ def __init__(self, rcore: RareCore, wrapper_widget: Type[WrapperSettings], paren
self.wrappers_widget = wrapper_widget(rcore, self)
- self.main_layout.addRow(self.tr("Wrappers"), self.wrappers_widget)
- self.main_layout.addRow(self.tr("Pre-launch"), prelaunch_layout)
+ self.main_layout.addRow(self.tr('Wrappers'), self.wrappers_widget)
+ self.main_layout.addRow(self.tr('Pre-launch'), prelaunch_layout)
def showEvent(self, a0: QShowEvent):
if a0.spontaneous():
return super().showEvent(a0)
- prelaunch = shlex.split(config.get_option(self.app_name, "pre_launch_command", fallback=""))
- command = prelaunch.pop(0) if len(prelaunch) else ""
+ prelaunch = shlex.split(config.get_option(self.app_name, 'pre_launch_command', fallback=''))
+ command = prelaunch.pop(0) if len(prelaunch) else ''
arguments = prelaunch if len(prelaunch) else []
- wait = config.get_boolean(self.app_name, "pre_launch_wait", fallback=False)
+ wait = config.get_boolean(self.app_name, 'pre_launch_wait', fallback=False)
self.prelaunch_cmd.setText(command)
self.prelaunch_args.setText(shlex.join(arguments))
@@ -84,7 +84,7 @@ def tool_enabled(self):
self.wrappers_widget.update_state()
@staticmethod
- def __prelaunch_cmd_edit_callback(text: str) -> Tuple[bool, str, int]:
+ def __prelaunch_cmd_edit_callback(text: str) -> tuple[bool, str, int]:
if not text.strip():
return True, text, IndicatorReasonsCommon.UNDEFINED
if not os.path.isfile(text) and not shutil.which(text):
@@ -98,18 +98,18 @@ def __prelaunch_cmd_save_callback(self, text):
@Slot(Qt.CheckState)
def __prelauch_check_changed(self, state: Qt.CheckState):
- config.set_boolean(self.app_name, "pre_launch_wait", state != Qt.CheckState.Unchecked)
+ config.set_boolean(self.app_name, 'pre_launch_wait', state != Qt.CheckState.Unchecked)
@Slot()
def __prelaunch_changed(self):
command = self.prelaunch_cmd.text().strip()
if not command:
- config.adjust_option(self.app_name, "pre_launch_command", command)
- config.remove_option(self.app_name, "pre_launch_wait")
+ config.adjust_option(self.app_name, 'pre_launch_command', command)
+ config.remove_option(self.app_name, 'pre_launch_wait')
return
command = shlex.quote(command)
arguments = self.prelaunch_args.text().strip()
- config.adjust_option(self.app_name, "pre_launch_command", " ".join([command, arguments]))
+ config.adjust_option(self.app_name, 'pre_launch_command', ' '.join([command, arguments]))
-LaunchSettingsType = TypeVar("LaunchSettingsType", bound=LaunchSettingsBase)
+LaunchSettingsType = TypeVar('LaunchSettingsType', bound=LaunchSettingsBase)
diff --git a/rare/components/tabs/settings/widgets/overlay.py b/rare/components/tabs/settings/widgets/overlay.py
index 127d9c1dc1..a12b22e4eb 100644
--- a/rare/components/tabs/settings/widgets/overlay.py
+++ b/rare/components/tabs/settings/widgets/overlay.py
@@ -1,7 +1,6 @@
from abc import abstractmethod
from enum import IntEnum
from logging import getLogger
-from typing import Dict, List, Optional, Tuple, Union
from PySide6.QtCore import Qt, Signal, Slot
from PySide6.QtGui import QDoubleValidator, QIntValidator, QShowEvent
@@ -10,7 +9,7 @@
from rare.ui.components.tabs.settings.widgets.overlay import Ui_OverlaySettings
from rare.utils import config_helper as config
-logger = getLogger("GameOverlays")
+logger = getLogger('GameOverlays')
class OverlayLineEdit(QLineEdit):
@@ -22,12 +21,12 @@ def __init__(self, option: str, placeholder: str, parent=None):
self.setPlaceholderText(placeholder)
def setDefault(self):
- self.setText("")
+ self.setText('')
- def getValue(self) -> Optional[str]:
- return f"{self.option}={text}" if (text := self.text()) else None
+ def getValue(self) -> str | None:
+ return f'{self.option}={text}' if (text := self.text()) else None
- def setValue(self, options: Dict[str, str]):
+ def setValue(self, options: dict[str, str]):
if (value := options.get(self.option, None)) is not None:
self.setText(value)
options.pop(self.option)
@@ -44,10 +43,10 @@ def __init__(self, option: str, parent=None):
def setDefault(self):
self.setCurrentIndex(0)
- def getValue(self) -> Optional[str]:
- return f"{self.option}={self.currentData(Qt.ItemDataRole.UserRole)}" if self.currentIndex() > 0 else None
+ def getValue(self) -> str | None:
+ return f'{self.option}={self.currentData(Qt.ItemDataRole.UserRole)}' if self.currentIndex() > 0 else None
- def setValue(self, options: Dict[str, str]):
+ def setValue(self, options: dict[str, str]):
if (value := options.get(self.option, None)) is not None:
self.setCurrentIndex(self.findData(value, Qt.ItemDataRole.UserRole))
options.pop(self.option)
@@ -60,9 +59,9 @@ def __init__(
self,
option: str,
title: str,
- desc: str = "",
+ desc: str = '',
default_enabled: bool = False,
- values: Tuple = None,
+ values: tuple = None,
parent=None,
):
self.option = option
@@ -75,17 +74,17 @@ def __init__(
def setDefault(self):
self.setChecked(self.default_enabled)
- def getValue(self) -> Optional[str]:
+ def getValue(self) -> str | None:
# lk: return the check state in case of non-default, otherwise None
checked = self.isChecked()
value = (
- f"{self.option}={self.values[int(checked)] if self.values else int(checked)}"
+ f'{self.option}={self.values[int(checked)] if self.values else int(checked)}'
if self.default_enabled or self.values
else self.option
)
return value if checked ^ self.default_enabled else None
- def setValue(self, options: Dict[str, str]):
+ def setValue(self, options: dict[str, str]):
if options.get(self.option, None) is not None:
if self.values:
self.setChecked(bool(self.values.index(options[self.option])))
@@ -102,14 +101,14 @@ def __init__(self, option: str, placeholder: str, parent=None):
class OverlayNumberInput(OverlayLineEdit):
- def __init__(self, option: str, placeholder: Union[int, float], parent=None):
+ def __init__(self, option: str, placeholder: int | float, parent=None):
super().__init__(option, str(placeholder), parent=parent)
validator = QDoubleValidator(self) if isinstance(placeholder, float) else QIntValidator(self)
self.setValidator(validator)
class OverlaySelectInput(OverlayComboBox):
- def __init__(self, option: str, values: Tuple, parent=None):
+ def __init__(self, option: str, values: tuple, parent=None):
super().__init__(option, parent=parent)
for item in values:
text, data = item
@@ -134,33 +133,33 @@ def __init__(self, parent=None):
self.ui = Ui_OverlaySettings()
self.ui.setupUi(self)
- self.ui.overlay_state_combo.addItem(self.tr("Global"), ActivationStates.GLOBAL)
- self.ui.overlay_state_combo.addItem(self.tr("Disabled"), ActivationStates.DISABLED)
- self.ui.overlay_state_combo.addItem(self.tr("Enabled (defaults)"), ActivationStates.DEFAULTS)
- self.ui.overlay_state_combo.addItem(self.tr("Enabled (custom)"), ActivationStates.CUSTOM)
+ self.ui.overlay_state_combo.addItem(self.tr('Global'), ActivationStates.GLOBAL)
+ self.ui.overlay_state_combo.addItem(self.tr('Disabled'), ActivationStates.DISABLED)
+ self.ui.overlay_state_combo.addItem(self.tr('Enabled (defaults)'), ActivationStates.DEFAULTS)
+ self.ui.overlay_state_combo.addItem(self.tr('Enabled (custom)'), ActivationStates.CUSTOM)
- self.control_key: Union[str, None] = None
- self.config_key: Union[str, None] = None
- self.force_disabled: Union[str, None] = None
- self.force_defaults: Union[str, None] = None
- self.separator: Union[str, None] = None
- self.grid_row_items: Union[int, None] = None
- self.app_name: str = "default"
+ self.control_key: str | None = None
+ self.config_key: str | None = None
+ self.force_disabled: str | None = None
+ self.force_defaults: str | None = None
+ self.separator: str | None = None
+ self.grid_row_items: int | None = None
+ self.app_name: str = 'default'
- self.option_widgets: List[Union[OverlayCheckBox, OverlayLineEdit, OverlayComboBox]] = []
+ self.option_widgets: list[OverlayCheckBox | OverlayLineEdit | OverlayComboBox] = []
# self.checkboxes: Dict[str, OverlayCheckBox] = {}
# self.values: Dict[str, Union[OverlayLineEdit, OverlayComboBox]] = {}
- self.ui.options_group.setTitle(self.tr("Custom options"))
+ self.ui.options_group.setTitle(self.tr('Custom options'))
self.ui.overlay_state_combo.currentIndexChanged.connect(self._update_settings)
self.environ_changed.connect(self._update_current_value)
def setupWidget(
self,
- grid_map: List[OverlayCheckBox],
- left_form_map: List[Tuple[Union[OverlayLineEdit, OverlayComboBox], str]],
- right_form_map: List[Tuple[Union[OverlayLineEdit, OverlayComboBox], str]],
+ grid_map: list[OverlayCheckBox],
+ left_form_map: list[tuple[OverlayLineEdit | OverlayComboBox, str]],
+ right_form_map: list[tuple[OverlayLineEdit | OverlayComboBox, str]],
label: str,
control_key: str,
config_key: str,
@@ -235,7 +234,7 @@ def _update_settings(self):
@Slot()
def _update_current_value(self):
- self.ui.current_value_info.setText(config.get_envvar_with_global(self.app_name, self.config_key, ""))
+ self.ui.current_value_info.setText(config.get_envvar_with_global(self.app_name, self.config_key, ''))
def setCurrentState(self, state: ActivationStates):
self.ui.overlay_state_combo.setCurrentIndex(self.ui.overlay_state_combo.findData(state, Qt.ItemDataRole.UserRole))
@@ -248,7 +247,7 @@ def showEvent(self, a0: QShowEvent):
config_options = config.get_envvar(self.app_name, self.config_key, fallback=None)
if config_options is None:
- logger.debug("Setting %s is not present", self.config_key)
+ logger.debug('Setting %s is not present', self.config_key)
self.setCurrentState(ActivationStates.GLOBAL)
elif config_options == self.force_disabled:
@@ -261,18 +260,18 @@ def showEvent(self, a0: QShowEvent):
self.setCurrentState(ActivationStates.CUSTOM)
opts = {}
for o in config_options.split(self.separator):
- if "=" in o:
- k, v = o.split("=")
+ if '=' in o:
+ k, v = o.split('=')
opts[k] = v
else:
# lk: The value doesn't matter other than not being None
- opts[o] = "enable"
+ opts[o] = 'enable'
for widget in self.option_widgets:
widget.setValue(opts)
if opts:
logger.info(
- "Remaining options without a gui switch: %s",
+ 'Remaining options without a gui switch: %s',
self.separator.join(opts.keys()),
)
@@ -284,36 +283,36 @@ def showEvent(self, a0: QShowEvent):
class DxvkHudSettings(OverlaySettings):
def __init__(self, parent=None):
super(DxvkHudSettings, self).__init__(parent=parent)
- self.setTitle(self.tr("DXVK HUD"))
+ self.setTitle(self.tr('DXVK HUD'))
grid = [
- OverlayCheckBox("fps", self.tr("FPS")),
- OverlayCheckBox("frametimes", self.tr("Frame time graph")),
- OverlayCheckBox("memory", self.tr("Memory usage")),
- OverlayCheckBox("allocations", self.tr("Memory chunk suballocation")),
- OverlayCheckBox("gpuload", self.tr("GPU usage")),
- OverlayCheckBox("devinfo", self.tr("Device info")),
- OverlayCheckBox("version", self.tr("DXVK version")),
- OverlayCheckBox("api", self.tr("D3D feature level")),
- OverlayCheckBox("compiler", self.tr("Compiler activity")),
- OverlayCheckBox("devinfo", self.tr("GPU driver and version")),
- OverlayCheckBox("drawcalls", self.tr("Draw calls per frame")),
- OverlayCheckBox("full", self.tr("All HUD elements")),
+ OverlayCheckBox('fps', self.tr('FPS')),
+ OverlayCheckBox('frametimes', self.tr('Frame time graph')),
+ OverlayCheckBox('memory', self.tr('Memory usage')),
+ OverlayCheckBox('allocations', self.tr('Memory chunk suballocation')),
+ OverlayCheckBox('gpuload', self.tr('GPU usage')),
+ OverlayCheckBox('devinfo', self.tr('Device info')),
+ OverlayCheckBox('version', self.tr('DXVK version')),
+ OverlayCheckBox('api', self.tr('D3D feature level')),
+ OverlayCheckBox('compiler', self.tr('Compiler activity')),
+ OverlayCheckBox('devinfo', self.tr('GPU driver and version')),
+ OverlayCheckBox('drawcalls', self.tr('Draw calls per frame')),
+ OverlayCheckBox('full', self.tr('All HUD elements')),
]
left_form = [
- (OverlayNumberInput("scale", 1.0), self.tr("Scale")),
- (OverlayNumberInput("opacity", 1.0), self.tr("Opacity")),
+ (OverlayNumberInput('scale', 1.0), self.tr('Scale')),
+ (OverlayNumberInput('opacity', 1.0), self.tr('Opacity')),
]
right_form = []
self.setupWidget(
grid,
left_form,
right_form,
- label=self.tr("Show HUD"),
- control_key="DXVK_HUD",
- config_key="DXVK_HUD",
- force_disabled="0",
- force_defaults="1",
- separator=",",
+ label=self.tr('Show HUD'),
+ control_key='DXVK_HUD',
+ config_key='DXVK_HUD',
+ force_disabled='0',
+ force_defaults='1',
+ separator=',',
grid_row_items=4,
)
@@ -324,9 +323,9 @@ def update_settings_override(self, state: ActivationStates):
class DxvkConfigSettings(OverlaySettings):
def __init__(self, parent=None):
super(DxvkConfigSettings, self).__init__(parent=parent)
- self.setTitle(self.tr("DXVK Config"))
- dxvk_config_boolean = (("Default", ""), ("True", "True"), ("False", "False"))
- dxvk_config_tristate = (("Default", ""), ("Auto", "Auto"), ("True", "True"), ("False", "False"))
+ self.setTitle(self.tr('DXVK Config'))
+ dxvk_config_boolean = (('Default', ''), ('True', 'True'), ('False', 'False'))
+ dxvk_config_tristate = (('Default', ''), ('Auto', 'Auto'), ('True', 'True'), ('False', 'False'))
# fmt: off
grid = []
left_form = [
@@ -350,12 +349,12 @@ def __init__(self, parent=None):
grid,
left_form,
right_form,
- label=self.tr("Mode"),
- control_key="DXVK_CONFIG",
- config_key="DXVK_CONFIG",
- force_disabled="0",
- force_defaults="",
- separator=";",
+ label=self.tr('Mode'),
+ control_key='DXVK_CONFIG',
+ config_key='DXVK_CONFIG',
+ force_disabled='0',
+ force_defaults='',
+ separator=';',
grid_row_items=4,
)
@@ -366,48 +365,48 @@ def update_settings_override(self, state: ActivationStates):
class DxvkNvapiDrsSettings(OverlaySettings):
def __init__(self, parent=None):
super(DxvkNvapiDrsSettings, self).__init__(parent=parent)
- self.setTitle(self.tr("DXVK NVAPI Driver Settings"))
+ self.setTitle(self.tr('DXVK NVAPI Driver Settings'))
- def preset_range(start: str, end: str) -> Tuple[Tuple, ...]:
- return tuple(tuple(f"{p}preset_{chr(c)}" for p in ("", "render_")) for c in range(ord(start), ord(end) + 1))
+ def preset_range(start: str, end: str) -> tuple[tuple, ...]:
+ return tuple(tuple(f'{p}preset_{chr(c)}' for p in ('', 'render_')) for c in range(ord(start), ord(end) + 1))
ngx_rr_presets = (
- ("off", "off"),
- *preset_range("a", "o"),
- ("latest", "render_preset_latest"),
- ("default", "default"),
+ ('off', 'off'),
+ *preset_range('a', 'o'),
+ ('latest', 'render_preset_latest'),
+ ('default', 'default'),
)
ngx_sr_presets = (
- ("off", "off"),
- *preset_range("a", "o"),
- ("latest", "render_preset_latest"),
- ("default", "default"),
+ ('off', 'off'),
+ *preset_range('a', 'o'),
+ ('latest', 'render_preset_latest'),
+ ('default', 'default'),
)
grid = [
OverlayCheckBox(
- "ngx_dlss_sr_override",
- self.tr("Super Resolution override"),
- values=("off", "on"),
+ 'ngx_dlss_sr_override',
+ self.tr('Super Resolution override'),
+ values=('off', 'on'),
),
OverlayCheckBox(
- "ngx_dlss_rr_override",
- self.tr("Ray Reconstruction override"),
- values=("off", "on"),
+ 'ngx_dlss_rr_override',
+ self.tr('Ray Reconstruction override'),
+ values=('off', 'on'),
),
OverlayCheckBox(
- "ngx_dlss_fg_override",
- self.tr("Frame Generation override"),
- values=("off", "on"),
+ 'ngx_dlss_fg_override',
+ self.tr('Frame Generation override'),
+ values=('off', 'on'),
),
]
left_form = [
(
- OverlaySelectInput("ngx_dlss_sr_override_render_preset_selection", ngx_sr_presets),
- "Super Resolution preset",
+ OverlaySelectInput('ngx_dlss_sr_override_render_preset_selection', ngx_sr_presets),
+ 'Super Resolution preset',
),
(
- OverlaySelectInput("ngx_dlss_rr_override_render_preset_selection", ngx_rr_presets),
- "Ray Reconstruction preset",
+ OverlaySelectInput('ngx_dlss_rr_override_render_preset_selection', ngx_rr_presets),
+ 'Ray Reconstruction preset',
),
]
right_form = []
@@ -415,12 +414,12 @@ def preset_range(start: str, end: str) -> Tuple[Tuple, ...]:
grid,
left_form,
right_form,
- label=self.tr("Mode"),
- control_key="DXVK_NVAPI_DRS_SETTINGS",
- config_key="DXVK_NVAPI_DRS_SETTINGS",
- force_disabled="0",
- force_defaults="",
- separator=",",
+ label=self.tr('Mode'),
+ control_key='DXVK_NVAPI_DRS_SETTINGS',
+ config_key='DXVK_NVAPI_DRS_SETTINGS',
+ force_disabled='0',
+ force_defaults='',
+ separator=',',
grid_row_items=4,
)
@@ -431,71 +430,71 @@ def update_settings_override(self, state: ActivationStates):
class MangoHudSettings(OverlaySettings):
def __init__(self, parent=None):
super(MangoHudSettings, self).__init__(parent=parent)
- self.setTitle(self.tr("MangoHud"))
+ self.setTitle(self.tr('MangoHud'))
mangohud_position = (
- ("default", "default"),
- ("top-left", "top-left"),
- ("top-right", "top-right"),
- ("middle-left", "middle-left"),
- ("middle-right", "middle-right"),
- ("bottom-left", "bottom-left"),
- ("bottom-right", "bottom-right"),
- ("top-center", "top-center"),
+ ('default', 'default'),
+ ('top-left', 'top-left'),
+ ('top-right', 'top-right'),
+ ('middle-left', 'middle-left'),
+ ('middle-right', 'middle-right'),
+ ('bottom-left', 'bottom-left'),
+ ('bottom-right', 'bottom-right'),
+ ('top-center', 'top-center'),
)
mangohud_vsync = (
- ("config", None),
- ("adaptive", "0"),
- ("off", "1"),
- ("mailbox", "2"),
- ("on", "3"),
+ ('config', None),
+ ('adaptive', '0'),
+ ('off', '1'),
+ ('mailbox', '2'),
+ ('on', '3'),
)
mangohud_gl_vsync = (
- ("config", None),
- ("off", "0"),
- ("on", "1"),
- ("half", "2"),
- ("third", "3"),
- ("quarter", "4"),
+ ('config', None),
+ ('off', '0'),
+ ('on', '1'),
+ ('half', '2'),
+ ('third', '3'),
+ ('quarter', '4'),
)
grid = [
- OverlayCheckBox("read_cfg", self.tr("Read config")),
- OverlayCheckBox("fps", self.tr("FPS"), default_enabled=True),
- OverlayCheckBox("frame_timing", self.tr("Frame time"), default_enabled=True),
- OverlayCheckBox("cpu_stats", self.tr("CPU load"), default_enabled=True),
- OverlayCheckBox("gpu_stats", self.tr("GPU load"), default_enabled=True),
- OverlayCheckBox("cpu_temp", self.tr("CPU temperature")),
- OverlayCheckBox("gpu_temp", self.tr("GPU temperature")),
- OverlayCheckBox("ram", self.tr("Memory usage")),
- OverlayCheckBox("vram", self.tr("VRAM usage")),
- OverlayCheckBox("time", self.tr("Local time")),
- OverlayCheckBox("version", self.tr("MangoHud version")),
- OverlayCheckBox("arch", self.tr("System architecture")),
- OverlayCheckBox("histogram", self.tr("FPS graph")),
- OverlayCheckBox("gpu_name", self.tr("GPU name")),
- OverlayCheckBox("cpu_power", self.tr("CPU power consumption")),
- OverlayCheckBox("gpu_power", self.tr("GPU power consumption")),
+ OverlayCheckBox('read_cfg', self.tr('Read config')),
+ OverlayCheckBox('fps', self.tr('FPS'), default_enabled=True),
+ OverlayCheckBox('frame_timing', self.tr('Frame time'), default_enabled=True),
+ OverlayCheckBox('cpu_stats', self.tr('CPU load'), default_enabled=True),
+ OverlayCheckBox('gpu_stats', self.tr('GPU load'), default_enabled=True),
+ OverlayCheckBox('cpu_temp', self.tr('CPU temperature')),
+ OverlayCheckBox('gpu_temp', self.tr('GPU temperature')),
+ OverlayCheckBox('ram', self.tr('Memory usage')),
+ OverlayCheckBox('vram', self.tr('VRAM usage')),
+ OverlayCheckBox('time', self.tr('Local time')),
+ OverlayCheckBox('version', self.tr('MangoHud version')),
+ OverlayCheckBox('arch', self.tr('System architecture')),
+ OverlayCheckBox('histogram', self.tr('FPS graph')),
+ OverlayCheckBox('gpu_name', self.tr('GPU name')),
+ OverlayCheckBox('cpu_power', self.tr('CPU power consumption')),
+ OverlayCheckBox('gpu_power', self.tr('GPU power consumption')),
]
left_form = [
- (OverlayNumberInput("font_size", 24), self.tr("Font size")),
- (OverlaySelectInput("position", mangohud_position), self.tr("Position")),
+ (OverlayNumberInput('font_size', 24), self.tr('Font size')),
+ (OverlaySelectInput('position', mangohud_position), self.tr('Position')),
]
right_form = [
- (OverlayNumberInput("fps_limit", 0), self.tr("FPS limit")),
- (OverlaySelectInput("vsync", mangohud_vsync), self.tr("Vulkan vsync")),
- (OverlaySelectInput("gl_vsync", mangohud_gl_vsync), self.tr("OpenGL vsync")),
+ (OverlayNumberInput('fps_limit', 0), self.tr('FPS limit')),
+ (OverlaySelectInput('vsync', mangohud_vsync), self.tr('Vulkan vsync')),
+ (OverlaySelectInput('gl_vsync', mangohud_gl_vsync), self.tr('OpenGL vsync')),
]
self.setupWidget(
grid,
left_form,
right_form,
- label=self.tr("Show HUD"),
- control_key="MANGOHUD",
- config_key="MANGOHUD_CONFIG",
- force_disabled="no_display",
- force_defaults="read_cfg",
- separator=",",
+ label=self.tr('Show HUD'),
+ control_key='MANGOHUD',
+ config_key='MANGOHUD_CONFIG',
+ force_disabled='no_display',
+ force_defaults='read_cfg',
+ separator=',',
grid_row_items=4,
)
@@ -510,15 +509,13 @@ def update_settings_override(self, state: ActivationStates): # pylint: disable=
if state == ActivationStates.GLOBAL:
config.remove_envvar(self.app_name, self.control_key)
elif state == ActivationStates.DISABLED:
- config.set_envvar(self.app_name, self.control_key, "0")
- elif state == ActivationStates.DEFAULTS:
- config.set_envvar(self.app_name, self.control_key, "1")
- elif state == ActivationStates.CUSTOM:
- config.set_envvar(self.app_name, self.control_key, "1")
+ config.set_envvar(self.app_name, self.control_key, '0')
+ elif state == ActivationStates.DEFAULTS or state == ActivationStates.CUSTOM:
+ config.set_envvar(self.app_name, self.control_key, '1')
self.environ_changed.emit(self.control_key)
-if __name__ == "__main__":
+if __name__ == '__main__':
import sys
from argparse import Namespace
@@ -528,10 +525,10 @@ def update_settings_override(self, state: ActivationStates): # pylint: disable=
config = Namespace()
def get_envvar(x, y, fallback):
- if y == "DXVK_NVAPI_DRS_SETTINGS":
- return "ngx_dlss_sr_override=off,ngx_dlss_rr_override_render_preset_selection=render_preset_d"
+ if y == 'DXVK_NVAPI_DRS_SETTINGS':
+ return 'ngx_dlss_sr_override=off,ngx_dlss_rr_override_render_preset_selection=render_preset_d'
else:
- return ""
+ return ''
config.get_envvar = get_envvar
config.set_option = lambda x, y, z: print(x, y, z)
diff --git a/rare/components/tabs/settings/widgets/proton.py b/rare/components/tabs/settings/widgets/proton.py
index 8b70304611..917af1239f 100644
--- a/rare/components/tabs/settings/widgets/proton.py
+++ b/rare/components/tabs/settings/widgets/proton.py
@@ -1,7 +1,6 @@
import os
from enum import IntEnum
from logging import getLogger
-from typing import Optional, Tuple, Union
from PySide6.QtCore import Qt, Signal, Slot
from PySide6.QtGui import QShowEvent
@@ -21,7 +20,7 @@
from rare.utils.paths import proton_compat_dir
from rare.widgets.indicator_edit import IndicatorReasonsCommon, PathEdit
-logger = getLogger("ProtonSettings")
+logger = getLogger('ProtonSettings')
class ProtonSettings(QGroupBox):
@@ -43,25 +42,25 @@ def __init__(self, rcore: RareCore, parent=None):
self.rcore = rcore
self.core = rcore.core()
self.wrappers: Wrappers = rcore.wrappers()
- self.tool_wrapper: Optional[Wrapper] = None
- self.app_name: str = "default"
+ self.tool_wrapper: Wrapper | None = None
+ self.app_name: str = 'default'
- self.setTitle(self.tr("Proton"))
+ self.setTitle(self.tr('Proton'))
self.tool_combo = QComboBox(self)
self.tool_combo.currentIndexChanged.connect(self._on_tool_changed)
self.compat_combo = QComboBox(self)
- self.compat_combo.addItem(self.tr("Shared"), ProtonSettings.CompatLocation.SHARED)
- self.compat_combo.addItem(self.tr("Isolated"), ProtonSettings.CompatLocation.ISOLATED)
- self.compat_combo.addItem(self.tr("Custom"), ProtonSettings.CompatLocation.CUSTOM)
+ self.compat_combo.addItem(self.tr('Shared'), ProtonSettings.CompatLocation.SHARED)
+ self.compat_combo.addItem(self.tr('Isolated'), ProtonSettings.CompatLocation.ISOLATED)
+ self.compat_combo.addItem(self.tr('Custom'), ProtonSettings.CompatLocation.CUSTOM)
self.compat_combo.currentIndexChanged.connect(self._on_compat_changed)
self.compat_edit = PathEdit(
file_mode=QFileDialog.FileMode.Directory,
edit_func=self._proton_prefix_edit,
save_func=self._proton_prefix_save,
- placeholder=self.tr("Please select path for proton prefix"),
+ placeholder=self.tr('Please select path for proton prefix'),
parent=self,
)
self.compat_edit.setReadOnly(True)
@@ -75,18 +74,18 @@ def __init__(self, rcore: RareCore, parent=None):
# button_layout.setAlignment(Qt.AlignmentFlag.AlignRight)
layout = QFormLayout(self)
- layout.addRow(self.tr("Proton tool"), self.tool_combo)
+ layout.addRow(self.tr('Proton tool'), self.tool_combo)
folder_layout = QHBoxLayout()
folder_layout.addWidget(self.compat_combo)
folder_layout.addWidget(self.compat_edit)
- layout.addRow(self.tr("Compat folder"), folder_layout)
+ layout.addRow(self.tr('Compat folder'), folder_layout)
layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
layout.setFormAlignment(Qt.AlignmentFlag.AlignLeading | Qt.AlignmentFlag.AlignVCenter)
# layout.addRow(button_layout)
def _get_compat_path(self, compat_location: CompatLocation):
- folder_name = "default"
+ folder_name = 'default'
compat_path = proton_compat_dir(folder_name)
return compat_path
@@ -130,7 +129,7 @@ def showEvent(self, a0: QShowEvent) -> None:
self.tool_combo.blockSignals(False)
enabled = bool(self.tool_combo.currentData(Qt.ItemDataRole.UserRole))
- compat_path = lgd_config.get_compat_data_path(self.app_name, fallback="")
+ compat_path = lgd_config.get_compat_data_path(self.app_name, fallback='')
self.compat_combo.blockSignals(True)
compat_location = self._update_compat_folder(compat_path)
@@ -146,20 +145,18 @@ def showEvent(self, a0: QShowEvent) -> None:
@Slot(int)
def _on_tool_changed(self, index: int):
- steam_tool: Union[steam.ProtonTool, steam.CompatibilityTool, None] = self.tool_combo.itemData(
- index, Qt.ItemDataRole.UserRole
- )
+ steam_tool: steam.ProtonTool | steam.CompatibilityTool | None = self.tool_combo.itemData(index, Qt.ItemDataRole.UserRole)
steam_environ = steam.get_steam_environment(steam_tool, self.compat_edit.text())
- library_paths = steam_environ["STEAM_COMPAT_LIBRARY_PATHS"] if "STEAM_COMPAT_LIBRARY_PATHS" in steam_environ else ""
- if self.app_name != "default":
+ library_paths = steam_environ.get('STEAM_COMPAT_LIBRARY_PATHS', '')
+ if self.app_name != 'default':
install_path = self.rcore.get_game(self.app_name).install_path
library_paths = (
- ":".join([library_paths, os.path.dirname(install_path)]) if library_paths else os.path.dirname(install_path)
+ ':'.join([library_paths, os.path.dirname(install_path)]) if library_paths else os.path.dirname(install_path)
)
# https://gitlab.steamos.cloud/steamrt/steam-runtime-tools/-/blob/main/docs/steam-compat-tool-interface.md#non-steam-games
- steam_environ["STEAM_COMPAT_INSTALL_PATH"] = install_path
- steam_environ["STEAM_COMPAT_LIBRARY_PATHS"] = library_paths
+ steam_environ['STEAM_COMPAT_INSTALL_PATH'] = install_path
+ steam_environ['STEAM_COMPAT_LIBRARY_PATHS'] = library_paths
for key, value in steam_environ.items():
lgd_config.adjust_envvar(self.app_name, key, value)
self.environ_changed.emit(key)
@@ -181,9 +178,9 @@ def _on_tool_changed(self, index: int):
self.compat_combo.setEnabled(steam_tool is not None)
self.compat_edit.setEnabled(steam_tool is not None)
- compat_path = ""
+ compat_path = ''
if steam_tool:
- compat_path = lgd_config.get_compat_data_path(self.app_name, fallback="")
+ compat_path = lgd_config.get_compat_data_path(self.app_name, fallback='')
if not compat_path:
compat_path = str(self._get_compat_path(ProtonSettings.CompatLocation.NONE))
self._update_compat_folder(compat_path)
@@ -205,7 +202,7 @@ def _on_compat_changed(self, index):
)
@staticmethod
- def _proton_prefix_edit(text: str) -> Tuple[bool, str, int]:
+ def _proton_prefix_edit(text: str) -> tuple[bool, str, int]:
if not text:
return False, text, IndicatorReasonsCommon.IS_EMPTY
if os.path.isdir(text):
@@ -213,7 +210,7 @@ def _proton_prefix_edit(text: str) -> Tuple[bool, str, int]:
if not dir_list:
return True, text, IndicatorReasonsCommon.VALID
if any(
- (x in dir_list) for x in ("pfx", "shadercache", "dosdevices", "drive_c", "system.reg", "user.reg", "userdef.reg")
+ (x in dir_list) for x in ('pfx', 'shadercache', 'dosdevices', 'drive_c', 'system.reg', 'user.reg', 'userdef.reg')
):
return True, text, IndicatorReasonsCommon.VALID
return False, text, IndicatorReasonsCommon.DIR_NOT_EMPTY
@@ -222,5 +219,5 @@ def _proton_prefix_edit(text: str) -> Tuple[bool, str, int]:
def _proton_prefix_save(self, text: str):
lgd_config.adjust_compat_data_path(self.app_name, text)
- self.environ_changed.emit("STEAM_COMPAT_DATA_PATH")
+ self.environ_changed.emit('STEAM_COMPAT_DATA_PATH')
self.compat_path_changed.emit(text)
diff --git a/rare/components/tabs/settings/widgets/runner.py b/rare/components/tabs/settings/widgets/runner.py
index f9a3ef9c55..571388fdb1 100644
--- a/rare/components/tabs/settings/widgets/runner.py
+++ b/rare/components/tabs/settings/widgets/runner.py
@@ -1,7 +1,7 @@
import os.path
import platform as pf
from getpass import getuser
-from typing import Type, TypeVar
+from typing import TypeVar
from PySide6.QtCore import Qt, QUrl, Signal, Slot
from PySide6.QtGui import QDesktopServices
@@ -13,7 +13,7 @@
from .wine import WineSettings
-if pf.system() in {"Linux", "FreeBSD"}:
+if pf.system() in {'Linux', 'FreeBSD'}:
from .proton import ProtonSettings
@@ -27,15 +27,15 @@ def __init__(
self,
settings: RareAppSettings,
rcore: RareCore,
- wine_widget: Type["WineSettings"],
- proton_widget: Type["ProtonSettings"] = None,
+ wine_widget: type['WineSettings'],
+ proton_widget: type['ProtonSettings'] = None,
parent=None,
):
super().__init__(parent=parent)
self.settings = settings
- self.app_name: str = "default"
+ self.app_name: str = 'default'
- self.setTitle(self.tr("Compatibility"))
+ self.setTitle(self.tr('Compatibility'))
self.main_layout = QVBoxLayout(self)
@@ -51,11 +51,11 @@ def __init__(
self.ctool.compat_path_changed.connect(self.wine.compat_path_changed)
self.main_layout.addWidget(self.ctool)
- self.pfx_folder_button = QPushButton(self.tr("Open prefix folder"), self)
+ self.pfx_folder_button = QPushButton(self.tr('Open prefix folder'), self)
self.pfx_folder_button.clicked.connect(self._open_pfx_folder)
- self.usr_folder_button = QPushButton(self.tr("Open user folder"), self)
+ self.usr_folder_button = QPushButton(self.tr('Open user folder'), self)
self.usr_folder_button.clicked.connect(self._open_usr_folder)
- self.winetricks_button = QPushButton(self.tr("Run winetricks"), self)
+ self.winetricks_button = QPushButton(self.tr('Run winetricks'), self)
self.button_layout = QHBoxLayout()
self.button_layout.addWidget(self.pfx_folder_button)
@@ -65,14 +65,14 @@ def __init__(
font = self.font()
font.setItalic(True)
- self.shader_cache_check = QCheckBox(self.tr("Use game-specific shader cache directory"), self)
+ self.shader_cache_check = QCheckBox(self.tr('Use game-specific shader cache directory'), self)
self.shader_cache_check.setFont(font)
self.shader_cache_check.setChecked(self.settings.get_value(app_settings.local_shader_cache))
self.shader_cache_check.checkStateChanged.connect(self._shader_cache_check_changed)
self.form_layout = QFormLayout()
- self.form_layout.addRow(self.tr(""), self.button_layout)
- self.form_layout.addRow(self.tr("Shader cache"), self.shader_cache_check)
+ self.form_layout.addRow(self.tr(''), self.button_layout)
+ self.form_layout.addRow(self.tr('Shader cache'), self.shader_cache_check)
self.form_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
self.form_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
self.form_layout.setFormAlignment(Qt.AlignmentFlag.AlignLeading | Qt.AlignmentFlag.AlignVCenter)
@@ -88,9 +88,9 @@ def _open_pfx_folder(self):
@Slot()
def _open_usr_folder(self):
- path = os.path.join(lgd_config.get_wine_prefix_with_global(self.app_name), "drive_c", "users", getuser())
+ path = os.path.join(lgd_config.get_wine_prefix_with_global(self.app_name), 'drive_c', 'users', getuser())
if not os.path.exists(path):
- path = os.path.join(lgd_config.get_wine_prefix_with_global(self.app_name), "drive_c", "users", "steamuser")
+ path = os.path.join(lgd_config.get_wine_prefix_with_global(self.app_name), 'drive_c', 'users', 'steamuser')
QDesktopServices.openUrl(path)
@Slot()
@@ -98,4 +98,4 @@ def _run_winetricks(self):
pass
-RunnerSettingsType = TypeVar("RunnerSettingsType", bound=RunnerSettingsBase)
+RunnerSettingsType = TypeVar('RunnerSettingsType', bound=RunnerSettingsBase)
diff --git a/rare/components/tabs/settings/widgets/wine.py b/rare/components/tabs/settings/widgets/wine.py
index 13592c1306..a140e7741f 100644
--- a/rare/components/tabs/settings/widgets/wine.py
+++ b/rare/components/tabs/settings/widgets/wine.py
@@ -1,6 +1,5 @@
import os
from logging import getLogger
-from typing import Optional, Tuple
from PySide6.QtCore import QSignalBlocker, Qt, Signal, Slot
from PySide6.QtGui import QShowEvent
@@ -11,7 +10,7 @@
from rare.utils import config_helper as lgd_config
from rare.widgets.indicator_edit import IndicatorReasonsCommon, PathEdit
-logger = getLogger("WineSettings")
+logger = getLogger('WineSettings')
class WineSettings(QGroupBox):
@@ -23,13 +22,13 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.settings = settings
self.signals = rcore.signals()
self.lgd_core = rcore.core()
- self.app_name: Optional[str] = "default"
+ self.app_name: str | None = 'default'
- self.setTitle(self.tr("Wine"))
+ self.setTitle(self.tr('Wine'))
# Wine prefix
self.wine_prefix_edit = PathEdit(
- path="",
+ path='',
file_mode=QFileDialog.FileMode.Directory,
edit_func=self._wine_prefix_edit,
save_func=self.save_prefix,
@@ -37,9 +36,9 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
# Wine executable
self.wine_execut_edit = PathEdit(
- path="",
+ path='',
file_mode=QFileDialog.FileMode.ExistingFile,
- name_filters=("wine", "wine64"),
+ name_filters=('wine', 'wine64'),
edit_func=lambda text: (
os.path.isfile(text) or not text,
text,
@@ -49,8 +48,8 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
)
layout = QFormLayout(self)
- layout.addRow(self.tr("Executable"), self.wine_execut_edit)
- layout.addRow(self.tr("Prefix folder"), self.wine_prefix_edit)
+ layout.addRow(self.tr('Executable'), self.wine_execut_edit)
+ layout.addRow(self.tr('Prefix folder'), self.wine_prefix_edit)
layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow)
layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
layout.setFormAlignment(Qt.AlignmentFlag.AlignLeading | Qt.AlignmentFlag.AlignVCenter)
@@ -60,7 +59,7 @@ def __update_widget(self):
self.wine_prefix_edit.setText(self.load_prefix())
_ = QSignalBlocker(self.wine_execut_edit)
self.wine_execut_edit.setText(self.load_execut())
- self.setDisabled(lgd_config.get_boolean(self.app_name, "no_wine", fallback=False))
+ self.setDisabled(lgd_config.get_boolean(self.app_name, 'no_wine', fallback=False))
def showEvent(self, a0: QShowEvent):
if a0.spontaneous():
@@ -70,47 +69,47 @@ def showEvent(self, a0: QShowEvent):
@Slot(str)
def compat_path_changed(self, text: str):
- path = os.path.join(text, "pfx") if text else text
+ path = os.path.join(text, 'pfx') if text else text
self.wine_prefix_edit.setText(path)
@Slot(bool)
def compat_tool_enabled(self, enabled: bool, path: str):
- old_wine_execut = self.settings.value(f"{self.app_name}/wine_execut", defaultValue=None)
- old_wine_prefix = self.settings.value(f"{self.app_name}/wine_prefix", defaultValue=None)
+ old_wine_execut = self.settings.value(f'{self.app_name}/wine_execut', defaultValue=None)
+ old_wine_prefix = self.settings.value(f'{self.app_name}/wine_prefix', defaultValue=None)
if enabled:
- wine_execut = lgd_config.get_option(self.app_name, "wine_executable", "")
- wine_prefix = lgd_config.get_wine_prefix(self.app_name, "")
+ wine_execut = lgd_config.get_option(self.app_name, 'wine_executable', '')
+ wine_prefix = lgd_config.get_wine_prefix(self.app_name, '')
if old_wine_execut is None:
- self.settings.setValue(f"{self.app_name}/wine_execut", wine_execut)
+ self.settings.setValue(f'{self.app_name}/wine_execut', wine_execut)
if old_wine_prefix is None:
- self.settings.setValue(f"{self.app_name}/wine_prefix", wine_prefix)
- self.wine_execut_edit.setText("")
- self.wine_prefix_edit.setText(os.path.join(path, "pfx"))
- lgd_config.set_boolean(self.app_name, "no_wine", True)
+ self.settings.setValue(f'{self.app_name}/wine_prefix', wine_prefix)
+ self.wine_execut_edit.setText('')
+ self.wine_prefix_edit.setText(os.path.join(path, 'pfx'))
+ lgd_config.set_boolean(self.app_name, 'no_wine', True)
else:
- self.settings.remove(f"{self.app_name}/wine_execut")
- self.settings.remove(f"{self.app_name}/wine_prefix")
+ self.settings.remove(f'{self.app_name}/wine_execut')
+ self.settings.remove(f'{self.app_name}/wine_prefix')
if old_wine_execut is not None:
self.wine_execut_edit.setText(old_wine_execut)
if old_wine_prefix is not None:
self.wine_prefix_edit.setText(old_wine_prefix)
- lgd_config.remove_option(self.app_name, "no_wine")
+ lgd_config.remove_option(self.app_name, 'no_wine')
self.setDisabled(enabled)
def load_prefix(self) -> str:
if self.app_name is None:
raise RuntimeError
- return lgd_config.get_wine_prefix(self.app_name, "")
+ return lgd_config.get_wine_prefix(self.app_name, '')
@staticmethod
- def _wine_prefix_edit(text: str) -> Tuple[bool, str, int]:
+ def _wine_prefix_edit(text: str) -> tuple[bool, str, int]:
if not text:
return True, text, IndicatorReasonsCommon.VALID
if os.path.isdir(text):
dir_list = os.listdir(text)
if not dir_list:
return True, text, IndicatorReasonsCommon.VALID
- if any((x in dir_list) for x in ("dosdevices", "drive_c", "system.reg", "user.reg", "userdef.reg")):
+ if any((x in dir_list) for x in ('dosdevices', 'drive_c', 'system.reg', 'user.reg', 'userdef.reg')):
return True, text, IndicatorReasonsCommon.VALID
return False, text, IndicatorReasonsCommon.DIR_NOT_EMPTY
else:
@@ -120,14 +119,14 @@ def save_prefix(self, path: str) -> None:
if self.app_name is None:
raise RuntimeError
lgd_config.adjust_wine_prefix(self.app_name, path)
- self.environ_changed.emit("WINEPREFIX")
+ self.environ_changed.emit('WINEPREFIX')
def load_execut(self) -> str:
if self.app_name is None:
raise RuntimeError
- return lgd_config.get_option(self.app_name, "wine_executable", "")
+ return lgd_config.get_option(self.app_name, 'wine_executable', '')
def save_execut(self, text: str) -> None:
if self.app_name is None:
raise RuntimeError
- lgd_config.adjust_option(self.app_name, "wine_executable", text)
+ lgd_config.adjust_option(self.app_name, 'wine_executable', text)
diff --git a/rare/components/tabs/settings/widgets/wrappers.py b/rare/components/tabs/settings/widgets/wrappers.py
index cfa7b58838..e69100608a 100644
--- a/rare/components/tabs/settings/widgets/wrappers.py
+++ b/rare/components/tabs/settings/widgets/wrappers.py
@@ -1,8 +1,8 @@
import platform as pf
import shlex
import shutil
+from collections.abc import Iterable
from logging import getLogger
-from typing import Iterable, Optional, Tuple
from PySide6.QtCore import QEvent, QMimeData, QObject, QSize, Qt, Signal, Slot
from PySide6.QtGui import (
@@ -37,10 +37,10 @@
from rare.utils.misc import qta_icon
from rare.widgets.dialogs import ButtonDialog, game_title
-if pf.system() in {"Linux", "FreeBSD"}:
+if pf.system() in {'Linux', 'FreeBSD'}:
from rare.utils.compat import steam
-logger = getLogger("WrapperSettings")
+logger = getLogger('WrapperSettings')
class WrapperEditDialog(ButtonDialog):
@@ -57,14 +57,14 @@ def __init__(self, parent=None):
self.setCentralLayout(self.widget_layout)
- self.accept_button.setText(self.tr("Save"))
- self.accept_button.setIcon(qta_icon("fa.edit", "fa5s.edit"))
+ self.accept_button.setText(self.tr('Save'))
+ self.accept_button.setIcon(qta_icon('fa.edit', 'fa5s.edit'))
self.accept_button.setEnabled(False)
- self.result: Tuple = (False, "")
+ self.result: tuple = (False, '')
def setup(self, wrapper: Wrapper):
- header = self.tr("Edit wrapper")
+ header = self.tr('Edit wrapper')
self.setWindowTitle(header)
self.setSubtitle(game_title(header, wrapper.name))
self.line_edit.setText(wrapper.as_str)
@@ -87,16 +87,16 @@ class WrapperAddDialog(WrapperEditDialog):
def __init__(self, parent=None):
super(WrapperAddDialog, self).__init__(parent=parent)
self.combo_box = QComboBox(self)
- self.combo_box.addItem("None", "")
+ self.combo_box.addItem('None', '')
self.combo_box.currentIndexChanged.connect(self.__on_index_changed)
self.widget_layout.insertWidget(0, self.combo_box)
def setup(self, wrappers: Iterable[Wrapper]):
- header = self.tr("Add wrapper")
+ header = self.tr('Add wrapper')
self.setWindowTitle(header)
self.setSubtitle(header)
for wrapper in wrappers:
- self.combo_box.addItem(f"{wrapper.name} ({wrapper.as_str})", wrapper.as_str)
+ self.combo_box.addItem(f'{wrapper.name} ({wrapper.as_str})', wrapper.as_str)
@Slot(int)
def __on_index_changed(self, index: int):
@@ -120,29 +120,29 @@ def __init__(self, wrapper: Wrapper, parent=None):
self.text_lbl = QCheckBox(wrapper.name, parent=self)
self.text_lbl.setChecked(wrapper.is_enabled)
- self.text_lbl.setFont(QFont("monospace"))
+ self.text_lbl.setFont(QFont('monospace'))
self.text_lbl.setEnabled(wrapper.is_editable)
self.text_lbl.checkStateChanged.connect(self.__on_state_changed)
image_lbl = QLabel(parent=self)
- image_lbl.setPixmap(qta_icon("mdi.drag-vertical").pixmap(QSize(20, 20)))
+ image_lbl.setPixmap(qta_icon('mdi.drag-vertical').pixmap(QSize(20, 20)))
- edit_action = QAction("Edit", parent=self)
+ edit_action = QAction('Edit', parent=self)
edit_action.triggered.connect(self.__on_edit)
- delete_action = QAction("Delete", parent=self)
+ delete_action = QAction('Delete', parent=self)
delete_action.triggered.connect(self.__on_delete)
manage_menu = QMenu(parent=self)
manage_menu.addActions([edit_action, delete_action])
manage_button = QPushButton(parent=self)
- manage_button.setIcon(qta_icon("mdi.menu", "fa5s.align-justify"))
+ manage_button.setIcon(qta_icon('mdi.menu', 'fa5s.align-justify'))
manage_button.setMenu(manage_menu)
manage_button.setEnabled(wrapper.is_editable)
if not wrapper.is_editable:
- manage_button.setToolTip(self.tr("Manage through settings"))
+ manage_button.setToolTip(self.tr('Manage through settings'))
else:
- manage_button.setToolTip(self.tr("Manage"))
+ manage_button.setToolTip(self.tr('Manage'))
layout = QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
@@ -155,7 +155,7 @@ def __init__(self, wrapper: Wrapper, parent=None):
# lk: set object names for the stylesheet
self.setObjectName(type(self).__name__)
- manage_button.setObjectName(f"{self.objectName()}Button")
+ manage_button.setObjectName(f'{self.objectName()}Button')
def data(self) -> Wrapper:
return self.wrapper
@@ -207,11 +207,11 @@ def __init__(self, parent=None):
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
self.setWidgetResizable(True)
- self.setProperty("no_kinetic_scroll", True)
+ self.setProperty('no_kinetic_scroll', True)
self.setObjectName(type(self).__name__)
- self.horizontalScrollBar().setObjectName(f"{self.objectName()}Bar")
- self.verticalScrollBar().setObjectName(f"{self.objectName()}Bar")
+ self.horizontalScrollBar().setObjectName(f'{self.objectName()}Bar')
+ self.verticalScrollBar().setObjectName(f'{self.objectName()}Bar')
def setWidget(self, w):
super().setWidget(w)
@@ -245,9 +245,9 @@ def __init__(self, rcore: RareCore, parent=None):
super(WrapperSettings, self).__init__(parent=parent)
self.core = rcore.core()
self.wrappers = rcore.wrappers()
- self.app_name: str = "default"
+ self.app_name: str = 'default'
- self.add_button = QPushButton(self.tr("Add wrapper"), self)
+ self.add_button = QPushButton(self.tr('Add wrapper'), self)
self.add_button.clicked.connect(self.__on_add)
self.wrapper_scroll = WrapperSettingsScroll(self)
@@ -257,14 +257,14 @@ def __init__(self, rcore: RareCore, parent=None):
self.wrapper_container.orderChanged.connect(self.__on_order_changed)
self.wrapper_scroll.setWidget(self.wrapper_container)
- self.wrapper_label = QLabel(self.tr("No wrappers defined"), self)
+ self.wrapper_label = QLabel(self.tr('No wrappers defined'), self)
self.wrapper_label.setFrameStyle(QLabel.Shape.StyledPanel | QLabel.Shadow.Plain)
self.wrapper_label.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
self.wrapper_container.main_layout.addWidget(self.wrapper_label)
# lk: set object names for the stylesheet
- self.setObjectName("WrapperSettings")
- self.wrapper_label.setObjectName(f"{self.objectName()}Label")
+ self.setObjectName('WrapperSettings')
+ self.wrapper_label.setObjectName(f'{self.objectName()}Label')
main_layout = QHBoxLayout(self)
main_layout.setContentsMargins(0, 0, 0, 0)
@@ -324,29 +324,29 @@ def add_user_wrapper(self, wrapper: Wrapper, position: int = -1):
if not wrapper:
return
- if pf.system() in {"Linux", "FreeBSD"}:
+ if pf.system() in {'Linux', 'FreeBSD'}:
compat_cmds = [tool.command() for tool in steam.find_tools()]
if wrapper.as_str in compat_cmds:
QMessageBox.warning(
self,
- self.tr("Warning"),
- self.tr("Do not insert compatibility tools manually. Add them through Proton settings"),
+ self.tr('Warning'),
+ self.tr('Do not insert compatibility tools manually. Add them through Proton settings'),
)
return
if wrapper.checksum in self.wrappers.get_checksums(self.app_name):
QMessageBox.warning(
self,
- self.tr("Warning"),
- self.tr("Wrapper {0} is already in the list").format(wrapper.as_str),
+ self.tr('Warning'),
+ self.tr('Wrapper {0} is already in the list').format(wrapper.as_str),
)
return
if not shutil.which(wrapper.executable):
ans = QMessageBox.question(
self,
- self.tr("Warning"),
- self.tr("Wrapper {0} is not in $PATH. Add it anyway?").format(wrapper.executable),
+ self.tr('Warning'),
+ self.tr('Wrapper {0} is not in $PATH. Add it anyway?').format(wrapper.executable),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
@@ -401,7 +401,7 @@ def __init__(self, parent=None):
super(WrapperContainer, self).__init__(parent=parent)
self.setAcceptDrops(True)
self.__layout = QHBoxLayout()
- self.__drag_widget: Optional[QWidget] = None
+ self.__drag_widget: QWidget | None = None
self.main_layout = QHBoxLayout(self)
self.main_layout.addLayout(self.__layout)
diff --git a/rare/components/tabs/store/__init__.py b/rare/components/tabs/store/__init__.py
index 5958ea6440..c6693b68b3 100644
--- a/rare/components/tabs/store/__init__.py
+++ b/rare/components/tabs/store/__init__.py
@@ -17,20 +17,20 @@ def __init__(self, core: LegendaryCore, parent=None):
self.core = core
# self.rcore = rcore
self.api = StoreAPI(
- self.core.egs.session.headers["Authorization"],
+ self.core.egs.session.headers['Authorization'],
self.core.language_code,
self.core.country_code,
[], # [i.asset_infos["Windows"].namespace for i in self.rcore.game_list if bool(i.asset_infos)]
)
self.landing = LandingPage(self.api, parent=self)
- self.landing_index = self.addTab(self.landing, self.tr("Store"))
+ self.landing_index = self.addTab(self.landing, self.tr('Store'))
self.search = SearchPage(self.api, parent=self)
- self.search_index = self.addTab(self.search, self.tr("Search"))
+ self.search_index = self.addTab(self.search, self.tr('Search'))
self.wishlist = WishlistPage(self.api, parent=self)
- self.wishlist_index = self.addTab(self.wishlist, self.tr("Wishlist"))
+ self.wishlist_index = self.addTab(self.wishlist, self.tr('Wishlist'))
def showEvent(self, a0: QShowEvent) -> None:
if a0.spontaneous() or self.init:
diff --git a/rare/components/tabs/store/__main__.py b/rare/components/tabs/store/__main__.py
index cf4747acc8..949c7ab339 100644
--- a/rare/components/tabs/store/__main__.py
+++ b/rare/components/tabs/store/__main__.py
@@ -22,7 +22,7 @@ def __init__(self):
self.store_tab.show()
-if __name__ == "__main__":
+if __name__ == '__main__':
import logging
# import rare.resources.stylesheets.RareStyle
@@ -31,13 +31,13 @@ def __init__(self):
logging.getLogger().setLevel(logging.DEBUG)
app = QApplication(sys.argv)
- app.setApplicationName("Rare")
- app.setOrganizationName("Rare")
+ app.setApplicationName('Rare')
+ app.setOrganizationName('Rare')
- set_style_sheet("")
- set_style_sheet("RareStyle")
+ set_style_sheet('')
+ set_style_sheet('RareStyle')
window = StoreWindow()
- window.setWindowTitle(f"{app.applicationName()} - Store")
+ window.setWindowTitle(f'{app.applicationName()} - Store')
window.resize(QSize(1280, 800))
window.show()
app.exec()
diff --git a/rare/components/tabs/store/api/debug.py b/rare/components/tabs/store/api/debug.py
index 27bb92bcdb..170404c9d3 100644
--- a/rare/components/tabs/store/api/debug.py
+++ b/rare/components/tabs/store/api/debug.py
@@ -1,3 +1,5 @@
+import contextlib
+
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QDialog, QTreeView, QVBoxLayout
@@ -12,10 +14,8 @@ def __init__(self, data, parent=None):
self.model = QJsonModel(self)
self.setModel(self.model)
self.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)
- try:
+ with contextlib.suppress(Exception):
self.model.load(data)
- except Exception:
- pass
self.resizeColumnToContents(0)
diff --git a/rare/components/tabs/store/api/models/diesel.py b/rare/components/tabs/store/api/models/diesel.py
index 9d50ca255a..589c58f17e 100644
--- a/rare/components/tabs/store/api/models/diesel.py
+++ b/rare/components/tabs/store/api/models/diesel.py
@@ -1,11 +1,11 @@
from dataclasses import dataclass, field
from logging import getLogger
-from typing import Any, Dict, List, Tuple, Type
+from typing import Any
-logger = getLogger("DieselModels")
+logger = getLogger('DieselModels')
# lk: Typing overloads for unimplemented types
-DieselSocialLinks = Dict
+DieselSocialLinks = dict
@dataclass
@@ -14,16 +14,16 @@ class DieselSystemDetailItem:
minimum: str = None
recommended: str = None
title: str = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["DieselSystemDetailItem"], src: Dict[str, Any]) -> "DieselSystemDetailItem":
+ def from_dict(cls: type['DieselSystemDetailItem'], src: dict[str, Any]) -> 'DieselSystemDetailItem':
d = src.copy()
return cls(
- _type=d.pop("_type", ""),
- minimum=d.pop("minimum", ""),
- recommended=d.pop("recommended", ""),
- title=d.pop("title", ""),
+ _type=d.pop('_type', ''),
+ minimum=d.pop('minimum', ''),
+ recommended=d.pop('recommended', ''),
+ title=d.pop('title', ''),
unmapped=d,
)
@@ -31,18 +31,18 @@ def from_dict(cls: Type["DieselSystemDetailItem"], src: Dict[str, Any]) -> "Dies
@dataclass
class DieselSystemDetail:
_type: str = None
- details: Tuple[DieselSystemDetailItem, ...] = None
+ details: tuple[DieselSystemDetailItem, ...] = None
systemType: str = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["DieselSystemDetail"], src: Dict[str, Any]) -> "DieselSystemDetail":
+ def from_dict(cls: type['DieselSystemDetail'], src: dict[str, Any]) -> 'DieselSystemDetail':
d = src.copy()
- details = tuple(map(DieselSystemDetailItem.from_dict, d.pop("details", [])))
+ details = tuple(map(DieselSystemDetailItem.from_dict, d.pop('details', [])))
return cls(
- _type=d.pop("_type", ""),
+ _type=d.pop('_type', ''),
details=details,
- systemType=d.pop("systemType", ""),
+ systemType=d.pop('systemType', ''),
unmapped=d,
)
@@ -50,19 +50,19 @@ def from_dict(cls: Type["DieselSystemDetail"], src: Dict[str, Any]) -> "DieselSy
@dataclass
class DieselSystemDetails:
_type: str = None
- languages: List[str] = None
- rating: Dict = None
- systems: Tuple[DieselSystemDetail, ...] = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ languages: list[str] = None
+ rating: dict = None
+ systems: tuple[DieselSystemDetail, ...] = None
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["DieselSystemDetails"], src: Dict[str, Any]) -> "DieselSystemDetails":
+ def from_dict(cls: type['DieselSystemDetails'], src: dict[str, Any]) -> 'DieselSystemDetails':
d = src.copy()
- systems = tuple(map(DieselSystemDetail.from_dict, d.pop("systems", [])))
+ systems = tuple(map(DieselSystemDetail.from_dict, d.pop('systems', [])))
return cls(
- _type=d.pop("_type", ""),
- languages=d.pop("languages", []),
- rating=d.pop("rating", {}),
+ _type=d.pop('_type', ''),
+ languages=d.pop('languages', []),
+ rating=d.pop('rating', {}),
systems=systems,
unmapped=d,
)
@@ -75,17 +75,17 @@ class DieselProductAbout:
developerAttribution: str = None
publisherAttribution: str = None
shortDescription: str = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["DieselProductAbout"], src: Dict[str, Any]) -> "DieselProductAbout":
+ def from_dict(cls: type['DieselProductAbout'], src: dict[str, Any]) -> 'DieselProductAbout':
d = src.copy()
return cls(
- _type=d.pop("_type", ""),
- description=d.pop("description", ""),
- developerAttribution=d.pop("developerAttribution", ""),
- publisherAttribution=d.pop("publisherAttribution", ""),
- shortDescription=d.pop("shortDescription", ""),
+ _type=d.pop('_type', ''),
+ description=d.pop('description', ''),
+ developerAttribution=d.pop('developerAttribution', ''),
+ publisherAttribution=d.pop('publisherAttribution', ''),
+ shortDescription=d.pop('shortDescription', ''),
unmapped=d,
)
@@ -96,18 +96,18 @@ class DieselProductDetail:
about: DieselProductAbout = None
requirements: DieselSystemDetails = None
socialLinks: DieselSocialLinks = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["DieselProductDetail"], src: Dict[str, Any]) -> "DieselProductDetail":
+ def from_dict(cls: type['DieselProductDetail'], src: dict[str, Any]) -> 'DieselProductDetail':
d = src.copy()
- about = DieselProductAbout.from_dict(x) if (x := d.pop("about"), {}) else None
- requirements = DieselSystemDetails.from_dict(x) if (x := d.pop("requirements", {})) else None
+ about = DieselProductAbout.from_dict(x) if (x := d.pop('about'), {}) else None
+ requirements = DieselSystemDetails.from_dict(x) if (x := d.pop('requirements', {})) else None
return cls(
- _type=d.pop("_type", ""),
+ _type=d.pop('_type', ''),
about=about,
requirements=requirements,
- socialLinks=d.pop("socialLinks", {}),
+ socialLinks=d.pop('socialLinks', {}),
unmapped=d,
)
@@ -115,32 +115,32 @@ def from_dict(cls: Type["DieselProductDetail"], src: Dict[str, Any]) -> "DieselP
@dataclass
class DieselProduct:
_id: str = None
- _images_: List[str] = None
+ _images_: list[str] = None
_locale: str = None
_slug: str = None
_title: str = None
_urlPattern: str = None
namespace: str = None
- pages: Tuple["DieselProduct", ...] = None
+ pages: tuple['DieselProduct', ...] = None
data: DieselProductDetail = None
productName: str = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["DieselProduct"], src: Dict[str, Any]) -> "DieselProduct":
+ def from_dict(cls: type['DieselProduct'], src: dict[str, Any]) -> 'DieselProduct':
d = src.copy()
- pages = tuple(map(DieselProduct.from_dict, d.pop("pages", [])))
- data = DieselProductDetail.from_dict(x) if (x := d.pop("data", {})) else None
+ pages = tuple(map(DieselProduct.from_dict, d.pop('pages', [])))
+ data = DieselProductDetail.from_dict(x) if (x := d.pop('data', {})) else None
return cls(
- _id=d.pop("_id", ""),
- _images_=d.pop("_images_", []),
- _locale=d.pop("_locale", ""),
- _slug=d.pop("_slug", ""),
- _title=d.pop("_title", ""),
- _urlPattern=d.pop("_urlPattern", ""),
- namespace=d.pop("namespace", ""),
+ _id=d.pop('_id', ''),
+ _images_=d.pop('_images_', []),
+ _locale=d.pop('_locale', ''),
+ _slug=d.pop('_slug', ''),
+ _title=d.pop('_title', ''),
+ _urlPattern=d.pop('_urlPattern', ''),
+ namespace=d.pop('namespace', ''),
pages=pages,
data=data,
- productName=d.pop("productName", ""),
+ productName=d.pop('productName', ''),
unmapped=d,
)
diff --git a/rare/components/tabs/store/api/models/query.py b/rare/components/tabs/store/api/models/query.py
index d41ff6f6d9..90b5f63783 100644
--- a/rare/components/tabs/store/api/models/query.py
+++ b/rare/components/tabs/store/api/models/query.py
@@ -1,6 +1,5 @@
from dataclasses import dataclass, field
from datetime import datetime, timezone
-from typing import List
@dataclass
@@ -11,71 +10,71 @@ class SearchDateRange:
def __str__(self):
def fmt_date(date: datetime) -> str:
# lk: The formatting accepted by the GraphQL API is either '%Y-%m-%dT%H:%M:%S.000Z' or '%Y-%m-%d'
- return datetime.strftime(date, "%Y-%m-%dT%H:%M:%S.000Z")
+ return datetime.strftime(date, '%Y-%m-%dT%H:%M:%S.000Z')
- return f"[{fmt_date(self.start_date)},{fmt_date(self.end_date)}]"
+ return f'[{fmt_date(self.start_date)},{fmt_date(self.end_date)}]'
@dataclass
class SearchStoreQuery:
- country: str = "US"
- category: str = "games/edition/base|bundles/games|editors|software/edition/base"
+ country: str = 'US'
+ category: str = 'games/edition/base|bundles/games|editors|software/edition/base'
count: int = 30
- keywords: str = ""
- language: str = "en"
- namespace: str = ""
+ keywords: str = ''
+ language: str = 'en'
+ namespace: str = ''
with_mapping: bool = True
- item_ns: str = ""
- sort_by: str = "releaseDate"
- sort_dir: str = "DESC"
+ item_ns: str = ''
+ sort_by: str = 'releaseDate'
+ sort_dir: str = 'DESC'
start: int = 0
- tag: List[str] = ""
+ tag: list[str] = ''
release_date: SearchDateRange = field(default_factory=SearchDateRange)
with_price: bool = True
with_promotions: bool = True
- price_range: str = ""
+ price_range: str = ''
free_game: bool = None
on_sale: bool = None
effective_date: SearchDateRange = field(default_factory=SearchDateRange)
def __post_init__(self):
- self.locale = f"{self.language}-{self.country}"
+ self.locale = f'{self.language}-{self.country}'
def to_dict(self):
payload = {
- "allowCountries": self.country,
- "category": self.category,
- "count": self.count,
- "country": self.country,
- "keywords": self.keywords,
- "locale": self.locale,
- "namespace": self.namespace,
- "withMapping": self.with_mapping,
- "itemNs": self.item_ns,
- "sortBy": self.sort_by,
- "sortDir": self.sort_dir,
- "start": self.start,
- "tag": self.tag,
- "releaseDate": str(self.release_date),
- "withPrice": self.with_price,
- "withPromotions": self.with_promotions,
- "priceRange": self.price_range,
- "freeGame": self.free_game,
- "onSale": self.on_sale,
- "effectiveDate": str(self.effective_date),
+ 'allowCountries': self.country,
+ 'category': self.category,
+ 'count': self.count,
+ 'country': self.country,
+ 'keywords': self.keywords,
+ 'locale': self.locale,
+ 'namespace': self.namespace,
+ 'withMapping': self.with_mapping,
+ 'itemNs': self.item_ns,
+ 'sortBy': self.sort_by,
+ 'sortDir': self.sort_dir,
+ 'start': self.start,
+ 'tag': self.tag,
+ 'releaseDate': str(self.release_date),
+ 'withPrice': self.with_price,
+ 'withPromotions': self.with_promotions,
+ 'priceRange': self.price_range,
+ 'freeGame': self.free_game,
+ 'onSale': self.on_sale,
+ 'effectiveDate': str(self.effective_date),
}
# payload.pop("withPromotions")
- payload.pop("onSale")
- if self.price_range == "free":
- payload["freeGame"] = True
- payload.pop("priceRange")
- elif self.price_range.startswith(""):
- payload["priceRange"] = self.price_range.replace("", "")
+ payload.pop('onSale')
+ if self.price_range == 'free':
+ payload['freeGame'] = True
+ payload.pop('priceRange')
+ elif self.price_range.startswith(''):
+ payload['priceRange'] = self.price_range.replace('', '')
if self.on_sale:
- payload["onSale"] = True
+ payload['onSale'] = True
if self.price_range:
- payload["effectiveDate"] = self.effective_date
+ payload['effectiveDate'] = self.effective_date
else:
- payload.pop("priceRange")
+ payload.pop('priceRange')
return payload
diff --git a/rare/components/tabs/store/api/models/response.py b/rare/components/tabs/store/api/models/response.py
index 540e2ac09b..13ae7e039c 100644
--- a/rare/components/tabs/store/api/models/response.py
+++ b/rare/components/tabs/store/api/models/response.py
@@ -1,22 +1,22 @@
from dataclasses import dataclass, field
from datetime import datetime
from logging import getLogger
-from typing import Any, Dict, List, Tuple, Type
+from typing import Any
from .utils import parse_date
-logger = getLogger("StoreApiModels")
+logger = getLogger('StoreApiModels')
# lk: Typing overloads for unimplemented types
-DieselSocialLinks = Dict
+DieselSocialLinks = dict
-CatalogNamespaceModel = Dict
-CategoryModel = Dict
-CustomAttributeModel = Dict
-ItemModel = Dict
-SellerModel = Dict
-PageSandboxModel = Dict
-TagModel = Dict
+CatalogNamespaceModel = dict
+CategoryModel = dict
+CustomAttributeModel = dict
+ItemModel = dict
+SellerModel = dict
+PageSandboxModel = dict
+TagModel = dict
@dataclass
@@ -24,39 +24,39 @@ class ImageUrlModel:
type: str = None
url: str = None
- def as_dict(self) -> Dict[str, Any]:
- tmp: Dict[str, Any] = {}
+ def as_dict(self) -> dict[str, Any]:
+ tmp: dict[str, Any] = {}
tmp.update({})
if self.type is not None:
- tmp["type"] = self.type
+ tmp['type'] = self.type
if self.url is not None:
- tmp["url"] = self.url
+ tmp['url'] = self.url
return tmp
@classmethod
- def from_dict(cls: Type["ImageUrlModel"], src: Dict[str, Any]) -> "ImageUrlModel":
+ def from_dict(cls: type['ImageUrlModel'], src: dict[str, Any]) -> 'ImageUrlModel':
d = src.copy()
- return cls(type=d.pop("type", ""), url=d.pop("url", ""))
+ return cls(type=d.pop('type', ''), url=d.pop('url', ''))
@dataclass
class KeyImagesModel:
- key_images: Tuple[ImageUrlModel, ...] = None
+ key_images: tuple[ImageUrlModel, ...] = None
tall_types = (
- "DieselGameBoxTall",
- "DieselStoreFrontTall",
- "OfferImageTall",
- "DieselGameBoxLogo",
- "Thumbnail",
- "ProductLogo",
+ 'DieselGameBoxTall',
+ 'DieselStoreFrontTall',
+ 'OfferImageTall',
+ 'DieselGameBoxLogo',
+ 'Thumbnail',
+ 'ProductLogo',
)
wide_types = (
- "DieselGameBoxwide",
- "DieselStoreFrontWide",
- "OfferImageWide",
- "DieselGameBox",
- "VaultClosed",
- "ProductLogo",
+ 'DieselGameBoxwide',
+ 'DieselStoreFrontWide',
+ 'OfferImageWide',
+ 'DieselGameBox',
+ 'VaultClosed',
+ 'ProductLogo',
)
def __getitem__(self, item):
@@ -66,17 +66,17 @@ def __bool__(self):
return bool(self.key_images)
@classmethod
- def from_list(cls: Type["KeyImagesModel"], src: List[Dict]):
+ def from_list(cls: type['KeyImagesModel'], src: list[dict]):
d = src.copy()
key_images = tuple(map(ImageUrlModel.from_dict, d))
return cls(key_images=key_images)
- def available_tall(self) -> List[ImageUrlModel]:
+ def available_tall(self) -> list[ImageUrlModel]:
tall_images = filter(lambda img: img.type in KeyImagesModel.tall_types, self.key_images)
tall_images = sorted(tall_images, key=lambda x: KeyImagesModel.tall_types.index(x.type))
return tall_images
- def available_wide(self) -> List[ImageUrlModel]:
+ def available_wide(self) -> list[ImageUrlModel]:
wide_images = filter(lambda img: img.type in KeyImagesModel.wide_types, self.key_images)
wide_images = sorted(wide_images, key=lambda x: KeyImagesModel.wide_types.index(x.type))
return wide_images
@@ -95,9 +95,9 @@ def for_dimensions(self, w: int, h: int) -> ImageUrlModel:
return model
-CurrencyModel = Dict
-FormattedPriceModel = Dict
-LineOffersModel = Dict
+CurrencyModel = dict
+FormattedPriceModel = dict
+LineOffersModel = dict
@dataclass
@@ -109,19 +109,19 @@ class TotalPriceModel:
currencyCode: str = None
currencyInfo: CurrencyModel = None
fmtPrice: FormattedPriceModel = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["TotalPriceModel"], src: Dict[str, Any]) -> "TotalPriceModel":
+ def from_dict(cls: type['TotalPriceModel'], src: dict[str, Any]) -> 'TotalPriceModel':
d = src.copy()
return cls(
- discountPrice=d.pop("discountPrice", 0),
- originalPrice=d.pop("originalPrice", 0),
- voucherDiscount=d.pop("voucherDiscount", 0),
- discount=d.pop("discount", 0),
- currencyCode=d.pop("currencyCode", ""),
- currencyInfo=d.pop("currrencyInfo", {}),
- fmtPrice=d.pop("fmtPrice", {}),
+ discountPrice=d.pop('discountPrice', 0),
+ originalPrice=d.pop('originalPrice', 0),
+ voucherDiscount=d.pop('voucherDiscount', 0),
+ discount=d.pop('discount', 0),
+ currencyCode=d.pop('currencyCode', ''),
+ currencyInfo=d.pop('currrencyInfo', {}),
+ fmtPrice=d.pop('fmtPrice', {}),
unmapped=d,
)
@@ -130,16 +130,16 @@ def from_dict(cls: Type["TotalPriceModel"], src: Dict[str, Any]) -> "TotalPriceM
class GetPriceResModel:
totalPrice: TotalPriceModel = None
lineOffers: LineOffersModel = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["GetPriceResModel"], src: Dict[str, Any]) -> "GetPriceResModel":
+ def from_dict(cls: type['GetPriceResModel'], src: dict[str, Any]) -> 'GetPriceResModel':
d = src.copy()
- total_price = TotalPriceModel.from_dict(x) if (x := d.pop("totalPrice", {})) else None
- return cls(totalPrice=total_price, lineOffers=d.pop("lineOffers", {}), unmapped=d)
+ total_price = TotalPriceModel.from_dict(x) if (x := d.pop('totalPrice', {})) else None
+ return cls(totalPrice=total_price, lineOffers=d.pop('lineOffers', {}), unmapped=d)
-DiscountSettingModel = Dict
+DiscountSettingModel = dict
@dataclass
@@ -147,44 +147,44 @@ class PromotionalOfferModel:
startDate: datetime = None
endDate: datetime = None
discountSetting: DiscountSettingModel = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["PromotionalOfferModel"], src: Dict[str, Any]) -> "PromotionalOfferModel":
+ def from_dict(cls: type['PromotionalOfferModel'], src: dict[str, Any]) -> 'PromotionalOfferModel':
d = src.copy()
- start_date = parse_date(x) if (x := d.pop("startDate", "")) else None
- end_date = parse_date(x) if (x := d.pop("endDate", "")) else None
+ start_date = parse_date(x) if (x := d.pop('startDate', '')) else None
+ end_date = parse_date(x) if (x := d.pop('endDate', '')) else None
return cls(
startDate=start_date,
endDate=end_date,
- discountSetting=d.pop("discountSetting", {}),
+ discountSetting=d.pop('discountSetting', {}),
unmapped=d,
)
@dataclass
class PromotionalOffersModel:
- promotionalOffers: Tuple[PromotionalOfferModel, ...] = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ promotionalOffers: tuple[PromotionalOfferModel, ...] = None
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_list(cls: Type["PromotionalOffersModel"], src: Dict[str, List]) -> "PromotionalOffersModel":
+ def from_list(cls: type['PromotionalOffersModel'], src: dict[str, list]) -> 'PromotionalOffersModel':
d = src.copy()
- promotional_offers = tuple(map(PromotionalOfferModel.from_dict, d.pop("promotionalOffers", [])))
+ promotional_offers = tuple(map(PromotionalOfferModel.from_dict, d.pop('promotionalOffers', [])))
return cls(promotionalOffers=promotional_offers, unmapped=d)
@dataclass
class PromotionsModel:
- promotionalOffers: Tuple[PromotionalOffersModel, ...] = None
- upcomingPromotionalOffers: Tuple[PromotionalOffersModel, ...] = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ promotionalOffers: tuple[PromotionalOffersModel, ...] = None
+ upcomingPromotionalOffers: tuple[PromotionalOffersModel, ...] = None
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["PromotionsModel"], src: Dict[str, Any]) -> "PromotionsModel":
+ def from_dict(cls: type['PromotionsModel'], src: dict[str, Any]) -> 'PromotionsModel':
d = src.copy()
- promotional_offers = tuple(map(PromotionalOffersModel.from_list, d.pop("promotionalOffers", [])))
- upcoming_promotional_offers = tuple(map(PromotionalOffersModel.from_list, d.pop("upcomingPromotionalOffers", [])))
+ promotional_offers = tuple(map(PromotionalOffersModel.from_list, d.pop('promotionalOffers', [])))
+ upcoming_promotional_offers = tuple(map(PromotionalOffersModel.from_list, d.pop('upcomingPromotionalOffers', [])))
return cls(
promotionalOffers=promotional_offers,
upcomingPromotionalOffers=upcoming_promotional_offers,
@@ -195,62 +195,62 @@ def from_dict(cls: Type["PromotionsModel"], src: Dict[str, Any]) -> "PromotionsM
@dataclass
class CatalogOfferModel:
catalogNs: CatalogNamespaceModel = None
- categories: List[CategoryModel] = None
- customAttributes: List[CustomAttributeModel] = None
+ categories: list[CategoryModel] = None
+ customAttributes: list[CustomAttributeModel] = None
description: str = None
effectiveDate: datetime = None
expiryDate: datetime = None
id: str = None
isCodeRedemptionOnly: bool = None
- items: List[ItemModel] = None
+ items: list[ItemModel] = None
keyImages: KeyImagesModel = None
namespace: str = None
- offerMappings: List[PageSandboxModel] = None
+ offerMappings: list[PageSandboxModel] = None
offerType: str = None
price: GetPriceResModel = None
productSlug: str = None
promotions: PromotionsModel = None
seller: SellerModel = None
status: str = None
- tags: List[TagModel] = None
+ tags: list[TagModel] = None
title: str = None
url: str = None
urlSlug: str = None
viewableDate: datetime = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["CatalogOfferModel"], src: Dict[str, Any]) -> "CatalogOfferModel":
+ def from_dict(cls: type['CatalogOfferModel'], src: dict[str, Any]) -> 'CatalogOfferModel':
d = src.copy()
- effective_date = parse_date(x) if (x := d.pop("effectiveDate", "")) else None
- expiry_date = parse_date(x) if (x := d.pop("expiryDate", "")) else None
- key_images = KeyImagesModel.from_list(d.pop("keyImages", []))
- price = GetPriceResModel.from_dict(x) if (x := d.pop("price", {})) else None
- promotions = PromotionsModel.from_dict(x) if (x := d.pop("promotions", {})) else None
- viewable_date = parse_date(x) if (x := d.pop("viewableDate", "")) else None
+ effective_date = parse_date(x) if (x := d.pop('effectiveDate', '')) else None
+ expiry_date = parse_date(x) if (x := d.pop('expiryDate', '')) else None
+ key_images = KeyImagesModel.from_list(d.pop('keyImages', []))
+ price = GetPriceResModel.from_dict(x) if (x := d.pop('price', {})) else None
+ promotions = PromotionsModel.from_dict(x) if (x := d.pop('promotions', {})) else None
+ viewable_date = parse_date(x) if (x := d.pop('viewableDate', '')) else None
return cls(
- catalogNs=d.pop("catalogNs", {}),
- categories=d.pop("categories", []),
- customAttributes=d.pop("customAttributes", []),
- description=d.pop("description", ""),
+ catalogNs=d.pop('catalogNs', {}),
+ categories=d.pop('categories', []),
+ customAttributes=d.pop('customAttributes', []),
+ description=d.pop('description', ''),
effectiveDate=effective_date,
expiryDate=expiry_date,
- id=d.pop("id", ""),
- isCodeRedemptionOnly=d.pop("isCodeRedemptionOnly", False),
- items=d.pop("items", []),
+ id=d.pop('id', ''),
+ isCodeRedemptionOnly=d.pop('isCodeRedemptionOnly', False),
+ items=d.pop('items', []),
keyImages=key_images,
- namespace=d.pop("namespace", ""),
- offerMappings=d.pop("offerMappings", []),
- offerType=d.pop("offerType", ""),
+ namespace=d.pop('namespace', ''),
+ offerMappings=d.pop('offerMappings', []),
+ offerType=d.pop('offerType', ''),
price=price,
- productSlug=d.pop("productSlug", ""),
+ productSlug=d.pop('productSlug', ''),
promotions=promotions,
- seller=d.pop("seller", {}),
- status=d.pop("status", ""),
- tags=d.pop("tags", []),
- title=d.pop("title", ""),
- url=d.pop("url", ""),
- urlSlug=d.pop("urlSlug", ""),
+ seller=d.pop('seller', {}),
+ status=d.pop('status', ''),
+ tags=d.pop('tags', []),
+ title=d.pop('title', ''),
+ url=d.pop('url', ''),
+ urlSlug=d.pop('urlSlug', ''),
viewableDate=viewable_date,
unmapped=d,
)
@@ -266,21 +266,21 @@ class WishlistItemModel:
order: Any = None
updated: datetime = None
offer: CatalogOfferModel = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["WishlistItemModel"], src: Dict[str, Any]) -> "WishlistItemModel":
+ def from_dict(cls: type['WishlistItemModel'], src: dict[str, Any]) -> 'WishlistItemModel':
d = src.copy()
- created = parse_date(x) if (x := d.pop("created", "")) else None
- offer = CatalogOfferModel.from_dict(x) if (x := d.pop("offer", {})) else None
- updated = parse_date(x) if (x := d.pop("updated", "")) else None
+ created = parse_date(x) if (x := d.pop('created', '')) else None
+ offer = CatalogOfferModel.from_dict(x) if (x := d.pop('offer', {})) else None
+ updated = parse_date(x) if (x := d.pop('updated', '')) else None
return cls(
created=created,
- id=d.pop("id", ""),
- namespace=d.pop("namespace", ""),
- isFirstTime=d.pop("isFirstTime", False),
- offerId=d.pop("offerId", ""),
- order=d.pop("order", ""),
+ id=d.pop('id', ''),
+ namespace=d.pop('namespace', ''),
+ isFirstTime=d.pop('isFirstTime', False),
+ offerId=d.pop('offerId', ''),
+ order=d.pop('order', ''),
updated=updated,
offer=offer,
unmapped=d,
@@ -291,80 +291,80 @@ def from_dict(cls: Type["WishlistItemModel"], src: Dict[str, Any]) -> "WishlistI
class PagingModel:
count: int = None
total: int = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["PagingModel"], src: Dict[str, Any]) -> "PagingModel":
+ def from_dict(cls: type['PagingModel'], src: dict[str, Any]) -> 'PagingModel':
d = src.copy()
- count = d.pop("count", 0)
- total = d.pop("total", 0)
+ count = d.pop('count', 0)
+ total = d.pop('total', 0)
return cls(count=count, total=total, unmapped=d)
@dataclass
class SearchStoreModel:
- elements: Tuple[CatalogOfferModel, ...] = None
+ elements: tuple[CatalogOfferModel, ...] = None
paging: PagingModel = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["SearchStoreModel"], src: Dict[str, Any]) -> "SearchStoreModel":
+ def from_dict(cls: type['SearchStoreModel'], src: dict[str, Any]) -> 'SearchStoreModel':
d = src.copy()
- _elements = d.pop("elements", [])
+ _elements = d.pop('elements', [])
elements = tuple(map(CatalogOfferModel.from_dict, _elements))
- paging = PagingModel.from_dict(x) if (x := d.pop("paging", {})) else None
+ paging = PagingModel.from_dict(x) if (x := d.pop('paging', {})) else None
return cls(elements=elements, paging=paging, unmapped=d)
@dataclass
class CatalogModel:
searchStore: SearchStoreModel = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["CatalogModel"], src: Dict[str, Any]) -> "CatalogModel":
+ def from_dict(cls: type['CatalogModel'], src: dict[str, Any]) -> 'CatalogModel':
d = src.copy()
- search_store = SearchStoreModel.from_dict(x) if (x := d.pop("searchStore", {})) else None
+ search_store = SearchStoreModel.from_dict(x) if (x := d.pop('searchStore', {})) else None
return cls(searchStore=search_store, unmapped=d)
@dataclass
class WishlistItemsModel:
- elements: Tuple[WishlistItemModel, ...] = None
+ elements: tuple[WishlistItemModel, ...] = None
paging: PagingModel = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["WishlistItemsModel"], src: Dict[str, Any]) -> "WishlistItemsModel":
+ def from_dict(cls: type['WishlistItemsModel'], src: dict[str, Any]) -> 'WishlistItemsModel':
d = src.copy()
- _elements = d.pop("elements", [])
+ _elements = d.pop('elements', [])
elements = tuple(map(WishlistItemModel.from_dict, _elements))
- paging = PagingModel.from_dict(x) if (x := d.pop("paging", {})) else None
+ paging = PagingModel.from_dict(x) if (x := d.pop('paging', {})) else None
return cls(elements=elements, paging=paging, unmapped=d)
@dataclass
class RemoveFromWishlistModel:
success: bool = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["RemoveFromWishlistModel"], src: Dict[str, Any]) -> "RemoveFromWishlistModel":
+ def from_dict(cls: type['RemoveFromWishlistModel'], src: dict[str, Any]) -> 'RemoveFromWishlistModel':
d = src.copy()
- return cls(success=d.pop("success", False), unmapped=d)
+ return cls(success=d.pop('success', False), unmapped=d)
@dataclass
class AddToWishlistModel:
wishlistItem: WishlistItemModel = None
success: bool = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["AddToWishlistModel"], src: Dict[str, Any]) -> "AddToWishlistModel":
+ def from_dict(cls: type['AddToWishlistModel'], src: dict[str, Any]) -> 'AddToWishlistModel':
d = src.copy()
- wishlist_item = WishlistItemModel.from_dict(x) if (x := d.pop("wishlistItem", {})) else None
- return cls(wishlistItem=wishlist_item, success=d.pop("success", False), unmapped=d)
+ wishlist_item = WishlistItemModel.from_dict(x) if (x := d.pop('wishlistItem', {})) else None
+ return cls(wishlistItem=wishlist_item, success=d.pop('success', False), unmapped=d)
@dataclass
@@ -372,14 +372,14 @@ class WishlistModel:
wishlistItems: WishlistItemsModel = None
removeFromWishlist: RemoveFromWishlistModel = None
addToWishlist: AddToWishlistModel = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["WishlistModel"], src: Dict[str, Any]) -> "WishlistModel":
+ def from_dict(cls: type['WishlistModel'], src: dict[str, Any]) -> 'WishlistModel':
d = src.copy()
- wishlist_items = WishlistItemsModel.from_dict(x) if (x := d.pop("wishlistItems", {})) else None
- remove_from_wishlist = RemoveFromWishlistModel.from_dict(x) if (x := d.pop("removeFromWishlist", {})) else None
- add_to_wishlist = AddToWishlistModel.from_dict(x) if (x := d.pop("addToWishlist", {})) else None
+ wishlist_items = WishlistItemsModel.from_dict(x) if (x := d.pop('wishlistItems', {})) else None
+ remove_from_wishlist = RemoveFromWishlistModel.from_dict(x) if (x := d.pop('removeFromWishlist', {})) else None
+ add_to_wishlist = AddToWishlistModel.from_dict(x) if (x := d.pop('addToWishlist', {})) else None
return cls(
wishlistItems=wishlist_items,
removeFromWishlist=remove_from_wishlist,
@@ -388,7 +388,7 @@ def from_dict(cls: Type["WishlistModel"], src: Dict[str, Any]) -> "WishlistModel
)
-ProductModel = Dict
+ProductModel = dict
@dataclass
@@ -396,14 +396,14 @@ class DataModel:
product: ProductModel = None
catalog: CatalogModel = None
wishlist: WishlistModel = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["DataModel"], src: Dict[str, Any]) -> "DataModel":
+ def from_dict(cls: type['DataModel'], src: dict[str, Any]) -> 'DataModel':
d = src.copy()
- catalog = CatalogModel.from_dict(x) if (x := d.pop("Catalog", {})) else None
- wishlist = WishlistModel.from_dict(x) if (x := d.pop("Wishlist", {})) else None
- return cls(product=d.pop("Product", {}), catalog=catalog, wishlist=wishlist, unmapped=d)
+ catalog = CatalogModel.from_dict(x) if (x := d.pop('Catalog', {})) else None
+ wishlist = WishlistModel.from_dict(x) if (x := d.pop('Wishlist', {})) else None
+ return cls(product=d.pop('Product', {}), catalog=catalog, wishlist=wishlist, unmapped=d)
@dataclass
@@ -411,28 +411,28 @@ class ErrorModel:
message: str = None
correlationId: str = None
serviceResponse: str = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
def __str__(self):
- return f"{self.correlationId} - {self.message}"
+ return f'{self.correlationId} - {self.message}'
@classmethod
- def from_dict(cls: Type["ErrorModel"], src: Dict[str, Any]) -> "ErrorModel":
+ def from_dict(cls: type['ErrorModel'], src: dict[str, Any]) -> 'ErrorModel':
d = src.copy()
return cls(
- message=d.pop("message", ""),
- correlationId=d.pop("correlationId", ""),
- serviceResponse=d.pop("serviceResponse", ""),
+ message=d.pop('message', ''),
+ correlationId=d.pop('correlationId', ''),
+ serviceResponse=d.pop('serviceResponse', ''),
unmapped=d,
)
@dataclass
class ExtensionsModel:
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["ExtensionsModel"], src: Dict[str, Any]) -> "ExtensionsModel":
+ def from_dict(cls: type['ExtensionsModel'], src: dict[str, Any]) -> 'ExtensionsModel':
d = src.copy()
return cls(unmapped=d)
@@ -440,15 +440,15 @@ def from_dict(cls: Type["ExtensionsModel"], src: Dict[str, Any]) -> "ExtensionsM
@dataclass
class ResponseModel:
data: DataModel = None
- errors: Tuple[ErrorModel, ...] = None
+ errors: tuple[ErrorModel, ...] = None
extensions: ExtensionsModel = None
- unmapped: Dict[str, Any] = field(default_factory=dict)
+ unmapped: dict[str, Any] = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["ResponseModel"], src: Dict[str, Any]) -> "ResponseModel":
+ def from_dict(cls: type['ResponseModel'], src: dict[str, Any]) -> 'ResponseModel':
d = src.copy()
- data = DataModel.from_dict(x) if (x := d.pop("data", {})) else None
- _errors = d.pop("errors", [])
+ data = DataModel.from_dict(x) if (x := d.pop('data', {})) else None
+ _errors = d.pop('errors', [])
errors = tuple(map(ErrorModel.from_dict, _errors))
- extensions = ExtensionsModel.from_dict(x) if (x := d.pop("extensions", {})) else None
+ extensions = ExtensionsModel.from_dict(x) if (x := d.pop('extensions', {})) else None
return cls(data=data, errors=errors, extensions=extensions, unmapped=d)
diff --git a/rare/components/tabs/store/constants.py b/rare/components/tabs/store/constants.py
index 5a81668236..8adb6da61c 100644
--- a/rare/components/tabs/store/constants.py
+++ b/rare/components/tabs/store/constants.py
@@ -7,40 +7,40 @@ def __init__(self):
super(Constants, self).__init__()
self.categories = sorted(
[
- (self.tr("Action"), "1216"),
- (self.tr("Adventure"), "1117"),
- (self.tr("Puzzle"), "1298"),
- (self.tr("Open world"), "1307"),
- (self.tr("Racing"), "1212"),
- (self.tr("RPG"), "1367"),
- (self.tr("Shooter"), "1210"),
- (self.tr("Strategy"), "1115"),
- (self.tr("Survival"), "1080"),
- (self.tr("First Person"), "1294"),
- (self.tr("Indie"), "1263"),
- (self.tr("Simulation"), "1393"),
- (self.tr("Sport"), "1283"),
+ (self.tr('Action'), '1216'),
+ (self.tr('Adventure'), '1117'),
+ (self.tr('Puzzle'), '1298'),
+ (self.tr('Open world'), '1307'),
+ (self.tr('Racing'), '1212'),
+ (self.tr('RPG'), '1367'),
+ (self.tr('Shooter'), '1210'),
+ (self.tr('Strategy'), '1115'),
+ (self.tr('Survival'), '1080'),
+ (self.tr('First Person'), '1294'),
+ (self.tr('Indie'), '1263'),
+ (self.tr('Simulation'), '1393'),
+ (self.tr('Sport'), '1283'),
],
key=lambda x: x[0],
)
self.platforms = [
- ("MacOS", "9548"),
- ("Windows", "9547"),
+ ('MacOS', '9548'),
+ ('Windows', '9547'),
]
self.others = [
- (self.tr("Single player"), "1370"),
- (self.tr("Multiplayer"), "1203"),
- (self.tr("Controller"), "9549"),
- (self.tr("Co-op"), "1264"),
+ (self.tr('Single player'), '1370'),
+ (self.tr('Multiplayer'), '1203'),
+ (self.tr('Controller'), '9549'),
+ (self.tr('Co-op'), '1264'),
]
self.types = [
- (self.tr("Editor"), "editors"),
- (self.tr("Game"), "games/edition/base"),
- (self.tr("Bundle"), "bundles/games"),
- (self.tr("Add-on"), "addons"),
- (self.tr("Apps"), "software/edition/base"),
+ (self.tr('Editor'), 'editors'),
+ (self.tr('Game'), 'games/edition/base'),
+ (self.tr('Bundle'), 'bundles/games'),
+ (self.tr('Add-on'), 'addons'),
+ (self.tr('Apps'), 'software/edition/base'),
]
@@ -56,7 +56,7 @@ def __init__(self):
prePurchaseOfferId
"""
-__PageSandboxModel = """
+__PageSandboxModel = f"""
pageSlug
pageType
productId
@@ -64,25 +64,25 @@ def __init__(self):
createdDate
updatedDate
deletedDate
-mappings {
- %s
-}
-""" % (__StorePageMapping)
+mappings {{
+ {__StorePageMapping}
+}}
+"""
-__CatalogNamespace = """
+__CatalogNamespace = f"""
parent
displayName
store
-home: mappings(pageType: "productHome") {
- %s
-}
-addons: mappings(pageType: "addon--cms-hybrid") {
- %s
-}
-offers: mappings(pageType: "offer") {
- %s
-}
-""" % (__PageSandboxModel, __PageSandboxModel, __PageSandboxModel)
+home: mappings(pageType: "productHome") {{
+ {__PageSandboxModel}
+}}
+addons: mappings(pageType: "addon--cms-hybrid") {{
+ {__PageSandboxModel}
+}}
+offers: mappings(pageType: "offer") {{
+ {__PageSandboxModel}
+}}
+"""
__CatalogItem = """
id
@@ -140,7 +140,7 @@ def __init__(self):
}
"""
-__CatalogOffer = """
+__CatalogOffer = f"""
title
id
namespace
@@ -150,59 +150,52 @@ def __init__(self):
isCodeRedemptionOnly
description
effectiveDate
-keyImages {
- %(image)s
-}
+keyImages {{
+ {__Image}
+}}
currentPrice
-seller {
+seller {{
id
name
-}
+}}
productSlug
urlSlug
url
-tags {
+tags {{
id
name
groupName
-}
-items {
- %(catalog_item)s
-}
-customAttributes {
+}}
+items {{
+ {__CatalogItem}
+}}
+customAttributes {{
key
value
-}
-categories {
+}}
+categories {{
path
-}
-catalogNs @include(if: $withMapping) {
- %(catalog_namespace)s
-}
-offerMappings @include(if: $withMapping) {
- %(page_sandbox_model)s
-}
-price(country: $country) @include(if: $withPrice) {
- %(get_price_res)s
-}
-promotions(category: $category) @include(if: $withPromotions) {
- %(promotions)s
-}
-""" % {
- "image": __Image,
- "catalog_item": __CatalogItem,
- "catalog_namespace": __CatalogNamespace,
- "page_sandbox_model": __PageSandboxModel,
- "get_price_res": __GetPriceRes,
- "promotions": __Promotions,
-}
+}}
+catalogNs @include(if: $withMapping) {{
+ {__CatalogNamespace}
+}}
+offerMappings @include(if: $withMapping) {{
+ {__PageSandboxModel}
+}}
+price(country: $country) @include(if: $withPrice) {{
+ {__GetPriceRes}
+}}
+promotions(category: $category) @include(if: $withPromotions) {{
+ {__Promotions}
+}}
+"""
__Pagination = """
count
total
"""
-SEARCH_STORE_QUERY = """
+SEARCH_STORE_QUERY = f"""
query searchStoreQuery(
$allowCountries: String
$category: String
@@ -224,8 +217,8 @@ def __init__(self):
$freeGame: Boolean
$onSale: Boolean
$effectiveDate: String
-) {
- Catalog {
+) {{
+ Catalog {{
searchStore(
allowCountries: $allowCountries
category: $category
@@ -244,20 +237,19 @@ def __init__(self):
freeGame: $freeGame
onSale: $onSale
effectiveDate: $effectiveDate
- ) {
- elements {
- %s
- }
- paging {
- %s
- }
- }
- }
-}
-""" % (__CatalogOffer, __Pagination)
+ ) {{
+ elements {{
+ {__CatalogOffer}
+ }}
+ paging {{
+ {__Pagination}
+ }}
+ }}
+ }}
+}}
+"""
-__WISHLIST_ITEM = (
- """
+__WISHLIST_ITEM = f"""
id
order
created
@@ -265,15 +257,12 @@ def __init__(self):
updated
namespace
isFirstTime
-offer(locale: $locale) {
- %s
-}
+offer(locale: $locale) {{
+ {__CatalogOffer}
+}}
"""
- % __CatalogOffer
-)
-WISHLIST_QUERY = (
- """
+WISHLIST_QUERY = f"""
query wishlistQuery(
$country: String!
$locale: String
@@ -281,21 +270,18 @@ def __init__(self):
$withMapping: Boolean = false
$withPrice: Boolean = false
$withPromotions: Boolean = false
-) {
- Wishlist {
- wishlistItems {
- elements {
- %s
- }
- }
- }
-}
+) {{
+ Wishlist {{
+ wishlistItems {{
+ elements {{
+ {__WISHLIST_ITEM}
+ }}
+ }}
+ }}
+}}
"""
- % __WISHLIST_ITEM
-)
-WISHLIST_ADD_QUERY = (
- """
+WISHLIST_ADD_QUERY = f"""
mutation addWishlistMutation(
$namespace: String!
$offerId: String!
@@ -305,22 +291,20 @@ def __init__(self):
$withMapping: Boolean = false
$withPrice: Boolean = false
$withPromotions: Boolean = false
-) {
- Wishlist {
+) {{
+ Wishlist {{
addToWishlist(
namespace: $namespace
offerId: $offerId
- ) {
- wishlistItem {
- %s
- }
+ ) {{
+ wishlistItem {{
+ {__WISHLIST_ITEM}
+ }}
success
- }
- }
-}
+ }}
+ }}
+}}
"""
- % __WISHLIST_ITEM
-)
WISHLIST_REMOVE_QUERY = """
mutation removeFromWishlistMutation(
@@ -447,7 +431,7 @@ def __init__(self):
def compress_query(query: str) -> str:
- return query.replace(" ", "").replace("\n", " ")
+ return query.replace(' ', '').replace('\n', ' ')
game_query = compress_query(SEARCH_STORE_QUERY)
@@ -459,5 +443,5 @@ def compress_query(query: str) -> str:
store_config_query = compress_query(STORE_CONFIG_QUERY)
-if __name__ == "__main__":
+if __name__ == '__main__':
print(SEARCH_STORE_QUERY)
diff --git a/rare/components/tabs/store/landing.py b/rare/components/tabs/store/landing.py
index 820c54d42f..c1e72a26a5 100644
--- a/rare/components/tabs/store/landing.py
+++ b/rare/components/tabs/store/landing.py
@@ -1,6 +1,5 @@
from datetime import datetime, timezone
from logging import getLogger
-from typing import List
from PySide6.QtCore import Qt, Signal, Slot
from PySide6.QtGui import QHideEvent, QShowEvent
@@ -26,7 +25,7 @@
from .widgets.groups import StoreGroup
from .widgets.items import StoreItemWidget
-logger = getLogger("StoreLanding")
+logger = getLogger('StoreLanding')
class LandingPage(SlidingStackedWidget, SideTabContents):
@@ -77,18 +76,18 @@ def __init__(self, api: StoreAPI, parent=None):
self.setLayout(layout)
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
- self.free_games_now = StoreGroup(self.tr("Free now"), layout=FlowLayout, parent=self)
+ self.free_games_now = StoreGroup(self.tr('Free now'), layout=FlowLayout, parent=self)
self.free_games_now.main_layout.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
self.free_games_now.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
- self.free_games_next = StoreGroup(self.tr("Free next week"), layout=FlowLayout, parent=self)
+ self.free_games_next = StoreGroup(self.tr('Free next week'), layout=FlowLayout, parent=self)
self.free_games_next.main_layout.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
self.free_games_next.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
- self.discounts_group = StoreGroup(self.tr("Wishlist discounts"), layout=FlowLayout, parent=self)
+ self.discounts_group = StoreGroup(self.tr('Wishlist discounts'), layout=FlowLayout, parent=self)
self.discounts_group.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
- self.games_group = StoreGroup(self.tr("Free to play"), FlowLayout, self)
+ self.games_group = StoreGroup(self.tr('Free to play'), FlowLayout, self)
self.games_group.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.games_group.loading(False)
self.games_group.setVisible(False)
@@ -112,7 +111,7 @@ def hideEvent(self, a0: QHideEvent) -> None:
# TODO: Implement tab unloading
return super().hideEvent(a0)
- def _update_wishlist_discounts(self, wishlist: List[WishlistItemModel]):
+ def _update_wishlist_discounts(self, wishlist: list[WishlistItemModel]):
for w in self.discounts_group.findChildren(StoreItemWidget, options=Qt.FindChildOption.FindDirectChildrenOnly):
self.discounts_group.layout().removeWidget(w)
w.disconnect(w)
@@ -126,7 +125,7 @@ def _update_wishlist_discounts(self, wishlist: List[WishlistItemModel]):
self.discounts_group.setVisible(have_discounts)
self.discounts_group.loading(False)
- def _update_free_games(self, free_games: List[CatalogOfferModel]):
+ def _update_free_games(self, free_games: list[CatalogOfferModel]):
for w in self.free_games_now.findChildren(StoreItemWidget, options=Qt.FindChildOption.FindDirectChildrenOnly):
self.free_games_now.layout().removeWidget(w)
w.disconnect(w)
@@ -145,7 +144,7 @@ def _update_free_games(self, free_games: List[CatalogOfferModel]):
if item.price.totalPrice.discountPrice == 0:
free_now.append(item)
continue
- if item.title == "Mystery Game":
+ if item.title == 'Mystery Game':
free_next.append(item)
continue
except KeyError as e:
@@ -174,7 +173,7 @@ def _update_free_games(self, free_games: List[CatalogOfferModel]):
self.free_games_next.setVisible(bool(free_next))
for item in free_next:
w = StoreItemWidget(self.api.cached_manager, item)
- if item.title != "Mystery Game":
+ if item.title != 'Mystery Game':
w.show_details.connect(self.show_details)
self.free_games_next.layout().addWidget(w)
self.free_games_next.loading(False)
diff --git a/rare/components/tabs/store/search.py b/rare/components/tabs/store/search.py
index aa8e9aedfc..626c601351 100644
--- a/rare/components/tabs/store/search.py
+++ b/rare/components/tabs/store/search.py
@@ -1,5 +1,4 @@
from logging import getLogger
-from typing import List
from PySide6.QtCore import Qt, Signal, Slot
from PySide6.QtWidgets import (
@@ -23,7 +22,7 @@
from .widgets.details import StoreDetailsWidget
from .widgets.items import SearchItemWidget
-logger = getLogger("Shop")
+logger = getLogger('Shop')
class SearchPage(SlidingStackedWidget, SideTabContents):
@@ -70,16 +69,16 @@ def __init__(self, store_api: StoreAPI, parent=None):
self.ui.filter_scrollarea.viewport().setAutoFillBackground(False)
self.store_api = store_api
- self.price = ""
+ self.price = ''
self.tags = []
self.types = []
self.update_games_allowed = True
self.active_search_request = False
- self.next_search = ""
- self.wishlist: List = []
+ self.next_search = ''
+ self.wishlist: list = []
- self.search_bar = ButtonLineEdit("fa5s.search", placeholder_text=self.tr("Search"))
+ self.search_bar = ButtonLineEdit('fa5s.search', placeholder_text=self.tr('Search'))
self.results_scrollarea = ResultsWidget(self.store_api, self)
self.results_scrollarea.show_details.connect(self.show_details)
@@ -103,27 +102,27 @@ def show_search_results(self):
def init_filter(self):
# self.ui.none_price.toggled.connect(lambda: self.prepare_request("") if self.ui.none_price.isChecked() else None)
self.ui.none_price.toggled.connect(
- (lambda obj: obj.prepare_request("") if self.ui.none_price.isChecked() else None).__get__(self)
+ (lambda obj: obj.prepare_request('') if self.ui.none_price.isChecked() else None).__get__(self)
)
# self.ui.free_button.toggled.connect(lambda: self.prepare_request("free") if self.ui.free_button.isChecked() else None)
self.ui.free_button.toggled.connect(
- (lambda obj: obj.prepare_request("free") if self.ui.free_button.isChecked() else None).__get__(self)
+ (lambda obj: obj.prepare_request('free') if self.ui.free_button.isChecked() else None).__get__(self)
)
# self.ui.under10.toggled.connect(lambda: self.prepare_request("[0, 1000)") if self.ui.under10.isChecked() else None)
self.ui.under10.toggled.connect(
- (lambda obj: obj.prepare_request("[0, 1000)") if self.ui.under10.isChecked() else None).__get__(self)
+ (lambda obj: obj.prepare_request('[0, 1000)') if self.ui.under10.isChecked() else None).__get__(self)
)
# self.ui.under20.toggled.connect(lambda: self.prepare_request("[0, 2000)") if self.ui.under20.isChecked() else None)
self.ui.under20.toggled.connect(
- (lambda obj: obj.prepare_request("[0, 2000)") if self.ui.under20.isChecked() else None).__get__(self)
+ (lambda obj: obj.prepare_request('[0, 2000)') if self.ui.under20.isChecked() else None).__get__(self)
)
# self.ui.under30.toggled.connect(lambda: self.prepare_request("[0, 3000)") if self.ui.under30.isChecked() else None)
self.ui.under30.toggled.connect(
- (lambda obj: obj.prepare_request("[0, 3000)") if self.ui.under30.isChecked() else None).__get__(self)
+ (lambda obj: obj.prepare_request('[0, 3000)') if self.ui.under30.isChecked() else None).__get__(self)
)
# self.ui.above.toggled.connect(lambda: self.prepare_request("[1499,]") if self.ui.above.isChecked() else None)
self.ui.above.toggled.connect(
- (lambda obj: obj.prepare_request("[1499,]") if self.ui.above.isChecked() else None).__get__(self)
+ (lambda obj: obj.prepare_request('[1499,]') if self.ui.above.isChecked() else None).__get__(self)
)
# self.on_discount.toggled.connect(
# lambda: self.prepare_request("sale") if self.on_discount.isChecked() else None
@@ -173,8 +172,8 @@ def prepare_request(
price: str = None,
added_tag: int = 0,
removed_tag: int = 0,
- added_type: str = "",
- removed_type: str = "",
+ added_type: str = '',
+ removed_type: str = '',
):
if not self.update_games_allowed:
return
@@ -207,10 +206,10 @@ def prepare_request(
price_range=self.price,
on_sale=self.ui.on_discount.isChecked(),
)
- browse_model.tag = "|".join(self.tags)
+ browse_model.tag = '|'.join(self.tags)
if self.types:
- browse_model.category = "|".join(self.types)
+ browse_model.category = '|'.join(self.types)
self.store_api.browse_games(browse_model, self.show_games)
@@ -252,7 +251,7 @@ def __init__(self, store_api, parent=None):
def load_results(self, text: str):
self.setEnabled(False)
- if text != "":
+ if text != '':
self.store_api.search_game(text, self.show_results)
def show_results(self, results: dict):
@@ -266,7 +265,7 @@ def show_results(self, results: dict):
w.deleteLater()
if not results:
- self.results_layout.addWidget(QLabel(self.tr("No results found")))
+ self.results_layout.addWidget(QLabel(self.tr('No results found')))
else:
for res in results:
w = SearchItemWidget(self.store_api.cached_manager, res, parent=self.results_container)
diff --git a/rare/components/tabs/store/store_api.py b/rare/components/tabs/store/store_api.py
index 1d513b74aa..f7f8cb02ec 100644
--- a/rare/components/tabs/store/store_api.py
+++ b/rare/components/tabs/store/store_api.py
@@ -1,5 +1,5 @@
+from collections.abc import Callable
from logging import getLogger
-from typing import Callable, Tuple
from PySide6.QtCore import QObject, Signal
from PySide6.QtWidgets import QApplication
@@ -11,7 +11,7 @@
wishlist_remove_query,
)
from rare.utils.paths import cache_dir
-from rare.utils.qt_requests import QtRequests
+from rare.utils.qrequests import QRequests
from .api.models.diesel import DieselProduct
from .api.models.query import SearchStoreQuery
@@ -19,12 +19,12 @@
ResponseModel,
)
-graphql_url = "https://store.epicgames.com/graphql"
+graphql_url = 'https://store.epicgames.com/graphql'
# graphql_url = "https://launcher.store.epicgames.com/graphql"
def DEBUG() -> bool:
- return "--debug" in QApplication.arguments()
+ return '--debug' in QApplication.arguments()
class StoreAPI(QObject):
@@ -36,10 +36,10 @@ def __init__(self, token, language: str, country: str, installed):
self.token = token
self.language_code: str = language
self.country_code: str = country
- self.locale = f"{self.language_code}-{self.country_code}"
- self.manager = QtRequests(parent=self)
- self.authed_manager = QtRequests(token=token, parent=self)
- self.cached_manager = QtRequests(cache=str(cache_dir().joinpath("store")), parent=self)
+ self.locale = f'{self.language_code}-{self.country_code}'
+ self.manager = QRequests(parent=self)
+ self.authed_manager = QRequests(token=token, parent=self)
+ self.cached_manager = QRequests(cache=str(cache_dir().joinpath('store')), parent=self)
self.installed = installed
@@ -47,11 +47,11 @@ def __init__(self, token, language: str, country: str, installed):
self.next_browse_request = tuple(())
def get_free(self, callback: Callable):
- url = "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions"
+ url = 'https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions'
params = {
- "locale": self.locale,
- "country": self.country_code,
- "allowCountries": self.country_code,
+ 'locale': self.locale,
+ 'country': self.country_code,
+ 'allowCountries': self.country_code,
}
self.manager.get(url, lambda data: self.__handle_free_games(data, callback), params=params)
@@ -66,7 +66,7 @@ def __handle_free_games(self, data, callback: Callable):
if DEBUG():
raise e
elements = False
- self.logger.error("Free games request failed with: %s", e)
+ self.logger.error('Free games request failed with: %s', e)
callback(elements)
def get_wishlist(self, callback: Callable):
@@ -74,16 +74,16 @@ def get_wishlist(self, callback: Callable):
graphql_url,
lambda data: self.__handle_wishlist(data, callback),
{
- "query": wishlist_query,
- "variables": {
- "country": self.country_code,
- "locale": self.locale,
- "withPrice": True,
+ 'query': wishlist_query,
+ 'variables': {
+ 'country': self.country_code,
+ 'locale': self.locale,
+ 'withPrice': True,
},
},
)
- def __handle_wishlist(self, data, callback: Callable[[Tuple], None]):
+ def __handle_wishlist(self, data, callback: Callable[[tuple], None]):
try:
response = ResponseModel.from_dict(data)
if response.errors:
@@ -94,30 +94,30 @@ def __handle_wishlist(self, data, callback: Callable[[Tuple], None]):
if DEBUG():
raise e
elements = False
- self.logger.error("Wishlist request failed with: %s", e)
+ self.logger.error('Wishlist request failed with: %s', e)
callback(elements)
def search_game(self, name, callback: Callable):
payload = {
- "query": search_query,
- "variables": {
- "category": "games/edition/base|bundles/games|editors|software/edition/base",
- "count": 20,
- "country": self.country_code,
- "keywords": name,
- "locale": self.locale,
- "sortDir": "DESC",
- "allowCountries": self.country_code,
- "start": 0,
- "tag": "",
- "withMapping": False,
- "withPrice": True,
+ 'query': search_query,
+ 'variables': {
+ 'category': 'games/edition/base|bundles/games|editors|software/edition/base',
+ 'count': 20,
+ 'country': self.country_code,
+ 'keywords': name,
+ 'locale': self.locale,
+ 'sortDir': 'DESC',
+ 'allowCountries': self.country_code,
+ 'start': 0,
+ 'tag': '',
+ 'withMapping': False,
+ 'withPrice': True,
},
}
self.manager.post(graphql_url, lambda data: self.__handle_search(data, callback), payload)
- def __handle_search(self, data, callback: Callable[[Tuple], None]):
+ def __handle_search(self, data, callback: Callable[[tuple], None]):
try:
response = ResponseModel.from_dict(data)
if response.errors:
@@ -128,7 +128,7 @@ def __handle_search(self, data, callback: Callable[[Tuple], None]):
if DEBUG():
raise e
elements = False
- self.logger.error("Search request failed with: %s", e)
+ self.logger.error('Search request failed with: %s', e)
callback(elements)
def browse_games(self, browse_model: SearchStoreQuery, callback):
@@ -136,7 +136,7 @@ def browse_games(self, browse_model: SearchStoreQuery, callback):
self.next_browse_request = (browse_model, callback)
return
self.browse_active = True
- payload = {"query": search_query, "variables": browse_model.to_dict()}
+ payload = {'query': search_query, 'variables': browse_model.to_dict()}
self.manager.post(
graphql_url,
lambda data: self.__handle_browse_games(data, callback),
@@ -158,7 +158,7 @@ def __handle_browse_games(self, data, callback):
if DEBUG():
raise e
elements = False
- self.logger.error("Browse request failed with: %s", e)
+ self.logger.error('Browse request failed with: %s', e)
callback(elements)
else:
self.browse_games(*self.next_browse_request) # pylint: disable=E1120
@@ -179,9 +179,9 @@ def __make_api_query(self):
pass
def get_game_config_cms(self, slug: str, is_bundle: bool, callback: Callable):
- url = "https://store-content.ak.epicgames.com/api"
- url += f"/{self.locale}/content/{'products' if not is_bundle else 'bundles'}/{slug}"
- self.logger.debug("Quering game config: %s", url)
+ url = 'https://store-content.ak.epicgames.com/api'
+ url += f'/{self.locale}/content/{"products" if not is_bundle else "bundles"}/{slug}'
+ self.logger.debug('Quering game config: %s', url)
self.manager.get(url, lambda data: self.__handle_get_game(data, callback))
def __handle_get_game(self, data, callback):
@@ -197,12 +197,12 @@ def __handle_get_game(self, data, callback):
# needs a captcha
def add_to_wishlist(self, namespace, offer_id, callback: Callable):
payload = {
- "query": wishlist_add_query,
- "variables": {
- "offerId": offer_id,
- "namespace": namespace,
- "country": self.country_code,
- "locale": self.locale,
+ 'query': wishlist_add_query,
+ 'variables': {
+ 'offerId': offer_id,
+ 'namespace': namespace,
+ 'country': self.country_code,
+ 'locale': self.locale,
},
}
self.authed_manager.post(
@@ -221,18 +221,18 @@ def _handle_add_to_wishlist(self, data, callback):
except Exception as e:
if DEBUG():
raise e
- self.logger.error("Add to wishlist request failed with: %s", e)
+ self.logger.error('Add to wishlist request failed with: %s', e)
success = False
callback(success)
self.update_wishlist.emit()
def remove_from_wishlist(self, namespace, offer_id, callback: Callable):
payload = {
- "query": wishlist_remove_query,
- "variables": {
- "offerId": offer_id,
- "namespace": namespace,
- "operation": "REMOVE",
+ 'query': wishlist_remove_query,
+ 'variables': {
+ 'offerId': offer_id,
+ 'namespace': namespace,
+ 'operation': 'REMOVE',
},
}
self.authed_manager.post(
@@ -251,7 +251,7 @@ def _handle_remove_from_wishlist(self, data, callback):
except Exception as e:
if DEBUG():
raise e
- self.logger.error("Remove from wishlist request failed with: %s", e)
+ self.logger.error('Remove from wishlist request failed with: %s', e)
success = False
callback(success)
self.update_wishlist.emit()
diff --git a/rare/components/tabs/store/widgets/details.py b/rare/components/tabs/store/widgets/details.py
index 6221055775..36f4efe3b0 100644
--- a/rare/components/tabs/store/widgets/details.py
+++ b/rare/components/tabs/store/widgets/details.py
@@ -1,5 +1,4 @@
from logging import getLogger
-from typing import List
from PySide6.QtCore import Qt, QUrl, Signal, Slot
from PySide6.QtGui import QDesktopServices, QKeyEvent
@@ -25,14 +24,14 @@
from rare.widgets.image_widget import LoadingSpinnerImageWidget
from rare.widgets.side_tab import SideTabContents, SideTabWidget
-logger = getLogger("StoreDetails")
+logger = getLogger('StoreDetails')
class StoreDetailsWidget(QWidget, SideTabContents):
back_clicked: Signal = Signal()
# TODO Design
- def __init__(self, installed: List, store_api: StoreAPI, parent=None):
+ def __init__(self, installed: list, store_api: StoreAPI, parent=None):
super(StoreDetailsWidget, self).__init__(parent=parent)
self.implements_scrollarea = True
@@ -60,18 +59,18 @@ def __init__(self, installed: List, store_api: StoreAPI, parent=None):
self.ui.requirements_layout.addWidget(self.requirements_tabs)
self.ui.requirements_layout.setAlignment(Qt.AlignmentFlag.AlignBottom)
- self.ui.back_button.setIcon(qta_icon("fa.chevron-left", "fa5s.chevron-left"))
+ self.ui.back_button.setIcon(qta_icon('fa.chevron-left', 'fa5s.chevron-left'))
self.ui.back_button.clicked.connect(self.back_clicked)
self.setDisabled(False)
- def handle_wishlist_update(self, wishlist: List[CatalogOfferModel]):
- if wishlist and wishlist[0] == "error":
+ def handle_wishlist_update(self, wishlist: list[CatalogOfferModel]):
+ if wishlist and wishlist[0] == 'error':
return
self.wishlist = [game.id for game in wishlist]
if self.id_str in self.wishlist:
self.in_wishlist = True
- self.ui.wishlist_button.setText(self.tr("Remove from Wishlist"))
+ self.ui.wishlist_button.setText(self.tr('Remove from Wishlist'))
else:
self.in_wishlist = False
@@ -91,29 +90,29 @@ def update_game(self, offer: CatalogOfferModel):
slug = offer.productSlug
if not slug:
for mapping in offer.offerMappings:
- if mapping["pageType"] == "productHome":
- slug = mapping["pageSlug"]
+ if mapping['pageType'] == 'productHome':
+ slug = mapping['pageSlug']
break
else:
- logger.error("Could not get page information")
- slug = ""
- if "/home" in slug:
- slug = slug.replace("/home", "")
+ logger.error('Could not get page information')
+ slug = ''
+ if '/home' in slug:
+ slug = slug.replace('/home', '')
self.slug = slug
if offer.namespace in self.installed:
- self.ui.store_button.setText(self.tr("Show Game on Epic Page"))
+ self.ui.store_button.setText(self.tr('Show Game on Epic Page'))
self.ui.status.setVisible(True)
else:
- self.ui.store_button.setText(self.tr("Buy Game in Epic Games Store"))
+ self.ui.store_button.setText(self.tr('Buy Game in Epic Games Store'))
self.ui.status.setVisible(False)
- self.ui.original_price.setText(self.tr("Loading"))
+ self.ui.original_price.setText(self.tr('Loading'))
# self.title.setText(self.tr("Loading"))
# self.image.setPixmap(QPixmap())
is_bundle = False
for i in offer.categories:
- if "bundles" in i.get("path", ""):
+ if 'bundles' in i.get('path', ''):
is_bundle = True
# init API request
@@ -128,17 +127,17 @@ def add_to_wishlist(self):
self.store_api.add_to_wishlist(
self.catalog_offer.namespace,
self.catalog_offer.id,
- lambda success: self.ui.wishlist_button.setText(self.tr("Remove from wishlist"))
+ lambda success: self.ui.wishlist_button.setText(self.tr('Remove from wishlist'))
if success
- else self.ui.wishlist_button.setText("Something went wrong"),
+ else self.ui.wishlist_button.setText('Something went wrong'),
)
else:
self.store_api.remove_from_wishlist(
self.catalog_offer.namespace,
self.catalog_offer.id,
- lambda success: self.ui.wishlist_button.setText(self.tr("Add to wishlist"))
+ lambda success: self.ui.wishlist_button.setText(self.tr('Add to wishlist'))
if success
- else self.ui.wishlist_button.setText("Something went wrong"),
+ else self.ui.wishlist_button.setText('Something went wrong'),
)
def data_received(self, product: DieselProduct):
@@ -152,17 +151,17 @@ def data_received(self, product: DieselProduct):
logger.error(str(e))
self.ui.original_price.setFont(self.font())
- price = self.catalog_offer.price.totalPrice.fmtPrice["originalPrice"]
- discount_price = self.catalog_offer.price.totalPrice.fmtPrice["discountPrice"]
- if price == "0" or price == 0:
- self.ui.original_price.setText(self.tr("Free"))
+ price = self.catalog_offer.price.totalPrice.fmtPrice['originalPrice']
+ discount_price = self.catalog_offer.price.totalPrice.fmtPrice['discountPrice']
+ if price == '0' or price == 0:
+ self.ui.original_price.setText(self.tr('Free'))
else:
self.ui.original_price.setText(price)
if price != discount_price:
font = self.font()
font.setStrikeOut(True)
self.ui.original_price.setFont(font)
- self.ui.discount_price.setText(discount_price if discount_price != "0" else self.tr("Free"))
+ self.ui.discount_price.setText(discount_price if discount_price != '0' else self.tr('Free'))
self.ui.discount_price.setVisible(True)
else:
self.ui.discount_price.setVisible(False)
@@ -185,9 +184,9 @@ def data_received(self, product: DieselProduct):
# self.image_stack.setCurrentIndex(0)
about = product_data.about
description = about.description
- description = description.replace("### ", "##### ")
- description = description.replace("## ", "#### ")
- description = description.replace("# ", "### ")
+ description = description.replace('### ', '##### ')
+ description = description.replace('## ', '#### ')
+ description = description.replace('# ', '### ')
self.ui.description_label.setMarkdown(description)
self.ui.developer.setText(about.developerAttribution)
# try:
@@ -197,8 +196,8 @@ def data_received(self, product: DieselProduct):
# self.ui.dev.setText(self.game.developer)
# except KeyError:
# pass
- tags = product_data.unmapped["meta"].get("tags", [])
- self.ui.tags.setText(", ".join(tags))
+ tags = product_data.unmapped['meta'].get('tags', [])
+ self.ui.tags.setText(', '.join(tags))
# clear Layout
for b in self.ui.social_links.findChildren(SocialButton, options=Qt.FindChildOption.FindDirectChildrenOnly):
@@ -209,16 +208,16 @@ def data_received(self, product: DieselProduct):
links = product_data.socialLinks
link_count = 0
for name, url in links.items():
- if name == "_type":
+ if name == '_type':
continue
- name = name.replace("link", "").lower()
- if name == "homepage":
- icn = qta_icon("mdi.web", "fa5s.globe", scale_factor=1.2)
- elif name == "title":
- icn = qta_icon("mdi.home-circle", "fa5s.home", scale_factor=1.2)
+ name = name.replace('link', '').lower()
+ if name == 'homepage':
+ icn = qta_icon('mdi.web', 'fa5s.globe', scale_factor=1.2)
+ elif name == 'title':
+ icn = qta_icon('mdi.home-circle', 'fa5s.home', scale_factor=1.2)
else:
try:
- icn = qta_icon(f"mdi.{name}", f"fa5b.{name}", scale_factor=1.2)
+ icn = qta_icon(f'mdi.{name}', f'fa5b.{name}', scale_factor=1.2)
except Exception as e:
logger.error(str(e))
continue
@@ -237,7 +236,7 @@ def data_received(self, product: DieselProduct):
# self.wishlist.append(game["offer"]["title"])
def button_clicked(self):
- QDesktopServices.openUrl(QUrl(f"https://www.epicgames.com/store/{self.store_api.language_code}/p/{self.slug}"))
+ QDesktopServices.openUrl(QUrl(f'https://www.epicgames.com/store/{self.store_api.language_code}/p/{self.slug}'))
def keyPressEvent(self, a0: QKeyEvent):
if a0.key() == Qt.Key.Key_Escape:
@@ -246,7 +245,7 @@ def keyPressEvent(self, a0: QKeyEvent):
class SocialButton(QPushButton):
def __init__(self, icn, url, parent=None):
- super(SocialButton, self).__init__(icn, "", parent=parent)
+ super(SocialButton, self).__init__(icn, '', parent=parent)
self.setFixedSize(36, 36)
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
self.url = url
@@ -267,9 +266,9 @@ def __init__(self, system: DieselSystemDetail, parent=None):
bold_font.setBold(True)
req_layout = QGridLayout(self)
- min_label = QLabel(self.tr("Minimum"), parent=self)
+ min_label = QLabel(self.tr('Minimum'), parent=self)
min_label.setFont(bold_font)
- rec_label = QLabel(self.tr("Recommend"), parent=self)
+ rec_label = QLabel(self.tr('Recommend'), parent=self)
rec_label.setFont(bold_font)
req_layout.addWidget(min_label, 0, 1)
req_layout.addWidget(rec_label, 0, 2)
diff --git a/rare/components/tabs/store/widgets/icon_widget.py b/rare/components/tabs/store/widgets/icon_widget.py
index c7905ae7dd..30513deb1b 100644
--- a/rare/components/tabs/store/widgets/icon_widget.py
+++ b/rare/components/tabs/store/widgets/icon_widget.py
@@ -9,7 +9,7 @@
)
-class IconWidget(object):
+class IconWidget:
def __init__(self):
self.mini_widget: QWidget = None
self.title_label: QLabel = None
@@ -20,12 +20,12 @@ def __init__(self):
def setupUi(self, widget: QWidget):
# on-hover popup
self.mini_widget = QWidget(parent=widget)
- self.mini_widget.setObjectName(f"{type(self).__name__}MiniWidget")
+ self.mini_widget.setObjectName(f'{type(self).__name__}MiniWidget')
self.mini_widget.setFixedHeight(int(widget.height() // 3))
# game title
self.title_label = QLabel(parent=self.mini_widget)
- self.title_label.setObjectName(f"{type(self).__name__}TitleLabel")
+ self.title_label.setObjectName(f'{type(self).__name__}TitleLabel')
self.title_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.title_label.setAlignment(Qt.AlignmentFlag.AlignTop)
self.title_label.setAutoFillBackground(False)
@@ -33,17 +33,17 @@ def setupUi(self, widget: QWidget):
# information below title
self.developer_label = QLabel(parent=self.mini_widget)
- self.developer_label.setObjectName(f"{type(self).__name__}TooltipLabel")
+ self.developer_label.setObjectName(f'{type(self).__name__}TooltipLabel')
self.developer_label.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
self.developer_label.setAutoFillBackground(False)
self.price_label = QLabel(parent=self.mini_widget)
- self.price_label.setObjectName(f"{type(self).__name__}TooltipLabel")
+ self.price_label.setObjectName(f'{type(self).__name__}TooltipLabel')
self.price_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
self.price_label.setAutoFillBackground(False)
self.discount_label = QLabel(parent=self.mini_widget)
- self.discount_label.setObjectName(f"{type(self).__name__}TooltipLabel")
+ self.discount_label.setObjectName(f'{type(self).__name__}TooltipLabel')
self.discount_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
self.discount_label.setAutoFillBackground(False)
diff --git a/rare/components/tabs/store/widgets/items.py b/rare/components/tabs/store/widgets/items.py
index f496f8d755..69233c068f 100644
--- a/rare/components/tabs/store/widgets/items.py
+++ b/rare/components/tabs/store/widgets/items.py
@@ -7,18 +7,18 @@
from rare.components.tabs.store.api.models.response import CatalogOfferModel
from rare.models.image import ImageSize
from rare.utils.misc import qta_icon
-from rare.utils.qt_requests import QtRequests
+from rare.utils.qrequests import QRequests
from rare.widgets.image_widget import LoadingSpinnerImageWidget
from .icon_widget import IconWidget
-logger = getLogger("StoreWidgets")
+logger = getLogger('StoreWidgets')
class ItemWidgetSpinner(LoadingSpinnerImageWidget):
show_details = Signal(CatalogOfferModel)
- def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel = None, parent=None):
+ def __init__(self, manager: QRequests, catalog_game: CatalogOfferModel = None, parent=None):
super(ItemWidgetSpinner, self).__init__(manager, parent=parent)
self.ui = IconWidget()
self.catalog_game = catalog_game
@@ -32,7 +32,7 @@ def mousePressEvent(self, a0: QMouseEvent) -> None:
class StoreItemWidget(ItemWidgetSpinner):
- def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel = None, parent=None):
+ def __init__(self, manager: QRequests, catalog_game: CatalogOfferModel = None, parent=None):
super(StoreItemWidget, self).__init__(manager, catalog_game, parent=parent)
self.setFixedSize(ImageSize.DisplayWide)
self.ui.setupUi(self)
@@ -41,25 +41,25 @@ def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel = None,
def init_ui(self, game: CatalogOfferModel):
if not game:
- self.ui.title_label.setText(self.tr("An error occurred"))
+ self.ui.title_label.setText(self.tr('An error occurred'))
return
self.ui.title_label.setText(game.title)
for attr in game.customAttributes:
- if attr["key"] == "developerName":
- developer = attr["value"]
+ if attr['key'] == 'developerName':
+ developer = attr['value']
break
else:
- developer = game.seller["name"]
+ developer = game.seller['name']
self.ui.developer_label.setText(developer)
- price = game.price.totalPrice.fmtPrice["originalPrice"]
- discount_price = game.price.totalPrice.fmtPrice["discountPrice"]
- self.ui.price_label.setText(f"{price if price != '0' else self.tr('Free')}")
+ price = game.price.totalPrice.fmtPrice['originalPrice']
+ discount_price = game.price.totalPrice.fmtPrice['discountPrice']
+ self.ui.price_label.setText(f'{price if price != "0" else self.tr("Free")}')
if price != discount_price:
font = self.ui.price_label.font()
font.setStrikeOut(True)
self.ui.price_label.setFont(font)
- self.ui.discount_label.setText(f"{discount_price if discount_price != '0' else self.tr('Free')}")
+ self.ui.discount_label.setText(f'{discount_price if discount_price != "0" else self.tr("Free")}')
else:
self.ui.discount_label.setVisible(False)
@@ -77,7 +77,7 @@ def init_ui(self, game: CatalogOfferModel):
class SearchItemWidget(ItemWidgetSpinner):
- def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel, parent=None):
+ def __init__(self, manager: QRequests, catalog_game: CatalogOfferModel, parent=None):
super(SearchItemWidget, self).__init__(manager, catalog_game, parent=parent)
self.setFixedSize(ImageSize.LibraryTall)
self.ui.setupUi(self)
@@ -87,14 +87,14 @@ def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel, parent=
self.ui.title_label.setText(catalog_game.title)
- price = catalog_game.price.totalPrice.fmtPrice["originalPrice"]
- discount_price = catalog_game.price.totalPrice.fmtPrice["discountPrice"]
- self.ui.price_label.setText(f"{price if price != '0' else self.tr('Free')}")
+ price = catalog_game.price.totalPrice.fmtPrice['originalPrice']
+ discount_price = catalog_game.price.totalPrice.fmtPrice['discountPrice']
+ self.ui.price_label.setText(f'{price if price != "0" else self.tr("Free")}')
if price != discount_price:
font = self.ui.price_label.font()
font.setStrikeOut(True)
self.ui.price_label.setFont(font)
- self.ui.discount_label.setText(f"{discount_price if discount_price != '0' else self.tr('Free')}")
+ self.ui.discount_label.setText(f'{discount_price if discount_price != "0" else self.tr("Free")}')
else:
self.ui.discount_label.setVisible(False)
@@ -102,35 +102,35 @@ def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel, parent=
class WishlistItemWidget(ItemWidgetSpinner):
delete_from_wishlist = Signal(CatalogOfferModel)
- def __init__(self, manager: QtRequests, catalog_game: CatalogOfferModel, parent=None):
+ def __init__(self, manager: QRequests, catalog_game: CatalogOfferModel, parent=None):
super(WishlistItemWidget, self).__init__(manager, catalog_game, parent=parent)
self.setFixedSize(ImageSize.DisplayWide)
self.ui.setupUi(self)
for attr in catalog_game.customAttributes:
- if attr["key"] == "developerName":
- developer = attr["value"]
+ if attr['key'] == 'developerName':
+ developer = attr['value']
break
else:
- developer = catalog_game.seller["name"]
- original_price = catalog_game.price.totalPrice.fmtPrice["originalPrice"]
- discount_price = catalog_game.price.totalPrice.fmtPrice["discountPrice"]
+ developer = catalog_game.seller['name']
+ original_price = catalog_game.price.totalPrice.fmtPrice['originalPrice']
+ discount_price = catalog_game.price.totalPrice.fmtPrice['discountPrice']
self.ui.title_label.setText(catalog_game.title)
self.ui.developer_label.setText(developer)
- self.ui.price_label.setText(f"{original_price if original_price != '0' else self.tr('Free')}")
+ self.ui.price_label.setText(f'{original_price if original_price != "0" else self.tr("Free")}')
if original_price != discount_price:
font = self.ui.price_label.font()
font.setStrikeOut(True)
self.ui.price_label.setFont(font)
- self.ui.discount_label.setText(f"{discount_price if discount_price != '0' else self.tr('Free')}")
+ self.ui.discount_label.setText(f'{discount_price if discount_price != "0" else self.tr("Free")}')
else:
self.ui.discount_label.setVisible(False)
key_images = catalog_game.keyImages
self.fetchPixmap(key_images.for_dimensions(self.width(), self.height()).url)
self.delete_button = QPushButton(self)
- self.delete_button.setIcon(qta_icon("mdi.delete", color="white"))
+ self.delete_button.setIcon(qta_icon('mdi.delete', color='white'))
self.delete_button.clicked.connect(self._on_delete_clicked)
self.layout().insertWidget(0, self.delete_button, alignment=Qt.AlignmentFlag.AlignRight)
diff --git a/rare/components/tabs/store/wishlist.py b/rare/components/tabs/store/wishlist.py
index 8bf2af4274..2f7632336a 100644
--- a/rare/components/tabs/store/wishlist.py
+++ b/rare/components/tabs/store/wishlist.py
@@ -1,5 +1,4 @@
from enum import IntEnum
-from typing import List
from PySide6.QtCore import Qt, Signal, Slot
from PySide6.QtGui import QShowEvent
@@ -74,24 +73,24 @@ def __init__(self, api: StoreAPI, parent=None):
self.ui.container_layout.addLayout(self.wishlist_layout, stretch=1)
filters = {
- WishlistFilter.NONE: self.tr("All items"),
- WishlistFilter.DISCOUNT: self.tr("Discount"),
+ WishlistFilter.NONE: self.tr('All items'),
+ WishlistFilter.DISCOUNT: self.tr('Discount'),
}
for data, text in filters.items():
self.ui.filter_combo.addItem(text, data)
self.ui.filter_combo.currentIndexChanged.connect(self.filter_wishlist)
sortings = {
- WishlistOrder.NAME: self.tr("Name"),
- WishlistOrder.PRICE: self.tr("Price"),
- WishlistOrder.DISCOUNT: self.tr("Discount"),
- WishlistOrder.DEVELOPER: self.tr("Developer"),
+ WishlistOrder.NAME: self.tr('Name'),
+ WishlistOrder.PRICE: self.tr('Price'),
+ WishlistOrder.DISCOUNT: self.tr('Discount'),
+ WishlistOrder.DEVELOPER: self.tr('Developer'),
}
for data, text in sortings.items():
self.ui.order_combo.addItem(text, data)
self.ui.order_combo.currentIndexChanged.connect(self.order_wishlist)
- self.ui.reload_button.setIcon(qta_icon("fa.refresh", "fa5s.sync", color="white"))
+ self.ui.reload_button.setIcon(qta_icon('fa.refresh', 'fa5s.sync', color='white'))
self.ui.reload_button.clicked.connect(self._update_widget)
self.ui.reverse_check.stateChanged.connect(self._on_reverse_changed)
@@ -112,7 +111,7 @@ def delete_from_wishlist(self, game: CatalogOfferModel):
game.id,
lambda success: self._update_widget()
if success
- else QMessageBox.warning(self, "Error", self.tr("Could not remove game from wishlist")),
+ else QMessageBox.warning(self, 'Error', self.tr('Could not remove game from wishlist')),
)
self.update_wishlist.emit()
@@ -133,7 +132,7 @@ def filter_wishlist(self, index: int = int(WishlistFilter.NONE)):
__ordering = {
WishlistOrder.NAME: lambda x: x.catalog_game.title,
WishlistOrder.PRICE: lambda x: x.catalog_game.price.totalPrice.discountPrice,
- WishlistOrder.DEVELOPER: lambda x: x.catalog_game.seller["name"],
+ WishlistOrder.DEVELOPER: lambda x: x.catalog_game.seller['name'],
WishlistOrder.DISCOUNT: lambda x: 1
- (x.catalog_game.price.totalPrice.discountPrice / x.catalog_game.price.totalPrice.originalPrice),
}
@@ -155,8 +154,8 @@ def _on_reverse_changed(self, state: Qt.CheckState):
self.order_wishlist(self.ui.order_combo.currentIndex())
@Slot(object)
- def set_wishlist(self, wishlist: List[WishlistItemModel] = None):
- if wishlist and wishlist[0] == "error":
+ def set_wishlist(self, wishlist: list[WishlistItemModel] = None):
+ if wishlist and wishlist[0] == 'error':
return
widgets = self.ui.container.findChildren(WishlistItemWidget, options=Qt.FindChildOption.FindDirectChildrenOnly)
diff --git a/rare/components/tabs/tab_widgets.py b/rare/components/tabs/tab_widgets.py
index 26a950141a..4ac53973e9 100644
--- a/rare/components/tabs/tab_widgets.py
+++ b/rare/components/tabs/tab_widgets.py
@@ -7,7 +7,7 @@ class MainTabBar(QTabBar):
def __init__(self, parent=None):
super(MainTabBar, self).__init__(parent=parent)
self.setObjectName(type(self).__name__)
- self.setProperty("drawBase", False)
+ self.setProperty('drawBase', False)
font = self.font()
font.setPointSize(font.pointSize() + 1)
diff --git a/rare/components/tray_icon.py b/rare/components/tray_icon.py
index a17dfe851a..a751425d32 100644
--- a/rare/components/tray_icon.py
+++ b/rare/components/tray_icon.py
@@ -1,5 +1,4 @@
from logging import getLogger
-from typing import List
from PySide6.QtCore import Signal, Slot
from PySide6.QtGui import QAction, QIcon
@@ -8,7 +7,7 @@
from rare.models.settings import RareAppSettings, app_settings
from rare.shared import RareCore
-logger = getLogger("TrayIcon")
+logger = getLogger('TrayIcon')
class TrayIcon(QSystemTrayIcon):
@@ -25,7 +24,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.signals = rcore.signals()
self.core = rcore.core()
- self.setIcon(QIcon(":/images/icon.png"))
+ self.setIcon(QIcon(':/images/icon.png'))
self.setVisible(True)
self.setToolTip(QApplication.applicationName())
@@ -36,17 +35,17 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.menu.addAction(self.show_action)
self.menu.addSeparator()
- self.text_action = QAction("Quick launch")
+ self.text_action = QAction('Quick launch')
self.text_action.setEnabled(False)
self.menu.addAction(self.text_action)
# We need to reference this separator to add game actions before it
self.separator = self.menu.addSeparator()
- self.exit_action = QAction(self.tr("Quit"))
+ self.exit_action = QAction(self.tr('Quit'))
self.exit_action.triggered.connect(self._on_exit_triggered)
self.menu.addAction(self.exit_action)
- self.game_actions: List[QAction] = []
+ self.game_actions: list[QAction] = []
self.update_actions()
self.setContextMenu(self.menu)
@@ -55,7 +54,7 @@ def __init__(self, settings: RareAppSettings, rcore: RareCore, parent=None):
self.signals.application.notify.connect(self.notify)
self.signals.application.update_tray.connect(self.update_actions)
- def last_played(self) -> List:
+ def last_played(self) -> list:
last_played = [game for game in self.rcore.games if (game.metadata and game.is_installed)]
last_played.sort(key=lambda g: g.metadata.last_played, reverse=True)
return last_played[:5]
@@ -68,7 +67,7 @@ def _on_exit_triggered(self):
def notify(self, title: str, body: str):
if self.settings.get_value(app_settings.notification):
self.showMessage(
- f"{title} - {QApplication.applicationName()}",
+ f'{title} - {QApplication.applicationName()}',
body,
QSystemTrayIcon.MessageIcon.Information,
4000,
@@ -86,7 +85,7 @@ def update_actions(self):
@Slot(str)
def remove_button(self, app_name: str):
- if action := next((i for i in self.game_actions if i.property("app_name") == app_name), None):
+ if action := next((i for i in self.game_actions if i.property('app_name') == app_name), None):
self.game_actions.remove(action)
action.deleteLater()
diff --git a/rare/lgndr/cli.py b/rare/lgndr/cli.py
index e58bbbec40..34555218df 100644
--- a/rare/lgndr/cli.py
+++ b/rare/lgndr/cli.py
@@ -218,7 +218,6 @@ def install_game(self, args: LgndrInstallGameArgs) -> Optional[Tuple[DLManager,
if not analysis.dl_size and not game.is_dlc:
logger.info('Download size is 0, the game is either already up to date or has not changed. Exiting...')
self.install_game_cleanup(game, igame, args.repair_mode, repair_file)
- # return
# Rare: Return what we know about the download to queue a 0 size DLC
res = self.core.check_installation_conditions(analysis=analysis, install=igame, game=game,
updating=self.core.is_installed(args.app_name),
@@ -526,7 +525,8 @@ def verify_game(self, args: Union[LgndrVerifyGameArgs, LgndrInstallGameArgs], pr
logger.info(f'Verifying "{igame.title}" version "{manifest.meta.build_version}"')
repair_file = []
- for result, path, result_hash, bytes_read in validate_files(igame.install_path, file_list):
+ for result, path, result_hash, bytes_read in validate_files(igame.install_path, file_list,
+ case_insensitive=igame.platform.startswith('Win')):
processed += bytes_read
percentage = (processed / total_size) * 100.0
num += 1
diff --git a/rare/lgndr/downloader/mp/manager.py b/rare/lgndr/downloader/mp/manager.py
index cbf09c3735..a3b827be64 100644
--- a/rare/lgndr/downloader/mp/manager.py
+++ b/rare/lgndr/downloader/mp/manager.py
@@ -59,7 +59,8 @@ def run_real(self):
self.log.info('Starting file writing worker...')
writer_p = FileWorker(self.writer_queue, self.writer_result_q, self.dl_dir,
- self.shared_memory.name, self.cache_dir, self.logging_queue)
+ self.shared_memory.name, self.cache_dir, self.logging_queue,
+ self.case_insensitive)
self.children.append(writer_p)
writer_p.start()
diff --git a/rare/main.py b/rare/main.py
index cb29d984c3..b713d2f7bd 100755
--- a/rare/main.py
+++ b/rare/main.py
@@ -7,112 +7,112 @@
def main() -> int:
- if getattr(sys, "frozen", False) or ("__compiled__" in globals()):
+ if getattr(sys, 'frozen', False) or ('__compiled__' in globals()):
# If we are on Windows, and we are in a "compiled" GUI application form
# stdout (and stderr?) will be None. So to avoid `'NoneType' object has no attribute 'write'`
# errors, redirect both of them to devnull
- if os.name == "nt":
+ if os.name == 'nt':
# Check if stdout and stderr are None before redirecting
# This is useful in the case of test builds that enable console
if sys.stdout is None:
- sys.stdout = open(os.devnull, "w")
+ sys.stdout = open(os.devnull, 'w') # noqa: SIM115
if sys.stderr is None:
- sys.stderr = open(os.devnull, "w")
+ sys.stderr = open(os.devnull, 'w') # noqa: SIM115
# Nuitka and possibly cx_freeze have issues with launching multiprocessing's resource_tracker due
# to the way it is invoked. To accomodate for that, check here if the "-c" argument was used, and
# execute the incoming command instead of Rare itself.
- if "MULTIPROCESSING_LAUNCH_TOKEN" in os.environ:
- if len(sys.argv) == 5 and sys.argv[1:4] == ["-S", "-s", "-c"]:
+ if 'MULTIPROCESSING_LAUNCH_TOKEN' in os.environ:
+ if len(sys.argv) == 5 and sys.argv[1:4] == ['-S', '-s', '-c']:
exec(sys.argv[4])
return 0
- os.environ["MULTIPROCESSING_LAUNCH_TOKEN"] = "1"
+ os.environ['MULTIPROCESSING_LAUNCH_TOKEN'] = '1'
# remove QT_QPA_PLATFORMTHEME to avoid broken theming
- if "QT_QPA_PLATFORMTHEME" in os.environ:
- del os.environ["QT_QPA_PLATFORMTHEME"]
- os.environ["QT_STYLE_OVERRIDE"] = "fusion"
+ if 'QT_QPA_PLATFORMTHEME' in os.environ:
+ del os.environ['QT_QPA_PLATFORMTHEME']
+ os.environ['QT_STYLE_OVERRIDE'] = 'fusion'
# remove XDG_CONFIG_HOME if running under Wine to stop legendary from using the Linux client config
- if platform.system() == "Windows" and "XDG_CONFIG_HOME" in os.environ:
- del os.environ["XDG_CONFIG_HOME"]
- if "LEGENDARY_CONFIG_PATH" in os.environ:
- os.environ["LEGENDARY_CONFIG_PATH"] = os.path.expanduser(os.environ["LEGENDARY_CONFIG_PATH"])
+ if platform.system() == 'Windows' and 'XDG_CONFIG_HOME' in os.environ:
+ del os.environ['XDG_CONFIG_HOME']
+ if 'LEGENDARY_CONFIG_PATH' in os.environ:
+ os.environ['LEGENDARY_CONFIG_PATH'] = os.path.expanduser(os.environ['LEGENDARY_CONFIG_PATH'])
# fix cx_freeze
multiprocessing.freeze_support()
# CLI Options
parser = ArgumentParser()
- parser.add_argument("-V", "--version", action="store_true", help="Shows version and exits")
+ parser.add_argument('-V', '--version', action='store_true', help='Shows version and exits')
parser.add_argument(
- "-S",
- "--silent",
- action="store_true",
- help="Launch Rare in background. Open it from System Tray Icon",
+ '-S',
+ '--silent',
+ action='store_true',
+ help='Launch Rare in background. Open it from System Tray Icon',
)
- parser.add_argument("--debug", action="store_true", help="Launch in debug mode")
- parser.add_argument("--offline", action="store_true", help="Launch Rare in offline mode")
- parser.add_argument("--test-start", action="store_true", help="Quit immediately after launch")
+ parser.add_argument('--debug', action='store_true', help='Launch in debug mode')
+ parser.add_argument('--offline', action='store_true', help='Launch Rare in offline mode')
+ parser.add_argument('--test-start', action='store_true', help='Quit immediately after launch')
parser.add_argument(
- "--desktop-shortcut",
- action="store_true",
- dest="desktop_shortcut",
- help="Use this, if there is no link on desktop to start Rare",
+ '--desktop-shortcut',
+ action='store_true',
+ dest='desktop_shortcut',
+ help='Use this, if there is no link on desktop to start Rare',
)
parser.add_argument(
- "--startmenu-shortcut",
- action="store_true",
- dest="startmenu_shortcut",
- help="Use this, if there is no link in start menu to launch Rare",
+ '--startmenu-shortcut',
+ action='store_true',
+ dest='startmenu_shortcut',
+ help='Use this, if there is no link in start menu to launch Rare',
)
- subparsers = parser.add_subparsers(title="Commands", dest="subparser")
+ subparsers = parser.add_subparsers(title='Commands', dest='subparser')
# Launch command
- launch_parser = subparsers.add_parser("launch", aliases=["start"])
- launch_parser.add_argument("--dry-run", action="store_true", help="Print arguments and exit")
- launch_parser.add_argument("--offline", action="store_true", help="Launch game offline")
- launch_parser.add_argument("--ask-sync-saves", action="store_true", help="Ask to sync cloud saves")
- launch_parser.add_argument("--skip-update-check", action="store_true", help="Do not check for updates")
+ launch_parser = subparsers.add_parser('launch', aliases=['start'])
+ launch_parser.add_argument('--dry-run', action='store_true', help='Print arguments and exit')
+ launch_parser.add_argument('--offline', action='store_true', help='Launch game offline')
+ launch_parser.add_argument('--ask-sync-saves', action='store_true', help='Ask to sync cloud saves')
+ launch_parser.add_argument('--skip-update-check', action='store_true', help='Do not check for updates')
launch_parser.add_argument(
- "--show-console",
- action="store_true",
+ '--show-console',
+ action='store_true',
help="Show a console window to log the application's output",
)
- if platform.system() != "Windows":
+ if platform.system() != 'Windows':
launch_parser.add_argument(
- "--wine-bin",
- action="store",
- dest="wine_bin",
- default=os.environ.get("LGDRY_WINE_BINARY", None),
- metavar="",
- help="Set WINE binary to use to launch the app",
+ '--wine-bin',
+ action='store',
+ dest='wine_bin',
+ default=os.environ.get('LGDRY_WINE_BINARY', None),
+ metavar='',
+ help='Set WINE binary to use to launch the app',
)
launch_parser.add_argument(
- "--wine-prefix",
- action="store",
- dest="wine_pfx",
- default=os.environ.get("LGDRY_WINE_PREFIX", None),
- metavar="",
- help="Set WINE prefix to use",
+ '--wine-prefix',
+ action='store',
+ dest='wine_pfx',
+ default=os.environ.get('LGDRY_WINE_PREFIX', None),
+ metavar='',
+ help='Set WINE prefix to use',
)
launch_parser.add_argument(
- "app_name",
- action="store",
- metavar="",
- help="AppName of the game to launch",
+ 'app_name',
+ action='store',
+ metavar='',
+ help='AppName of the game to launch',
)
# Login command
- login_parser = subparsers.add_parser("login", aliases=["auth"])
+ login_parser = subparsers.add_parser('login', aliases=['auth'])
login_parser.add_argument(
- "egl_version",
- action="store",
- metavar="",
- help="Epic Games Launcher User Agent version",
+ 'egl_version',
+ action='store',
+ metavar='',
+ help='Epic Games Launcher User Agent version',
)
# Subreaper command
- subparsers.add_parser("subreaper", aliases=["reaper"])
+ subparsers.add_parser('subreaper', aliases=['reaper'])
args, other = parser.parse_known_args()
@@ -120,34 +120,34 @@ def main() -> int:
from rare.utils.paths import create_desktop_link
if args.desktop_shortcut:
- create_desktop_link(app_name="rare_shortcut", link_type="desktop")
+ create_desktop_link(app_name='rare_shortcut', link_type='desktop')
if args.startmenu_shortcut:
- create_desktop_link(app_name="rare_shortcut", link_type="start_menu")
+ create_desktop_link(app_name='rare_shortcut', link_type='start_menu')
- print("Link created", file=sys.stderr)
+ print('Link created', file=sys.stderr)
return 0
if args.version:
from rare import __codename__, __version__
- print(f"Rare {__version__} Codename: {__codename__}", file=sys.stderr)
+ print(f'Rare {__version__} Codename: {__codename__}', file=sys.stderr)
return 0
- if args.subparser in {"login", "auth"}:
+ if args.subparser in {'login', 'auth'}:
from rare.commands.webview import webview
return webview(args)
- if args.subparser in {"launch", "start"}:
+ if args.subparser in {'launch', 'start'}:
from rare.commands.launcher import launcher
return launcher(args)
- if args.subparser in {"subreaper", "reaper"}:
+ if args.subparser in {'subreaper', 'reaper'}:
from rare.commands.subreaper import subreaper
- sep = other.index("--")
+ sep = other.index('--')
other = other[sep + 1 :]
args.command = other.pop(0)
args.workdir = os.getcwd()
@@ -161,23 +161,29 @@ def main() -> int:
me = singleton.SingleInstance() # noqa: F841
except singleton.SingleInstanceException:
- print("Rare is already running", file=sys.stderr)
+ print('Rare is already running', file=sys.stderr)
from rare.utils.paths import lock_file
- with open(lock_file(), "w") as file:
- file.write("show")
+ with open(lock_file(), 'w') as file:
+ file.write('show')
file.close()
return -1
from rare.components import start
- return start(args)
+ ec = start(args)
+ if os.name == 'nt':
+ if not sys.stdout.closed:
+ sys.stdout.close()
+ if not sys.stderr.closed:
+ sys.stderr.close()
+ return ec
-if __name__ == "__main__":
+if __name__ == '__main__':
# insert source directory if running `main.py` as python script
# Required by AppImage
- if "__compiled__" not in globals():
+ if '__compiled__' not in globals():
sys.path.insert(0, str(pathlib.Path(__file__).parents[1].absolute()))
sys.exit(main())
diff --git a/rare/models/game.py b/rare/models/game.py
index 837c38bb4f..eb2cd9ef10 100644
--- a/rare/models/game.py
+++ b/rare/models/game.py
@@ -6,7 +6,7 @@
from dataclasses import dataclass, field
from datetime import datetime, timezone
from threading import Lock
-from typing import Dict, List, Optional, Set, Tuple
+from typing import Optional
from legendary.lfs import eos
from legendary.models.game import Game, InstalledGame
@@ -23,22 +23,22 @@
from rare.shared.image_manager import ImageManager
from rare.utils import config_helper as config
from rare.utils.paths import compat_shaders_dir, data_dir, get_rare_executable
-from rare.utils.steam_grades import SteamGrades
+from rare.utils.steam_grades import steam_grades
from rare.utils.workarounds import apply_workarounds
@dataclass
class RareGameMetadata:
queued: bool = False
- queue_pos: Optional[int] = None
+ queue_pos: int | None = None
last_played: datetime = datetime.min.replace(tzinfo=timezone.utc)
achievements_date: datetime = datetime.min.replace(tzinfo=timezone.utc)
grant_date: datetime = datetime.min.replace(tzinfo=timezone.utc)
- steam_appid: Optional[str] = None
- steam_grade: Optional[str] = None
+ steam_appid: str | None = None
+ steam_grade: str | None = None
steam_date: datetime = datetime.min.replace(tzinfo=timezone.utc)
- steam_shortcut: Optional[int] = None
- tags: Tuple[str, ...] = field(default_factory=tuple)
+ steam_shortcut: int | None = None
+ tags: tuple[str, ...] = field(default_factory=tuple)
# For compatibility with previously created game metadata
@staticmethod
@@ -47,18 +47,18 @@ def parse_date(strdate: str):
return dt.replace(tzinfo=timezone.utc)
@classmethod
- def from_dict(cls, data: Dict):
+ def from_dict(cls, data: dict):
return cls(
- queued=data.get("queued", False),
- queue_pos=data.get("queue_pos", None),
- last_played=RareGameMetadata.parse_date(data.get("last_played", "")),
- achievements_date=RareGameMetadata.parse_date(data.get("achievements_date", "")),
- grant_date=RareGameMetadata.parse_date(data.get("grant_date", "")),
- steam_appid=str(appid) if (appid := data.get("steam_appid", "")) else None,
- steam_grade=data.get("steam_grade", None),
- steam_date=RareGameMetadata.parse_date(data.get("steam_date", "")),
- steam_shortcut=data.get("steam_shortcut", None),
- tags=data.get("tags", ()),
+ queued=data.get('queued', False),
+ queue_pos=data.get('queue_pos'),
+ last_played=RareGameMetadata.parse_date(data.get('last_played', '')),
+ achievements_date=RareGameMetadata.parse_date(data.get('achievements_date', '')),
+ grant_date=RareGameMetadata.parse_date(data.get('grant_date', '')),
+ steam_appid=str(appid) if (appid := data.get('steam_appid', '')) else None,
+ steam_grade=data.get('steam_grade'),
+ steam_date=RareGameMetadata.parse_date(data.get('steam_date', '')),
+ steam_shortcut=data.get('steam_shortcut'),
+ tags=data.get('tags', ()),
)
@property
@@ -91,27 +91,27 @@ def __init__(
game: Game,
):
super(RareGame, self).__init__(settings, legendary_core, game)
- self.__origin_install_path: Optional[str] = None
- self.__origin_install_size: Optional[int] = None
+ self.__origin_install_path: str | None = None
+ self.__origin_install_size: int | None = None
self.image_manager = image_manager
# Update names for Unreal Engine
- if self.game.app_title == "Unreal Engine":
- self.game.app_title += f" {self.game.app_name.split('_')[-1]}"
+ if self.game.app_title == 'Unreal Engine':
+ self.game.app_title += f' {self.game.app_name.split("_")[-1]}'
self.has_pixmap: bool = False
self.metadata: RareGameMetadata = RareGameMetadata()
self.__load_metadata()
self.grant_date()
- self.owned_dlcs: Set[RareGame] = set()
- self.__parent_rgame: Optional[RareGame] = None
+ self.owned_dlcs: set[RareGame] = set()
+ self.__parent_rgame: RareGame | None = None
if self.has_update:
- self.logger.info(f"Update available for game: {self.app_name} ({self.app_title})")
+ self.logger.info(f'Update available for game: {self.app_name} ({self.app_title})')
- self.__worker: Optional[QRunnable] = None
+ self.__worker: QRunnable | None = None
self.progress: int = 0
self.signals.progress.start.connect(self.__on_progress_update)
self.signals.progress.refresh.connect(self.__on_progress_update)
@@ -135,11 +135,11 @@ def add_dlc(self, dlc) -> None:
self.owned_dlcs.add(dlc)
@property
- def parent_rgame(self) -> Optional["RareGame"]:
+ def parent_rgame(self) -> Optional['RareGame']:
return self.__parent_rgame if self.is_dlc else None
@parent_rgame.setter
- def parent_rgame(self, rgame: "RareGame") -> None:
+ def parent_rgame(self, rgame: 'RareGame') -> None:
if self.is_dlc:
self.__parent_rgame = rgame
@@ -148,7 +148,7 @@ def parent_rgame(self, rgame: "RareGame") -> None:
def __on_progress_update(self, progress: int = 0):
self.progress = progress
- def get_worker(self) -> Optional[QRunnable]:
+ def get_worker(self) -> QRunnable | None:
return self.__worker
@Slot(object)
@@ -183,27 +183,27 @@ def _game_finished(self, exit_code: int):
self.state = RareGame.State.IDLE
self.signals.game.finished.emit(self.app_name)
- __metadata_json: Optional[Dict] = None
+ __metadata_json: dict | None = None
__metadata_lock: Lock = Lock()
- def __load_metadata_json(self) -> Optional[Dict]:
+ def __load_metadata_json(self) -> dict | None:
if RareGame.__metadata_json is None:
metadata = {}
- file = os.path.join(data_dir(), "game_meta.json")
+ file = os.path.join(data_dir(), 'game_meta.json')
try:
- with open(file, "r", encoding="utf-8") as f:
+ with open(file, encoding='utf-8') as f:
metadata = json.load(f)
except FileNotFoundError:
- self.logger.info("%s does not exist", file)
+ self.logger.info('%s does not exist', file)
except json.JSONDecodeError:
- self.logger.warning("%s is corrupt", file)
+ self.logger.warning('%s is corrupt', file)
finally:
RareGame.__metadata_json = metadata
return RareGame.__metadata_json
def __load_metadata(self):
with RareGame.__metadata_lock:
- metadata: Dict = self.__load_metadata_json()
+ metadata: dict = self.__load_metadata_json()
# pylint: disable=unsupported-membership-test
if self.app_name in metadata:
# pylint: disable=unsubscriptable-object
@@ -211,10 +211,10 @@ def __load_metadata(self):
def __save_metadata(self):
with RareGame.__metadata_lock:
- metadata: Dict = self.__load_metadata_json()
+ metadata: dict = self.__load_metadata_json()
# pylint: disable=unsupported-assignment-operation
metadata[self.app_name] = vars(self.metadata)
- with open(os.path.join(data_dir(), "game_meta.json"), "w+", encoding="utf-8") as file:
+ with open(os.path.join(data_dir(), 'game_meta.json'), 'w+', encoding='utf-8') as file:
json.dump(metadata, file, indent=2)
def update_game(self):
@@ -242,7 +242,7 @@ def developer(self) -> str:
@return str
"""
- return self.game.metadata["developer"]
+ return self.game.metadata['developer']
@property
def install_size(self) -> int:
@@ -256,7 +256,7 @@ def install_size(self) -> int:
return self.igame.install_size if self.igame is not None else 0
@property
- def install_path(self) -> Optional[str]:
+ def install_path(self) -> str | None:
if self.is_origin:
# TODO Linux is also C:\\...
return self.__origin_install_path
@@ -300,7 +300,7 @@ def has_update(self) -> bool:
if self.remote_version != self.igame.version:
return True
except ValueError:
- self.logger.error(f"Asset error for {self.game.app_title}")
+ self.logger.error(f'Asset error for {self.game.app_title}')
return False
return False
@@ -375,7 +375,7 @@ def is_foreign(self) -> bool:
_ = self.core.get_asset(self.game.app_name, platform=self.igame.platform).build_version
ret = False
except ValueError:
- self.logger.warning(f"Game {self.game.app_title} has no metadata. Set offline true")
+ self.logger.warning(f'Game {self.game.app_title} has no metadata. Set offline true')
except AttributeError:
ret = False
return ret
@@ -419,7 +419,7 @@ def needs_verification(self, needs: bool) -> None:
@property
def repair_file(self) -> str:
- return os.path.join(self.core.lgd.get_tmp_path(), f"{self.app_name}.repair")
+ return os.path.join(self.core.lgd.get_tmp_path(), f'{self.app_name}.repair')
@property
def needs_repair(self) -> bool:
@@ -437,7 +437,7 @@ def is_unreal(self) -> bool:
@return bool
"""
- return False if self.is_non_asset else self.game.asset_infos["Windows"].namespace == "ue"
+ return False if self.is_non_asset else self.game.asset_infos['Windows'].namespace == 'ue'
@property
def is_non_asset(self) -> bool:
@@ -460,18 +460,18 @@ def is_android_only(self) -> bool:
@property
def is_ubisoft(self) -> bool:
- return self.game.partner_link_type == "ubisoft"
+ return self.game.partner_link_type == 'ubisoft'
@property
def folder_name(self) -> str:
return (
folder_name
- if (folder_name := self.game.metadata.get("customAttributes", {}).get("FolderName", {}).get("value"))
+ if (folder_name := self.game.metadata.get('customAttributes', {}).get('FolderName', {}).get('value'))
else self.app_title
)
@property
- def save_path(self) -> Optional[str]:
+ def save_path(self) -> str | None:
return super(RareGame, self).save_path
@save_path.setter
@@ -482,9 +482,9 @@ def save_path(self, path: str) -> None:
self.signals.widget.refresh.emit()
@property
- def achievements(self) -> Optional[Namespace]:
+ def achievements(self) -> Namespace | None:
if not self.game.achievements or not self.game.achievements.achievements:
- self.logger.info("No achievements found for %s (%s)", self.app_name, self.app_title)
+ self.logger.info('No achievements found for %s (%s)', self.app_name, self.app_title)
return None
# if self.game.achievements is None:
@@ -497,7 +497,7 @@ def achievements(self) -> Optional[Namespace]:
update = not self.core.lgd.achievements or not self.core.lgd.achievements.get(self.game.namespace, None)
update = update or self.metadata.achievements_date < self.metadata.last_played
if update:
- self.logger.info("Updating achievements for %s (%s)", self.app_name, self.app_title)
+ self.logger.info('Updating achievements for %s (%s)', self.app_name, self.app_title)
ret = self.core.get_achievements(self.game, update=update)
self.metadata.achievements_date = self.metadata.last_played
self.__save_metadata()
@@ -506,10 +506,10 @@ def achievements(self) -> Optional[Namespace]:
return ret
@property
- def eulas(self) -> List:
- eulas = self.game.metadata.get("eulaIds") or ["$"]
+ def eulas(self) -> list:
+ eulas = self.game.metadata.get('eulaIds') or ['$']
- pattern = r"\w+"
+ pattern = r'\w+'
keys = []
for eula in eulas:
keys += re.findall(pattern, eula)
@@ -525,7 +525,7 @@ def eulas(self) -> List:
return not_accepted_eulas
- def sdl_data(self, platform: str) -> Optional[Dict[str, Dict]]:
+ def sdl_data(self, platform: str) -> dict[str, dict] | None:
sdl_data = {}
sdl_name = get_sdl_appname(self.app_name)
@@ -533,7 +533,7 @@ def sdl_data(self, platform: str) -> Optional[Dict[str, Dict]]:
sdl_data.update(data)
known_install_tags = set()
if sdl_data:
- known_install_tags = set(tag for _, info in sdl_data.items() for tag in info["tags"])
+ known_install_tags = set(tag for _, info in sdl_data.items() for tag in info['tags'])
if self.igame is not None and not self.has_update:
manifest_data = self.core.lgd.load_manifest(self.app_name, self.igame.version, self.igame.platform)
@@ -549,8 +549,8 @@ def sdl_data(self, platform: str) -> Optional[Dict[str, Dict]]:
manifest_install_tags.add(tag)
extra_install_tags = manifest_install_tags.difference(known_install_tags)
- for extra_tag in extra_install_tags:
- sdl_data[extra_tag] = {"name": extra_tag, "description": "", "tags": [extra_tag]}
+ for extra_tag in sorted(extra_install_tags):
+ sdl_data[extra_tag] = {'name': extra_tag, 'description': '', 'tags': [extra_tag]}
return sdl_data
@@ -571,34 +571,34 @@ def reset_steam_date(self):
self.signals.widget.refresh.emit()
@property
- def steam_appid(self) -> Optional[str]:
+ def steam_appid(self) -> str | None:
return self.metadata.steam_appid
@steam_appid.setter
def steam_appid(self, appid: str) -> None:
- config.adjust_envvar(self.app_name, "SteamAppId", appid)
- config.adjust_envvar(self.app_name, "SteamGameId", appid)
- config.adjust_envvar(self.app_name, "STEAM_COMPAT_APP_ID", f"rare_{self.app_name}")
- config.adjust_envvar(self.app_name, "UMU_ID", f"umu-{appid}" if appid else "umu-default")
+ config.adjust_envvar(self.app_name, 'SteamAppId', appid)
+ config.adjust_envvar(self.app_name, 'SteamGameId', appid)
+ config.adjust_envvar(self.app_name, 'STEAM_COMPAT_APP_ID', f'rare_{self.app_name}')
+ config.adjust_envvar(self.app_name, 'UMU_ID', f'umu-{appid}' if appid else 'umu-default')
self.metadata.steam_appid = appid
self.__save_metadata()
def get_steam_grade(self) -> str:
- if platform.system() == "Windows" or self.is_unreal:
- return "na"
+ if platform.system() == 'Windows' or self.is_unreal:
+ return 'na'
if self.__steam_grade_pending:
- return "pending"
+ return 'pending'
elapsed_time = abs(datetime.now(timezone.utc) - self.metadata.steam_date)
if elapsed_time.days > 3:
- self.logger.info("Refreshing ProtonDB grade for %s", self.app_title)
+ self.logger.info('Refreshing ProtonDB grade for %s', self.app_title)
worker = QRunnable.create(self.set_steam_grade)
self.__steam_grade_pending = True
QThreadPool.globalInstance().start(worker)
- return "pending"
+ return 'pending'
return self.metadata.steam_grade
def set_steam_grade(self) -> None:
- appid, grade = SteamGrades().get_rating(self.core, self.app_name, self.steam_appid)
+ appid, grade = steam_grades.get_rating(self.core, self.app_name, self.steam_appid)
if appid and self.steam_appid is None:
self.steam_appid = appid
self.metadata.steam_grade = grade
@@ -611,26 +611,25 @@ def grant_date(self, force=False) -> datetime:
if not (entitlements := self.core.lgd.entitlements):
return self.metadata.grant_date
if self.metadata.grant_date == datetime.min.replace(tzinfo=timezone.utc) or force:
- self.logger.info("Grant date for %s not found in metadata, resolving", self.app_name)
- matching = filter(lambda ent: ent["namespace"] == self.game.namespace, entitlements)
- entitlement = next(matching, None)
+ self.logger.info('Grant date for %s not found in metadata, resolving', self.app_name)
+ entitlement = next((ent for ent in entitlements if ent['namespace'] == self.game.namespace), None)
grant_date = (
- datetime.fromisoformat(entitlement["grantDate"].replace("Z", "+00:00"))
+ datetime.fromisoformat(entitlement['grantDate'].replace('Z', '+00:00'))
if entitlement
- else datetime.fromisoformat(self.game.metadata["creationDate"].replace("Z", "+00:00"))
+ else datetime.fromisoformat(self.game.metadata['creationDate'].replace('Z', '+00:00'))
)
self.metadata.grant_date = grant_date
self.__save_metadata()
return self.metadata.grant_date
@property
- def tags(self) -> Tuple[str, ...]:
+ def tags(self) -> tuple[str, ...]:
return tuple(tag for tag in map(lambda x: x.lower().strip(), self.metadata.tags) if bool(tag))
@tags.setter
- def tags(self, tags: Tuple[str, ...]) -> None:
+ def tags(self, tags: tuple[str, ...]) -> None:
self.metadata.tags = tuple(tag for tag in map(lambda x: x.lower().strip(), tags) if bool(tag))
- self.logger.debug(f"Saving tags for {self.game.app_title}: {self.metadata.tags}")
+ self.logger.debug(f'Saving tags for {self.game.app_title}: {self.metadata.tags}')
self.__save_metadata()
def set_origin_attributes(self, path: str, size: int = 0) -> None:
@@ -729,7 +728,7 @@ def uninstall(self) -> bool:
UninstallOptionsModel(
app_name=self.app_name,
keep_folder=self.is_dlc,
- keep_config=self.sdl_available or self.is_dlc or platform.system() not in {"Windows"},
+ keep_config=self.sdl_available or self.is_dlc or platform.system() not in {'Windows'},
)
)
return True
@@ -739,48 +738,48 @@ def launch(
debug: bool = False,
offline: bool = False,
skip_update_check: bool = False,
- wine_bin: Optional[str] = None,
- wine_pfx: Optional[str] = None,
+ wine_bin: str | None = None,
+ wine_pfx: str | None = None,
) -> bool:
if not self.can_launch:
return False
cmd_line = get_rare_executable()
executable, args = cmd_line[0], cmd_line[1:]
- args.append("launch")
- if offline or config.get_boolean(self.app_name, "offline", fallback=False):
- args.append("--offline")
+ args.append('launch')
+ if offline or config.get_boolean(self.app_name, 'offline', fallback=False):
+ args.append('--offline')
if debug:
- args.append("--debug")
+ args.append('--debug')
if self.settings.get_value(app_settings.log_games):
- args.append("--show-console")
- if skip_update_check or config.get_boolean(self.app_name, "skip_update_check", fallback=False):
- args.append("--skip-update-check")
+ args.append('--show-console')
+ if skip_update_check or config.get_boolean(self.app_name, 'skip_update_check', fallback=False):
+ args.append('--skip-update-check')
if wine_bin:
- args.extend(["--wine-bin", wine_bin])
+ args.extend(['--wine-bin', wine_bin])
if wine_pfx:
- args.extend(["--wine-prefix", wine_pfx])
+ args.extend(['--wine-prefix', wine_pfx])
args.append(self.app_name)
- if not config.get_envvar(self.app_name, "STORE", False):
- config.set_envvar(self.app_name, "STORE", "egs")
- if not config.get_envvar(self.app_name, "UMU_ID", False):
+ if not config.get_envvar(self.app_name, 'STORE', False):
+ config.set_envvar(self.app_name, 'STORE', 'egs')
+ if not config.get_envvar(self.app_name, 'UMU_ID', False):
config.set_envvar(
self.app_name,
- "UMU_ID",
- f"umu-{self.metadata.steam_appid}" if self.metadata.steam_appid else "umu-default",
+ 'UMU_ID',
+ f'umu-{self.metadata.steam_appid}' if self.metadata.steam_appid else 'umu-default',
)
if self.settings.get_with_global(app_settings.local_shader_cache, self.app_name):
config.set_envvar(
self.app_name,
- "STEAM_COMPAT_SHADER_PATH",
+ 'STEAM_COMPAT_SHADER_PATH',
compat_shaders_dir(self.folder_name).as_posix(),
)
else:
- config.remove_envvar(self.app_name, "STEAM_COMPAT_SHADER_PATH")
+ config.remove_envvar(self.app_name, 'STEAM_COMPAT_SHADER_PATH')
config.save_config()
- self.logger.info(f"Starting game process: ({executable} {' '.join(args)})")
+ self.logger.info(f'Starting game process: ({executable} {" ".join(args)})')
proc = QProcess()
proc.setProgram(executable)
proc.setArguments(args)
@@ -801,7 +800,7 @@ def __init__(
self.settings = settings
self.image_manager = image_manager
- self.igame: Optional[InstalledGame] = self.core.lgd.get_overlay_install_info()
+ self.igame: InstalledGame | None = self.core.lgd.get_overlay_install_info()
self.image_manager.download_image(game, self._update_pixmap, 0, False)
@Slot()
@@ -829,31 +828,31 @@ def has_update(self) -> bool:
# and legendary does it too during login
return self.core.overlay_update_available
- def is_enabled(self, prefix: Optional[str] = None) -> bool:
+ def is_enabled(self, prefix: str | None = None) -> bool:
try:
reg_paths = eos.query_registry_entries(prefix)
except ValueError as e:
- self.logger.info("%s %s", e, prefix)
+ self.logger.info('%s %s', e, prefix)
return False
- return reg_paths["overlay_path"] and self.core.is_overlay_install(reg_paths["overlay_path"])
+ return reg_paths['overlay_path'] and self.core.is_overlay_install(reg_paths['overlay_path'])
- def active_path(self, prefix: Optional[str] = None) -> str:
+ def active_path(self, prefix: str | None = None) -> str:
try:
- path = eos.query_registry_entries(prefix)["overlay_path"]
+ path = eos.query_registry_entries(prefix)['overlay_path']
except ValueError as e:
- self.logger.info("%s %s", e, prefix)
- return ""
- return path if path and self.core.is_overlay_install(path) else ""
+ self.logger.info('%s %s', e, prefix)
+ return ''
+ return path if path and self.core.is_overlay_install(path) else ''
- def available_paths(self, prefix: Optional[str] = None) -> List[str]:
+ def available_paths(self, prefix: str | None = None) -> list[str]:
try:
installs = self.core.search_overlay_installs(prefix)
except ValueError as e:
- self.logger.info("%s %s", e, prefix)
+ self.logger.info('%s %s', e, prefix)
return []
return installs
- def enable(self, prefix: Optional[str] = None, path: Optional[str] = None) -> bool:
+ def enable(self, prefix: str | None = None, path: str | None = None) -> bool:
if self.is_enabled(prefix):
return False
if not path:
@@ -862,9 +861,9 @@ def enable(self, prefix: Optional[str] = None, path: Optional[str] = None) -> bo
else:
path = self.available_paths(prefix)[-1]
reg_paths = eos.query_registry_entries(prefix)
- if old_path := reg_paths["overlay_path"]:
+ if old_path := reg_paths['overlay_path']:
if os.path.normpath(old_path) == path:
- self.logger.info("Overlay already enabled, nothing to do.")
+ self.logger.info('Overlay already enabled, nothing to do.')
return True
else:
self.logger.info(f'Updating overlay registry entries from "{old_path}" to "{path}"')
@@ -872,20 +871,20 @@ def enable(self, prefix: Optional[str] = None, path: Optional[str] = None) -> bo
try:
eos.add_registry_entries(path, prefix)
except PermissionError as e:
- self.logger.error("Exception while writing registry to enable the overlay.")
+ self.logger.error('Exception while writing registry to enable the overlay.')
self.logger.error(e)
return False
- self.logger.info(f"Enabled overlay at: {path} for prefix: {prefix}")
+ self.logger.info(f'Enabled overlay at: {path} for prefix: {prefix}')
return True
- def disable(self, prefix: Optional[str] = None) -> bool:
+ def disable(self, prefix: str | None = None) -> bool:
if not self.is_enabled(prefix):
return False
- self.logger.info(f"Disabling overlay (removing registry keys) for prefix: {prefix}")
+ self.logger.info(f'Disabling overlay (removing registry keys) for prefix: {prefix}')
try:
eos.remove_registry_entries(prefix)
except PermissionError as e:
- self.logger.error("Exception while writing registry to disable the overlay.")
+ self.logger.error('Exception while writing registry to disable the overlay.')
self.logger.error(e)
return False
return True
@@ -897,7 +896,7 @@ def install(self) -> bool:
InstallOptionsModel(
app_name=self.app_name,
base_path=self.core.get_default_install_dir(),
- platform="Windows",
+ platform='Windows',
update=self.is_installed,
overlay=True,
)
@@ -910,10 +909,10 @@ def uninstall(self) -> bool:
self.signals.game.uninstall.emit(
UninstallOptionsModel(
app_name=self.app_name,
- keep_overlay_keys=platform.system() not in {"Windows"},
+ keep_overlay_keys=platform.system() not in {'Windows'},
)
)
return True
-__all__ = ["RareGame", "RareEosOverlay"]
+__all__ = ['RareGame', 'RareEosOverlay']
diff --git a/rare/models/game_slim.py b/rare/models/game_slim.py
index edf7b05ef3..f97b074982 100644
--- a/rare/models/game_slim.py
+++ b/rare/models/game_slim.py
@@ -4,7 +4,6 @@
from datetime import datetime
from enum import IntEnum
from logging import getLogger
-from typing import List, Optional, Tuple
from legendary.lfs import eos
from legendary.models.game import Game, InstalledGame, SaveGameFile, SaveGameStatus
@@ -17,11 +16,11 @@
@dataclass
class RareSaveGame:
- file: Optional[SaveGameFile]
+ file: SaveGameFile | None
status: SaveGameStatus = SaveGameStatus.NO_SAVE
- dt_local: Optional[datetime] = None
- dt_remote: Optional[datetime] = None
- description: Optional[str] = ""
+ dt_local: datetime | None = None
+ dt_remote: datetime | None = None
+ description: str | None = ''
class RareGameSignalsProgress(QObject):
@@ -98,11 +97,11 @@ def deleteLater(self):
super(RareGameBase, self).deleteLater()
@property
- def state(self) -> "RareGameBase.State":
+ def state(self) -> 'RareGameBase.State':
return self._state
@state.setter
- def state(self, state: "RareGameBase.State"):
+ def state(self, state: 'RareGameBase.State'):
if state != self._state:
self._state = state
self.signals.widget.refresh.emit()
@@ -129,7 +128,7 @@ def set_installed(self, installed: bool) -> None:
pass
@property
- def platforms(self) -> Tuple:
+ def platforms(self) -> tuple:
"""!
@brief Property that holds the platforms a game is available for
@@ -139,7 +138,7 @@ def platforms(self) -> Tuple:
@property
def default_platform(self) -> str:
- return self.core.default_platform if self.core.default_platform in self.platforms else "Windows"
+ return self.core.default_platform if self.core.default_platform in self.platforms else 'Windows'
@property
def is_mac(self) -> bool:
@@ -148,7 +147,7 @@ def is_mac(self) -> bool:
@return bool
"""
- return "Mac" in self.game.asset_infos.keys()
+ return 'Mac' in self.game.asset_infos
@property
def is_win32(self) -> bool:
@@ -157,7 +156,7 @@ def is_win32(self) -> bool:
@return bool
"""
- return "Win32" in self.game.asset_infos.keys()
+ return 'Win32' in self.game.asset_infos
@property
def is_origin(self) -> bool:
@@ -170,14 +169,14 @@ def is_origin(self) -> bool:
@return bool If the game is an Origin game
"""
- return self.game.third_party_store in {"Origin", "the EA app"}
+ return self.game.third_party_store in {'Origin', 'the EA app'}
@property
def is_android(self) -> bool:
- release_info = self.game.metadata.get("releaseInfo")
+ release_info = self.game.metadata.get('releaseInfo')
if not release_info:
return False
- return "Android" in release_info[0]["platform"]
+ return 'Android' in release_info[0]['platform']
@property
def is_overlay(self):
@@ -215,7 +214,7 @@ def version(self) -> str:
return self.igame.version if self.igame is not None else self.game.app_version(self.default_platform)
@property
- def install_path(self) -> Optional[str]:
+ def install_path(self) -> str | None:
if self.igame:
return self.igame.install_path
return None
@@ -226,8 +225,8 @@ def __init__(self, settings: RareAppSettings, legendary_core: LegendaryCore, gam
super(RareGameSlim, self).__init__(legendary_core, game)
self.settings = settings
# None if origin or not installed
- self.igame: Optional[InstalledGame] = self.core.get_installed_game(game.app_name)
- self.saves: List[RareSaveGame] = []
+ self.igame: InstalledGame = self.core.get_installed_game(game.app_name)
+ self.saves: list[RareSaveGame] = []
@property
def is_installed(self) -> bool:
@@ -244,7 +243,7 @@ def auto_sync_saves(self):
return self.supports_cloud_saves and auto_sync_cloud
@property
- def save_path(self) -> Optional[str]:
+ def save_path(self) -> str | None:
if self.igame is not None:
return self.igame.save_path
return None
@@ -259,7 +258,7 @@ def latest_save(self) -> RareSaveGame:
@property
def save_game_state(
self,
- ) -> Tuple[SaveGameStatus, Tuple[Optional[datetime], Optional[datetime]]]:
+ ) -> tuple[SaveGameStatus, tuple[datetime | None, datetime | None]]:
if self.save_path:
latest = self.latest_save
# lk: if the save path wasn't known at startup, dt_local will be None
@@ -318,7 +317,7 @@ def _download():
_download()
self.state = RareGameSlim.State.IDLE
- def load_saves(self, saves: List[SaveGameFile]):
+ def load_saves(self, saves: list[SaveGameFile]):
"""Use only in a thread"""
self.saves.clear()
for save in saves:
@@ -350,14 +349,14 @@ def is_save_up_to_date(self):
@property
def raw_save_path(self) -> str:
if self.game.supports_cloud_saves:
- return self.game.metadata.get("customAttributes", {}).get("CloudSaveFolder", {}).get("value")
- return ""
+ return self.game.metadata.get('customAttributes', {}).get('CloudSaveFolder', {}).get('value')
+ return ''
@property
def raw_save_path_mac(self) -> str:
if self.game.supports_mac_cloud_saves:
- return self.game.metadata.get("customAttributes", {}).get("CloudSaveFolder_MAC", {}).get("value")
- return ""
+ return self.game.metadata.get('customAttributes', {}).get('CloudSaveFolder_MAC', {}).get('value')
+ return ''
@property
def supports_cloud_saves(self):
diff --git a/rare/models/image.py b/rare/models/image.py
index b995fb693a..154f06d387 100644
--- a/rare/models/image.py
+++ b/rare/models/image.py
@@ -1,5 +1,4 @@
from enum import Enum
-from typing import Tuple
from PySide6.QtCore import QSize
@@ -18,7 +17,7 @@ def __init__(
divisor: float,
pixel_ratio: float,
orientation: ImageType = ImageType.Tall,
- base: "ImageSize.Preset" = None,
+ base: 'ImageSize.Preset' = None,
):
self.__divisor = divisor
self.__pixel_ratio = pixel_ratio
@@ -37,7 +36,7 @@ def __init__(
self.__smooth_transform = divisor <= 2
self.__base = base if base is not None else self
- def __eq__(self, other: "ImageSize.Preset"):
+ def __eq__(self, other: 'ImageSize.Preset'):
return (
self.__size == other.size
and self.__divisor == other.divisor
@@ -66,7 +65,7 @@ def orientation(self) -> ImageType:
return self.__orientation
@property
- def aspect_ratio(self) -> Tuple[int, int]:
+ def aspect_ratio(self) -> tuple[int, int]:
if self.__orientation == ImageType.Tall:
return 3, 4
elif self.__orientation == ImageType.Wide:
@@ -75,7 +74,7 @@ def aspect_ratio(self) -> Tuple[int, int]:
return 0, 0
@property
- def base(self) -> "ImageSize.Preset":
+ def base(self) -> 'ImageSize.Preset':
return self.__base
Tall = Preset(1, 1)
diff --git a/rare/models/install.py b/rare/models/install.py
index cc5d93f89f..321c6c8661 100644
--- a/rare/models/install.py
+++ b/rare/models/install.py
@@ -2,7 +2,6 @@
from dataclasses import dataclass
from datetime import datetime, timedelta
from pathlib import Path
-from typing import Dict, List, Optional, Tuple
from legendary.models.downloading import AnalysisResult, ConditionCheckResult
from legendary.models.game import Game, InstalledGame
@@ -13,12 +12,12 @@
@dataclass
class InstallOptionsModel:
app_name: str
- base_path: str = ""
+ base_path: str = ''
shared_memory: int = 1024
max_workers: int = os.cpu_count() * 2
force: bool = False
- platform: str = "Windows"
- install_tag: Optional[List[str]] = None
+ platform: str = 'Windows'
+ install_tag: list[str] | None = None
read_files: bool = False
order_opt: bool = False
repair_mode: bool = False
@@ -37,11 +36,11 @@ class InstallOptionsModel:
silent: bool = False
install_prereqs: bool = False
- def as_install_kwargs(self) -> Dict:
+ def as_install_kwargs(self) -> dict:
return {
k: getattr(self, k)
for k in vars(self)
- if k not in ["update", "silent", "create_shortcut", "overlay", "install_prereqs"]
+ if k not in ['update', 'silent', 'create_shortcut', 'overlay', 'install_prereqs']
}
@@ -58,19 +57,19 @@ class InstallDownloadModel:
class InstallQueueItemModel:
def __init__(self, options: InstallOptionsModel, download: InstallDownloadModel = None):
- self.options: Optional[InstallOptionsModel] = options
+ self.options: InstallOptionsModel | None = options
# lk: internal attribute holders
- self.__download: Optional[InstallDownloadModel] = None
- self.__date: Optional[datetime] = None
+ self.__download: InstallDownloadModel | None = None
+ self.__date: datetime | None = None
self.download = download
@property
- def download(self) -> Optional[InstallDownloadModel]:
+ def download(self) -> InstallDownloadModel | None:
return self.__download
@download.setter
- def download(self, download: Optional[InstallDownloadModel]):
+ def download(self, download: InstallDownloadModel | None):
self.__download = download
if download is not None:
self.__date = datetime.now()
@@ -93,7 +92,7 @@ class UninstallOptionsModel:
keep_overlay_keys: bool = None
@property
- def __values(self) -> Tuple[bool, bool, bool, bool, bool]:
+ def __values(self) -> tuple[bool, bool, bool, bool, bool]:
return (
self.accepted,
self.keep_files,
@@ -103,7 +102,7 @@ def __values(self) -> Tuple[bool, bool, bool, bool, bool]:
)
@__values.setter
- def __values(self, values: Tuple[bool, bool, bool, bool, bool]):
+ def __values(self, values: tuple[bool, bool, bool, bool, bool]):
(
self.accepted,
self.keep_files,
@@ -129,7 +128,7 @@ def set_rejected(self):
class SelectiveDownloadsModel:
app_name: str
accepted: bool = None
- install_tag: Optional[List[str]] = None
+ install_tag: list[str] | None = None
def __bool__(self):
return bool(self.app_name) and (self.accepted is not None) and (self.install_tag is not None)
@@ -180,7 +179,7 @@ def target_path(self, path: str):
@property
def target_name(self) -> str:
if self._rename_path:
- return ""
+ return ''
if self._reset_name:
return self.default_name
return self._target_name
diff --git a/rare/models/launcher.py b/rare/models/launcher.py
index 2728873891..45993226fb 100644
--- a/rare/models/launcher.py
+++ b/rare/models/launcher.py
@@ -1,5 +1,4 @@
from dataclasses import dataclass
-from typing import Dict
class Actions:
@@ -16,8 +15,8 @@ class BaseModel:
app_name: str
@classmethod
- def from_json(cls, data: Dict):
- return cls(action=data["action"], app_name=data["app_name"])
+ def from_json(cls, data: dict):
+ return cls(action=data['action'], app_name=data['app_name'])
@dataclass
@@ -26,11 +25,11 @@ class FinishedModel(BaseModel):
playtime: int # seconds
@classmethod
- def from_json(cls, data: Dict):
+ def from_json(cls, data: dict):
return cls(
**vars(BaseModel.from_json(data)),
- exit_code=data["exit_code"],
- playtime=data["playtime"],
+ exit_code=data['exit_code'],
+ playtime=data['playtime'],
)
@@ -45,11 +44,11 @@ class States:
new_state: int
@classmethod
- def from_json(cls, data: Dict):
+ def from_json(cls, data: dict):
return cls(
- action=data["action"],
- app_name=data["app_name"],
- new_state=data["new_state"],
+ action=data['action'],
+ app_name=data['app_name'],
+ new_state=data['new_state'],
)
@@ -58,5 +57,5 @@ class ErrorModel(BaseModel):
error_string: str
@classmethod
- def from_json(cls, data: Dict):
- return cls(**vars(BaseModel.from_json(data)), error_string=data["error_string"])
+ def from_json(cls, data: dict):
+ return cls(**vars(BaseModel.from_json(data)), error_string=data['error_string'])
diff --git a/rare/models/pathspec.py b/rare/models/pathspec.py
index b7e6bdf5b0..af90ee52e4 100644
--- a/rare/models/pathspec.py
+++ b/rare/models/pathspec.py
@@ -1,5 +1,4 @@
import os
-from typing import List, Union
from legendary.models.game import InstalledGame
@@ -10,32 +9,32 @@
class PathSpec:
@staticmethod
def egl_appdata() -> str:
- return r"%LOCALAPPDATA%\EpicGamesLauncher\Saved\Config\Windows"
+ return r'%LOCALAPPDATA%\EpicGamesLauncher\Saved\Config\Windows'
@staticmethod
def egl_programdata() -> str:
- return r"%PROGRAMDATA%\Epic\EpicGamesLauncher\Data\Manifests"
+ return r'%PROGRAMDATA%\Epic\EpicGamesLauncher\Data\Manifests'
@staticmethod
def wine_programdata() -> str:
- return r"ProgramData"
+ return r'ProgramData'
@staticmethod
def wine_egl_programdata() -> str:
- return PathSpec.egl_programdata().replace("\\", "/").replace("%PROGRAMDATA%", PathSpec.wine_programdata())
+ return PathSpec.egl_programdata().replace('\\', '/').replace('%PROGRAMDATA%', PathSpec.wine_programdata())
@staticmethod
def prefix_egl_programdata(prefix: str) -> str:
- return os.path.join(prefix, "dosdevices/c:", PathSpec.wine_egl_programdata())
+ return os.path.join(prefix, 'dosdevices/c:', PathSpec.wine_egl_programdata())
@staticmethod
- def wine_egl_prefixes(results: int = 0) -> Union[List[str], str]:
+ def wine_egl_prefixes(results: int = 0) -> list[str] | str:
possible_prefixes = get_prefixes()
prefixes = [
prefix for prefix, _ in possible_prefixes if os.path.exists(os.path.join(prefix, PathSpec.wine_egl_programdata()))
]
if not prefixes:
- return ""
+ return ''
if not results:
return prefixes
elif results == 1:
@@ -45,18 +44,18 @@ def wine_egl_prefixes(results: int = 0) -> Union[List[str], str]:
def __init__(self, core: LegendaryCore = None, igame: InstalledGame = None):
self.__egl_path_vars = {
- "{appdata}": os.path.expandvars("%LOCALAPPDATA%"),
- "{userdir}": os.path.expandvars("%USERPROFILE%/Documents"),
- "{userprofile}": os.path.expandvars("%userprofile%"), # possibly wrong
- "{usersavedgames}": os.path.expandvars("%USERPROFILE%/Saved Games"),
+ '{appdata}': os.path.expandvars('%LOCALAPPDATA%'),
+ '{userdir}': os.path.expandvars('%USERPROFILE%/Documents'),
+ '{userprofile}': os.path.expandvars('%userprofile%'), # possibly wrong
+ '{usersavedgames}': os.path.expandvars('%USERPROFILE%/Saved Games'),
}
if core is not None:
- self.__egl_path_vars["{epicid}"] = core.lgd.userdata["account_id"]
+ self.__egl_path_vars['{epicid}'] = core.lgd.userdata['account_id']
if igame is not None:
- self.__egl_path_vars["{installdir}"] = igame.install_path
+ self.__egl_path_vars['{installdir}'] = igame.install_path
- def resolve_egl_path_vars(self, path: str) -> Union[str, bytes]:
- cooked_path = (self.__egl_path_vars.get(p.lower(), p) for p in path.split("/"))
+ def resolve_egl_path_vars(self, path: str) -> str | bytes:
+ cooked_path = (self.__egl_path_vars.get(p.lower(), p) for p in path.split('/'))
return os.path.join(*cooked_path)
diff --git a/rare/models/settings.py b/rare/models/settings.py
index 3d16b182fa..bf0071fb56 100644
--- a/rare/models/settings.py
+++ b/rare/models/settings.py
@@ -1,7 +1,7 @@
import locale
import platform as pf
from argparse import Namespace
-from typing import Any, Optional, Type
+from typing import Any, Optional
from PySide6.QtCore import QSettings
@@ -11,7 +11,7 @@
class Setting(Namespace):
key: str
default: Any
- dtype: Type
+ dtype: type
def __len__(self):
return len(self.__dict__)
@@ -22,85 +22,85 @@ def __iter__(self):
# The key names are set to the existing option name
class Settings(Namespace):
- win32_meta = Setting(key="win32_meta", default=False, dtype=bool)
- macos_meta = Setting(key="macos_meta", default=pf.system() == "Darwin", dtype=bool)
- unreal_meta = Setting(key="unreal_meta", default=False, dtype=bool)
- exclude_non_asset = Setting(key="exclude_non_asset", default=False, dtype=bool)
- exclude_entitlements = Setting(key="exclude_entitlements", default=False, dtype=bool)
-
- language = Setting(key="language", default=locale.getlocale()[0], dtype=str)
-
- sys_tray_close = Setting(key="sys_tray", default=True, dtype=bool)
- sys_tray_start = Setting(key="sys_tray_start", default=False, dtype=bool)
-
- auto_update = Setting(key="auto_update", default=False, dtype=bool)
- auto_sync_cloud = Setting(key="auto_sync_cloud", default=False, dtype=bool)
- confirm_start = Setting(key="confirm_start", default=False, dtype=bool)
- restore_window = Setting(key="save_size", default=False, dtype=bool)
- window_width = Setting(key="window_width", default=1280, dtype=int)
- window_height = Setting(key="window_height", default=720, dtype=int)
- notification = Setting(key="notification", default=True, dtype=bool)
- log_games = Setting(key="show_console", default=pf.system() != "Windows", dtype=bool)
-
- color_scheme = Setting(key="color_scheme", default="", dtype=str)
- style_sheet = Setting(key="style_sheet", default="RareStyle", dtype=str)
-
- library_view = Setting(key="library_view", default=int(LibraryView.COVER), dtype=int)
+ win32_meta = Setting(key='win32_meta', default=False, dtype=bool)
+ macos_meta = Setting(key='macos_meta', default=pf.system() == 'Darwin', dtype=bool)
+ unreal_meta = Setting(key='unreal_meta', default=False, dtype=bool)
+ exclude_non_asset = Setting(key='exclude_non_asset', default=False, dtype=bool)
+ exclude_entitlements = Setting(key='exclude_entitlements', default=False, dtype=bool)
+
+ language = Setting(key='language', default=locale.getlocale()[0], dtype=str)
+
+ sys_tray_close = Setting(key='sys_tray', default=True, dtype=bool)
+ sys_tray_start = Setting(key='sys_tray_start', default=False, dtype=bool)
+
+ auto_update = Setting(key='auto_update', default=False, dtype=bool)
+ auto_sync_cloud = Setting(key='auto_sync_cloud', default=False, dtype=bool)
+ confirm_start = Setting(key='confirm_start', default=False, dtype=bool)
+ restore_window = Setting(key='save_size', default=False, dtype=bool)
+ window_width = Setting(key='window_width', default=1280, dtype=int)
+ window_height = Setting(key='window_height', default=720, dtype=int)
+ notification = Setting(key='notification', default=True, dtype=bool)
+ log_games = Setting(key='show_console', default=pf.system() != 'Windows', dtype=bool)
+
+ color_scheme = Setting(key='color_scheme', default='', dtype=str)
+ style_sheet = Setting(key='style_sheet', default='RareStyle', dtype=str)
+
+ library_view = Setting(key='library_view', default=int(LibraryView.COVER), dtype=int)
library_filter = Setting(
- key="library_filter",
- default=int(LibraryFilter.MAC if pf.system() == "Darwin" else LibraryFilter.ALL),
+ key='library_filter',
+ default=int(LibraryFilter.MAC if pf.system() == 'Darwin' else LibraryFilter.ALL),
dtype=int,
)
- library_order = Setting(key="library_order", default=int(LibraryOrder.TITLE), dtype=int)
+ library_order = Setting(key='library_order', default=int(LibraryOrder.TITLE), dtype=int)
- discord_rpc_mode = Setting(key="discord_rpc_mode", default=int(DiscordRPCMode.GAME_ONLY), dtype=int)
- discord_rpc_game = Setting(key="discord_rpc_game", default=True, dtype=bool)
- discord_rpc_time = Setting(key="discord_rpc_time", default=True, dtype=bool)
- discord_rpc_os = Setting(key="discord_rpc_os", default=True, dtype=bool)
+ discord_rpc_mode = Setting(key='discord_rpc_mode', default=int(DiscordRPCMode.GAME_ONLY), dtype=int)
+ discord_rpc_game = Setting(key='discord_rpc_game', default=True, dtype=bool)
+ discord_rpc_time = Setting(key='discord_rpc_time', default=True, dtype=bool)
+ discord_rpc_os = Setting(key='discord_rpc_os', default=True, dtype=bool)
- local_shader_cache = Setting(key="local_shader_cache", default=False, dtype=bool)
- create_shortcut = Setting(key="create_shortcut", default=pf.system() == "Windows", dtype=bool)
+ local_shader_cache = Setting(key='local_shader_cache', default=False, dtype=bool)
+ create_shortcut = Setting(key='create_shortcut', default=pf.system() == 'Windows', dtype=bool)
app_settings = Settings()
class RareAppSettings(QSettings):
- __instance: Optional["RareAppSettings"] = None
+ __instance: Optional['RareAppSettings'] = None
def __init__(self, parent=None):
if RareAppSettings.__instance is not None:
- raise RuntimeError("RareSettings already initialized")
+ raise RuntimeError('RareSettings already initialized')
super(RareAppSettings, self).__init__(parent)
RareAppSettings.__instance = self
@staticmethod
- def instance() -> "RareAppSettings":
+ def instance() -> 'RareAppSettings':
if RareAppSettings.__instance is None:
- raise RuntimeError("Uninitialized use of RareSettings")
+ raise RuntimeError('Uninitialized use of RareSettings')
return RareAppSettings.__instance
def get_value(self, option: Setting, prefix: str = None) -> Any:
if prefix:
- return self.value(f"{prefix}/{option.key}", defaultValue=option.default, type=option.dtype)
+ return self.value(f'{prefix}/{option.key}', defaultValue=option.default, type=option.dtype)
else:
return self.value(option.key, defaultValue=option.default, type=option.dtype)
def set_value(self, option: Setting, value: Any, prefix: str = None) -> None:
if prefix:
- self.setValue(f"{prefix}/{option.key}", option.dtype(value))
+ self.setValue(f'{prefix}/{option.key}', option.dtype(value))
else:
self.setValue(option.key, option.dtype(value))
def rem_value(self, option: Setting, prefix: str = None) -> None:
if prefix:
- self.remove(f"{prefix}/{option.key}")
+ self.remove(f'{prefix}/{option.key}')
else:
self.remove(option.key)
def get_with_global(self, option: Setting, prefix: str) -> Any:
_global = self.get_value(option, None)
- return self.value(f"{prefix}/{option.key}", defaultValue=_global, type=option.dtype)
+ return self.value(f'{prefix}/{option.key}', defaultValue=_global, type=option.dtype)
def set_with_global(self, option: Setting, value: Any, prefix: str) -> None:
_global = self.get_value(option, None)
@@ -115,10 +115,10 @@ def deleteLater(self):
__all__ = [
- "app_settings",
- "RareAppSettings",
- "LibraryFilter",
- "LibraryOrder",
- "LibraryView",
- "DiscordRPCMode",
+ 'app_settings',
+ 'RareAppSettings',
+ 'LibraryFilter',
+ 'LibraryOrder',
+ 'LibraryView',
+ 'DiscordRPCMode',
]
diff --git a/rare/models/steam.py b/rare/models/steam.py
index e1f656864b..5651c5516a 100644
--- a/rare/models/steam.py
+++ b/rare/models/steam.py
@@ -3,11 +3,11 @@
import shlex
from dataclasses import dataclass, field
from datetime import datetime, timezone
-from typing import Any, Dict, List, Type
+from typing import Any
class SteamUser:
- def __init__(self, long_id: str, user: Dict):
+ def __init__(self, long_id: str, user: dict):
super(SteamUser, self).__init__()
self._long_id: str = long_id
self._user = user.copy()
@@ -22,19 +22,19 @@ def short_id(self) -> int:
@property
def account_name(self) -> str:
- return self._user.get("AccountName", "")
+ return self._user.get('AccountName', '')
@property
def persona_name(self) -> str:
- return self._user.get("PersonaName", "")
+ return self._user.get('PersonaName', '')
@property
def most_recent(self) -> bool:
- return bool(int(self._user.get("MostRecent", "0")))
+ return bool(int(self._user.get('MostRecent', '0')))
@property
def last_login(self) -> datetime:
- return datetime.fromtimestamp(float(self._user.get("Timestamp", "0")), timezone.utc)
+ return datetime.fromtimestamp(float(self._user.get('Timestamp', '0')), timezone.utc)
@property
def __dict__(self):
@@ -69,46 +69,46 @@ class SteamShortcut:
DevkitOverrideAppID: int
LastPlayTime: int
FlatpakAppID: str
- tags: Dict = field(default_factory=dict)
+ tags: dict = field(default_factory=dict)
@classmethod
- def from_dict(cls: Type["SteamShortcut"], src: Dict[str, Any]) -> "SteamShortcut":
+ def from_dict(cls: type['SteamShortcut'], src: dict[str, Any]) -> 'SteamShortcut':
d = src.copy()
tmp = cls(
- appid=d.pop("appid", 0),
- AppName=d.pop("AppName", ""),
- Exe=d.pop("Exe", ""),
- StartDir=d.pop("StartDir", ""),
- icon=d.pop("icon", ""),
- ShortcutPath=d.pop("ShortcutPath", ""),
- LaunchOptions=d.pop("LaunchOptions", ""),
- IsHidden=bool(d.pop("IsHidden", 0)),
- AllowDesktopConfig=bool(d.pop("AllowDesktopConfig", 1)),
- AllowOverlay=bool(d.pop("AllowOverlay", 1)),
- OpenVR=bool(d.pop("OpenVR", 0)),
- Devkit=bool(d.pop("Devkit", 0)),
- DevkitGameID=d.pop("DevkitGameID", ""),
- DevkitOverrideAppID=d.pop("DevkitOverrideAppID", 0),
- LastPlayTime=d.pop("LastPlayTime", 0),
- FlatpakAppID=d.pop("FlatpakAppID", ""),
- tags=d.pop("tags", {}),
+ appid=d.pop('appid', 0),
+ AppName=d.pop('AppName', ''),
+ Exe=d.pop('Exe', ''),
+ StartDir=d.pop('StartDir', ''),
+ icon=d.pop('icon', ''),
+ ShortcutPath=d.pop('ShortcutPath', ''),
+ LaunchOptions=d.pop('LaunchOptions', ''),
+ IsHidden=bool(d.pop('IsHidden', 0)),
+ AllowDesktopConfig=bool(d.pop('AllowDesktopConfig', 1)),
+ AllowOverlay=bool(d.pop('AllowOverlay', 1)),
+ OpenVR=bool(d.pop('OpenVR', 0)),
+ Devkit=bool(d.pop('Devkit', 0)),
+ DevkitGameID=d.pop('DevkitGameID', ''),
+ DevkitOverrideAppID=d.pop('DevkitOverrideAppID', 0),
+ LastPlayTime=d.pop('LastPlayTime', 0),
+ FlatpakAppID=d.pop('FlatpakAppID', ''),
+ tags=d.pop('tags', {}),
)
return tmp
@classmethod
def create(
- cls: Type["SteamShortcut"],
+ cls: type['SteamShortcut'],
app_name: str,
app_title: str,
executable: str,
start_dir: str,
icon: str,
- launch_options: List[str],
- ) -> "SteamShortcut":
+ launch_options: list[str],
+ ) -> 'SteamShortcut':
shortcut = cls.from_dict({})
shortcut.appid = cls.calculate_appid(app_name)
shortcut.AppName = app_title
- shortcut.Exe = executable if platform.system() == "Windows" else shlex.quote(executable)
+ shortcut.Exe = executable if platform.system() == 'Windows' else shlex.quote(executable)
shortcut.StartDir = start_dir
shortcut.icon = icon
shortcut.LaunchOptions = shlex.join(launch_options)
@@ -116,8 +116,8 @@ def create(
@staticmethod
def calculate_appid(app_name) -> int:
- key = "rare_steam_shortcut_" + app_name
- top = binascii.crc32(str.encode(key, "utf-8")) | 0x80000000
+ key = 'rare_steam_shortcut_' + app_name
+ top = binascii.crc32(str.encode(key, 'utf-8')) | 0x80000000
return (((top << 32) | 0x02000000) >> 32) - 0x100000000
def shortcut_appid(self) -> int:
@@ -128,19 +128,19 @@ def coverart_appid(self) -> int:
@property
def grid_wide(self) -> str:
- return f"{self.coverart_appid()}.png"
+ return f'{self.coverart_appid()}.png'
@property
def grid_tall(self) -> str:
- return f"{self.coverart_appid()}p.png"
+ return f'{self.coverart_appid()}p.png'
@property
def game_hero(self) -> str:
- return f"{self.coverart_appid()}_hero.png"
+ return f'{self.coverart_appid()}_hero.png'
@property
def game_logo(self) -> str:
- return f"{self.coverart_appid()}_logo.png"
+ return f'{self.coverart_appid()}_logo.png'
@property
def last_played(self):
diff --git a/rare/models/wrapper.py b/rare/models/wrapper.py
index 057b6b01b9..50d9567ede 100644
--- a/rare/models/wrapper.py
+++ b/rare/models/wrapper.py
@@ -2,7 +2,6 @@
import shlex
from enum import IntEnum
from hashlib import md5
-from typing import Dict, List, Union
class WrapperType(IntEnum):
@@ -15,12 +14,12 @@ class WrapperType(IntEnum):
class Wrapper:
def __init__(
self,
- command: Union[str, List[str]],
+ command: str | list[str],
name: str = None,
wtype: WrapperType = None,
enabled: bool = True,
):
- self.__command: List[str] = shlex.split(command) if isinstance(command, str) else command
+ self.__command: list[str] = shlex.split(command) if isinstance(command, str) else command
self.__name: str = name if name is not None else os.path.basename(self.__command[0])
self.__wtype: WrapperType = wtype if wtype is not None else WrapperType.USER_DEFINED
self.__enabled: bool = enabled or self.__wtype == WrapperType.COMPAT_TOOL
@@ -43,20 +42,20 @@ def is_enabled(self, state: bool) -> None:
@property
def checksum(self) -> str:
- return md5(self.as_str.encode("utf-8")).hexdigest()
+ return md5(self.as_str.encode('utf-8')).hexdigest()
@property
def executable(self) -> str:
return shlex.quote(self.__command[0])
@property
- def command(self) -> List[str]:
+ def command(self) -> list[str]:
return self.__command
@property
def as_str(self) -> str:
# return " ".join(shlex.quote(part) for part in self.__command)
- return " ".join(map(shlex.quote, self.__command))
+ return ' '.join(map(shlex.quote, self.__command))
@property
def name(self) -> str:
@@ -76,11 +75,11 @@ def __bool__(self) -> bool:
return True if not self.is_editable else bool(self.as_str.strip())
@classmethod
- def from_dict(cls, data: Dict):
+ def from_dict(cls, data: dict):
return cls(
- command=data.get("command"),
- name=data.get("name"),
- wtype=WrapperType(data.get("wtype", WrapperType.USER_DEFINED)),
+ command=data.get('command'),
+ name=data.get('name'),
+ wtype=WrapperType(data.get('wtype', WrapperType.USER_DEFINED)),
)
@property
diff --git a/rare/resources/__init__.py b/rare/resources/__init__.py
index c18e049eea..7b08215d28 100644
--- a/rare/resources/__init__.py
+++ b/rare/resources/__init__.py
@@ -4,4 +4,4 @@
import rare.resources.static_css as static_css
import rare.resources.stylesheets as stylesheets
-__all__ = ["resources", "static_css", "stylesheets"]
+__all__ = ['resources', 'static_css', 'stylesheets']
diff --git a/rare/resources/resources.py b/rare/resources/resources.py
index 274ea4bf8a..5916bab475 100644
--- a/rare/resources/resources.py
+++ b/rare/resources/resources.py
@@ -35633,7 +35633,7 @@
\xab\xe0\xa3\
"
-qt_resource_name = b"\
+qt_resource_name = b'\
\x00\x07\
\x09\x9e\xc3#\
\x00s\
@@ -35682,9 +35682,9 @@
\x04\xa8\x91\xc2\
\x00D\
\x00a\x00r\x00k\x00e\x00r\
-"
+'
-qt_resource_struct = b"\
+qt_resource_struct = b'\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x14\x00\x02\x00\x00\x00\x02\x00\x00\x00\x0b\
@@ -35711,12 +35711,15 @@
\x00\x00\x01\x9c\xc3\x1e\xdd\x86\
\x00\x00\x00&\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x905 \x19\x03\
-"
+'
+
def qInitResources():
QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
def qCleanupResources():
QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
qInitResources()
diff --git a/rare/resources/static_css/stylesheet.py b/rare/resources/static_css/stylesheet.py
index b9b4cb635c..0b3a86c35b 100644
--- a/rare/resources/static_css/stylesheet.py
+++ b/rare/resources/static_css/stylesheet.py
@@ -1,5 +1,4 @@
import os
-from typing import Type, Union
import qstylizer.style
from PySide6.QtCore import QObject
@@ -11,12 +10,12 @@
verbose = True
compressLevel = 6
-compressAlgo = "zlib"
+compressAlgo = 'zlib'
compressThreshold = 0
-def css_name(widget: Union[wrappertype, QObject, Type], subwidget: str = ""):
- return f"#{widget_object_name(widget, '')}{subwidget}"
+def css_name(widget: wrappertype | QObject | type, subwidget: str = ''):
+ return f'#{widget_object_name(widget, "")}{subwidget}'
style = qstylizer.style.StyleSheet()
@@ -30,89 +29,89 @@ def css_name(widget: Union[wrappertype, QObject, Type], subwidget: str = ""):
style.QLineEdit,
style.QComboBox,
):
- elem.setValues(minHeight="2.75ex")
+ elem.setValues(minHeight='2.75ex')
# Generic flat button
style['QPushButton[flat="true"]'].setValues(
- border="0px",
- borderRadius="5px",
- backgroundColor="rgba(255, 255, 255, 5%)",
+ border='0px',
+ borderRadius='5px',
+ backgroundColor='rgba(255, 255, 255, 5%)',
)
# InfoLabel
-style.QLabel["#InfoLabel"].setValues(
- color="#999",
- fontStyle="italic",
- fontWeight="normal",
+style.QLabel['#InfoLabel'].setValues(
+ color='#999',
+ fontStyle='italic',
+ fontWeight='normal',
)
-verify_color = QColor("#d6af57")
-move_color = QColor("#41cad9")
-cloud_sync_color = QColor("#dbd3d1")
+verify_color = QColor('#d6af57')
+move_color = QColor('#41cad9')
+cloud_sync_color = QColor('#dbd3d1')
# [Un]InstallButton
-style.QPushButton["#InstallButton"].setValues(borderColor=QColor(0, 180, 0).name(), backgroundColor=QColor(0, 100, 0).name())
-style.QPushButton["#InstallButton"].hover.setValues(borderColor=QColor(0, 135, 0).name(), backgroundColor=QColor(0, 70, 0).name())
-style.QPushButton["#InstallButton"].disabled.setValues(
+style.QPushButton['#InstallButton'].setValues(borderColor=QColor(0, 180, 0).name(), backgroundColor=QColor(0, 100, 0).name())
+style.QPushButton['#InstallButton'].hover.setValues(borderColor=QColor(0, 135, 0).name(), backgroundColor=QColor(0, 70, 0).name())
+style.QPushButton['#InstallButton'].disabled.setValues(
borderColor=QColor(0, 60, 0).name(), backgroundColor=QColor(0, 40, 0).name()
)
-style.QPushButton["#UninstallButton"].setValues(borderColor=QColor(180, 0, 0).name(), backgroundColor=QColor(100, 0, 0).name())
-style.QPushButton["#UninstallButton"].hover.setValues(
+style.QPushButton['#UninstallButton'].setValues(borderColor=QColor(180, 0, 0).name(), backgroundColor=QColor(100, 0, 0).name())
+style.QPushButton['#UninstallButton'].hover.setValues(
borderColor=QColor(135, 0, 0).name(), backgroundColor=QColor(70, 0, 0).name()
)
-style.QPushButton["#UninstallButton"].disabled.setValues(
+style.QPushButton['#UninstallButton'].disabled.setValues(
borderColor=QColor(60, 0, 0).name(), backgroundColor=QColor(40, 0, 0).name()
)
# VerifyButton
-style.QPushButton["#VerifyButton"].setValues(
+style.QPushButton['#VerifyButton'].setValues(
borderColor=verify_color.darker(200).name(), backgroundColor=verify_color.darker(400).name()
)
-style.QPushButton["#VerifyButton"].hover.setValues(
+style.QPushButton['#VerifyButton'].hover.setValues(
borderColor=verify_color.darker(300).name(), backgroundColor=verify_color.darker(500).name()
)
-style.QPushButton["#VerifyButton"].disabled.setValues(
+style.QPushButton['#VerifyButton'].disabled.setValues(
borderColor=verify_color.darker(400).name(), backgroundColor=verify_color.darker(600).name()
)
# MoveButton
-style.QPushButton["#MoveButton"].setValues(
+style.QPushButton['#MoveButton'].setValues(
borderColor=move_color.darker(200).name(), backgroundColor=move_color.darker(400).name()
)
-style.QPushButton["#MoveButton"].hover.setValues(
+style.QPushButton['#MoveButton'].hover.setValues(
borderColor=move_color.darker(300).name(), backgroundColor=move_color.darker(500).name()
)
-style.QPushButton["#MoveButton"].disabled.setValues(
+style.QPushButton['#MoveButton'].disabled.setValues(
borderColor=move_color.darker(400).name(), backgroundColor=move_color.darker(600).name()
)
# QueueWorkerLabel
-style.QLabel["#QueueWorkerLabel"].setValues(
- borderWidth="1px",
- borderStyle="solid",
- borderRadius="3px",
+style.QLabel['#QueueWorkerLabel'].setValues(
+ borderWidth='1px',
+ borderStyle='solid',
+ borderRadius='3px',
)
from rare.shared.workers.verify import VerifyWorker # noqa: E402
-style.QLabel["#QueueWorkerLabel"][f'[workertype="{widget_object_name(VerifyWorker, "")}"]'].setValues(
+style.QLabel['#QueueWorkerLabel'][f'[workertype="{widget_object_name(VerifyWorker, "")}"]'].setValues(
borderColor=verify_color.darker(200).name(),
backgroundColor=verify_color.darker(400).name(),
)
from rare.shared.workers.move import MoveWorker # noqa: E402
-style.QLabel["#QueueWorkerLabel"][f'[workertype="{widget_object_name(MoveWorker, "")}"]'].setValues(
+style.QLabel['#QueueWorkerLabel'][f'[workertype="{widget_object_name(MoveWorker, "")}"]'].setValues(
borderColor=move_color.darker(200).name(),
backgroundColor=move_color.darker(400).name(),
)
from rare.shared.workers.cloud_sync import CloudSyncWorker # noqa: E402
-style.QLabel["#QueueWorkerLabel"][f'[workertype="{widget_object_name(CloudSyncWorker, "")}"]'].setValues(
+style.QLabel['#QueueWorkerLabel'][f'[workertype="{widget_object_name(CloudSyncWorker, "")}"]'].setValues(
borderColor=cloud_sync_color.darker(200).name(),
backgroundColor=cloud_sync_color.darker(400).name(),
)
@@ -121,10 +120,10 @@ def css_name(widget: Union[wrappertype, QObject, Type], subwidget: str = ""):
from rare.components.tabs.library.widgets.library_widget import ProgressLabel # noqa: E402
style.QLabel[css_name(ProgressLabel)].setValues(
- borderWidth="1px",
- borderRadius="5%",
- fontWeight="bold",
- fontSize="16pt",
+ borderWidth='1px',
+ borderRadius='5%',
+ fontWeight='bold',
+ fontSize='16pt',
)
@@ -132,127 +131,127 @@ def css_name(widget: Union[wrappertype, QObject, Type], subwidget: str = ""):
from rare.components.tabs.library.widgets.icon_widget import IconWidget # noqa: E402
icon_background_props = {
- "backgroundColor": "rgba(0, 0, 0, 65%)",
+ 'backgroundColor': 'rgba(0, 0, 0, 65%)',
}
-style.QLabel[css_name(IconWidget, "StatusLabel")].setValues(
- fontWeight="bold",
- color="white",
+style.QLabel[css_name(IconWidget, 'StatusLabel')].setValues(
+ fontWeight='bold',
+ color='white',
**icon_background_props,
- borderRadius="5%",
- borderTopLeftRadius="11%",
- borderTopRightRadius="11%",
+ borderRadius='5%',
+ borderTopLeftRadius='11%',
+ borderTopRightRadius='11%',
)
-style.QWidget[css_name(IconWidget, "MiniWidget")].setValues(
- color="rgb(238, 238, 238)",
+style.QWidget[css_name(IconWidget, 'MiniWidget')].setValues(
+ color='rgb(238, 238, 238)',
**icon_background_props,
- borderRadius="5%",
- borderBottomLeftRadius="9%",
- borderBottomRightRadius="9%",
+ borderRadius='5%',
+ borderBottomLeftRadius='9%',
+ borderBottomRightRadius='9%',
)
icon_bottom_label_props = {
- "color": "white",
- "backgroundColor": "rgba(0, 0, 0, 0%)",
+ 'color': 'white',
+ 'backgroundColor': 'rgba(0, 0, 0, 0%)',
}
-style.QLabel[css_name(IconWidget, "TitleLabel")].setValues(
- fontWeight="bold",
+style.QLabel[css_name(IconWidget, 'TitleLabel')].setValues(
+ fontWeight='bold',
**icon_bottom_label_props,
)
-style.QLabel[css_name(IconWidget, "TooltipLabel")].setValues(
+style.QLabel[css_name(IconWidget, 'TooltipLabel')].setValues(
**icon_bottom_label_props,
)
icon_square_button_props = {
- "border": "1px solid black",
- "borderRadius": "10%",
+ 'border': '1px solid black',
+ 'borderRadius': '10%',
}
icon_square_button_props.update(icon_background_props)
-style.QPushButton[css_name(IconWidget, "Button")].setValues(**icon_square_button_props)
-style.QPushButton[css_name(IconWidget, "Button")].hover.borderColor.setValue("gray")
+style.QPushButton[css_name(IconWidget, 'Button')].setValues(**icon_square_button_props)
+style.QPushButton[css_name(IconWidget, 'Button')].hover.borderColor.setValue('gray')
# ListGameWidget
from rare.components.tabs.library.widgets.list_widget import ListWidget # noqa: E402
-style.QLabel[css_name(ListWidget, "TitleLabel")].fontWeight.setValue("bold")
+style.QLabel[css_name(ListWidget, 'TitleLabel')].fontWeight.setValue('bold')
list_status_label_props = {
- "color": "white",
- "backgroundColor": "rgba(0, 0, 0, 75%)",
- "border": "0px solid black",
- "borderRadius": "3px",
- "paddingLeft": "0.3em",
- "paddingRight": "0.3em",
+ 'color': 'white',
+ 'backgroundColor': 'rgba(0, 0, 0, 75%)',
+ 'border': '0px solid black',
+ 'borderRadius': '3px',
+ 'paddingLeft': '0.3em',
+ 'paddingRight': '0.3em',
}
-style.QLabel[css_name(ListWidget, "StatusLabel")].setValues(
- fontWeight="bold",
+style.QLabel[css_name(ListWidget, 'StatusLabel')].setValues(
+ fontWeight='bold',
**list_status_label_props,
)
-style.QLabel[css_name(ListWidget, "TooltipLabel")].setValues(
+style.QLabel[css_name(ListWidget, 'TooltipLabel')].setValues(
**list_status_label_props,
)
-style.QPushButton[css_name(ListWidget, "Button")].textAlign.setValue("left")
-style.QLabel[css_name(ListWidget, "InfoLabel")].color.setValue("#999")
+style.QPushButton[css_name(ListWidget, 'Button')].textAlign.setValue('left')
+style.QLabel[css_name(ListWidget, 'InfoLabel')].color.setValue('#999')
# MainTabBar
from rare.components.tabs.tab_widgets import MainTabBar # noqa: E402
-style.QTabBar[css_name(MainTabBar, "")].tab.disabled.setValues(
- border="0px",
- backgroundColor="transparent",
+style.QTabBar[css_name(MainTabBar, '')].tab.disabled.setValues(
+ border='0px',
+ backgroundColor='transparent',
)
# SelectViewWidget
from rare.components.tabs.library.head_bar import SelectViewWidget # noqa: E402
-style.QPushButton[css_name(SelectViewWidget, "Button")].setValues(
- border="none",
- backgroundColor="transparent",
+style.QPushButton[css_name(SelectViewWidget, 'Button')].setValues(
+ border='none',
+ backgroundColor='transparent',
)
# ButtonLineEdit
from rare.widgets.button_edit import ButtonLineEdit # noqa: E402
-style.QPushButton[css_name(ButtonLineEdit, "Button")].setValues(
- backgroundColor="transparent",
- border="0px",
- padding="0px",
+style.QPushButton[css_name(ButtonLineEdit, 'Button')].setValues(
+ backgroundColor='transparent',
+ border='0px',
+ padding='0px',
)
# GameTagCheckBox
from rare.components.tabs.library.details.details import GameTagCheckBox # noqa: E402
-style.QCheckBox[css_name(GameTagCheckBox, "")].setValues(
- margin="1px",
- borderWidth="1px",
- borderStyle="solid",
- borderColor="black",
- borderRadius="3px",
- padding="3px",
+style.QCheckBox[css_name(GameTagCheckBox, '')].setValues(
+ margin='1px',
+ borderWidth='1px',
+ borderStyle='solid',
+ borderColor='black',
+ borderRadius='3px',
+ padding='3px',
)
-if __name__ == "__main__":
- with open(os.path.join(os.path.dirname(__file__), "stylesheet.qss"), "w", encoding="utf-8") as stylesheet:
+if __name__ == '__main__':
+ with open(os.path.join(os.path.dirname(__file__), 'stylesheet.qss'), 'w', encoding='utf-8') as stylesheet:
stylesheet.write(f'/* This file is auto-generated from "{os.path.basename(__file__)}". DO NOT EDIT!!! */\n\n')
stylesheet.write(style.toString(recursive=True))
qt_tool_wrapper(
- "rcc",
+ 'rcc',
[
- "-g",
- "python",
- "--compress",
+ '-g',
+ 'python',
+ '--compress',
str(compressLevel),
- "--compress-algo",
+ '--compress-algo',
compressAlgo,
- "--threshold",
+ '--threshold',
str(compressThreshold),
- "--verbose" if verbose else "",
- os.path.join(os.path.dirname(__file__), "stylesheet.qrc"),
- "-o",
- os.path.join(os.path.dirname(__file__), "__init__.py"),
+ '--verbose' if verbose else '',
+ os.path.join(os.path.dirname(__file__), 'stylesheet.qrc'),
+ '-o',
+ os.path.join(os.path.dirname(__file__), '__init__.py'),
],
True,
)
diff --git a/rare/resources/stylesheets/RareStyle/stylesheet.py b/rare/resources/stylesheets/RareStyle/stylesheet.py
index 6060e06de2..679a331b9c 100644
--- a/rare/resources/stylesheets/RareStyle/stylesheet.py
+++ b/rare/resources/stylesheets/RareStyle/stylesheet.py
@@ -1,5 +1,4 @@
import os
-from typing import Type, Union
import qstylizer.parser
import qstylizer.style
@@ -13,16 +12,16 @@
verbose = True
compressLevel = 6
-compressAlgo = "zlib"
+compressAlgo = 'zlib'
compressThreshold = 0
-def css_name(widget: Union[wrappertype, QObject, Type], subwidget: str = ""):
- return f"#{widget_object_name(widget, '')}{subwidget}"
+def css_name(widget: wrappertype | QObject | type, subwidget: str = ''):
+ return f'#{widget_object_name(widget, "")}{subwidget}'
# style = qstylizer.style.StyleSheet()
-with open(os.path.join(os.path.dirname(__file__), "template.qss"), "r", encoding="utf-8") as template:
+with open(os.path.join(os.path.dirname(__file__), 'template.qss'), encoding='utf-8') as template:
style = qstylizer.parser.parse(template.read())
background_color_base = QColor(32, 34, 37)
@@ -33,64 +32,64 @@ def css_name(widget: Union[wrappertype, QObject, Type], subwidget: str = ""):
border_color_editable = QColor(47, 79, 79)
common_attributes = {
- "borderWidth": "1px",
- "borderStyle": "solid",
- "borderRadius": "2px",
- "padding": "1px",
+ 'borderWidth': '1px',
+ 'borderStyle': 'solid',
+ 'borderRadius': '2px',
+ 'padding': '1px',
}
common_attributes_editable = {
- "borderColor": border_color_editable.name(),
- "backgroundColor": background_color_editable.name(),
- "selectionBackgroundColor": background_color_selection.name(),
+ 'borderColor': border_color_editable.name(),
+ 'backgroundColor': background_color_editable.name(),
+ 'selectionBackgroundColor': background_color_selection.name(),
}
-style.QPushButton.paddingTop.setValue("2px")
-style.QPushButton.paddingBottom.setValue("2px")
-style.QToolButton.paddingTop.setValue("2px")
-style.QToolButton.paddingBottom.setValue("2px")
+style.QPushButton.paddingTop.setValue('2px')
+style.QPushButton.paddingBottom.setValue('2px')
+style.QToolButton.paddingTop.setValue('2px')
+style.QToolButton.paddingBottom.setValue('2px')
-style.QScrollBar.vertical.width.setValue("1em")
-style.QScrollBar.horizontal.height.setValue("1em")
+style.QScrollBar.vertical.width.setValue('1em')
+style.QScrollBar.horizontal.height.setValue('1em')
-style["QTableView QTableCornerButton::section"].setValues(**common_attributes, **common_attributes_editable)
+style['QTableView QTableCornerButton::section'].setValues(**common_attributes, **common_attributes_editable)
style.QComboBox.comboboxPopup.setValue(0)
-style["QComboBox QAbstractItemView"].setValues(**common_attributes, **common_attributes_editable)
+style['QComboBox QAbstractItemView'].setValues(**common_attributes, **common_attributes_editable)
style[f'QLabel[frameShape="{int(QFrame.Shape.StyledPanel)}"][frameShadow="{int(QFrame.Shadow.Sunken)}"]'].setValues(
- color="#bbb",
+ color='#bbb',
borderColor=background_color_base.darker(200).name(),
backgroundColor=background_color_base.darker(110).name(),
)
for selector in (
- "QListView QLineEdit",
- "QTreeView QLineEdit",
- "QTableView QLineEdit",
+ 'QListView QLineEdit',
+ 'QTreeView QLineEdit',
+ 'QTableView QLineEdit',
):
- style[selector].padding.setValue("0px")
+ style[selector].padding.setValue('0px')
-if __name__ == "__main__":
- with open(os.path.join(os.path.dirname(__file__), "stylesheet.qss"), "w", encoding="utf-8") as stylesheet:
+if __name__ == '__main__':
+ with open(os.path.join(os.path.dirname(__file__), 'stylesheet.qss'), 'w', encoding='utf-8') as stylesheet:
stylesheet.write(f'/* This file is auto-generated from "{os.path.basename(__file__)}". DO NOT EDIT!!! */\n\n')
stylesheet.write(style.toString(recursive=True))
qt_tool_wrapper(
- "rcc",
+ 'rcc',
[
- "-g",
- "python",
- "--compress",
+ '-g',
+ 'python',
+ '--compress',
str(compressLevel),
- "--compress-algo",
+ '--compress-algo',
compressAlgo,
- "--threshold",
+ '--threshold',
str(compressThreshold),
- "--verbose" if verbose else "",
- os.path.join(os.path.dirname(__file__), "stylesheet.qrc"),
- "-o",
- os.path.join(os.path.dirname(__file__), "__init__.py"),
+ '--verbose' if verbose else '',
+ os.path.join(os.path.dirname(__file__), 'stylesheet.qrc'),
+ '-o',
+ os.path.join(os.path.dirname(__file__), '__init__.py'),
],
True,
)
diff --git a/rare/resources/stylesheets/__init__.py b/rare/resources/stylesheets/__init__.py
index 29183dfb53..83846f6205 100644
--- a/rare/resources/stylesheets/__init__.py
+++ b/rare/resources/stylesheets/__init__.py
@@ -3,4 +3,4 @@
# Incomplete
# import rare.resources.stylesheets.ChildOfMetropolis as ChildOfMetropolis
-__all__ = ["RareStyle"]
+__all__ = ['RareStyle']
diff --git a/rare/shared/__init__.py b/rare/shared/__init__.py
index b31a33d034..5036b4d357 100644
--- a/rare/shared/__init__.py
+++ b/rare/shared/__init__.py
@@ -7,4 +7,4 @@
from .rare_core import RareCore
-__all__ = ["RareCore"]
+__all__ = ['RareCore']
diff --git a/rare/shared/game_process.py b/rare/shared/game_process.py
index f28b3860da..a05905ef36 100644
--- a/rare/shared/game_process.py
+++ b/rare/shared/game_process.py
@@ -1,3 +1,4 @@
+import contextlib
import json
from enum import IntEnum
from logging import getLogger
@@ -9,7 +10,7 @@
from rare.models.launcher import Actions, ErrorModel, FinishedModel, StateChangedModel
-logger = getLogger("GameProcess")
+logger = getLogger('GameProcess')
class GameProcess(QObject):
@@ -33,7 +34,7 @@ def __init__(self, game: Game):
self.timer = QTimer(self)
self.timer.timeout.connect(self.__connect)
self.socket = QLocalSocket()
- self.socket_name = f"rare_{self.game.app_name}"
+ self.socket_name = f'rare_{self.game.app_name}'
self.socket.connected.connect(self.__on_connected)
try:
@@ -43,7 +44,7 @@ def __init__(self, game: Game):
# 100,
# lambda: self._error_occurred(QLocalSocket.UnknownSocketError) if self.socket.error() else None
# )
- logger.warning("Do not handle errors on QLocalSocket, because of an old qt version")
+ logger.warning('Do not handle errors on QLocalSocket, because of an old qt version')
self.socket.readyRead.connect(self.__on_message)
self.socket.disconnected.connect(self.__close)
@@ -53,10 +54,8 @@ def connect_to_server(self, on_startup: bool):
@Slot()
def __close(self):
- try:
+ with contextlib.suppress(RuntimeError):
self.socket.close()
- except RuntimeError:
- pass
@Slot()
def __connect(self):
@@ -66,9 +65,9 @@ def __connect(self):
if self.tried_connections > 50: # 10 seconds
QMessageBox.warning(
None,
- self.tr("Error - {}").format(self.game.app_title),
+ self.tr('Error - {}').format(self.game.app_title),
self.tr(
- "Connection to game launcher failed for {}.\nThis normally means that the game is already running."
+ 'Connection to game launcher failed for {}.\nThis normally means that the game is already running.'
).format(self.game.app_title),
)
self.timer.stop()
@@ -78,47 +77,47 @@ def __connect(self):
@Slot()
def __on_message(self):
message = self.socket.readAll().data()
- if not message.startswith(b"{"):
- logger.error(f"Received unsupported message: {message.decode('utf-8')}")
+ if not message.startswith(b'{'):
+ logger.error(f'Received unsupported message: {message.decode("utf-8")}')
return
try:
data = json.loads(message)
except json.JSONDecodeError as e:
logger.error(e)
- logger.error("Could not load json data")
+ logger.error('Could not load json data')
return
- action = data.get("action", False)
+ action = data.get('action', False)
if not action:
- logger.error("Got unexpected action")
+ logger.error('Got unexpected action')
elif action == Actions.finished:
- logger.info(f"{self.game.app_name} {self.game.app_title} finished")
+ logger.info(f'{self.game.app_name} {self.game.app_title} finished')
model = FinishedModel.from_json(data)
self.socket.close()
self.__game_finished(model.exit_code)
elif action == Actions.error:
model = ErrorModel.from_json(data)
- logger.error(f"Error in game {self.game.app_title}: {model.error_string}")
+ logger.error(f'Error in game {self.game.app_title}: {model.error_string}')
self.socket.close()
self.__game_finished(GameProcess.Code.ERROR)
QMessageBox.warning(
None,
- "Error",
- self.tr("Error in game {}:\n{}").format(self.game.app_title, model.error_string),
+ 'Error',
+ self.tr('Error in game {}:\n{}').format(self.game.app_title, model.error_string),
)
elif action == Actions.state_update:
model = StateChangedModel.from_json(data)
if model.new_state == StateChangedModel.States.started:
- logger.info("Launched Game")
+ logger.info('Launched Game')
self.launched.emit(GameProcess.Code.SUCCESS)
@Slot()
def __on_connected(self):
self.timer.stop()
- logger.info(f"Connection established for {self.game.app_name} ({self.game.app_title})")
+ logger.info(f'Connection established for {self.game.app_name} ({self.game.app_title})')
if self.on_startup:
- logger.info(f"Found {self.game.app_name} ({self.game.app_title}) running at startup")
+ logger.info(f'Found {self.game.app_name} ({self.game.app_title}) running at startup')
self.launched.emit(GameProcess.Code.ON_STARTUP)
@Slot(QLocalSocket.LocalSocketError)
@@ -128,7 +127,7 @@ def __on_error(self, _: QLocalSocket.LocalSocketError):
self.__close()
self.__game_finished(GameProcess.Code.ON_STARTUP) # 1234 is exit code for startup
else:
- logger.error(f"{self.game.app_name} ({self.game.app_title}): {self.socket.errorString()}")
+ logger.error(f'{self.game.app_name} ({self.game.app_title}): {self.socket.errorString()}')
def __game_finished(self, exit_code: int):
self.finished.emit(exit_code)
diff --git a/rare/shared/image_manager.py b/rare/shared/image_manager.py
index f8718dc1f2..b2a0a9e87e 100644
--- a/rare/shared/image_manager.py
+++ b/rare/shared/image_manager.py
@@ -3,10 +3,11 @@
import pickle
import threading
import zlib
+from collections.abc import Callable
from logging import getLogger
from multiprocessing import cpu_count
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union
+from typing import TYPE_CHECKING, Any
import requests
from legendary.lfs.eos import EOSOverlayApp
@@ -78,22 +79,22 @@ def __init__(self, signals: GlobalSignals, core: LegendaryCore):
# lk: the ordering in _img_types matters for the order of fallbacks
# {'AndroidIcon', 'DieselGameBox', 'DieselGameBoxLogo', 'DieselGameBoxTall', 'DieselGameBoxWide',
# 'ESRB', 'Featured', 'OfferImageTall', 'OfferImageWide', 'Screenshot', 'Thumbnail'}
- self._img_tall_types: Tuple = (
- "DieselGameBoxTall",
- "OfferImageTall",
- "Thumbnail",
+ self._img_tall_types: tuple = (
+ 'DieselGameBoxTall',
+ 'OfferImageTall',
+ 'Thumbnail',
)
- self._img_wide_types: Tuple = (
- "DieselGameBoxWide",
- "DieselGameBox",
- "OfferImageWide",
- "Screenshot",
+ self._img_wide_types: tuple = (
+ 'DieselGameBoxWide',
+ 'DieselGameBox',
+ 'OfferImageWide',
+ 'Screenshot',
)
- self._img_logo_types: Tuple = ("DieselGameBoxLogo",)
- self._img_types: Tuple = self._img_tall_types + self._img_wide_types + self._img_logo_types
+ self._img_logo_types: tuple = ('DieselGameBoxLogo',)
+ self._img_types: tuple = self._img_tall_types + self._img_wide_types + self._img_logo_types
self._dl_retries = 1
self._worker_lock = threading.Lock()
- self._worker_app_names: Set[str] = set()
+ self._worker_app_names: set[str] = set()
super(ImageManager, self).__init__()
self.signals = signals
self.core = core
@@ -101,21 +102,21 @@ def __init__(self, signals: GlobalSignals, core: LegendaryCore):
self.image_dir: Path = image_dir()
if not self.image_dir.is_dir():
self.image_dir.mkdir()
- self.logger.info("Created image directory at %s", self.image_dir)
+ self.logger.info('Created image directory at %s', self.image_dir)
self.threadpool = QThreadPool(self)
self.threadpool.setMaxThreadCount(min(cpu_count() * 2, 16))
@staticmethod
def _img_json(app_name: str) -> Path:
- return image_dir_game(app_name).joinpath("image.json")
+ return image_dir_game(app_name).joinpath('image.json')
@staticmethod
def _img_cache(app_name: str) -> Path:
- return image_dir_game(app_name).joinpath("image.cache")
+ return image_dir_game(app_name).joinpath('image.cache')
@staticmethod
- def _img_all(app_name: str) -> Tuple:
+ def _img_all(app_name: str) -> tuple:
return (
image_tall_path(app_name),
image_tall_path(app_name, color=False),
@@ -129,7 +130,7 @@ def _img_all(app_name: str) -> Tuple:
def has_pixmaps(self, app_name: str) -> bool:
return all(file.is_file() for file in self._img_all(app_name))
- def _prepare_download(self, game: Game, force: bool = False) -> Tuple[List, Dict]:
+ def _prepare_download(self, game: Game, force: bool = False) -> tuple[list, dict]:
if force and image_dir_game(game.app_name).exists():
for file in self._img_all(game.app_name):
file.unlink(missing_ok=True)
@@ -138,16 +139,17 @@ def _prepare_download(self, game: Game, force: bool = False) -> Tuple[List, Dict
# Load image checksums
if not self._img_json(game.app_name).is_file():
- json_data: Dict = dict(zip(self._img_types, [None] * len(self._img_types)))
- json_data["version"] = self._cache_version
+ json_data: dict = dict(zip(self._img_types, [None] * len(self._img_types), strict=False))
+ json_data['version'] = self._cache_version
else:
- json_data = json.load(open(self._img_json(game.app_name), "r"))
+ with self._img_json(game.app_name).open() as fd:
+ json_data = json.load(fd)
# Only download the best matching candidate for each image category
- def best_match(key_images: List, image_types: Tuple) -> Dict:
+ def best_match(key_images: list, image_types: tuple) -> dict:
matches = sorted(
- filter(lambda image: image["type"] in image_types, key_images),
- key=lambda x: image_types.index(x["type"]) if x["type"] in image_types else len(image_types),
+ filter(lambda image: image['type'] in image_types, key_images),
+ key=lambda x: image_types.index(x['type']) if x['type'] in image_types else len(image_types),
reverse=False,
)
try:
@@ -159,9 +161,9 @@ def best_match(key_images: List, image_types: Tuple) -> Dict:
candidates = tuple(
image
for image in [
- best_match(game.metadata.get("keyImages", []), self._img_tall_types),
- best_match(game.metadata.get("keyImages", []), self._img_wide_types),
- best_match(game.metadata.get("keyImages", []), self._img_logo_types),
+ best_match(game.metadata.get('keyImages', []), self._img_tall_types),
+ best_match(game.metadata.get('keyImages', []), self._img_wide_types),
+ best_match(game.metadata.get('keyImages', []), self._img_logo_types),
]
if bool(image)
)
@@ -171,40 +173,40 @@ def best_match(key_images: List, image_types: Tuple) -> Dict:
# lk: so everything below it is skipped
# TODO: Move this into the thread, maybe, concurrency could help here too
updates = []
- if (not self.has_pixmaps(game.app_name)):
+ if not self.has_pixmaps(game.app_name):
if not candidates:
- cover = "epic.png" if game.app_name == EOSOverlayApp.app_name else "cover.png"
+ cover = 'epic.png' if game.app_name == EOSOverlayApp.app_name else 'cover.png'
# lk: fast path for games without images, convert Rare's logo
- cache_data: Dict = dict(zip(self._img_types, [None] * len(self._img_types)))
- with open(resources_path.joinpath("images", cover), "rb") as fd:
- cache_data["DieselGameBoxTall"] = fd.read()
- with open(resources_path.joinpath("images", cover), "rb") as fd:
- cache_data["DieselGameBoxWide"] = fd.read()
+ cache_data: dict = dict(zip(self._img_types, [None] * len(self._img_types), strict=False))
+ with open(resources_path.joinpath('images', cover), 'rb') as fd:
+ cache_data['DieselGameBoxTall'] = fd.read()
+ with open(resources_path.joinpath('images', cover), 'rb') as fd:
+ cache_data['DieselGameBoxWide'] = fd.read()
# cache_data["DieselGameBoxLogo"] = open(
# resources_path.joinpath("images", "logo.png"), "rb").read()
self._convert(game, cache_data)
- json_data["cache"] = None
- json_data["scale"] = ImageSize.Tall.pixel_ratio
- json_data["size"] = {
- "w": ImageSize.Tall.size.width(),
- "h": ImageSize.Tall.size.height(),
+ json_data['cache'] = None
+ json_data['scale'] = ImageSize.Tall.pixel_ratio
+ json_data['size'] = {
+ 'w': ImageSize.Tall.size.width(),
+ 'h': ImageSize.Tall.size.height(),
}
- with open(self._img_json(game.app_name), "w", encoding="utf-8") as file:
+ with open(self._img_json(game.app_name), 'w', encoding='utf-8') as file:
json.dump(json_data, file)
else:
- updates = [image for image in candidates if image["type"] in self._img_types]
+ updates = [image for image in candidates if image['type'] in self._img_types]
else:
for image in candidates:
- if image["type"] in self._img_types:
- if image["type"] not in json_data.keys() or json_data[image["type"]] != image["md5"]:
+ if image['type'] in self._img_types:
+ if image['type'] not in json_data or json_data[image['type']] != image['md5']:
updates.append(image)
return updates, json_data
- def _download(self, updates: List, json_data: Dict, game: Game) -> bool:
+ def _download(self, updates: list, json_data: dict, game: Game) -> bool:
# Decompress existing image.cache
if not self._img_cache(game.app_name).is_file():
- cache_data: Dict[str, Any] = dict(zip(self._img_types, [None] * len(self._img_types)))
+ cache_data: dict[str, Any] = dict(zip(self._img_types, [None] * len(self._img_types), strict=False))
else:
cache_data = self._decompress(game)
@@ -213,38 +215,38 @@ def _download(self, updates: List, json_data: Dict, game: Game) -> bool:
downloads = [
image
for image in updates
- if (cache_data.get(image["type"], None) is None or json_data[image["type"]] != image["md5"])
+ if (cache_data.get(image['type'], None) is None or json_data[image['type']] != image['md5'])
]
for image in downloads:
self.logger.debug(
- "Downloading %s for %s (%s)",
- image["type"],
+ 'Downloading %s for %s (%s)',
+ image['type'],
game.app_name,
game.app_title,
)
- json_data[image["type"]] = image["md5"]
- if image["type"] in self._img_tall_types:
+ json_data[image['type']] = image['md5']
+ if image['type'] in self._img_tall_types:
payload = {
- "resize": 1,
- "w": ImageSize.Tall.size.width(),
- "h": ImageSize.Tall.size.height(),
+ 'resize': 1,
+ 'w': ImageSize.Tall.size.width(),
+ 'h': ImageSize.Tall.size.height(),
}
- elif image["type"] in self._img_wide_types:
+ elif image['type'] in self._img_wide_types:
payload = {
- "resize": 1,
- "w": ImageSize.Wide.size.width(),
- "h": ImageSize.Wide.size.height(),
+ 'resize': 1,
+ 'w': ImageSize.Wide.size.width(),
+ 'h': ImageSize.Wide.size.height(),
}
else:
# Set the larger of the sizes for everything else
payload = {
- "resize": 1,
- "w": ImageSize.Wide.size.width(),
- "h": ImageSize.Wide.size.height(),
+ 'resize': 1,
+ 'w': ImageSize.Wide.size.width(),
+ 'h': ImageSize.Wide.size.height(),
}
try:
- cache_data[image["type"]] = requests.get(image["url"], params=payload, timeout=10).content
+ cache_data[image['type']] = requests.get(image['url'], params=payload, timeout=10).content
except Exception as e:
self.logger.error(e)
return False
@@ -252,14 +254,14 @@ def _download(self, updates: List, json_data: Dict, game: Game) -> bool:
# lk: test the cached and downloaded data if they describe an image with valid dimensions
# I do not like this, it should add a bunch of processing for something simple but I am out of ideas
for image in updates:
- image_data = QImage().fromData(cache_data[image["type"]])
+ image_data = QImage().fromData(cache_data[image['type']])
if not (image_data.width() and image_data.height()):
- with open(resources_path.joinpath("images", "cover.png"), "rb") as fd:
- cache_data[image["type"]] = fd.read()
- json_data[image["type"]] = None
+ with open(resources_path.joinpath('images', 'cover.png'), 'rb') as fd:
+ cache_data[image['type']] = fd.read()
+ json_data[image['type']] = None
self.logger.error(
- "Invalid image %s data for %s (%s)",
- image["type"],
+ 'Invalid image %s data for %s (%s)',
+ image['type'],
game.app_name,
game.app_title,
)
@@ -273,25 +275,25 @@ def _download(self, updates: List, json_data: Dict, game: Game) -> bool:
# hash image cache
try:
- with open(self._img_cache(game.app_name), "rb") as archive:
+ with open(self._img_cache(game.app_name), 'rb') as archive:
archive_hash = hashlib.md5(archive.read()).hexdigest()
except FileNotFoundError:
archive_hash = None
- json_data["cache"] = archive_hash
- json_data["scale"] = ImageSize.Tall.pixel_ratio
- json_data["size"] = {
- "w": ImageSize.Tall.size.width(),
- "h": ImageSize.Tall.size.height(),
+ json_data['cache'] = archive_hash
+ json_data['scale'] = ImageSize.Tall.pixel_ratio
+ json_data['size'] = {
+ 'w': ImageSize.Tall.size.width(),
+ 'h': ImageSize.Tall.size.height(),
}
# write image.json
- with open(self._img_json(game.app_name), "w", encoding="utf-8") as file:
+ with open(self._img_json(game.app_name), 'w', encoding='utf-8') as file:
json.dump(json_data, file)
return bool(updates)
- _icon_overlay: Optional[QPainterPath] = None
+ _icon_overlay: QPainterPath | None = None
def _generate_icon_overlay(self, rect: QRect) -> QPainterPath:
if self._icon_overlay is not None:
@@ -376,21 +378,21 @@ def _save_image(self, image: QImage, color_path: Path, gray_path: Path):
# image = image.convertToFormat(QImage.Format_Indexed8)
# add the alpha channel back to the cover
image = image.convertToFormat(QImage.Format.Format_ARGB32_Premultiplied)
- image.save(color_path.as_posix(), format="PNG")
+ image.save(color_path.as_posix(), format='PNG')
# quick way to convert to grayscale, but keep the alpha channel
alpha = image.convertToFormat(QImage.Format.Format_Alpha8)
image = image.convertToFormat(QImage.Format.Format_Grayscale8)
# add the alpha channel back to the grayscale cover
image = image.convertToFormat(QImage.Format.Format_ARGB32_Premultiplied)
image.setAlphaChannel(alpha)
- image.save(gray_path.as_posix(), format="PNG")
+ image.save(gray_path.as_posix(), format='PNG')
def _convert(self, game, images, force=False) -> None:
for file in self._img_all(game.app_name):
if force and file.exists():
file.unlink(missing_ok=True)
- def find_image_data(image_types: Tuple):
+ def find_image_data(image_types: tuple):
data = None
for image_type in image_types:
if images.get(image_type, None) is not None:
@@ -402,7 +404,7 @@ def find_image_data(image_types: Tuple):
wide_data = find_image_data(self._img_wide_types)
logo_data = find_image_data(self._img_logo_types)
- icon_source = "wide" if tall_data is None else "tall"
+ icon_source = 'wide' if tall_data is None else 'tall'
if tall_data is None and wide_data is not None:
tall_data = wide_data
@@ -424,7 +426,7 @@ def find_image_data(image_types: Tuple):
image_wide_path(game.app_name, color=False),
)
- icon = self._convert_icon(tall if icon_source == "tall" else wide)
+ icon = self._convert_icon(tall if icon_source == 'tall' else wide)
self._save_image(
icon,
image_icon_path(game.app_name),
@@ -435,19 +437,19 @@ def find_image_data(image_types: Tuple):
format=desktop_icon_suffix().upper(),
)
- def _compress(self, game: Game, data: Dict) -> None:
- archive = open(self._img_cache(game.app_name), "wb")
+ def _compress(self, game: Game, data: dict) -> None:
+ archive = open(self._img_cache(game.app_name), 'wb') # noqa: SIM115
cdata = zlib.compress(pickle.dumps(data), level=-1)
archive.write(cdata)
archive.close()
- def _decompress(self, game: Game) -> Dict:
- archive = open(self._img_cache(game.app_name), "rb")
+ def _decompress(self, game: Game) -> dict:
+ archive = open(self._img_cache(game.app_name), 'rb') # noqa: SIM115
try:
data = zlib.decompress(archive.read())
data = pickle.loads(data)
except zlib.error:
- data = dict(zip(self._img_types, [None] * len(self._img_types)))
+ data = dict(zip(self._img_types, [None] * len(self._img_types), strict=False))
finally:
archive.close()
return data
@@ -471,7 +473,7 @@ def _download_image(self, game, force: bool):
updates, json_data = self._prepare_download(game, force)
if updates:
self._download(updates, json_data, game)
- self.logger.debug("Emitting singal for %s (%s)", game.app_name, game.app_title)
+ self.logger.debug('Emitting singal for %s (%s)', game.app_name, game.app_title)
def download_image(self, game: Game, load_callback: Callable[[], None], priority: int, force: bool = False) -> None:
if game.app_name in self._worker_app_names:
@@ -499,11 +501,11 @@ def download_image_blocking(self, game: Game, load_callback: Callable[[], None],
@staticmethod
def _get_cover(
- container: Union[Type[QPixmap], Type[QImage]],
+ container: type[QPixmap] | type[QImage],
app_name: str,
preset: ImageSize.Preset,
color: bool,
- ) -> Union[QPixmap, QImage]:
+ ) -> QPixmap | QImage:
ret = container()
if preset.orientation == ImageType.Icon:
if image_icon_path(app_name, color).is_file():
@@ -515,7 +517,7 @@ def _get_cover(
if image_wide_path(app_name, color).is_file():
ret.load(image_wide_path(app_name, color).as_posix())
else:
- raise RuntimeError("Unknown image preset")
+ raise RuntimeError('Unknown image preset')
if not ret.isNull():
device = ImageSize.Preset(
divisor=preset.base.divisor,
diff --git a/rare/shared/rare_core.py b/rare/shared/rare_core.py
index 0456029cf3..0e7307ffa8 100644
--- a/rare/shared/rare_core.py
+++ b/rare/shared/rare_core.py
@@ -2,9 +2,10 @@
import os
import time
from argparse import Namespace
+from collections.abc import Callable, Iterable, Iterator
from itertools import chain
from logging import getLogger
-from typing import Callable, Dict, Iterable, Iterator, List, Optional, Set, Tuple, Union
+from typing import Optional
from legendary.lfs.eos import EOSOverlayApp
from legendary.models.game import Game, SaveGameFile
@@ -30,7 +31,7 @@
QueueWorker,
VerifyWorker,
)
-from .workers.fetch import SteamAppIdsWorker
+from .workers.fetch import RuntimeAssetsWorker
from .workers.uninstall import uninstall_game
from .workers.worker import QueueWorkerInfo, QueueWorkerState
from .wrappers import Wrappers
@@ -45,19 +46,19 @@ class RareCore(QObject):
# completed_entitlements = Signal()
# lk: special case class attribute, this has to be here
- __instance: Optional["RareCore"] = None
+ __instance: Optional['RareCore'] = None
def __init__(self, settings: RareAppSettings, args: Namespace):
if self.__instance is not None:
- raise RuntimeError("RareCore already initialized")
+ raise RuntimeError('RareCore already initialized')
super(RareCore, self).__init__()
self.logger = getLogger(type(self).__name__)
self.__settings = settings
- self.__args: Optional[Namespace] = None
- self.__signals: Optional[GlobalSignals] = None
- self.__core: Optional[LegendaryCore] = None
- self.__image_manager: Optional[ImageManager] = None
- self.__wrappers: Optional[Wrappers] = None
+ self.__args: Namespace | None = None
+ self.__signals: GlobalSignals | None = None
+ self.__core: LegendaryCore | None = None
+ self.__image_manager: ImageManager | None = None
+ self.__wrappers: Wrappers | None = None
self.__start_time = time.perf_counter()
@@ -68,14 +69,14 @@ def __init__(self, settings: RareAppSettings, args: Namespace):
self.image_manager(init=True)
self.__wrappers = Wrappers()
- self.workers_disk: List[QueueWorker] = []
+ self.workers_disk: list[QueueWorker] = []
self.threadpool_disk = QThreadPool()
self.threadpool_disk.setMaxThreadCount(2)
- self.workers_net: List[QueueWorker] = []
+ self.workers_net: list[QueueWorker] = []
self.threadpool_net = QThreadPool()
self.threadpool_net.setMaxThreadCount(2)
- self.__library: Dict[str, RareGame] = {}
+ self.__library: dict[str, RareGame] = {}
self.__eos_overlay = RareEosOverlay(self.__settings, self.__core, self.__image_manager, EOSOverlayApp)
self.__eos_overlay.signals.game.install.connect(self.__signals.game.install)
self.__eos_overlay.signals.game.uninstall.connect(self.__signals.game.uninstall)
@@ -103,7 +104,7 @@ def enqueue_worker(self, rgame: RareGame, worker: QueueWorker):
self.workers_disk.append(worker)
self.threadpool_disk.start(worker, priority=0)
else:
- raise RuntimeError(f"Cannot enqueue unkown worker type {type(worker).__name__}")
+ raise RuntimeError(f'Cannot enqueue unkown worker type {type(worker).__name__}')
self.__signals.application.update_statusbar.emit()
def _on_worker_finished(self, worker: QueueWorker):
@@ -135,98 +136,98 @@ def queue_info(self) -> Iterable[QueueWorkerInfo]:
yield w.worker_info()
@staticmethod
- def instance() -> "RareCore":
- raise RuntimeError("RareCore.instance() method is deprecated")
+ def instance() -> 'RareCore':
+ raise RuntimeError('RareCore.instance() method is deprecated')
def signals(self, init: bool = False) -> GlobalSignals:
if self.__signals is None and not init:
- raise RuntimeError("Uninitialized use of GlobalSignalsSingleton")
+ raise RuntimeError('Uninitialized use of GlobalSignalsSingleton')
if self.__signals is not None and init:
- raise RuntimeError("GlobalSignals already initialized")
+ raise RuntimeError('GlobalSignals already initialized')
if init:
self.__signals = GlobalSignals()
return self.__signals
- def args(self, args: Namespace = None) -> Optional[Namespace]:
+ def args(self, args: Namespace = None) -> Namespace | None:
if self.__args is None and args is None:
- raise RuntimeError("Uninitialized use of ArgumentsSingleton")
+ raise RuntimeError('Uninitialized use of ArgumentsSingleton')
if self.__args is not None and args is not None:
- raise RuntimeError("Arguments already initialized")
+ 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")
+ raise RuntimeError('Uninitialized use of LegendaryCoreSingleton')
if self.__core is not None and init:
- raise RuntimeError("LegendaryCore already initialized")
+ raise RuntimeError('LegendaryCore already initialized')
if init:
try:
self.__core = LegendaryCore()
except configparser.MissingSectionHeaderError as e:
- self.logger.warning("Config is corrupt: %s", e)
- if config_path := os.environ.get("LEGENDARY_CONFIG_PATH"):
+ self.logger.warning('Config is corrupt: %s', e)
+ if config_path := os.environ.get('LEGENDARY_CONFIG_PATH'):
path = config_path
- elif config_path := os.environ.get("XDG_CONFIG_HOME"):
- path = os.path.join(config_path, "legendary")
+ elif config_path := os.environ.get('XDG_CONFIG_HOME'):
+ path = os.path.join(config_path, 'legendary')
else:
- path = os.path.expanduser("~/.config/legendary")
- self.logger.info("Creating config in path: %s", config_path)
- with open(os.path.join(path, "config.ini"), "w", encoding="utf-8") as config_file:
- config_file.write("[Legendary]")
+ path = os.path.expanduser('~/.config/legendary')
+ self.logger.info('Creating config in path: %s', config_path)
+ with open(os.path.join(path, 'config.ini'), 'w', encoding='utf-8') as config_file:
+ config_file.write('[Legendary]')
self.__core = LegendaryCore()
- self.__core.egs._store_gql_host = "launcher.store.epicgames.com"
+ self.__core.egs._store_gql_host = 'launcher.store.epicgames.com'
# Initialize sections if they don't exist
- for section in ["Legendary", "default", "default.env"]:
+ for section in ['Legendary', 'default', 'default.env']:
if section not in self.__core.lgd.config.sections():
self.__core.lgd.config.add_section(section)
# Set some platform defaults if unset
- def check_config(option: str, accepted: Set = None) -> bool:
- _exists = self.__core.lgd.config.has_option("Legendary", option)
- _value = self.__core.lgd.config.get("Legendary", option, fallback="")
+ def check_config(option: str, accepted: set = None) -> bool:
+ _exists = self.__core.lgd.config.has_option('Legendary', option)
+ _value = self.__core.lgd.config.get('Legendary', option, fallback='')
_accepted = _value in accepted if accepted is not None else True
return _exists and bool(_value) and _accepted
- if not check_config("default_platform", {"Windows", "Win32", "Mac"}):
- self.__core.lgd.config.set("Legendary", "default_platform", self.__core.default_platform)
- if not check_config("install_dir"):
- self.__core.lgd.config.set("Legendary", "install_dir", self.__core.get_default_install_dir())
- if not check_config("mac_install_dir"):
+ if not check_config('default_platform', {'Windows', 'Win32', 'Mac'}):
+ self.__core.lgd.config.set('Legendary', 'default_platform', self.__core.default_platform)
+ if not check_config('install_dir'):
+ self.__core.lgd.config.set('Legendary', 'install_dir', self.__core.get_default_install_dir())
+ if not check_config('mac_install_dir'):
self.__core.lgd.config.set(
- "Legendary",
- "mac_install_dir",
+ 'Legendary',
+ 'mac_install_dir',
self.__core.get_default_install_dir(self.__core.default_platform),
)
# Always set these options
# Avoid implicitly falling back to Windows games on macOS
- self.__core.lgd.config.set("Legendary", "install_platform_fallback", str(False))
+ self.__core.lgd.config.set('Legendary', 'install_platform_fallback', str(False))
# Force-disable automatic use of crossover on macOS (remove this when we support crossover)
- self.__core.lgd.config.set("Legendary", "disable_auto_crossover", str(True))
+ self.__core.lgd.config.set('Legendary', 'disable_auto_crossover', str(True))
# Force-disable automatic sync with EGL, it seems to have issues
- self.__core.lgd.config.set("Legendary", "egl_sync", str(False))
+ self.__core.lgd.config.set('Legendary', 'egl_sync', str(False))
# 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.config.remove_option('Legendary', 'egl_sync')
else:
if not os.path.exists(self.__core.egl.programdata_path):
- self.__core.lgd.config.remove_option("Legendary", "egl_sync")
+ 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")
+ raise RuntimeError('Uninitialized use of ImageManagerSingleton')
if self.__image_manager is not None and init:
- raise RuntimeError("ImageManager already initialized")
+ raise RuntimeError('ImageManager already initialized')
if self.__image_manager is None:
self.__image_manager = ImageManager(self.signals(), self.core())
return self.__image_manager
@@ -270,26 +271,26 @@ def __validate_install(self, rgame: RareGame):
dlc.igame = None
self.logger.info(f'Removing "{rgame.app_title}" because "{rgame.igame.install_path}" does not exist...')
uninstall_game(self.__core, rgame, self.logger, keep_files=True, keep_config=True)
- self.logger.info(f"Uninstalled {rgame.app_title}, because no game files exist")
+ self.logger.info(f'Uninstalled {rgame.app_title}, because no game files exist')
rgame.igame = None
return
# lk: games that don't have an override and can't find their executable due to case sensitivity
# lk: will still erroneously require verification. This might need to be removed completely
# lk: or be decoupled from the verification requirement
- if override_exe := self.__core.lgd.config.get(rgame.app_name, "override_exe", fallback=""):
+ if override_exe := self.__core.lgd.config.get(rgame.app_name, 'override_exe', fallback=''):
igame_executable = override_exe
else:
igame_executable = rgame.igame.executable
# lk: Case-insensitive search for the game's executable (example: Brothers - A Tale of two Sons)
- executable_path = os.path.join(rgame.igame.install_path, igame_executable.replace("\\", "/").lstrip("/"))
+ executable_path = os.path.join(rgame.igame.install_path, igame_executable.replace('\\', '/').lstrip('/'))
file_list = map(str.lower, os.listdir(os.path.dirname(executable_path)))
if os.path.basename(executable_path).lower() not in file_list:
rgame.igame.needs_verification = True
self.__core.lgd.set_installed_game(rgame.app_name, rgame.igame)
rgame.update_igame()
- self.logger.info(f"{rgame.app_title} needs verification")
+ self.logger.info(f'{rgame.app_title} needs verification')
- def get_game(self, app_name: str) -> Union[RareEosOverlay, RareGame]:
+ def get_game(self, app_name: str) -> RareEosOverlay | RareGame:
if app_name == EOSOverlayApp.app_name:
return self.__eos_overlay
return self.__library[app_name]
@@ -320,15 +321,15 @@ def __filter_games(self, condition: Callable[[RareGame], bool]) -> Iterator[Rare
def __create_or_update_rgame(self, game: Game) -> RareGame:
if rgame := self.__library.get(game.app_name, False):
- self.logger.warning(f"{rgame.app_name} already present in {type(self).__name__}")
- self.logger.info(f"Updating Game for {rgame.app_name}")
+ self.logger.warning(f'{rgame.app_name} already present in {type(self).__name__}')
+ self.logger.info(f'Updating Game for {rgame.app_name}')
rgame.update_rgame()
else:
rgame = RareGame(self.__settings, self.__core, self.__image_manager, game)
self.__add_game(rgame)
return rgame
- def __add_games_and_dlcs(self, games: List[Game], dlcs_dict: Dict[str, List]) -> None:
+ def __add_games_and_dlcs(self, games: list[Game], dlcs_dict: dict[str, list]) -> None:
length = len(games)
for idx, game in enumerate(games):
rgame = self.__create_or_update_rgame(game)
@@ -347,8 +348,8 @@ def __add_games_and_dlcs(self, games: List[Game], dlcs_dict: Dict[str, List]) ->
self.logger.info(f'Marking "{rgame.app_title}" as not installed because an exception has occurred...')
self.logger.error(e)
rgame.set_installed(False)
- progress = int(idx / length * self.__fetch_progress) + (100 - self.__fetch_progress)
- self.progress.emit(progress, self.tr("Loaded {}").format(rgame.app_title))
+ progress = int((idx / length) * (100 - self.__fetch_progress)) + self.__fetch_progress
+ self.progress.emit(progress, self.tr('Loaded {}').format(rgame.app_title))
@Slot(int, str)
def __on_fetch_progress(self, increment: int, message: str):
@@ -356,7 +357,7 @@ def __on_fetch_progress(self, increment: int, message: str):
self.progress.emit(self.__fetch_progress, message)
@Slot(object, int)
- def __on_fetch_result(self, result: Tuple, result_type: int):
+ def __on_fetch_result(self, result: tuple, result_type: int):
if result_type == FetchWorker.Result.GAMESDLCS:
self.__add_games_and_dlcs(*result)
self.__fetched_games_dlcs = True
@@ -365,10 +366,10 @@ def __on_fetch_result(self, result: Tuple, result_type: int):
self.__core.lgd.entitlements = result
self.__fetched_entitlements = True
- if result_type == FetchWorker.Result.STEAMAPPIDS:
+ if result_type == FetchWorker.Result.EXTRAS:
self.__fetched_steamappids = True
- self.logger.info("Acquired data from %s worker", FetchWorker.Result(result_type).name)
+ self.logger.info('Acquired data from %s worker', FetchWorker.Result(result_type).name)
# Return early if there are still things to fetch
if not all(
@@ -380,54 +381,54 @@ def __on_fetch_result(self, result: Tuple, result_type: int):
):
return
- self.logger.debug("Fetch time %s seconds", time.perf_counter() - self.__start_time)
+ self.logger.debug('Fetch time %s seconds', time.perf_counter() - self.__start_time)
self.__wrappers.import_wrappers(self.__settings, self.__core, [rgame.app_name for rgame in self.games])
# Look for Rare shortcuts in Steam
steam_shortcuts.load_steam_shortcuts()
- self.progress.emit(100, self.tr("Launching Rare"))
+ self.progress.emit(100, self.tr('Launching Rare'))
self.completed.emit()
QTimer.singleShot(100, self.__post_init)
def fetch(self):
self.__start_time = time.perf_counter()
- games_dlcs_worker = GamesDlcsWorker(self.__settings, self.__core, self.__args)
+ games_dlcs_worker = GamesDlcsWorker(self.__settings, self.__core, self.__args, segment=40)
games_dlcs_worker.signals.progress.connect(self.__on_fetch_progress)
games_dlcs_worker.signals.result.connect(self.__on_fetch_result)
- entitlements_worker = EntitlementsWorker(self.__settings, self.__core, self.__args)
+ entitlements_worker = EntitlementsWorker(self.__settings, self.__core, self.__args, segment=5)
entitlements_worker.signals.progress.connect(self.__on_fetch_progress)
entitlements_worker.signals.result.connect(self.__on_fetch_result)
- steamappids_worker = SteamAppIdsWorker(self.__settings, self.__core, self.__args)
- steamappids_worker.signals.progress.connect(self.__on_fetch_progress)
- steamappids_worker.signals.result.connect(self.__on_fetch_result)
+ runtime_assets_worker = RuntimeAssetsWorker(self.__settings, self.__core, self.__args, segment=10)
+ runtime_assets_worker.signals.progress.connect(self.__on_fetch_progress)
+ runtime_assets_worker.signals.result.connect(self.__on_fetch_result)
QThreadPool.globalInstance().start(games_dlcs_worker)
QThreadPool.globalInstance().start(entitlements_worker)
- QThreadPool.globalInstance().start(steamappids_worker)
+ QThreadPool.globalInstance().start(runtime_assets_worker)
def __fetch_saves(self) -> None:
- saves_dict: Dict[str, List[SaveGameFile]] = {}
+ saves_dict: dict[str, list[SaveGameFile]] = {}
try:
- with timelogger(self.logger, "Request saves"):
+ with timelogger(self.logger, 'Request saves'):
saves_list = self.__core.get_save_games()
for s in saves_list:
- if s.app_name not in saves_dict.keys():
+ if s.app_name not in saves_dict:
saves_dict[s.app_name] = [s]
else:
saves_dict[s.app_name].append(s)
for app_name, saves in saves_dict.items():
- if app_name not in self.__library.keys():
+ if app_name not in self.__library:
continue
self.__library[app_name].load_saves(saves)
except (HTTPError, ConnectionError) as e:
- self.logger.error("Exception while fetching saves from EGS.")
+ self.logger.error('Exception while fetching saves from EGS.')
self.logger.error(e)
return
- self.logger.info(f"Saves: {len(saves_dict)}")
+ self.logger.info(f'Saves: {len(saves_dict)}')
def fetch_saves(self):
saves_worker = QRunnable.create(self.__fetch_saves)
@@ -443,8 +444,8 @@ def __post_init(self) -> None:
self.resolve_origin()
@property
- def game_tags(self) -> Tuple[str, ...]:
- default_tags = ("favorite", "backlog", "completed", "hidden")
+ def game_tags(self) -> tuple[str, ...]:
+ default_tags = ('favorite', 'backlog', 'completed', 'hidden')
custom_tags = set()
for rgame in self.games:
custom_tags.update(rgame.tags)
@@ -478,7 +479,7 @@ def game_list(self) -> Iterator[Game]:
yield game.game
@property
- def dlcs(self) -> Dict[str, set[RareGame]]:
+ def dlcs(self) -> dict[str, set[RareGame]]:
"""!
RareGames that ARE DLCs themselves
"""
diff --git a/rare/shared/workers/__init__.py b/rare/shared/workers/__init__.py
index b4e725abaf..92382c4156 100644
--- a/rare/shared/workers/__init__.py
+++ b/rare/shared/workers/__init__.py
@@ -8,16 +8,16 @@
from .worker import QueueWorker, Worker
__all__ = [
- "CloudSyncWorker",
- "EntitlementsWorker",
- "FetchWorker",
- "GamesDlcsWorker",
- "InstallInfoWorker",
- "MoveInfoWorker",
- "MoveWorker",
- "OriginWineWorker",
- "QueueWorker",
- "UninstallWorker",
- "VerifyWorker",
- "Worker",
+ 'CloudSyncWorker',
+ 'EntitlementsWorker',
+ 'FetchWorker',
+ 'GamesDlcsWorker',
+ 'InstallInfoWorker',
+ 'MoveInfoWorker',
+ 'MoveWorker',
+ 'OriginWineWorker',
+ 'QueueWorker',
+ 'UninstallWorker',
+ 'VerifyWorker',
+ 'Worker',
]
diff --git a/rare/shared/workers/cloud_sync.py b/rare/shared/workers/cloud_sync.py
index c9ad9b8934..89113b5390 100644
--- a/rare/shared/workers/cloud_sync.py
+++ b/rare/shared/workers/cloud_sync.py
@@ -33,7 +33,7 @@ def worker_info(self) -> QueueWorkerInfo:
app_name=self.rgame.app_name,
app_title=self.rgame.app_title,
type=type(self).__name__,
- prefix="Syncing",
+ prefix='Syncing',
state=self.state,
)
diff --git a/rare/shared/workers/fetch.py b/rare/shared/workers/fetch.py
index 4c2dad2adc..2ff332a3af 100644
--- a/rare/shared/workers/fetch.py
+++ b/rare/shared/workers/fetch.py
@@ -1,5 +1,7 @@
+import os
import platform
from argparse import Namespace
+from datetime import datetime
from enum import IntEnum
from PySide6.QtCore import QObject, Signal
@@ -8,7 +10,9 @@
from rare.lgndr.core import LegendaryCore
from rare.models.settings import RareAppSettings, app_settings
from rare.utils.metrics import timelogger
-from rare.utils.steam_grades import SteamGrades
+from rare.utils.steam_grades import steam_grades
+from rare.utils.workarounds import workarounds
+from rare.utils.wrapper_exe import download_wrapper_exe
from .worker import Worker
@@ -23,44 +27,65 @@ class Result(IntEnum):
ERROR = 0
GAMESDLCS = 1
ENTITLEMENTS = 2
- STEAMAPPIDS = 3
+ EXTRAS = 3
- def __init__(self, settings: RareAppSettings, core: LegendaryCore, args: Namespace):
+ def __init__(self, settings: RareAppSettings, core: LegendaryCore, args: Namespace, segment: int):
super(FetchWorker, self).__init__()
self.signals = FetchWorkerSignals()
self.settings = settings
self.core = core
self.args = args
+ self.segment = segment
-class SteamAppIdsWorker(FetchWorker):
+class RuntimeAssetsWorker(FetchWorker):
def run_real(self):
- if platform.system() != "Windows" and not self.args.offline:
- self.signals.progress.emit(0, self.signals.tr("Updating Steam AppIds"))
- with timelogger(self.logger, "Request Steam AppIds"):
+ increment = self.segment // 3
+
+ if not self.args.offline and platform.system() not in {'Windows'}:
+ self.signals.progress.emit(increment, self.signals.tr('Updating Steam AppIds'))
+ with timelogger(self.logger, 'Request Steam AppIds'):
+ try:
+ steam_grades.load_steam_appids()
+ except Exception as e:
+ self.logger.warning(e)
+ if not self.args.offline:
+ self.signals.progress.emit(increment, self.signals.tr('Updating workarounds'))
+ with timelogger(self.logger, 'Request workarounds'):
try:
- SteamGrades().load_steam_appids()
+ workarounds.load_workarounds()
except Exception as e:
self.logger.warning(e)
- self.signals.result.emit((), FetchWorker.Result.STEAMAPPIDS)
+ if not self.args.offline:
+ self.signals.progress.emit(increment, self.signals.tr('Updating workarounds'))
+ with timelogger(self.logger, 'Request wrapper exe'):
+ try:
+ download_wrapper_exe()
+ except Exception as e:
+ self.logger.warning(e)
+ self.signals.result.emit((), FetchWorker.Result.EXTRAS)
return
class EntitlementsWorker(FetchWorker):
def run_real(self):
- want_entitlements = not self.settings.get_value(app_settings.exclude_entitlements)
+ mod_time = datetime.fromtimestamp(os.path.getmtime(os.path.join(self.core.lgd.path, 'entitlements.json')))
+ elapsed_days = abs(datetime.now() - mod_time).days
+
+ want_entitlements = not self.settings.get_value(app_settings.exclude_entitlements) and elapsed_days > 1
+ want_entitlements = want_entitlements and not self.args.offline
entitlements = ()
- if want_entitlements and not self.args.offline:
+ if want_entitlements:
# Get entitlements, Ubisoft integration also uses them
- self.signals.progress.emit(0, self.signals.tr("Updating entitlements"))
- with timelogger(self.logger, "Request entitlements"):
+ self.signals.progress.emit(self.segment, self.signals.tr('Updating entitlements'))
+ with timelogger(self.logger, 'Request entitlements'):
try:
entitlements = self.core.egs.get_user_entitlements_full()
except Exception as e:
self.logger.warning(e)
self.core.lgd.entitlements = entitlements
- self.logger.info("Entitlements: %s", len(list(entitlements)))
+ self.logger.info('Entitlements: %s', len(list(entitlements)))
self.signals.result.emit(entitlements, FetchWorker.Result.ENTITLEMENTS)
return
@@ -75,67 +100,70 @@ def run_real(self):
want_win32 = self.settings.get_value(app_settings.win32_meta)
want_macos = self.settings.get_value(app_settings.macos_meta)
want_non_asset = not self.settings.get_value(app_settings.exclude_non_asset)
- need_macos = platform.system() == "Darwin"
+ need_macos = platform.system() == 'Darwin'
need_windows = not any([want_win32, want_macos, need_macos]) and not self.args.offline
+ # One more step for the "Preparing library" message
+ steps = sum((want_win32, need_macos or want_macos, want_non_asset)) + 2
+ increment = self.segment // steps
+
if want_win32:
self.logger.info(
- "Requesting Win32 metadata due to %s, %s Unreal engine",
- "settings" if want_win32 else "debug",
- "with" if want_unreal else "without",
+ 'Requesting Win32 metadata due to %s, %s Unreal engine',
+ 'settings' if want_win32 else 'debug',
+ 'with' if want_unreal else 'without',
)
- self.signals.progress.emit(00, self.signals.tr("Updating game metadata for Windows"))
- with timelogger(self.logger, "Request Win32 games"):
+ self.signals.progress.emit(increment, self.signals.tr('Updating game metadata for Windows'))
+ with timelogger(self.logger, 'Request Win32 games'):
self.core.get_game_and_dlc_list(
update_assets=not self.args.offline,
- platform="Win32",
+ platform='Win32',
skip_ue=not want_unreal,
)
if need_macos or want_macos:
self.logger.info(
- "Requesting macOS metadata due to %s, %s Unreal engine",
- "platform" if need_macos else "settings" if want_macos else "debug",
- "with" if want_unreal else "without",
+ 'Requesting macOS metadata due to %s, %s Unreal engine',
+ 'platform' if need_macos else 'settings' if want_macos else 'debug',
+ 'with' if want_unreal else 'without',
)
- self.signals.progress.emit(15, self.signals.tr("Updating game metadata for macOS"))
- with timelogger(self.logger, "Request macOS games"):
+ self.signals.progress.emit(increment, self.signals.tr('Updating game metadata for macOS'))
+ with timelogger(self.logger, 'Request macOS games'):
self.core.get_game_and_dlc_list(
update_assets=not self.args.offline,
- platform="Mac",
+ platform='Mac',
skip_ue=not want_unreal,
)
- if need_windows:
- self.signals.progress.emit(00, self.signals.tr("Updating game metadata for Windows"))
- self.logger.info(
- "Requesting Windows metadata, %s Unreal engine",
- "with" if want_unreal else "without",
- )
- with timelogger(self.logger, "Request Windows games"):
+ self.signals.progress.emit(increment, self.signals.tr('Updating game metadata for Windows'))
+ self.logger.info(
+ 'Requesting Windows metadata, %s Unreal engine',
+ 'with' if want_unreal else 'without',
+ )
+ with timelogger(self.logger, 'Request Windows games'):
games, dlc_dict = self.core.get_game_and_dlc_list(
- update_assets=need_windows, platform="Windows", skip_ue=not want_unreal
+ update_assets=need_windows, platform='Windows', skip_ue=not want_unreal
)
- self.logger.info("Games: %s. Games with DLCs: %s", len(games), len(dlc_dict))
+ self.logger.info('Games: %s. Games with DLCs: %s', len(games), len(dlc_dict))
# Fetch non-asset games
if want_non_asset:
- self.signals.progress.emit(30, self.signals.tr("Updating non-asset game metadata"))
+ self.signals.progress.emit(increment, self.signals.tr('Updating non-asset game metadata'))
try:
- with timelogger(self.logger, "Request non-asset"):
+ with timelogger(self.logger, 'Request non-asset'):
na_games, na_dlc_dict = self.core.get_non_asset_library_items(force_refresh=False, skip_ue=False)
except (HTTPError, ConnectionError) as e:
- self.logger.error("Network error while updating non-asset games")
+ self.logger.error('Network error while updating non-asset games')
self.logger.error(e)
na_games, na_dlc_dict = ([], {})
# NOTE: This is here because of broken appIds from Epic
# https://discord.com/channels/826881530310819914/884510635642216499/1111321692703305729
except Exception as e:
- self.logger.error("General exception while updating non-asset games from EGS.")
+ self.logger.error('General exception while updating non-asset games from EGS.')
self.logger.error(e)
na_games, na_dlc_dict = ([], {})
self.logger.info(
- "Non-asset: %s. Non-asset with DLCs: %s",
+ 'Non-asset: %s. Non-asset with DLCs: %s',
len(na_games),
len(na_dlc_dict),
)
@@ -143,11 +171,11 @@ def run_real(self):
# Combine the two games lists and the two dlc dictionaries between regular and non-asset results
games += na_games
for catalog_id, dlcs in na_dlc_dict.items():
- if catalog_id in dlc_dict.keys():
+ if catalog_id in dlc_dict:
dlc_dict[catalog_id] += dlcs
else:
dlc_dict[catalog_id] = dlcs
- self.logger.info(f"Games: {len(games)}. Games with DLCs: {len(dlc_dict)}")
+ self.logger.info(f'Games: {len(games)}. Games with DLCs: {len(dlc_dict)}')
- self.signals.progress.emit(40, self.signals.tr("Preparing library"))
+ self.signals.progress.emit(increment, self.signals.tr('Preparing library'))
self.signals.result.emit((games, dlc_dict), FetchWorker.Result.GAMESDLCS)
diff --git a/rare/shared/workers/install.py b/rare/shared/workers/install.py
index f83bfb6ce8..6c7a9a0dd0 100644
--- a/rare/shared/workers/install.py
+++ b/rare/shared/workers/install.py
@@ -44,7 +44,7 @@ def run_real(self):
igame=igame,
game=EOSOverlayApp,
repair=False,
- repair_file="",
+ repair_file='',
res=ConditionCheckResult(), # empty
)
@@ -52,7 +52,7 @@ def run_real(self):
self.signals.result.emit(download)
else:
# self.signals.failed.emit("\n".join(str(i) for i in download.res.failures))
- self.signals.failed.emit("\n".join(map(str, download.res.failures)))
+ self.signals.failed.emit('\n'.join(map(str, download.res.failures)))
except LgndrException as ret:
self.signals.failed.emit(ret.message)
except Exception as e:
diff --git a/rare/shared/workers/move.py b/rare/shared/workers/move.py
index ed95f6c499..a1a517f510 100644
--- a/rare/shared/workers/move.py
+++ b/rare/shared/workers/move.py
@@ -1,7 +1,7 @@
import os
import shutil
+from collections.abc import Iterator
from enum import auto
-from typing import Iterator
from legendary.lfs.utils import validate_files
from legendary.models.game import VerifyResult
@@ -125,7 +125,7 @@ def worker_info(self) -> QueueWorkerInfo:
app_name=self.rgame.app_name,
app_title=self.rgame.app_title,
type=type(self).__name__,
- prefix="Moving",
+ prefix='Moving',
state=self.state,
)
@@ -176,8 +176,8 @@ def copy_with_progress(src, dst):
manifest.file_manifest_list.elements,
key=lambda a: a.filename.lower(),
)
- if config_tags := self.core.lgd.config.get(self.rgame.app_name, "install_tags", fallback=None):
- install_tags = set(i.strip() for i in config_tags.split(","))
+ if config_tags := self.core.lgd.config.get(self.rgame.app_name, 'install_tags', fallback=None):
+ install_tags = set(i.strip() for i in config_tags.split(','))
file_list = [
(f.filename, f.sha_hash.hex())
for f in files
@@ -207,7 +207,8 @@ def copy_dir_structure(src, dst):
dirs_exist_ok=True,
)
- for result, path, _, _ in validate_files(self.dst_path, file_list):
+ is_win = self.rgame.igame.platform.startswith('Win')
+ for result, path, _, _ in validate_files(self.dst_path, file_list, case_insensitive=is_win):
dst_path = os.path.join(self.dst_path, path)
src_path = os.path.join(self.rgame.install_path, path)
if os.path.isfile(src_path):
@@ -217,15 +218,15 @@ def copy_dir_structure(src, dst):
try:
shutil.copy2(src_path, dst_path)
dst_size += os.stat(dst_path).st_size
- except (IOError, OSError) as e:
+ except OSError as e:
self.rgame.signals.progress.finish.emit(True)
self.signals.error.emit(self.rgame, str(e))
return
else:
- self.logger.warning(f"Copying file {src_path} to {dst_path} failed")
+ self.logger.warning(f'Copying file {src_path} to {dst_path} failed')
self.progress(total_size, dst_size)
else:
- self.logger.warning(f"Source dir does not have file {src_path}. File will be missing in the destination dir.")
+ self.logger.warning(f'Source dir does not have file {src_path}. File will be missing in the destination dir.')
self.rgame.needs_verification = True
shutil.rmtree(self.rgame.install_path)
diff --git a/rare/shared/workers/uninstall.py b/rare/shared/workers/uninstall.py
index 0dea7b2c10..146f4da919 100644
--- a/rare/shared/workers/uninstall.py
+++ b/rare/shared/workers/uninstall.py
@@ -1,7 +1,6 @@
import logging
import platform
import shutil
-from typing import Tuple
from legendary.lfs.eos import remove_registry_entries
from PySide6.QtCore import QObject, Signal
@@ -31,18 +30,18 @@ def uninstall_game(
keep_folder=True,
keep_config=False,
keep_overlay_keys=False,
-) -> Tuple[bool, str]:
+) -> tuple[bool, str]:
if rgame.is_overlay:
- logger.info("Deleting overlay installation...")
+ logger.info('Deleting overlay installation...')
core.remove_overlay_install()
if keep_overlay_keys:
- return True, ""
+ return True, ''
- logger.info("Removing registry entries...")
- if platform.system() != "Window":
+ logger.info('Removing registry entries...')
+ if platform.system() != 'Window':
prefixes = config.get_prefixes()
- if platform.system() == "Darwin":
+ if platform.system() == 'Darwin':
# TODO: add crossover support
pass
if len(prefixes):
@@ -50,18 +49,18 @@ def uninstall_game(
try:
remove_registry_entries(prefix)
except Exception as e:
- logger.error("%s %s", e, prefix)
- logger.debug("Removed registry entries for prefix %s", prefix)
+ logger.error('%s %s', e, prefix)
+ logger.debug('Removed registry entries for prefix %s', prefix)
else:
remove_registry_entries()
- return True, ""
+ return True, ''
# remove shortcuts link
if desktop_links_supported():
for link_type in desktop_link_types():
link_path = desktop_link_path(
- rgame.game.metadata.get("customAttributes", {}).get("FolderName", {}).get("value"),
+ rgame.game.metadata.get('customAttributes', {}).get('FolderName', {}).get('value'),
link_type,
)
if link_path.exists():
@@ -82,13 +81,13 @@ def uninstall_game(
keep_folder = keep_files if keep_files else keep_folder
if not keep_folder:
- logger.info("Removing game install directory")
+ logger.info('Removing game install directory')
shutil.rmtree(install_path, ignore_errors=True)
if not keep_config:
- logger.info("Removing sections in config file")
+ logger.info('Removing sections in config file')
config.remove_section(rgame.app_name)
- config.remove_section(f"{rgame.app_name}.env")
+ config.remove_section(f'{rgame.app_name}.env')
config.save_config()
diff --git a/rare/shared/workers/verify.py b/rare/shared/workers/verify.py
index a34ab5d643..857f729e20 100644
--- a/rare/shared/workers/verify.py
+++ b/rare/shared/workers/verify.py
@@ -38,7 +38,7 @@ def worker_info(self) -> QueueWorkerInfo:
app_name=self.rgame.app_name,
app_title=self.rgame.app_title,
type=type(self).__name__,
- prefix="Verifying",
+ prefix='Verifying',
state=self.state,
)
@@ -86,7 +86,7 @@ def run_real(self):
if success:
# lk: if verification was successful we delete the repair file and run the clean procedure
# lk: this could probably be cut down to what is relevant for this use-case and skip the `cli` call
- repair_file = os.path.join(self.core.lgd.get_tmp_path(), f"{self.rgame.app_name}.repair")
+ repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{self.rgame.app_name}.repair')
cli.install_game_cleanup(
game=self.rgame.game,
igame=self.rgame.igame,
diff --git a/rare/shared/workers/wine_resolver.py b/rare/shared/workers/wine_resolver.py
index 13b4667785..6ffb3ddd49 100644
--- a/rare/shared/workers/wine_resolver.py
+++ b/rare/shared/workers/wine_resolver.py
@@ -1,8 +1,8 @@
import os
import platform
import time
+from collections.abc import Iterable
from configparser import ConfigParser
-from typing import Dict, Iterable, List, Tuple, Union
from PySide6.QtCore import QObject, Signal
@@ -15,7 +15,7 @@
from .worker import Worker
-if platform.system() == "Windows":
+if platform.system() == 'Windows':
# noinspection PyUnresolvedReferences
import winreg # pylint: disable=E0401
@@ -38,10 +38,10 @@ def __init__(self, core: LegendaryCore, app_name: str, path: str):
self.path = path
@staticmethod
- def _configure_process(core: LegendaryCore, app_name: str) -> Tuple[List, Dict]:
+ def _configure_process(core: LegendaryCore, app_name: str) -> tuple[list, dict]:
tool: steam.CompatibilityTool = None
- if config.get_boolean(app_name, "no_wine"):
+ if config.get_boolean(app_name, 'no_wine'):
wrappers = Wrappers()
for w in wrappers.get_wrappers(app_name):
if w.is_compat_tool:
@@ -53,17 +53,17 @@ def _configure_process(core: LegendaryCore, app_name: str) -> Tuple[List, Dict]:
cmd = core.get_app_launch_command(
app_name,
wrapper=tool.as_str(steam.SteamVerb.RUN_IN_PREFIX) if tool is not None else None,
- disable_wine=config.get_boolean(app_name, "no_wine"),
+ disable_wine=config.get_boolean(app_name, 'no_wine'),
)
- env = core.get_app_environment(app_name, disable_wine=config.get_boolean(app_name, "no_wine"))
+ env = core.get_app_environment(app_name, disable_wine=config.get_boolean(app_name, 'no_wine'))
# pylint: disable=E0606
env = compat_utils.get_host_environment(env, silent=True)
env.update(
{
- "SteamAppId": config.get_envvar_with_global(app_name, "SteamAppId", "default"),
- "SteamGameId": config.get_envvar_with_global(app_name, "SteamGameId", "default"),
- "STEAM_COMPAT_APP_ID": config.get_envvar_with_global(app_name, "STEAM_COMPAT_APP_ID", "default"),
- "UMU_ID": config.get_envvar_with_global(app_name, "UMU_ID", "default"),
+ 'SteamAppId': config.get_envvar_with_global(app_name, 'SteamAppId', 'default'),
+ 'SteamGameId': config.get_envvar_with_global(app_name, 'SteamGameId', 'default'),
+ 'STEAM_COMPAT_APP_ID': config.get_envvar_with_global(app_name, 'STEAM_COMPAT_APP_ID', 'default'),
+ 'UMU_ID': config.get_envvar_with_global(app_name, 'UMU_ID', 'default'),
}
)
@@ -71,9 +71,9 @@ def _configure_process(core: LegendaryCore, app_name: str) -> Tuple[List, Dict]:
_cmd = core.get_app_launch_command(
app_name,
wrapper=tool.as_str(steam.SteamVerb.WAIT_FOR_EXIT_AND_RUN) if tool is not None else None,
- disable_wine=config.get_boolean(app_name, "no_wine"),
+ disable_wine=config.get_boolean(app_name, 'no_wine'),
)
- compat_utils.execute(_cmd, ["c:\\windows\\system32\\wineboot.exe", "-u"], env)
+ compat_utils.execute(_cmd, ['c:\\windows\\system32\\wineboot.exe', '-u'], env)
return cmd, env
@@ -88,8 +88,8 @@ def _resolve_unix_path(self, cmd, env, path: str) -> str:
def run_real(self):
command, environ = self._configure_process(self.core, self.app_name)
if not (command and environ):
- self.logger.error("Cannot setup %s, missing infomation", {type(self).__name__})
- self.signals.result_ready.emit("", self.app_name)
+ self.logger.error('Cannot setup %s, missing infomation', {type(self).__name__})
+ self.signals.result_ready.emit('', self.app_name)
path = self._resolve_unix_path(command, environ, self.path)
self.signals.result_ready.emit(path, self.app_name)
@@ -103,11 +103,11 @@ def __init__(self, core: LegendaryCore, rgame: RareGame):
self.rgame = rgame
def run_real(self):
- self.logger.info("Resolving save path for %s (%s)", self.rgame.app_title, self.rgame.app_name)
+ self.logger.info('Resolving save path for %s (%s)', self.rgame.app_title, self.rgame.app_name)
command, environ = self._configure_process(self.core, self.rgame.app_name)
if not (command and environ):
- self.logger.error("Cannot setup %s, missing infomation", {type(self).__name__})
- self.signals.result_ready.emit("", self.rgame.app_name)
+ self.logger.error('Cannot setup %s, missing infomation', {type(self).__name__})
+ self.signals.result_ready.emit('', self.rgame.app_name)
path = self._resolve_unix_path(command, environ, self.path)
# Clean wine output
@@ -119,8 +119,8 @@ def run_real(self):
class OriginWineWorker(WinePathResolver):
- def __init__(self, core: LegendaryCore, games: Union[Iterable[RareGame], RareGame]):
- super(OriginWineWorker, self).__init__(core, "", "")
+ def __init__(self, core: LegendaryCore, games: Iterable[RareGame] | RareGame):
+ super(OriginWineWorker, self).__init__(core, '', '')
self.__cache: dict[str, ConfigParser] = {}
self.core = core
self.games = [games] if isinstance(games, RareGame) else games
@@ -129,15 +129,15 @@ def run_real(self) -> None:
t = time.time()
for rgame in self.games:
- reg_path: str = rgame.game.metadata.get("customAttributes", {}).get("RegistryPath", {}).get("value", "")
+ reg_path: str = rgame.game.metadata.get('customAttributes', {}).get('RegistryPath', {}).get('value', '')
if not reg_path:
continue
- reg_key: str = rgame.game.metadata.get("customAttributes", {}).get("RegistryKey", {}).get("value", "")
+ reg_key: str = rgame.game.metadata.get('customAttributes', {}).get('RegistryKey', {}).get('value', '')
if not reg_key:
continue
- if platform.system() == "Windows":
+ if platform.system() == 'Windows':
install_dir = windows_helpers.query_registry_value(winreg.HKEY_LOCAL_MACHINE, reg_path, reg_key)
else:
command, environ = self._configure_process(self.core, rgame.app_name)
@@ -149,23 +149,23 @@ def run_real(self) -> None:
use_wine = True
if not use_wine:
# lk: this is the original way of getting the path by parsing "system.reg"
- reg = self.__cache.get(prefix, None) or compat_utils.read_registry("system.reg", prefix)
+ reg = self.__cache.get(prefix, None) or compat_utils.read_registry('system.reg', prefix)
self.__cache[prefix] = reg
- reg_path = reg_path.replace("SOFTWARE", "Software").replace("WOW6432Node", "Wow6432Node")
+ reg_path = reg_path.replace('SOFTWARE', 'Software').replace('WOW6432Node', 'Wow6432Node')
# lk: split and rejoin the registry path to avoid slash expansion
- reg_path = "\\\\".join([x for x in reg_path.split("\\") if bool(x)])
+ reg_path = '\\\\'.join([x for x in reg_path.split('\\') if bool(x)])
install_dir = reg.get(reg_path, f'"{reg_key}"', fallback=None)
else:
# lk: this is the alternative way of getting the path by using wine itself
- install_dir = compat_utils.query_reg_key(command, environ, f"HKLM\\{reg_path}", reg_key)
+ install_dir = compat_utils.query_reg_key(command, environ, f'HKLM\\{reg_path}', reg_key)
if install_dir:
- self.logger.debug("Found Wine install directory %s", install_dir)
+ self.logger.debug('Found Wine install directory %s', install_dir)
install_dir = compat_utils.convert_to_unix_path(command, environ, install_dir)
if install_dir:
- self.logger.debug("Found Unix install directory %s", install_dir)
+ self.logger.debug('Found Unix install directory %s', install_dir)
else:
self.logger.info(
"Could not find Unix install directory for '%s'",
@@ -195,4 +195,4 @@ def run_real(self) -> None:
)
else:
self.logger.info("Origin game '%s' is not installed", rgame.app_title)
- self.logger.info("Origin worker finished in %ss", time.time() - t)
+ self.logger.info('Origin worker finished in %ss', time.time() - t)
diff --git a/rare/shared/wrappers.py b/rare/shared/wrappers.py
index 908a7aed6c..67ae27968a 100644
--- a/rare/shared/wrappers.py
+++ b/rare/shared/wrappers.py
@@ -1,8 +1,8 @@
import json
import os
import shlex
+from collections.abc import Iterable
from logging import getLogger
-from typing import Dict, Iterable, List, Set
from rare.lgndr.core import LegendaryCore
from rare.models.settings import RareAppSettings
@@ -10,7 +10,7 @@
from rare.utils import config_helper as config
from rare.utils.paths import config_dir
-logger = getLogger("Wrappers")
+logger = getLogger('Wrappers')
class WrapperEntry:
@@ -27,8 +27,8 @@ def enabled(self) -> bool:
return self.__enabled
@classmethod
- def from_dict(cls, data: Dict):
- return cls(checksum=data.get("checksum"), enabled=data.get("enabled", True))
+ def from_dict(cls, data: dict):
+ return cls(checksum=data.get('checksum'), enabled=data.get('enabled', True))
@property
def __dict__(self):
@@ -40,24 +40,24 @@ def __dict__(self):
class Wrappers:
def __init__(self):
- self._file = os.path.join(config_dir(), "wrappers.json")
+ self._file = os.path.join(config_dir(), 'wrappers.json')
self._wrappers_dict = {}
try:
- with open(self._file, "r", encoding="utf-8") as f:
+ with open(self._file, encoding='utf-8') as f:
self._wrappers_dict = json.load(f)
except FileNotFoundError:
- logger.info("%s does not exist", self._file)
+ logger.info('%s does not exist', self._file)
except json.JSONDecodeError:
- logger.warning("%s is corrupt", self._file)
+ logger.warning('%s is corrupt', self._file)
- self._version = self._wrappers_dict.get("version", 1)
+ self._version = self._wrappers_dict.get('version', 1)
- self._wrappers: Dict[str, Wrapper] = {}
- for wrap_id, wrapper in self._wrappers_dict.get("wrappers", {}).items():
+ self._wrappers: dict[str, Wrapper] = {}
+ for wrap_id, wrapper in self._wrappers_dict.get('wrappers', {}).items():
self._wrappers.update({wrap_id: Wrapper.from_dict(wrapper)})
- self._applists: Dict[str, List[WrapperEntry]] = {}
- for app_name, wrapper_list in self._wrappers_dict.get("applists", {}).items():
+ self._applists: dict[str, list[WrapperEntry]] = {}
+ for app_name, wrapper_list in self._wrappers_dict.get('applists', {}).items():
if all(isinstance(x, str) for x in wrapper_list):
wlist = [WrapperEntry(y) for y in wrapper_list]
elif all(isinstance(x, dict) for x in wrapper_list):
@@ -69,35 +69,35 @@ def __init__(self):
# set current file version
self._version = 2
- def import_wrappers(self, settings: RareAppSettings, core: LegendaryCore, app_names: List):
+ def import_wrappers(self, settings: RareAppSettings, core: LegendaryCore, app_names: list):
for app_name in app_names:
wrappers = self.get_wrappers(app_name)
- if not wrappers and (commands := settings.value(f"{app_name}/wrapper", [], type=list)):
+ if not wrappers and (commands := settings.value(f'{app_name}/wrapper', [], type=list)):
logger.info("Importing wrappers from Rare's config")
- settings.remove(f"{app_name}/wrapper")
+ settings.remove(f'{app_name}/wrapper')
for command in commands:
wrapper = Wrapper(command=shlex.split(command))
wrappers.append(wrapper)
self.set_wrappers(app_name, wrappers)
logger.debug(
- "Imported previous wrappers in %s Rare: %s",
+ 'Imported previous wrappers in %s Rare: %s',
app_name,
wrapper.name,
)
# NOTE: compatibility with Legendary
# No Rare settings wrappers, but legendary config wrappers, for backwards compatibility
- if not wrappers and (command := core.lgd.config.get(app_name, "wrapper", fallback="")):
+ if not wrappers and (command := core.lgd.config.get(app_name, 'wrapper', fallback='')):
logger.info("Importing wrappers from legendary's config")
wrapper = Wrapper(
command=shlex.split(command),
- name="Imported from Legendary",
+ name='Imported from Legendary',
wtype=WrapperType.LEGENDARY_IMPORT,
)
wrappers = [wrapper]
self.set_wrappers(app_name, wrappers)
logger.debug(
- "Imported existing wrappers in %s legendary: %s",
+ 'Imported existing wrappers in %s legendary: %s',
app_name,
wrapper.name,
)
@@ -111,12 +111,12 @@ def user_wrappers(self) -> Iterable[Wrapper]:
def wrapper_command(self, app_name: str) -> str:
commands = [wrapper.as_str for wrapper in self.get_wrappers(app_name) if wrapper.is_enabled]
- return " ".join(commands)
+ return ' '.join(commands)
- def get_checksums(self, app_name: str) -> Set[str]:
+ def get_checksums(self, app_name: str) -> set[str]:
return {entry.checksum for entry in self._applists.get(app_name, [])}
- def get_wrappers(self, app_name: str) -> List[Wrapper]:
+ def get_wrappers(self, app_name: str) -> list[Wrapper]:
wrappers = []
for entry in self._applists.get(app_name, []):
if wrap := self._wrappers.get(entry.checksum, None):
@@ -124,13 +124,13 @@ def get_wrappers(self, app_name: str) -> List[Wrapper]:
wrappers.append(wrap)
return wrappers
- def set_wrappers(self, app_name: str, wrappers: List[Wrapper]) -> None:
+ def set_wrappers(self, app_name: str, wrappers: list[Wrapper]) -> None:
_wrappers = sorted(wrappers, key=lambda w: w.is_compat_tool)
for w in _wrappers:
- if (md5sum := w.checksum) in self._wrappers.keys():
+ if (md5sum := w.checksum) in self._wrappers:
if w != self._wrappers[md5sum]:
logger.error(
- "Equal csum for unequal wrappers %s, %s",
+ 'Equal csum for unequal wrappers %s, %s',
w.name,
self._wrappers[md5sum].name,
)
@@ -144,24 +144,24 @@ def set_wrappers(self, app_name: str, wrappers: List[Wrapper]) -> None:
def __save_config(self, app_name: str):
command = self.wrapper_command(app_name)
- config.adjust_option(app_name, "wrapper", command)
+ config.adjust_option(app_name, 'wrapper', command)
def __save_wrappers(self):
- existing = {csum for csum in self._wrappers.keys()}
+ existing = {csum for csum in self._wrappers}
in_use = {entry.checksum for wrappers in self._applists.values() for entry in wrappers}
for redudant in existing.difference(in_use):
del self._wrappers[redudant]
- self._wrappers_dict["version"] = self._version
- self._wrappers_dict["wrappers"] = self._wrappers
- self._wrappers_dict["applists"] = self._applists
+ self._wrappers_dict['version'] = self._version
+ self._wrappers_dict['wrappers'] = self._wrappers
+ self._wrappers_dict['applists'] = self._applists
- with open(os.path.join(self._file), "w+", encoding="utf-8") as f:
+ with open(os.path.join(self._file), 'w+', encoding='utf-8') as f:
json.dump(self._wrappers_dict, f, default=lambda o: vars(o), indent=2)
-if __name__ == "__main__":
+if __name__ == '__main__':
from argparse import Namespace
from pprint import pprint
@@ -171,40 +171,40 @@ def __save_wrappers(self):
config_dir = os.getcwd
global config
config = Namespace()
- config.set_option = lambda x, y, z: print("set_option:", x, y, z)
- config.remove_option = lambda x, y: print("remove_option:", x, y)
- config.save_config = lambda: print("save_config:")
- config.adjust_option = lambda x, y, z: print("save_option:", x, y, z)
+ config.set_option = lambda x, y, z: print('set_option:', x, y, z)
+ config.remove_option = lambda x, y: print('remove_option:', x, y)
+ config.save_config = lambda: print('save_config:')
+ config.adjust_option = lambda x, y, z: print('save_option:', x, y, z)
wr = Wrappers()
- w1 = Wrapper(command=["/usr/bin/w1"], wtype=WrapperType.NONE)
- w2 = Wrapper(command=["/usr/bin/w2"], wtype=WrapperType.COMPAT_TOOL)
- w3 = Wrapper(command=["/usr/bin/w3"], wtype=WrapperType.USER_DEFINED, enabled=False)
- w4 = Wrapper(command=["/usr/bin/w4"], wtype=WrapperType.USER_DEFINED)
- wr.set_wrappers("testgame", [w1, w2, w3, w4])
+ w1 = Wrapper(command=['/usr/bin/w1'], wtype=WrapperType.NONE)
+ w2 = Wrapper(command=['/usr/bin/w2'], wtype=WrapperType.COMPAT_TOOL)
+ w3 = Wrapper(command=['/usr/bin/w3'], wtype=WrapperType.USER_DEFINED, enabled=False)
+ w4 = Wrapper(command=['/usr/bin/w4'], wtype=WrapperType.USER_DEFINED)
+ wr.set_wrappers('testgame', [w1, w2, w3, w4])
- w5 = Wrapper(command=["/usr/bin/w5"], wtype=WrapperType.COMPAT_TOOL)
- wr.set_wrappers("testgame2", [w2, w1, w5])
+ w5 = Wrapper(command=['/usr/bin/w5'], wtype=WrapperType.COMPAT_TOOL)
+ wr.set_wrappers('testgame2', [w2, w1, w5])
- w6 = Wrapper(command=["/usr/bin/w 6", "-w", "-t"], wtype=WrapperType.USER_DEFINED)
- wr.set_wrappers("testgame", [w1, w2, w3, w6])
+ w6 = Wrapper(command=['/usr/bin/w 6', '-w', '-t'], wtype=WrapperType.USER_DEFINED)
+ wr.set_wrappers('testgame', [w1, w2, w3, w6])
- w7 = Wrapper(command=["/usr/bin/w2"], wtype=WrapperType.COMPAT_TOOL)
- app_wrappers = wr.get_wrappers("testgame")
+ w7 = Wrapper(command=['/usr/bin/w2'], wtype=WrapperType.COMPAT_TOOL)
+ app_wrappers = wr.get_wrappers('testgame')
pprint([w.as_str for w in app_wrappers])
# item = next(item for item in app_wrappers if item.checksum == w3.checksum)
app_wrappers.remove(w3)
- wr.set_wrappers("testgame", app_wrappers)
+ wr.set_wrappers('testgame', app_wrappers)
- game_wrappers = wr.get_wrappers("testgame")
+ game_wrappers = wr.get_wrappers('testgame')
pprint([w.as_str for w in game_wrappers])
- game_wrappers = wr.get_wrappers("testgame2")
+ game_wrappers = wr.get_wrappers('testgame2')
pprint([w.as_str for w in game_wrappers])
for i, tool in enumerate(steam.find_tools()):
wt = Wrapper(command=tool.command(), name=tool.name, wtype=WrapperType.COMPAT_TOOL)
- wr.set_wrappers(f"compat_game_{i}", [wt])
+ wr.set_wrappers(f'compat_game_{i}', [wt])
print(wt.as_str)
for wrp in wr.user_wrappers:
diff --git a/rare/utils/compat/miniproton.py b/rare/utils/compat/miniproton.py
index d69ae55fab..aeaf69839e 100644
--- a/rare/utils/compat/miniproton.py
+++ b/rare/utils/compat/miniproton.py
@@ -4,51 +4,51 @@
import subprocess
import sys
from pathlib import Path
-from typing import Any, Dict, List
+from typing import Any
def log(msg: Any):
- sys.stderr.write(str(msg) + "\n")
+ sys.stderr.write(str(msg) + '\n')
sys.stderr.flush()
-def run_proc(args: List, env: Dict):
+def run_proc(args: list, env: dict):
return subprocess.call(args, env=env, stderr=sys.stderr, stdout=sys.stdout)
-if __name__ == "__main__":
- if "STEAM_COMPAT_DATA_PATH" not in os.environ:
- log("No compat data path?")
+if __name__ == '__main__':
+ if 'STEAM_COMPAT_DATA_PATH' not in os.environ:
+ log('No compat data path?')
sys.exit(1)
- wine_bin = "/usr/bin/wine"
- wineserver_bin = "/usr/bin/wineserver"
+ wine_bin = '/usr/bin/wine'
+ wineserver_bin = '/usr/bin/wineserver'
- compat_data = Path(os.environ["STEAM_COMPAT_DATA_PATH"]).resolve(strict=False)
+ compat_data = Path(os.environ['STEAM_COMPAT_DATA_PATH']).resolve(strict=False)
compat_data.mkdir(parents=True, exist_ok=True)
- compat_data.joinpath("creation_sync_guard").touch(exist_ok=True)
- compat_data.joinpath("tracked_files").touch(exist_ok=True)
+ compat_data.joinpath('creation_sync_guard').touch(exist_ok=True)
+ compat_data.joinpath('tracked_files').touch(exist_ok=True)
- wine_prefix = compat_data.joinpath("pfx").resolve(strict=False)
+ wine_prefix = compat_data.joinpath('pfx').resolve(strict=False)
if not wine_prefix.exists():
wine_prefix.mkdir(parents=True, exist_ok=True)
dlls = {
- "d3d8": "n,b",
- "d3d9": "n,b",
- "dxgi": "n,b",
- "d3d10core": "n,b",
- "d3d11": "n,b",
- "d3d12": "n,b;",
- "d3d12core": "n,b",
+ 'd3d8': 'n,b',
+ 'd3d9': 'n,b',
+ 'dxgi': 'n,b',
+ 'd3d10core': 'n,b',
+ 'd3d11': 'n,b',
+ 'd3d12': 'n,b;',
+ 'd3d12core': 'n,b',
}
- dlloverrides = ";".join(("=".join((k, v)) for k, v in dlls.items()))
- dlloverrides = ";".join((os.environ.get("WINEDLLOVERRIDES", ""), dlloverrides))
+ dlloverrides = ';'.join(('='.join((k, v)) for k, v in dlls.items()))
+ dlloverrides = ';'.join((os.environ.get('WINEDLLOVERRIDES', ''), dlloverrides))
env = {
- "WINE": wine_bin,
- "WINEPREFIX": wine_prefix.as_posix(),
- "WINEDLLOVERRIDES": dlloverrides,
+ 'WINE': wine_bin,
+ 'WINEPREFIX': wine_prefix.as_posix(),
+ 'WINEDLLOVERRIDES': dlloverrides,
}
log(env)
@@ -57,30 +57,30 @@ def run_proc(args: List, env: Dict):
# determine mode
rc = 0
- if sys.argv[1] == "run":
+ if sys.argv[1] == 'run':
# start target app
rc = run_proc([wine_bin] + sys.argv[2:], local_env)
- elif sys.argv[1] == "waitforexitandrun":
+ elif sys.argv[1] == 'waitforexitandrun':
# wait for wineserver to shut down
- run_proc([wineserver_bin, "-w"], local_env)
+ run_proc([wineserver_bin, '-w'], local_env)
# then run
rc = run_proc([wine_bin] + sys.argv[2:], local_env)
- elif sys.argv[1] == "runinprefix":
+ elif sys.argv[1] == 'runinprefix':
rc = run_proc([wine_bin] + sys.argv[2:], local_env)
- elif sys.argv[1] == "getcompatpath":
+ elif sys.argv[1] == 'getcompatpath':
# linux -> windows path
- path = subprocess.check_output([wine_bin, "winepath", "-w", sys.argv[2]], env=local_env, stderr=sys.stderr)
+ path = subprocess.check_output([wine_bin, 'winepath', '-w', sys.argv[2]], env=local_env, stderr=sys.stderr)
sys.stdout.buffer.write(path)
sys.stdout.buffer.flush()
sys.stderr.buffer.flush()
- elif sys.argv[1] == "getnativepath":
+ elif sys.argv[1] == 'getnativepath':
# windows -> linux path
- path = subprocess.check_output([wine_bin, "winepath", sys.argv[2]], env=local_env, stderr=sys.stderr)
+ path = subprocess.check_output([wine_bin, 'winepath', sys.argv[2]], env=local_env, stderr=sys.stderr)
sys.stdout.buffer.write(path)
sys.stdout.buffer.flush()
sys.stderr.buffer.flush()
else:
- log("Need a verb.")
+ log('Need a verb.')
sys.exit(1)
sys.exit(rc)
diff --git a/rare/utils/compat/steam.py b/rare/utils/compat/steam.py
index 580592cb84..df3353a8fe 100644
--- a/rare/utils/compat/steam.py
+++ b/rare/utils/compat/steam.py
@@ -4,48 +4,47 @@
from enum import Enum
from hashlib import md5
from logging import getLogger
-from typing import Dict, List, Optional, Set, Union
import vdf
from rare.utils.paths import data_dir
-logger = getLogger("SteamTools")
+logger = getLogger('SteamTools')
-steam_client_install_paths = [os.path.expanduser("~/.local/share/Steam")]
-umu_install_paths = [os.path.expanduser("~/.local/share/umu")]
+steam_client_install_paths = [os.path.expanduser('~/.local/share/Steam')]
+umu_install_paths = [os.path.expanduser('~/.local/share/umu')]
-def find_steam() -> Optional[str]:
+def find_steam() -> str | None:
# return the first valid path
for path in steam_client_install_paths:
- if os.path.isdir(path) and os.path.isfile(os.path.join(path, "steam.sh")):
+ if os.path.isdir(path) and os.path.isfile(os.path.join(path, 'steam.sh')):
return path
return None
-def find_libraries(steam_path: str) -> Set[str]:
- vdf_path = os.path.join(steam_path, "config", "libraryfolders.vdf")
+def find_libraries(steam_path: str) -> set[str]:
+ vdf_path = os.path.join(steam_path, 'config', 'libraryfolders.vdf')
if not os.path.isfile(vdf_path):
return set()
- with open(vdf_path, "r", encoding="utf-8") as f:
- libraryfolders = vdf.load(f)["libraryfolders"]
+ with open(vdf_path, encoding='utf-8') as f:
+ libraryfolders = vdf.load(f)['libraryfolders']
# libraries = [os.path.join(folder["path"], "steamapps") for key, folder in libraryfolders.items()]
- libraries = {os.path.join(folder["path"], "steamapps") for key, folder in libraryfolders.items()}
+ libraries = {os.path.join(folder['path'], 'steamapps') for key, folder in libraryfolders.items()}
libraries = set(filter(lambda x: os.path.isdir(x), libraries))
return libraries
UMU_RUNTIMES = {
- "1391110": "steamrt2",
- "1628350": "steamrt3",
- "3810310": "steamrt3-arm64",
- "4183110": "steamrt4",
- "4185400": "steamrt4-arm64",
+ '1391110': 'steamrt2',
+ '1628350': 'steamrt3',
+ '3810310': 'steamrt3-arm64',
+ '4183110': 'steamrt4',
+ '4185400': 'steamrt4-arm64',
}
-def find_umu() -> Optional[str]:
+def find_umu() -> str | None:
for path in umu_install_paths:
if os.path.isdir(path) and any(rt in os.listdir(path) for rt in UMU_RUNTIMES.values()):
return path
@@ -64,12 +63,12 @@ def find_umu() -> Optional[str]:
class SteamVerb(Enum):
- RUN = "run"
- WAIT_FOR_EXIT_AND_RUN = "waitforexitandrun"
- RUN_IN_PREFIX = "runinprefix"
- DESTROY_PREFIX = "destroyprefix"
- GET_COMPAT_PATH = "getcompatpath"
- GET_NATIVE_PATH = "getnativepath"
+ RUN = 'run'
+ WAIT_FOR_EXIT_AND_RUN = 'waitforexitandrun'
+ RUN_IN_PREFIX = 'runinprefix'
+ DESTROY_PREFIX = 'destroyprefix'
+ GET_COMPAT_PATH = 'getcompatpath'
+ GET_NATIVE_PATH = 'getnativepath'
DEFAULT = WAIT_FOR_EXIT_AND_RUN
@@ -77,7 +76,7 @@ class SteamVerb(Enum):
class SteamBase:
steam_path: str
tool_path: str
- toolmanifest: Dict
+ toolmanifest: dict
def __eq__(self, other):
return self.tool_path == other.tool_path
@@ -86,41 +85,41 @@ def __hash__(self):
return hash(self.tool_path)
@property
- def required_tool(self) -> Optional[str]:
- return self.toolmanifest.get("require_tool_appid", None)
+ def required_tool(self) -> str | None:
+ return self.toolmanifest.get('require_tool_appid', None)
@property
- def layer(self) -> Optional[str]:
- return self.toolmanifest.get("compatmanager_layer_name", None)
+ def layer(self) -> str | None:
+ return self.toolmanifest.get('compatmanager_layer_name', None)
- def command(self, verb: SteamVerb = SteamVerb.DEFAULT) -> List[str]:
+ def command(self, verb: SteamVerb = SteamVerb.DEFAULT) -> list[str]:
tool_path = os.path.normpath(self.tool_path)
- cmd = "".join([shlex.quote(tool_path), self.toolmanifest["commandline"]])
+ cmd = ''.join([shlex.quote(tool_path), self.toolmanifest['commandline']])
# NOTE: "waitforexitandrun" seems to be the verb used in by steam to execute stuff
# `run` is used when setting up the environment, so use that if we are setting up the prefix.
- cmd = cmd.replace("%verb%", verb.value)
+ cmd = cmd.replace('%verb%', verb.value)
return shlex.split(cmd)
def as_str(self, verb: SteamVerb = SteamVerb.DEFAULT):
- return " ".join(map(shlex.quote, self.command(verb)))
+ return ' '.join(map(shlex.quote, self.command(verb)))
@property
def checksum(self) -> str:
- return md5(self.as_str().encode("utf-8")).hexdigest()
+ return md5(self.as_str().encode('utf-8')).hexdigest()
@dataclass
class SteamRuntime(SteamBase):
steam_library: str
- appmanifest: Dict
+ appmanifest: dict
@property
def name(self) -> str:
- return self.appmanifest["AppState"]["name"]
+ return self.appmanifest['AppState']['name']
@property
def appid(self) -> str:
- return self.appmanifest["AppState"]["appid"]
+ return self.appmanifest['AppState']['appid']
@dataclass
@@ -134,7 +133,7 @@ class SteamAntiCheat:
steam_path: str
tool_path: str
steam_library: str
- appmanifest: Dict
+ appmanifest: dict
def __eq__(self, other):
return self.tool_path == other.tool_path
@@ -144,24 +143,24 @@ def __hash__(self):
@property
def name(self) -> str:
- return self.appmanifest["AppState"]["name"]
+ return self.appmanifest['AppState']['name']
@property
def appid(self) -> str:
- return self.appmanifest["AppState"]["appid"]
+ return self.appmanifest['AppState']['appid']
@dataclass
class ProtonTool(SteamRuntime):
- runtime: Union[SteamRuntime, UmuRuntime] = None
- anticheat: Dict[str, SteamAntiCheat] = None
+ runtime: SteamRuntime | UmuRuntime = None
+ anticheat: dict[str, SteamAntiCheat] = None
def __bool__(self) -> bool:
if appid := self.required_tool:
return self.runtime is not None and self.runtime.appid == appid
return True
- def command(self, verb: SteamVerb = SteamVerb.DEFAULT) -> List[str]:
+ def command(self, verb: SteamVerb = SteamVerb.DEFAULT) -> list[str]:
cmd = self.runtime.command(verb)
cmd.extend(super().command(verb))
return cmd
@@ -169,9 +168,9 @@ def command(self, verb: SteamVerb = SteamVerb.DEFAULT) -> List[str]:
@dataclass
class CompatibilityTool(SteamBase):
- compatibilitytool: Dict
- runtime: Union[SteamRuntime, UmuRuntime] = None
- anticheat: Dict[str, SteamAntiCheat] = None
+ compatibilitytool: dict
+ runtime: SteamRuntime | UmuRuntime = None
+ anticheat: dict[str, SteamAntiCheat] = None
def __bool__(self) -> bool:
if appid := self.required_tool:
@@ -180,41 +179,41 @@ def __bool__(self) -> bool:
@property
def name(self) -> str:
- return self.compatibilitytool["display_name"]
+ return self.compatibilitytool['display_name']
- def command(self, verb: SteamVerb = SteamVerb.DEFAULT) -> List[str]:
+ def command(self, verb: SteamVerb = SteamVerb.DEFAULT) -> list[str]:
cmd = self.runtime.command(verb) if self.runtime is not None else []
cmd.extend(super().command(verb))
return cmd
-def find_appmanifests(library: str) -> List[dict]:
+def find_appmanifests(library: str) -> list[dict]:
appmanifests = []
for entry in os.scandir(library):
- if entry.is_file() and entry.name.endswith(".acf"):
- with open(os.path.join(library, entry.name), "r", encoding="utf-8") as f:
+ if entry.is_file() and entry.name.endswith('.acf'):
+ with open(os.path.join(library, entry.name), encoding='utf-8') as f:
appmanifest = vdf.load(f)
appmanifests.append(appmanifest)
return appmanifests
ANTICHEAT_RUNTIMES = {
- "eac_runtime": "1826330",
- "battleye_runtime": "1161040",
+ 'eac_runtime': '1826330',
+ 'battleye_runtime': '1161040',
}
def find_anticheat(steam_path: str, library: str):
runtimes = {}
appmanifests = find_appmanifests(library)
- common = os.path.join(library, "common")
+ common = os.path.join(library, 'common')
for appmanifest in appmanifests:
- if appmanifest["AppState"]["appid"] not in ANTICHEAT_RUNTIMES.values():
+ if appmanifest['AppState']['appid'] not in ANTICHEAT_RUNTIMES.values():
continue
- folder = appmanifest["AppState"]["installdir"]
+ folder = appmanifest['AppState']['installdir']
runtimes.update(
{
- appmanifest["AppState"]["appid"]: SteamAntiCheat(
+ appmanifest['AppState']['appid']: SteamAntiCheat(
steam_path=steam_path,
steam_library=library,
appmanifest=appmanifest,
@@ -225,51 +224,51 @@ def find_anticheat(steam_path: str, library: str):
return runtimes
-def find_steam_runtimes(steam_path: str, library: str) -> Dict[str, SteamRuntime]:
+def find_steam_runtimes(steam_path: str, library: str) -> dict[str, SteamRuntime]:
runtimes = {}
appmanifests = find_appmanifests(library)
- common = os.path.join(library, "common")
+ common = os.path.join(library, 'common')
for appmanifest in appmanifests:
- folder = appmanifest["AppState"]["installdir"]
+ folder = appmanifest['AppState']['installdir']
tool_path = os.path.join(common, folder)
- if os.path.isfile(vdf_file := os.path.join(tool_path, "toolmanifest.vdf")):
- with open(vdf_file, "r", encoding="utf-8") as f:
+ if os.path.isfile(vdf_file := os.path.join(tool_path, 'toolmanifest.vdf')):
+ with open(vdf_file, encoding='utf-8') as f:
toolmanifest = vdf.load(f)
- if toolmanifest["manifest"].get("version") != "2":
+ if toolmanifest['manifest'].get('version') != '2':
continue
- if toolmanifest["manifest"].get("compatmanager_layer_name") == "container-runtime":
+ if toolmanifest['manifest'].get('compatmanager_layer_name') == 'container-runtime':
runtimes.update(
{
- appmanifest["AppState"]["appid"]: SteamRuntime(
+ appmanifest['AppState']['appid']: SteamRuntime(
steam_path=steam_path,
steam_library=library,
appmanifest=appmanifest,
tool_path=tool_path,
- toolmanifest=toolmanifest["manifest"],
+ toolmanifest=toolmanifest['manifest'],
)
}
)
return runtimes
-def find_umu_runtimes(umu_path: str) -> Dict[str, UmuRuntime]:
+def find_umu_runtimes(umu_path: str) -> dict[str, UmuRuntime]:
runtimes = {}
for appid, folder in UMU_RUNTIMES.items():
tool_path = os.path.join(umu_path, folder)
- if os.path.isdir(tool_path) and os.path.isfile(vdf_file := os.path.join(tool_path, "toolmanifest.vdf")):
- with open(vdf_file, "r", encoding="utf-8") as f:
+ if os.path.isdir(tool_path) and os.path.isfile(vdf_file := os.path.join(tool_path, 'toolmanifest.vdf')):
+ with open(vdf_file, encoding='utf-8') as f:
toolmanifest = vdf.load(f)
- if toolmanifest["manifest"].get("version") != "2":
+ if toolmanifest['manifest'].get('version') != '2':
continue
- if toolmanifest["manifest"].get("compatmanager_layer_name") == "container-runtime":
+ if toolmanifest['manifest'].get('compatmanager_layer_name') == 'container-runtime':
runtimes.update(
{
appid: UmuRuntime(
steam_path=None,
- name=f"umu-{folder}",
+ name=f'umu-{folder}',
appid=appid,
tool_path=tool_path,
- toolmanifest=toolmanifest["manifest"],
+ toolmanifest=toolmanifest['manifest'],
)
}
)
@@ -277,42 +276,42 @@ def find_umu_runtimes(umu_path: str) -> Dict[str, UmuRuntime]:
return runtimes
-def find_steam_tools(steam_path: str, library: str) -> List[ProtonTool]:
+def find_steam_tools(steam_path: str, library: str) -> list[ProtonTool]:
tools = []
appmanifests = find_appmanifests(library)
- common = os.path.join(library, "common")
+ common = os.path.join(library, 'common')
for appmanifest in appmanifests:
- folder = appmanifest["AppState"]["installdir"]
+ folder = appmanifest['AppState']['installdir']
tool_path = os.path.join(common, folder)
- if os.path.isfile(vdf_file := os.path.join(tool_path, "toolmanifest.vdf")):
- with open(vdf_file, "r", encoding="utf-8") as f:
+ if os.path.isfile(vdf_file := os.path.join(tool_path, 'toolmanifest.vdf')):
+ with open(vdf_file, encoding='utf-8') as f:
toolmanifest = vdf.load(f)
- if toolmanifest["manifest"].get("version") != "2":
+ if toolmanifest['manifest'].get('version') != '2':
continue
- if toolmanifest["manifest"].get("compatmanager_layer_name") == "proton":
+ if toolmanifest['manifest'].get('compatmanager_layer_name') == 'proton':
tools.append(
ProtonTool(
steam_path=steam_path,
steam_library=library,
appmanifest=appmanifest,
tool_path=tool_path,
- toolmanifest=toolmanifest["manifest"],
+ toolmanifest=toolmanifest['manifest'],
)
)
return tools
-def find_compatibility_tools(steam_path: str) -> List[CompatibilityTool]:
+def find_compatibility_tools(steam_path: str) -> list[CompatibilityTool]:
compatibilitytools_paths = {
- data_dir().joinpath("tools").as_posix(),
- os.path.expanduser("~/.local/share/umu/compatibilitytools"),
- os.path.expanduser("~/.steam/compatibilitytools.d"),
- os.path.expanduser("~/.steam/root/compatibilitytools.d"),
- "/usr/share/steam/compatibilitytools.d",
+ data_dir().joinpath('tools').as_posix(),
+ os.path.expanduser('~/.local/share/umu/compatibilitytools'),
+ os.path.expanduser('~/.steam/compatibilitytools.d'),
+ os.path.expanduser('~/.steam/root/compatibilitytools.d'),
+ '/usr/share/steam/compatibilitytools.d',
}
if steam_path:
compatibilitytools_paths.add(
- os.path.expanduser(os.path.join(steam_path, "compatibilitytools.d")),
+ os.path.expanduser(os.path.join(steam_path, 'compatibilitytools.d')),
)
compatibilitytools_paths = {os.path.realpath(path) for path in compatibilitytools_paths if os.path.isdir(path)}
tools = []
@@ -320,8 +319,8 @@ def find_compatibility_tools(steam_path: str) -> List[CompatibilityTool]:
for entry in os.scandir(path):
entry_path = os.path.join(path, entry.name)
if entry.is_dir():
- tool_vdf = os.path.join(entry_path, "compatibilitytool.vdf")
- elif entry.is_file() and entry.name.endswith(".vdf"):
+ tool_vdf = os.path.join(entry_path, 'compatibilitytool.vdf')
+ elif entry.is_file() and entry.name.endswith('.vdf'):
tool_vdf = entry_path
else:
continue
@@ -329,31 +328,31 @@ def find_compatibility_tools(steam_path: str) -> List[CompatibilityTool]:
if not os.path.isfile(tool_vdf):
continue
- with open(tool_vdf, "r", encoding="utf-8") as f:
+ with open(tool_vdf, encoding='utf-8') as f:
compatibilitytool = vdf.load(f)
- entry_tools = compatibilitytool["compatibilitytools"]["compat_tools"]
+ entry_tools = compatibilitytool['compatibilitytools']['compat_tools']
for entry_tool in entry_tools.values():
- if entry_tool.get("from_oslist") != "windows" or entry_tool.get("to_oslist") != "linux":
+ if entry_tool.get('from_oslist') != 'windows' or entry_tool.get('to_oslist') != 'linux':
continue
- install_path = entry_tool["install_path"]
+ install_path = entry_tool['install_path']
# lk: os.path.join understands the leading '/' in the last argument and it returns
# only the last argument if it starts with one.
tool_path = os.path.abspath(os.path.join(os.path.dirname(tool_vdf), install_path))
- manifest_vdf = os.path.join(tool_path, "toolmanifest.vdf")
+ manifest_vdf = os.path.join(tool_path, 'toolmanifest.vdf')
if not os.path.isfile(manifest_vdf):
continue
- with open(manifest_vdf, "r", encoding="utf-8") as f:
+ with open(manifest_vdf, encoding='utf-8') as f:
manifest = vdf.load(f)
tools.append(
CompatibilityTool(
steam_path=steam_path,
tool_path=tool_path,
- toolmanifest=manifest["manifest"],
+ toolmanifest=manifest['manifest'],
compatibilitytool=entry_tool,
)
)
@@ -361,84 +360,84 @@ def find_compatibility_tools(steam_path: str) -> List[CompatibilityTool]:
def get_runtime(
- tool: Union[ProtonTool, CompatibilityTool], runtimes: Dict[str, Union[SteamRuntime, UmuRuntime]]
-) -> Union[SteamRuntime, UmuRuntime, None]:
+ tool: ProtonTool | CompatibilityTool, runtimes: dict[str, SteamRuntime | UmuRuntime]
+) -> SteamRuntime | UmuRuntime | None:
required_tool = tool.required_tool
if required_tool is None:
return None
- return runtimes.get(required_tool, None)
+ return runtimes.get(required_tool)
-def get_umu_environment(tool: Optional[ProtonTool] = None, compat_path: Optional[str] = None) -> Dict:
+def get_umu_environment(tool: ProtonTool | None = None, compat_path: str | None = None) -> dict:
# If the tool is unset, return all affected env variable names
# IMPORTANT: keep this in sync with the code below
- environ = {"WINEPREFIX": compat_path if compat_path else ""}
+ environ = {'WINEPREFIX': compat_path if compat_path else ''}
if tool is None:
- environ["WINEPREFIX"] = ""
- environ["PROTONPATH"] = ""
- environ["GAMEID"] = ""
- environ["STORE"] = ""
+ environ['WINEPREFIX'] = ''
+ environ['PROTONPATH'] = ''
+ environ['GAMEID'] = ''
+ environ['STORE'] = ''
return environ
- environ["PROTONPATH"] = tool.tool_path
- environ["STORE"] = "egs"
+ environ['PROTONPATH'] = tool.tool_path
+ environ['STORE'] = 'egs'
return environ
def get_steam_environment(
- tool: Optional[Union[ProtonTool, CompatibilityTool]] = None,
- compat_path: Optional[str] = None,
-) -> Dict:
+ tool: ProtonTool | CompatibilityTool | None = None,
+ compat_path: str | None = None,
+) -> dict:
# If the tool is unset, return all affected env variable names
# IMPORTANT: keep this in sync with the code below
- environ = {"STEAM_COMPAT_DATA_PATH": compat_path if compat_path else ""}
-
- environ["STORE"] = ""
- environ["STEAM_COMPAT_CLIENT_INSTALL_PATH"] = ""
- environ["STEAM_COMPAT_LAUNCHER_SERVICE"] = ""
- environ["STEAM_COMPAT_LIBRARY_PATHS"] = ""
- environ["STEAM_COMPAT_MOUNTS"] = ""
- environ["STEAM_COMPAT_TOOL_PATHS"] = ""
- environ["PROTON_EAC_RUNTIME"] = ""
- environ["PROTON_BATTLEYE_RUNTIME"] = ""
+ environ = {'STEAM_COMPAT_DATA_PATH': compat_path if compat_path else ''}
+
+ environ['STORE'] = ''
+ environ['STEAM_COMPAT_CLIENT_INSTALL_PATH'] = ''
+ environ['STEAM_COMPAT_LAUNCHER_SERVICE'] = ''
+ environ['STEAM_COMPAT_LIBRARY_PATHS'] = ''
+ environ['STEAM_COMPAT_MOUNTS'] = ''
+ environ['STEAM_COMPAT_TOOL_PATHS'] = ''
+ environ['PROTON_EAC_RUNTIME'] = ''
+ environ['PROTON_BATTLEYE_RUNTIME'] = ''
if tool is None:
- environ["STEAM_COMPAT_DATA_PATH"] = ""
- environ["STEAM_COMPAT_INSTALL_PATH"] = ""
+ environ['STEAM_COMPAT_DATA_PATH'] = ''
+ environ['STEAM_COMPAT_INSTALL_PATH'] = ''
return environ
- environ["STORE"] = "egs"
+ environ['STORE'] = 'egs'
if tool.steam_path:
- environ["STEAM_COMPAT_CLIENT_INSTALL_PATH"] = tool.steam_path
- environ["STEAM_COMPAT_LAUNCHER_SERVICE"] = tool.layer
+ environ['STEAM_COMPAT_CLIENT_INSTALL_PATH'] = tool.steam_path
+ environ['STEAM_COMPAT_LAUNCHER_SERVICE'] = tool.layer
if isinstance(tool, ProtonTool):
- environ["STEAM_COMPAT_LIBRARY_PATHS"] = tool.steam_library
+ environ['STEAM_COMPAT_LIBRARY_PATHS'] = tool.steam_library
if tool.runtime is not None:
compat_mounts = [tool.tool_path, tool.runtime.tool_path]
- environ["STEAM_COMPAT_MOUNTS"] = ":".join(compat_mounts)
+ environ['STEAM_COMPAT_MOUNTS'] = ':'.join(compat_mounts)
tool_paths = [tool.tool_path]
if tool.runtime is not None:
tool_paths.append(tool.runtime.tool_path)
- environ["STEAM_COMPAT_TOOL_PATHS"] = ":".join(tool_paths)
+ environ['STEAM_COMPAT_TOOL_PATHS'] = ':'.join(tool_paths)
if tool.anticheat is not None:
- if (appid := ANTICHEAT_RUNTIMES["eac_runtime"]) in tool.anticheat.keys():
- environ["PROTON_EAC_RUNTIME"] = tool.anticheat[appid].tool_path
- if (appid := ANTICHEAT_RUNTIMES["battleye_runtime"]) in tool.anticheat.keys():
- environ["PROTON_BATTLEYE_RUNTIME"] = tool.anticheat[appid].tool_path
+ if (appid := ANTICHEAT_RUNTIMES['eac_runtime']) in tool.anticheat:
+ environ['PROTON_EAC_RUNTIME'] = tool.anticheat[appid].tool_path
+ if (appid := ANTICHEAT_RUNTIMES['battleye_runtime']) in tool.anticheat:
+ environ['PROTON_BATTLEYE_RUNTIME'] = tool.anticheat[appid].tool_path
return environ
-def _find_tools() -> List[Union[ProtonTool, CompatibilityTool]]:
+def _find_tools() -> list[ProtonTool | CompatibilityTool]:
steam_path = find_steam()
if steam_path is None:
- logger.info("Steam folder could not be found")
+ logger.info('Steam folder could not be found')
else:
- logger.info("Found Steam folder in %s", steam_path)
+ logger.info('Found Steam folder in %s', steam_path)
steam_libraries = set()
if steam_path:
steam_libraries.update(find_libraries(steam_path))
- logger.debug("Searching for tools in Steam libraries:")
- logger.debug("%s", steam_libraries)
+ logger.debug('Searching for tools in Steam libraries:')
+ logger.debug('%s', steam_libraries)
runtimes = {}
for library in steam_libraries:
@@ -446,9 +445,9 @@ def _find_tools() -> List[Union[ProtonTool, CompatibilityTool]]:
umu_path = find_umu()
if umu_path is None:
- logger.info("UMU folder could not be found")
+ logger.info('UMU folder could not be found')
else:
- logger.info("Found UMU folder in %s", umu_path)
+ logger.info('Found UMU folder in %s', umu_path)
if umu_path:
runtimes.update(find_umu_runtimes(umu_path))
@@ -465,7 +464,7 @@ def _find_tools() -> List[Union[ProtonTool, CompatibilityTool]]:
for tool in tools:
runtime = get_runtime(tool, runtimes)
tool.runtime = runtime
- if tool.layer == "proton":
+ if tool.layer == 'proton':
tool.anticheat = anticheat
tools = list(filter(lambda t: bool(t), tools))
@@ -474,25 +473,25 @@ def _find_tools() -> List[Union[ProtonTool, CompatibilityTool]]:
return tools
-_tools: Optional[List[Union[ProtonTool, CompatibilityTool]]] = None
+_tools: list[ProtonTool | CompatibilityTool] | None = None
-def find_tools() -> List[Union[ProtonTool, CompatibilityTool]]:
+def find_tools() -> list[ProtonTool | CompatibilityTool]:
global _tools
if _tools is None:
_tools = _find_tools()
- return list(filter(lambda t: t.layer != "umu-launcher", _tools))
+ return list(filter(lambda t: t.layer != 'umu-launcher', _tools))
-def find_umu_launcher() -> Optional[CompatibilityTool]:
+def find_umu_launcher() -> CompatibilityTool | None:
global _tools
if _tools is None:
_tools = _find_tools()
- _umu = list(filter(lambda t: t.layer == "umu-launcher", _tools))
+ _umu = list(filter(lambda t: t.layer == 'umu-launcher', _tools))
return _umu[0] if _umu else None
-if __name__ == "__main__":
+if __name__ == '__main__':
from pprint import pprint
tools = find_tools()
@@ -505,4 +504,4 @@ def find_umu_launcher() -> Optional[CompatibilityTool]:
print(get_steam_environment(tool))
print(tool.name)
print(tool.command(SteamVerb.RUN))
- print(" ".join(tool.command(SteamVerb.RUN_IN_PREFIX)))
+ print(' '.join(tool.command(SteamVerb.RUN_IN_PREFIX)))
diff --git a/rare/utils/compat/utils.py b/rare/utils/compat/utils.py
index 1c7a692e2d..8aae8e4033 100644
--- a/rare/utils/compat/utils.py
+++ b/rare/utils/compat/utils.py
@@ -1,52 +1,52 @@
import os
import platform as pf
import subprocess
+from collections.abc import Mapping
from configparser import ConfigParser
from getpass import getuser
from logging import getLogger
-from typing import Dict, List, Mapping, Tuple
from PySide6.QtCore import QProcess, QProcessEnvironment
-if pf.system() != "Windows":
- if pf.system() in {"Linux", "FreeBSD"}:
+if pf.system() != 'Windows':
+ if pf.system() in {'Linux', 'FreeBSD'}:
pass
-logger = getLogger("CompatUtils")
+logger = getLogger('CompatUtils')
# this is a copied function from legendary.utils.wine_helpers, but registry file can be specified
def read_registry(registry: str, prefix: str) -> ConfigParser:
- accepted = ["system.reg", "user.reg"]
+ accepted = ['system.reg', 'user.reg']
if registry not in accepted:
raise RuntimeError(f'Unknown target "{registry}" not in {accepted}')
- reg = ConfigParser(comment_prefixes=(";", "#", "/", "WINE"), allow_no_value=True, strict=False)
+ reg = ConfigParser(comment_prefixes=(';', '#', '/', 'WINE'), allow_no_value=True, strict=False)
reg.optionxform = str
- reg.read(os.path.join(prefix, "system.reg"))
+ reg.read(os.path.join(prefix, 'system.reg'))
return reg
-def prepare_process(command: List[str], environment: Dict) -> Tuple[str, List[str], Dict]:
- logger.debug("Preparing process: %s", command)
+def prepare_process(command: list[str], environment: dict) -> tuple[str, list[str], dict]:
+ logger.debug('Preparing process: %s', command)
_env = os.environ.copy()
_command = command.copy()
- if os.environ.get("container") == "flatpak":
- flatpak_command = ["flatpak-spawn", "--host"]
- flatpak_command.extend(f"--env={name}={value}" for name, value in environment.items())
+ if os.environ.get('container') == 'flatpak': # noqa: SIM112
+ flatpak_command = ['flatpak-spawn', '--host']
+ flatpak_command.extend(f'--env={name}={value}' for name, value in environment.items())
_command = flatpak_command + command
else:
_env.update(environment)
return _command[0], _command[1:] if len(_command) > 1 else [], _env
-def dict_to_qprocenv(env: Dict) -> QProcessEnvironment:
+def dict_to_qprocenv(env: dict) -> QProcessEnvironment:
_env = QProcessEnvironment()
for name, value in env.items():
_env.insert(name, value)
return _env
-def get_configured_qprocess(command: List[str], environment: Dict) -> QProcess:
+def get_configured_qprocess(command: list[str], environment: dict) -> QProcess:
cmd, args, env = prepare_process(command, environment)
proc = QProcess()
proc.setProcessChannelMode(QProcess.ProcessChannelMode.SeparateChannels)
@@ -56,7 +56,7 @@ def get_configured_qprocess(command: List[str], environment: Dict) -> QProcess:
return proc
-def get_configured_subprocess(command: List[str], environment: Dict) -> subprocess.Popen:
+def get_configured_subprocess(command: list[str], environment: dict) -> subprocess.Popen:
cmd, args, env = prepare_process(command, environment)
return subprocess.Popen(
(cmd, *args),
@@ -69,34 +69,34 @@ def get_configured_subprocess(command: List[str], environment: Dict) -> subproce
)
-def execute_subprocess(command: List[str], arguments: List[str], environment: Mapping) -> Tuple[str, str]:
+def execute_subprocess(command: list[str], arguments: list[str], environment: Mapping) -> tuple[str, str]:
proc = get_configured_subprocess(command + arguments, environment)
out, err = proc.communicate()
out, err = (
- out.decode("utf-8", "ignore") if out else "",
- err.decode("utf-8", "ignore") if err else "",
+ out.decode('utf-8', 'ignore') if out else '',
+ err.decode('utf-8', 'ignore') if err else '',
)
# lk: the following is a work-around for wineserver sometimes hanging around after
- proc = get_configured_subprocess(command + ["wineboot", "-e"], environment)
+ proc = get_configured_subprocess(command + ['wineboot', '-e'], environment)
_, _ = proc.communicate()
return out, err
-def execute_qprocess(command: List[str], arguments: List[str], environment: Mapping) -> Tuple[str, str]:
- logger.debug("WINEPREFIX: %s", environment.get("WINEPREFIX", None))
- logger.debug("STEAM_COMPAT_DATA_PATH: %s", environment.get("STEAM_COMPAT_DATA_PATH", None))
+def execute_qprocess(command: list[str], arguments: list[str], environment: Mapping) -> tuple[str, str]:
+ logger.debug('WINEPREFIX: %s', environment.get('WINEPREFIX', None))
+ logger.debug('STEAM_COMPAT_DATA_PATH: %s', environment.get('STEAM_COMPAT_DATA_PATH', None))
proc = get_configured_qprocess(command + arguments, environment)
proc.start()
proc.waitForFinished(-1)
out, err = (
- proc.readAllStandardOutput().data().decode("utf-8", "ignore"),
- proc.readAllStandardError().data().decode("utf-8", "ignore"),
+ proc.readAllStandardOutput().data().decode('utf-8', 'ignore'),
+ proc.readAllStandardError().data().decode('utf-8', 'ignore'),
)
proc.deleteLater()
# lk: the following is a work-around for wineserver sometimes hanging around after
- proc = get_configured_qprocess(command + ["wineboot", "-e"], environment)
+ proc = get_configured_qprocess(command + ['wineboot', '-e'], environment)
proc.start()
proc.waitForFinished(-1)
proc.deleteLater()
@@ -104,19 +104,19 @@ def execute_qprocess(command: List[str], arguments: List[str], environment: Mapp
return out, err
-def execute(command: List[str], arguments: List[str], environment: Mapping) -> Tuple[str, str]:
+def execute(command: list[str], arguments: list[str], environment: Mapping) -> tuple[str, str]:
try:
out, err = execute_qprocess(command, arguments, environment)
except Exception as e:
- out, err = "", str(e)
+ out, err = '', str(e)
return out, err
-def resolve_path(command: List[str], environment: Mapping, path: str) -> str:
- path = path.strip().replace("/", "\\")
+def resolve_path(command: list[str], environment: Mapping, path: str) -> str:
+ path = path.strip().replace('/', '\\')
# lk: if path does not exist form
- arguments = ["c:\\windows\\system32\\cmd.exe", "/c", "echo", path]
+ arguments = ['c:\\windows\\system32\\cmd.exe', '/c', 'echo', path]
# lk: if path exists and needs a case-sensitive interpretation form
# cmd = [wine_cmd, 'cmd', '/c', f'cd {path} & cd']
out, err = execute(command, arguments, environment)
@@ -131,29 +131,29 @@ def query_reg_path(wine_exec: str, wine_env: Mapping, reg_path: str):
raise NotImplementedError
-def query_reg_key(command: List[str], environment: Mapping, reg_path: str, reg_key) -> str:
- arguments = ["c:\\windows\\system32\\reg.exe", "query", reg_path, "/v", reg_key]
+def query_reg_key(command: list[str], environment: Mapping, reg_path: str, reg_key) -> str:
+ arguments = ['c:\\windows\\system32\\reg.exe', 'query', reg_path, '/v', reg_key]
out, err = execute(command, arguments, environment)
out, err = out.strip(), err.strip()
if not out:
logger.error('Failed to query registry key due to "%s"', err)
return out
- lines = out.split("\n")
- keys: Dict = {}
+ lines = out.split('\n')
+ keys: dict = {}
for line in lines:
- if line.startswith(" " * 4):
- key = [x for x in line.split(" " * 4, 3) if bool(x)]
+ if line.startswith(' ' * 4):
+ key = [x for x in line.split(' ' * 4, 3) if bool(x)]
keys.update({key[0]: key[2]})
- return keys.get(reg_key, "")
+ return keys.get(reg_key, '')
def convert_to_windows_path(wine_exec: str, wine_env: Mapping, path: str) -> str:
raise NotImplementedError
-def convert_to_unix_path(command: List[str], environment: Mapping, path: str) -> str:
+def convert_to_unix_path(command: list[str], environment: Mapping, path: str) -> str:
path = path.strip().strip('"')
- arguments = ["c:\\windows\\system32\\winepath.exe", "-u", path]
+ arguments = ['c:\\windows\\system32\\winepath.exe', '-u', path]
out, err = execute(command, arguments, environment)
out, err = out.strip(), err.strip()
if not out:
@@ -161,33 +161,33 @@ def convert_to_unix_path(command: List[str], environment: Mapping, path: str) ->
return os.path.realpath(out) if (out := out.strip()) else out
-def get_host_environment(app_environment: Dict, silent: bool = False) -> Dict:
+def get_host_environment(app_environment: dict, silent: bool = False) -> dict:
# Get a clean environment if we are in flatpak, this environment will be passed
# to `flatpak-spawn`, otherwise use the system's.
_environ = app_environment.copy()
if silent:
- _environ["WINEESYNC"] = "0"
- _environ["WINEFSYNC"] = "0"
- _environ["WINE_DISABLE_FAST_SYNC"] = "1"
- _environ["WINEDEBUG"] = "-all"
- _environ["WINEDLLOVERRIDES"] = "winemenubuilder=d;mscoree=d;mshtml=d;"
- _environ["WINEDLLOVERRIDES"] += "winex11.drv,winewayland.drv=d;"
+ _environ['WINEESYNC'] = '0'
+ _environ['WINEFSYNC'] = '0'
+ _environ['WINE_DISABLE_FAST_SYNC'] = '1'
+ _environ['WINEDEBUG'] = '-all'
+ _environ['WINEDLLOVERRIDES'] = 'winemenubuilder=d;mscoree=d;mshtml=d;'
+ _environ['WINEDLLOVERRIDES'] += 'winex11.drv,winewayland.drv=d;'
# lk: pressure-vessel complains about this but it doesn't fail due to it
# _environ["DISPLAY"] = ""
return _environ
def create_compat_users(pfx: str):
- pfx_users = os.path.join(pfx, "drive_c", "users")
+ pfx_users = os.path.join(pfx, 'drive_c', 'users')
os.makedirs(pfx_users, exist_ok=True)
- steam_user = os.path.join(pfx_users, "steamuser")
+ steam_user = os.path.join(pfx_users, 'steamuser')
unix_user = os.path.join(pfx_users, getuser())
if not os.path.exists(steam_user) and not os.path.exists(unix_user):
os.makedirs(steam_user, exist_ok=True)
pwd = os.getcwd()
os.chdir(pfx_users)
if os.path.exists(steam_user) and not os.path.exists(unix_user):
- os.symlink("steamuser", getuser())
+ os.symlink('steamuser', getuser())
if not os.path.exists(steam_user) and os.path.exists(unix_user):
- os.symlink(getuser(), "steamuser")
+ os.symlink(getuser(), 'steamuser')
os.chdir(pwd)
diff --git a/rare/utils/compat/wine.py b/rare/utils/compat/wine.py
index c92189f4b5..2dd441ea61 100644
--- a/rare/utils/compat/wine.py
+++ b/rare/utils/compat/wine.py
@@ -1,93 +1,92 @@
import os
from dataclasses import dataclass
from logging import getLogger
-from typing import Dict, List, Optional, Tuple
-logger = getLogger("Wine")
+logger = getLogger('Wine')
-lutris_runtime_paths = [os.path.expanduser("~/.local/share/lutris")]
+lutris_runtime_paths = [os.path.expanduser('~/.local/share/lutris')]
__lutris_runtime: str = None
__lutris_wine: str = None
-def find_lutris() -> Tuple[str, str]:
+def find_lutris() -> tuple[str, str]:
global __lutris_runtime, __lutris_wine
for path in lutris_runtime_paths:
- runtime_path = os.path.join(path, "runtime")
- wine_path = os.path.join(path, "runners", "wine")
+ runtime_path = os.path.join(path, 'runtime')
+ wine_path = os.path.join(path, 'runners', 'wine')
if os.path.isdir(path) and os.path.isdir(runtime_path) and os.path.isdir(wine_path):
__lutris_runtime, __lutris_wine = runtime_path, wine_path
return runtime_path, wine_path
- return "", ""
+ return '', ''
@dataclass
class WineRuntime:
name: str
path: str
- environ: Dict
+ environ: dict
@dataclass
class WineRunner:
name: str
path: str
- environ: Dict
- runtime: Optional[WineRuntime] = None
+ environ: dict
+ runtime: WineRuntime | None = None
-def find_lutris_wines(runtime_path: str = None, wine_path: str = None) -> List[WineRunner]:
+def find_lutris_wines(runtime_path: str = None, wine_path: str = None) -> list[WineRunner]:
runners = []
if not runtime_path and not wine_path:
return runners
return runners
-def __get_lib_path(executable: str, basename: str = "") -> str:
+def __get_lib_path(executable: str, basename: str = '') -> str:
path = os.path.dirname(os.path.dirname(executable))
- lib32 = os.path.realpath(os.path.join(path, "lib32", basename))
- lib64 = os.path.realpath(os.path.join(path, "lib64", basename))
- lib = os.path.realpath(os.path.join(path, "lib", basename))
+ lib32 = os.path.realpath(os.path.join(path, 'lib32', basename))
+ lib64 = os.path.realpath(os.path.join(path, 'lib64', basename))
+ lib = os.path.realpath(os.path.join(path, 'lib', basename))
if lib32 == lib or not os.path.exists(lib32):
- ldpath = ":".join([lib64, lib])
+ ldpath = ':'.join([lib64, lib])
elif lib64 == lib or not os.path.exists(lib64):
- ldpath = ":".join([lib, lib32])
+ ldpath = ':'.join([lib, lib32])
else:
ldpath = lib if os.path.exists(lib) else lib64
return ldpath
-def get_wine_environment(executable: str = None, prefix: str = None) -> Dict:
+def get_wine_environment(executable: str = None, prefix: str = None) -> dict:
# If the tool is unset, return all affected env variable names
# IMPORTANT: keep this in sync with the code below
- environ = {"WINEPREFIX": prefix if prefix is not None else ""}
+ environ = {'WINEPREFIX': prefix if prefix is not None else ''}
if executable is None:
- environ["WINEDLLPATH"] = ""
- environ["LD_LIBRARY_PATH"] = ""
+ environ['WINEDLLPATH'] = ''
+ environ['LD_LIBRARY_PATH'] = ''
else:
- winedllpath = __get_lib_path(executable, "wine")
- environ["WINEDLLPATH"] = winedllpath
- librarypath = __get_lib_path(executable, "")
- environ["LD_LIBRARY_PATH"] = librarypath
+ winedllpath = __get_lib_path(executable, 'wine')
+ environ['WINEDLLPATH'] = winedllpath
+ librarypath = __get_lib_path(executable, '')
+ environ['LD_LIBRARY_PATH'] = librarypath
return environ
-if __name__ == "__main__":
+if __name__ == '__main__':
from pprint import pprint
- pprint(get_wine_environment("/opt/wine-ge-custom/bin/wine", None))
- pprint(get_wine_environment("/usr/bin/wine", None))
- pprint(get_wine_environment("/usr/share/steam/compatitiblitytools.d/dist/bin/wine", None))
+ pprint(get_wine_environment('/opt/wine-ge-custom/bin/wine', None))
+ pprint(get_wine_environment('/usr/bin/wine', None))
+ pprint(get_wine_environment('/usr/share/steam/compatitiblitytools.d/dist/bin/wine', None))
pprint(
get_wine_environment(
- os.path.expanduser("~/.local/share/Steam/compatibilitytools.d/GE-Proton8-14/files/bin/wine"),
+ os.path.expanduser('~/.local/share/Steam/compatibilitytools.d/GE-Proton8-14/files/bin/wine'),
None,
)
)
pprint(
get_wine_environment(
- os.path.expanduser("~/.local/share/lutris/runners/wine/lutris-GE-Proton8-14-x86_64/bin/wine"),
+ os.path.expanduser('~/.local/share/lutris/runners/wine/lutris-GE-Proton8-14-x86_64/bin/wine'),
None,
)
)
diff --git a/rare/utils/config_helper.py b/rare/utils/config_helper.py
index 2f0747cf34..683b0e202a 100644
--- a/rare/utils/config_helper.py
+++ b/rare/utils/config_helper.py
@@ -1,13 +1,14 @@
import os
+from collections.abc import Callable
from configparser import SectionProxy
-from typing import Any, Callable, Optional, Set, Tuple
+from typing import Any
from legendary.models.config import LGDConf
from rare.lgndr.core import LegendaryCore
-_config: Optional[LGDConf] = None
-_save_config: Optional[Callable[[], None]] = None
+_config: LGDConf | None = None
+_save_config: Callable[[], None] | None = None
def init_config_handler(core: LegendaryCore):
@@ -18,12 +19,12 @@ def init_config_handler(core: LegendaryCore):
def save_config():
if _save_config is None:
- raise RuntimeError("Uninitialized use of config_helper")
+ raise RuntimeError('Uninitialized use of config_helper')
_save_config()
def set_option(app_name: str, option: str, value: str) -> None:
- value = value.replace("%%", "%").replace("%", "%%")
+ value = value.replace('%%', '%').replace('%', '%%')
if not _config.has_section(app_name):
_config[app_name] = {}
_config.set(app_name, option, value)
@@ -35,7 +36,7 @@ def set_boolean(app_name: str, option: str, value: bool) -> None:
def set_envvar(app_name: str, envvar: str, value: str) -> None:
- set_option(f"{app_name}.env", envvar, value)
+ set_option(f'{app_name}.env', envvar, value)
def remove_section(app_name: str) -> None:
@@ -55,7 +56,7 @@ def remove_option(app_name: str, option: str) -> None:
def remove_envvar(app_name: str, option: str) -> None:
- remove_option(f"{app_name}.env", option)
+ remove_option(f'{app_name}.env', option)
def adjust_option(app_name: str, option: str, value: str) -> None:
@@ -70,7 +71,7 @@ def get_option(app_name: str, option: str, fallback: Any = None) -> str:
def get_option_with_global(app_name: str, option: str, fallback: Any = None) -> str:
- _option = get_option("default", option, fallback=fallback)
+ _option = get_option('default', option, fallback=fallback)
_option = get_option(app_name, option, fallback=_option)
return _option
@@ -87,90 +88,90 @@ def adjust_envvar(app_name: str, option: str, value: str) -> None:
def get_envvar(app_name: str, option: str, fallback: Any = None) -> str:
- return get_option(f"{app_name}.env", option, fallback=fallback)
+ return get_option(f'{app_name}.env', option, fallback=fallback)
def get_envvar_with_global(app_name: str, option: str, fallback: Any = None) -> str:
- _option = _config.get("default.env", option, fallback=fallback)
- _option = _config.get(f"{app_name}.env", option, fallback=_option)
+ _option = _config.get('default.env', option, fallback=fallback)
+ _option = _config.get(f'{app_name}.env', option, fallback=_option)
return _option
def adjust_wine_prefix(app_name: str, value: str) -> None:
- adjust_envvar(app_name, "WINEPREFIX", value)
- adjust_option(app_name, "wine_prefix", value)
+ adjust_envvar(app_name, 'WINEPREFIX', value)
+ adjust_option(app_name, 'wine_prefix', value)
def get_wine_prefix(app_name: str, fallback: Any = None):
- _prefix = get_envvar(app_name, "WINEPREFIX", fallback=fallback)
- _prefix = get_option(app_name, "wine_prefix", fallback=_prefix)
+ _prefix = get_envvar(app_name, 'WINEPREFIX', fallback=fallback)
+ _prefix = get_option(app_name, 'wine_prefix', fallback=_prefix)
return _prefix
def get_wine_prefix_with_global(app_name: str, fallback: Any = None) -> str:
- _prefix = get_wine_prefix("default", fallback)
+ _prefix = get_wine_prefix('default', fallback)
_prefix = get_wine_prefix(app_name, fallback=_prefix)
return _prefix
def adjust_compat_data_path(app_name: str, value: str) -> None:
- adjust_envvar(app_name, "STEAM_COMPAT_DATA_PATH", value)
+ adjust_envvar(app_name, 'STEAM_COMPAT_DATA_PATH', value)
-def get_compat_data_path(app_name: Optional[str] = None, fallback: Any = None) -> str:
- _compat = get_envvar(app_name, "STEAM_COMPAT_DATA_PATH", fallback=fallback)
+def get_compat_data_path(app_name: str | None = None, fallback: Any = None) -> str:
+ _compat = get_envvar(app_name, 'STEAM_COMPAT_DATA_PATH', fallback=fallback)
# return os.path.join(_compat, "pfx") if _compat else fallback
return _compat
-def get_compat_data_path_with_global(app_name: Optional[str] = None, fallback: Any = None) -> str:
- _compat = get_envvar_with_global(app_name, "STEAM_COMPAT_DATA_PATH", fallback=fallback)
+def get_compat_data_path_with_global(app_name: str | None = None, fallback: Any = None) -> str:
+ _compat = get_envvar_with_global(app_name, 'STEAM_COMPAT_DATA_PATH', fallback=fallback)
# return os.path.join(_compat, "pfx") if _compat else fallback
return _compat
def prefix_exists(pfx: str) -> bool:
- return os.path.isdir(pfx) and os.path.isfile(os.path.join(pfx, "user.reg"))
+ return os.path.isdir(pfx) and os.path.isfile(os.path.join(pfx, 'user.reg'))
-def _get_prefixes(lookup_fn: Callable[[SectionProxy], str]) -> Set[Tuple[str, str]]:
- _prefixes: Set[Tuple[str, str]] = set()
+def _get_prefixes(lookup_fn: Callable[[SectionProxy], str]) -> set[tuple[str, str]]:
+ _prefixes: set[tuple[str, str]] = set()
for name, section in _config.items():
pfx = lookup_fn(section)
if pfx:
- _prefixes.update([(pfx, name[: -len(".env")] if name.endswith(".env") else name)])
+ _prefixes.update([(pfx, name[: -len('.env')] if name.endswith('.env') else name)])
_prefixes = {(os.path.expanduser(p), n) for p, n in _prefixes}
return {(p, n) for p, n in _prefixes if prefix_exists(p)}
-def get_wine_prefixes() -> Set[Tuple[str, str]]:
- return _get_prefixes(lambda s: s.get("WINEPREFIX") or s.get("wine_prefix"))
+def get_wine_prefixes() -> set[tuple[str, str]]:
+ return _get_prefixes(lambda s: s.get('WINEPREFIX') or s.get('wine_prefix'))
-def get_proton_prefixes() -> Set[Tuple[str, str]]:
- return _get_prefixes(lambda s: os.path.join(compat_path, "pfx") if (compat_path := s.get("STEAM_COMPAT_DATA_PATH")) else "")
+def get_proton_prefixes() -> set[tuple[str, str]]:
+ return _get_prefixes(lambda s: os.path.join(compat_path, 'pfx') if (compat_path := s.get('STEAM_COMPAT_DATA_PATH')) else '')
-def get_prefixes() -> Set[Tuple[str, str]]:
+def get_prefixes() -> set[tuple[str, str]]:
return get_wine_prefixes().union(get_proton_prefixes())
-def get_prefix(app_name: str = "default") -> Optional[str]:
- _compat_path = _config.get(f"{app_name}.env", "STEAM_COMPAT_DATA_PATH", fallback=None)
- if _compat_path and prefix_exists(_compat_prefix := os.path.join(_compat_path, "pfx")):
+def get_prefix(app_name: str = 'default') -> str | None:
+ _compat_path = _config.get(f'{app_name}.env', 'STEAM_COMPAT_DATA_PATH', fallback=None)
+ if _compat_path and prefix_exists(_compat_prefix := os.path.join(_compat_path, 'pfx')):
return _compat_prefix
- _wine_prefix = _config.get(f"{app_name}.env", "WINEPREFIX", fallback=None)
- _wine_prefix = _config.get(app_name, "wine_prefix", fallback=_wine_prefix)
+ _wine_prefix = _config.get(f'{app_name}.env', 'WINEPREFIX', fallback=None)
+ _wine_prefix = _config.get(app_name, 'wine_prefix', fallback=_wine_prefix)
if _wine_prefix and prefix_exists(_wine_prefix):
return _wine_prefix
- _compat_path = _config.get("default.env", "STEAM_COMPAT_DATA_PATH", fallback=None)
- if _compat_path and prefix_exists(_compat_prefix := os.path.join(_compat_path, "pfx")):
+ _compat_path = _config.get('default.env', 'STEAM_COMPAT_DATA_PATH', fallback=None)
+ if _compat_path and prefix_exists(_compat_prefix := os.path.join(_compat_path, 'pfx')):
return _compat_prefix
- _wine_prefix = _config.get("default.env", "WINEPREFIX", fallback=None)
- _wine_prefix = _config.get("default", "wine_prefix", fallback=_wine_prefix)
+ _wine_prefix = _config.get('default.env', 'WINEPREFIX', fallback=None)
+ _wine_prefix = _config.get('default', 'wine_prefix', fallback=_wine_prefix)
if _wine_prefix and prefix_exists(_wine_prefix):
return _wine_prefix
diff --git a/rare/utils/discord_rpc.py b/rare/utils/discord_rpc.py
index 88bad87bda..de9457738e 100644
--- a/rare/utils/discord_rpc.py
+++ b/rare/utils/discord_rpc.py
@@ -1,7 +1,6 @@
import platform
import time
from logging import getLogger
-from typing import List
from pypresence import Presence, exceptions
from PySide6.QtCore import QObject, Slot
@@ -9,8 +8,8 @@
from rare.models.settings import DiscordRPCMode, RareAppSettings, app_settings
from rare.shared import RareCore
-client_id = "830732538225360908"
-logger = getLogger("DiscordRPC")
+client_id = '830732538225360908'
+logger = getLogger('DiscordRPC')
class DiscordRPC(QObject):
@@ -40,7 +39,7 @@ def remove_presence(self, app_name: str):
@Slot()
@Slot(list)
- def update_settings(self, game_running: List = None):
+ def update_settings(self, game_running: list = None):
rpc_mode = DiscordRPCMode(self.settings.get_value(app_settings.discord_rpc_mode))
if rpc_mode == DiscordRPCMode.NEVER:
self.remove_rpc()
@@ -60,10 +59,10 @@ def remove_rpc(self):
try:
self.rpc.close()
except Exception:
- logger.warning("Already closed")
+ logger.warning('Already closed')
del self.rpc
self.rpc = None
- logger.info("Remove RPC")
+ logger.info('Remove RPC')
else:
self.set_discord_rpc()
@@ -73,15 +72,15 @@ def set_discord_rpc(self, app_name=None):
self.rpc = Presence(client_id) # Rare app: https://discord.com/developers/applications
self.rpc.connect()
except ConnectionRefusedError as e:
- logger.warning(f"Discord is not active\n{e}")
+ logger.warning(f'Discord is not active\n{e}')
self.rpc = None
return
except FileNotFoundError as e:
- logger.warning(f"File not found error\n{e}")
+ logger.warning(f'File not found error\n{e}')
self.rpc = None
return
except exceptions.InvalidPipe as e:
- logger.error(f"Is Discord running? \n{e}")
+ logger.error(f'Is Discord running? \n{e}')
self.rpc = None
return
except Exception as e:
@@ -97,19 +96,19 @@ def update_rpc(self, app_name=None):
return
title = None
if not app_name:
- self.rpc.update(large_image="logo", details="https://github.com/RareDevs/Rare")
+ self.rpc.update(large_image='logo', details='https://github.com/RareDevs/Rare')
return
if self.settings.get_value(app_settings.discord_rpc_game):
try:
title = self.core.get_installed_game(app_name).title
except AttributeError:
- logger.error(f"Could not get title of game: {app_name}")
+ logger.error(f'Could not get title of game: {app_name}')
title = app_name
start = None
if self.settings.get_value(app_settings.discord_rpc_time):
- start = str(time.time()).split(".")[0]
+ start = str(time.time()).split('.')[0]
os = None
if self.settings.get_value(app_settings.discord_rpc_os):
- os = f"via Rare on {platform.system()}"
+ os = f'via Rare on {platform.system()}'
- self.rpc.update(large_image="logo", details=title, large_text=title, state=os, start=start)
+ self.rpc.update(large_image='logo', details=title, large_text=title, state=os, start=start)
diff --git a/rare/utils/json_formatter.py b/rare/utils/json_formatter.py
index d9e60dbb77..dc8e1a084e 100644
--- a/rare/utils/json_formatter.py
+++ b/rare/utils/json_formatter.py
@@ -42,12 +42,12 @@
from PySide6 import QtCore, QtWidgets
-class QJsonTreeItem(object):
+class QJsonTreeItem:
def __init__(self, parent=None):
self._parent = parent
- self._key = ""
- self._value = ""
+ self._key = ''
+ self._value = ''
self._type = None
self._children = list()
@@ -93,22 +93,22 @@ def type(self, typ):
@classmethod
def load(self, value, parent=None, sort=True):
rootItem = QJsonTreeItem(parent)
- rootItem.key = "root"
+ rootItem.key = 'root'
if isinstance(value, dict):
items = sorted(value.items()) if sort else value.items()
- for key, value in items:
- child = self.load(value, rootItem)
+ for key, val in items:
+ child = self.load(val, rootItem)
child.key = key
- child.type = type(value)
+ child.type = type(val)
rootItem.appendChild(child)
elif isinstance(value, list):
- for index, value in enumerate(value):
- child = self.load(value, rootItem)
- child.key = index
- child.type = type(value)
+ for idx, val in enumerate(value):
+ child = self.load(val, rootItem)
+ child.key = idx
+ child.type = type(val)
rootItem.appendChild(child)
else:
@@ -123,7 +123,7 @@ def __init__(self, parent=None):
super(QJsonModel, self).__init__(parent)
self._rootItem = QJsonTreeItem()
- self._headers = ("key", "value")
+ self._headers = ('key', 'value')
def clear(self):
self.load({})
@@ -136,7 +136,7 @@ def load(self, document):
"""
- assert isinstance(document, (dict, list, tuple)), "`document` must be of dict, list or tuple, not %s" % type(document)
+ assert isinstance(document, (dict, list, tuple)), f'`document` must be of dict, list or tuple, not {type(document)}'
self.beginResetModel()
@@ -179,6 +179,8 @@ def data(self, index, role):
if index.column() == 1:
return item.value
+ return None
+
def setData(self, index, value, role):
if role == QtCore.Qt.ItemDataRole.EditRole:
if index.column() == 1:
@@ -198,7 +200,12 @@ def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Orientation.Horizontal:
return self._headers[section]
- def index(self, row, column, parent=QtCore.QModelIndex()):
+ return None
+
+ def index(self, row, column, parent=None):
+ if parent is None:
+ parent = QtCore.QModelIndex()
+
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
@@ -225,7 +232,9 @@ def parent(self, index):
return self.createIndex(parentItem.row(), 0, parentItem)
- def rowCount(self, parent=QtCore.QModelIndex()):
+ def rowCount(self, parent=None):
+ if parent is None:
+ parent = QtCore.QModelIndex()
if parent.column() > 0:
return 0
@@ -236,7 +245,9 @@ def rowCount(self, parent=QtCore.QModelIndex()):
return parentItem.childCount()
- def columnCount(self, parent=QtCore.QModelIndex()):
+ def columnCount(self, parent=None):
+ if parent is None:
+ parent = QtCore.QModelIndex()
return 2
def flags(self, index):
@@ -251,24 +262,24 @@ def genJson(self, item):
nchild = item.childCount()
if item.type is dict:
- document = {}
+ _document = {}
for i in range(nchild):
ch = item.child(i)
- document[ch.key] = self.genJson(ch)
- return document
+ _document[ch.key] = self.genJson(ch)
+ return _document
elif item.type is list:
- document = []
+ _document = []
for i in range(nchild):
ch = item.child(i)
- document.append(self.genJson(ch))
- return document
+ _document.append(self.genJson(ch))
+ return _document
else:
return item.value
-if __name__ == "__main__":
+if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
diff --git a/rare/utils/metrics.py b/rare/utils/metrics.py
index bcc969d781..cd95db1f0d 100644
--- a/rare/utils/metrics.py
+++ b/rare/utils/metrics.py
@@ -7,4 +7,4 @@
def timelogger(logger: Logger, title: str):
start = time.perf_counter()
yield
- logger.debug("%s: %s seconds", title, time.perf_counter() - start)
+ logger.debug('%s: %s seconds', title, time.perf_counter() - start)
diff --git a/rare/utils/misc.py b/rare/utils/misc.py
index 065385f16d..7d06dcecd0 100644
--- a/rare/utils/misc.py
+++ b/rare/utils/misc.py
@@ -1,9 +1,9 @@
import functools
import os
+from collections.abc import Iterable
from datetime import UTC, datetime
from enum import IntEnum
from logging import getLogger
-from typing import Dict, Iterable, Tuple, Type, Union
import qtawesome
from PySide6.QtCore import (
@@ -20,7 +20,7 @@
from rare.utils.paths import resources_path
-logger = getLogger("Utils")
+logger = getLogger('Utils')
class ExitCodes(IntEnum):
@@ -28,35 +28,35 @@ class ExitCodes(IntEnum):
LOGOUT = -133742
-color_role_map: Dict[int, str] = {
- 0: "WindowText",
- 1: "Button",
- 2: "Light",
- 3: "Midlight",
- 4: "Dark",
- 5: "Mid",
- 6: "Text",
- 7: "BrightText",
- 8: "ButtonText",
- 9: "Base",
- 10: "Window",
- 11: "Shadow",
- 12: "Highlight",
- 13: "HighlightedText",
- 14: "Link",
- 15: "LinkVisited",
- 16: "AlternateBase",
+color_role_map: dict[int, str] = {
+ 0: 'WindowText',
+ 1: 'Button',
+ 2: 'Light',
+ 3: 'Midlight',
+ 4: 'Dark',
+ 5: 'Mid',
+ 6: 'Text',
+ 7: 'BrightText',
+ 8: 'ButtonText',
+ 9: 'Base',
+ 10: 'Window',
+ 11: 'Shadow',
+ 12: 'Highlight',
+ 13: 'HighlightedText',
+ 14: 'Link',
+ 15: 'LinkVisited',
+ 16: 'AlternateBase',
# 17: "NoRole",
- 18: "ToolTipBase",
- 19: "ToolTipText",
- 20: "PlaceholderText",
+ 18: 'ToolTipBase',
+ 19: 'ToolTipText',
+ 20: 'PlaceholderText',
# 21: "NColorRoles",
}
-color_group_map: Dict[int, str] = {
- 0: "Active",
- 1: "Disabled",
- 2: "Inactive",
+color_group_map: dict[int, str] = {
+ 0: 'Active',
+ 1: 'Disabled',
+ 2: 'Inactive',
}
@@ -64,7 +64,7 @@ def load_color_scheme(path: str) -> QPalette:
palette = QPalette()
scheme = QSettings(path, QSettings.Format.IniFormat)
try:
- scheme.beginGroup("ColorScheme")
+ scheme.beginGroup('ColorScheme')
for g in color_group_map:
scheme.beginGroup(color_group_map[g])
group = QPalette.ColorGroup(g)
@@ -83,9 +83,9 @@ def load_color_scheme(path: str) -> QPalette:
def get_static_style() -> str:
- file = QFile(":/static_css/stylesheet.qss")
+ file = QFile(':/static_css/stylesheet.qss')
file.open(QFile.OpenModeFlag.ReadOnly)
- static = file.readAll().data().decode("utf-8")
+ static = file.readAll().data().decode('utf-8')
file.close()
return static
@@ -95,13 +95,13 @@ def set_color_pallete(color_scheme: str) -> None:
qApp: QApplication = QApplication.instance()
if not color_scheme:
- qApp.setStyle(QStyleFactory.create(qApp.property("rareDefaultQtStyle")))
+ qApp.setStyle(QStyleFactory.create(qApp.property('rareDefaultQtStyle')))
qApp.setPalette(qApp.style().standardPalette())
qApp.setStyleSheet(static)
return
- qApp.setStyle(QStyleFactory.create("Fusion"))
- custom_palette = load_color_scheme(f":/schemes/{color_scheme}")
+ qApp.setStyle(QStyleFactory.create('Fusion'))
+ custom_palette = load_color_scheme(f':/schemes/{color_scheme}')
if custom_palette is not None:
qApp.setPalette(custom_palette)
qApp.setStyleSheet(static)
@@ -111,7 +111,7 @@ def set_color_pallete(color_scheme: str) -> None:
def get_color_schemes() -> Iterable[str]:
- it = QDirIterator(":/schemes/")
+ it = QDirIterator(':/schemes/')
while it.hasNext():
it.next()
yield it.fileName()
@@ -122,44 +122,44 @@ def set_style_sheet(style_sheet: str) -> None:
qApp: QApplication = QApplication.instance()
if not style_sheet:
- qApp.setStyle(QStyleFactory.create(qApp.property("rareDefaultQtStyle")))
+ qApp.setStyle(QStyleFactory.create(qApp.property('rareDefaultQtStyle')))
qApp.setStyleSheet(static)
return
- qApp.setStyle(QStyleFactory.create("Fusion"))
- file = QFile(f":/stylesheets/{style_sheet}/stylesheet.qss")
+ qApp.setStyle(QStyleFactory.create('Fusion'))
+ file = QFile(f':/stylesheets/{style_sheet}/stylesheet.qss')
file.open(QFile.OpenModeFlag.ReadOnly)
- stylesheet = file.readAll().data().decode("utf-8")
+ stylesheet = file.readAll().data().decode('utf-8')
file.close()
qApp.setStyleSheet(stylesheet + static)
icon_color_normal = qApp.palette().color(QPalette.ColorRole.Text).name() # noqa: F841
icon_color_disabled = qApp.palette().color(QPalette.ColorRole.Text).name() # noqa: F841
- qtawesome.set_defaults(color="#eee", color_disabled="#eee")
+ qtawesome.set_defaults(color='#eee', color_disabled='#eee')
def get_style_sheets() -> Iterable[str]:
- it = QDirIterator(":/stylesheets/")
+ it = QDirIterator(':/stylesheets/')
while it.hasNext():
it.next()
yield it.fileName()
-def get_translations() -> Tuple[Tuple[str, str], ...]:
+def get_translations() -> tuple[tuple[str, str], ...]:
langs = []
- for i in os.listdir(os.path.join(resources_path, "languages")):
- if i.endswith(".qm") and i.startswith("rare_"):
- locale = QLocale(i.removesuffix(".qm").removeprefix("rare_"))
+ for i in os.listdir(os.path.join(resources_path, 'languages')):
+ if i.endswith('.qm') and i.startswith('rare_'):
+ locale = QLocale(i.removesuffix('.qm').removeprefix('rare_'))
langs.append(
(
locale.name(),
- f"{locale.nativeLanguageName()} ({locale.nativeCountryName()})",
+ f'{locale.nativeLanguageName()} ({locale.nativeCountryName()})',
)
)
return tuple(langs)
-def path_size(path: Union[str, os.PathLike]) -> int:
+def path_size(path: str | os.PathLike) -> int:
return sum(
os.stat(os.path.join(dp, f)).st_size
for dp, dn, filenames in os.walk(path)
@@ -168,10 +168,10 @@ def path_size(path: Union[str, os.PathLike]) -> int:
)
-def format_size(b: Union[int, float]) -> str:
- for s in ("", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"):
+def format_size(b: int | float) -> str:
+ for s in ('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei'):
if b < 1024:
- return f"{b:.2f} {s}B"
+ return f'{b:.2f} {s}B'
b /= 1024
return str(b)
@@ -180,23 +180,23 @@ def relative_date(date):
diff = datetime.now(UTC) - date
s = diff.seconds
if diff.days > 7 or diff.days < 0:
- return date.strftime("%d %b %y")
+ return date.strftime('%d %b %y')
elif diff.days == 1:
- return "1 day ago"
+ return '1 day ago'
elif diff.days > 1:
- return "{} days ago".format(diff.days)
+ return f'{diff.days} days ago'
elif s <= 1:
- return "just now"
+ return 'just now'
elif s < 60:
- return "{} seconds ago".format(s)
+ return f'{s} seconds ago'
elif s < 120:
- return "1 minute ago"
+ return '1 minute ago'
elif s < 3600:
- return "{} minutes ago".format(s // 60)
+ return f'{s // 60} minutes ago'
elif s < 7200:
- return "1 hour ago"
+ return '1 hour ago'
else:
- return "{} hours ago".format(s // 3600)
+ return f'{s // 3600} hours ago'
def qta_icon(icn_str: str, fallback: str = None, **kwargs):
@@ -204,15 +204,15 @@ def qta_icon(icn_str: str, fallback: str = None, **kwargs):
return qtawesome.icon(icn_str, **kwargs)
except Exception as e:
if not fallback:
- logger.warning(f"{e} {icn_str}")
+ logger.warning(f'{e} {icn_str}')
if fallback:
try:
return qtawesome.icon(fallback, **kwargs)
except Exception as e:
- logger.error(f"{e} {icn_str}")
- if kwargs.get("color"):
- kwargs["color"] = "red"
- return qtawesome.icon("ei.error", **kwargs)
+ logger.error(f'{e} {icn_str}')
+ if kwargs.get('color'):
+ kwargs['color'] = 'red'
+ return qtawesome.icon('ei.error', **kwargs)
# Source - https://stackoverflow.com/a
@@ -226,14 +226,14 @@ def partial_bound_method(bound_method, *args, **kwargs):
return (lambda *args: f(*args)).__get__(bound_method.__self__)
-def widget_object_name(widget: Union[QObject, ShibokenObject, Type], suffix: str) -> str:
- suffix = f"_{suffix}" if suffix else ""
+def widget_object_name(widget: QObject | ShibokenObject | type, suffix: str) -> str:
+ suffix = f'_{suffix}' if suffix else ''
if isinstance(widget, QObject):
- return f"{type(widget).__name__}{suffix}"
- elif isinstance(widget, ShibokenObject) or isinstance(widget, type):
- return f"{widget.__name__}{suffix}"
+ return f'{type(widget).__name__}{suffix}'
+ elif isinstance(widget, (ShibokenObject, type)):
+ return f'{widget.__name__}{suffix}'
else:
- raise RuntimeError(f"Argument {widget} not a QObject or type of QObject")
+ raise RuntimeError(f'Argument {widget} not a QObject or type of QObject')
def elide_text(label: QLabel, text: str) -> str:
@@ -242,4 +242,4 @@ def elide_text(label: QLabel, text: str) -> str:
def style_hyperlink(link: str, title: str) -> str:
- return "{}".format(link, title)
+ return f"{title}"
diff --git a/rare/utils/paths.py b/rare/utils/paths.py
index 9f50db1aab..6bb64dc0af 100644
--- a/rare/utils/paths.py
+++ b/rare/utils/paths.py
@@ -5,36 +5,35 @@
import sys
from logging import getLogger
from pathlib import Path
-from typing import Dict, List
from PySide6.QtCore import QStandardPaths
-if platform.system() == "Windows":
+if platform.system() == 'Windows':
# noinspection PyUnresolvedReferences
from win32com.client import Dispatch # pylint: disable=E0401
-logger = getLogger("Paths")
+logger = getLogger('Paths')
# This depends on the location of this file (obviously)
-resources_path = Path(__file__).absolute().parent.parent.joinpath("resources")
+resources_path = Path(__file__).absolute().parent.parent.joinpath('resources')
# lk: delete old Rare directories
for old_dir in [
Path(
QStandardPaths.writableLocation(QStandardPaths.StandardLocation.CacheLocation),
- "rare",
- ).joinpath("tmp"),
+ 'rare',
+ ).joinpath('tmp'),
Path(
QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppDataLocation),
- "rare",
- ).joinpath("images"),
+ 'rare',
+ ).joinpath('images'),
Path(
QStandardPaths.writableLocation(QStandardPaths.StandardLocation.CacheLocation),
- "rare",
+ 'rare',
),
Path(
QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppDataLocation),
- "rare",
+ 'rare',
),
]:
if old_dir.exists():
@@ -48,7 +47,7 @@
def lock_file() -> Path:
return Path(
QStandardPaths.writableLocation(QStandardPaths.StandardLocation.TempLocation),
- "Rare.lock",
+ 'Rare.lock',
)
@@ -67,7 +66,7 @@ def cache_dir() -> Path:
def image_dir() -> Path:
- return data_dir().joinpath("images")
+ return data_dir().joinpath('images')
def image_dir_game(app_name: str) -> Path:
@@ -75,30 +74,34 @@ def image_dir_game(app_name: str) -> Path:
def image_tall_path(app_name: str, color: bool = True) -> Path:
- return image_dir_game(app_name).joinpath("tall.png" if color else "tall_gray.png")
+ return image_dir_game(app_name).joinpath('tall.png' if color else 'tall_gray.png')
def image_wide_path(app_name: str, color: bool = True) -> Path:
- return image_dir_game(app_name).joinpath("wide.png" if color else "wide_gray.png")
+ return image_dir_game(app_name).joinpath('wide.png' if color else 'wide_gray.png')
def image_icon_path(app_name: str, color: bool = True) -> Path:
- return image_dir_game(app_name).joinpath("icon.png" if color else "icon_gray.png")
+ return image_dir_game(app_name).joinpath('icon.png' if color else 'icon_gray.png')
+
+
+def runtime_assets_json() -> Path:
+ return data_dir().joinpath('runtime_assets.json')
def log_dir() -> Path:
- return cache_dir().joinpath("logs")
+ return cache_dir().joinpath('logs')
def tmp_dir() -> Path:
- return cache_dir().joinpath("tmp")
+ return cache_dir().joinpath('tmp')
def create_dirs() -> None:
for path in (data_dir(), cache_dir(), image_dir(), log_dir(), tmp_dir()):
if not path.exists():
path.mkdir(parents=True)
- logger.info(f"Created directory at {path}")
+ logger.info(f'Created directory at {path}')
def home_dir() -> Path:
@@ -114,66 +117,66 @@ def applications_dir() -> Path:
def proton_compat_dir(name: str) -> Path:
- if not (compat_dir := data_dir().joinpath(f"compatdata/{name}")).is_dir():
+ if not (compat_dir := data_dir().joinpath(f'compatdata/{name}')).is_dir():
compat_dir.mkdir(parents=True)
- if not (prefix_dir := compat_dir.joinpath("pfx")).is_dir():
+ if not (prefix_dir := compat_dir.joinpath('pfx')).is_dir():
prefix_dir.mkdir(parents=True)
return compat_dir
def wine_prefix_dir(name: str) -> Path:
- if not (prefix_dir := proton_compat_dir(name).joinpath("pfx")).is_dir():
+ if not (prefix_dir := proton_compat_dir(name).joinpath('pfx')).is_dir():
prefix_dir.mkdir(parents=True)
return prefix_dir
def compat_shaders_dir(name: str) -> Path:
- if not (shader_dir := proton_compat_dir(name).joinpath("shadercache")).is_dir():
+ if not (shader_dir := proton_compat_dir(name).joinpath('shadercache')).is_dir():
shader_dir.mkdir(parents=True)
return shader_dir
def compat_logs_dir(name: str) -> Path:
- if not (logs_dir := proton_compat_dir(name).joinpath("logs")).is_dir():
+ if not (logs_dir := proton_compat_dir(name).joinpath('logs')).is_dir():
logs_dir.mkdir(parents=True)
return logs_dir
-def setup_compat_shaders_dir(path: str) -> Dict:
+def setup_compat_shaders_dir(path: str) -> dict:
"""Setup per-game shader cache if shader pre-caching is disabled"""
environ = {}
- shader_cache_name = "steamapp_shader_cache"
+ shader_cache_name = 'steamapp_shader_cache'
shader_cache_vars = {
# Nvidia
- "__GL_SHADER_DISK_CACHE_APP_NAME": shader_cache_name,
- "__GL_SHADER_DISK_CACHE_PATH": os.path.join(path, "nvidiav1"),
- "__GL_SHADER_DISK_CACHE_READ_ONLY_APP_NAME": "steam_shader_cache;steamapp_merged_shader_cache",
- "__GL_SHADER_DISK_CACHE_SIZE": "10737418240", # 10GiB
+ '__GL_SHADER_DISK_CACHE_APP_NAME': shader_cache_name,
+ '__GL_SHADER_DISK_CACHE_PATH': os.path.join(path, 'nvidiav1'),
+ '__GL_SHADER_DISK_CACHE_READ_ONLY_APP_NAME': 'steam_shader_cache;steamapp_merged_shader_cache',
+ '__GL_SHADER_DISK_CACHE_SIZE': '10737418240', # 10GiB
# "__GL_SHADER_DISK_CACHE_SKIP_CLEANUP": "1",
# Mesa
- "MESA_DISK_CACHE_READ_ONLY_FOZ_DBS": "steam_cache,steam_precompiled",
- "MESA_DISK_CACHE_SINGLE_FILE": "1",
- "MESA_GLSL_CACHE_DIR": path,
- "MESA_GLSL_CACHE_MAX_SIZE": "10G",
- "MESA_SHADER_CACHE_DIR": path,
- "MESA_SHADER_CACHE_MAX_SIZE": "10G",
+ 'MESA_DISK_CACHE_READ_ONLY_FOZ_DBS': 'steam_cache,steam_precompiled',
+ 'MESA_DISK_CACHE_SINGLE_FILE': '1',
+ 'MESA_GLSL_CACHE_DIR': path,
+ 'MESA_GLSL_CACHE_MAX_SIZE': '10G',
+ 'MESA_SHADER_CACHE_DIR': path,
+ 'MESA_SHADER_CACHE_MAX_SIZE': '10G',
# AMD VK
- "AMD_VK_PIPELINE_CACHE_FILENAME": shader_cache_name,
- "AMD_VK_PIPELINE_CACHE_PATH": os.path.join(path, "AMDv1"),
- "AMD_VK_USE_PIPELINE_CACHE": "1",
+ 'AMD_VK_PIPELINE_CACHE_FILENAME': shader_cache_name,
+ 'AMD_VK_PIPELINE_CACHE_PATH': os.path.join(path, 'AMDv1'),
+ 'AMD_VK_USE_PIPELINE_CACHE': '1',
# DXVK
- "DXVK_STATE_CACHE_PATH": os.path.join(path, "DXVK_state_cache"),
+ 'DXVK_STATE_CACHE_PATH': os.path.join(path, 'DXVK_state_cache'),
# VKD3D
- "VKD3D_SHADER_CACHE_PATH": os.path.join(path, "VKD3D_shader_cache"),
+ 'VKD3D_SHADER_CACHE_PATH': os.path.join(path, 'VKD3D_shader_cache'),
}
for key, value in shader_cache_vars.items():
if key in {
- "__GL_SHADER_DISK_CACHE_PATH",
- "MESA_GLSL_CACHE_DIR",
- "MESA_SHADER_CACHE_DIR",
- "AMD_VK_PIPELINE_CACHE_PATH",
- "DXVK_STATE_CACHE_PATH",
- "VKD3D_SHADER_CACHE_PATH",
+ '__GL_SHADER_DISK_CACHE_PATH',
+ 'MESA_GLSL_CACHE_DIR',
+ 'MESA_SHADER_CACHE_DIR',
+ 'AMD_VK_PIPELINE_CACHE_PATH',
+ 'DXVK_STATE_CACHE_PATH',
+ 'VKD3D_SHADER_CACHE_PATH',
}:
os.makedirs(value, exist_ok=True)
environ[key] = value
@@ -222,7 +225,7 @@ def desktop_icon_path(app_name: str) -> Path:
}
-def desktop_link_types() -> List:
+def desktop_link_types() -> list:
return list(__link_type.keys())
@@ -240,10 +243,10 @@ def desktop_link_path(link_name: str, link_type: str) -> Path:
:return Path:
shortcut path
"""
- return __link_type[link_type].joinpath(f"{link_name}.{__link_suffix[platform.system()]['link']}")
+ return __link_type[link_type].joinpath(f'{link_name}.{__link_suffix[platform.system()]["link"]}')
-def get_rare_executable(*, external: bool = False) -> List[str]:
+def get_rare_executable(*, external: bool = False) -> list[str]:
"""
Returns the command list to invoke Rare for different platforms and packaging solutions
When used with container based packaging, such as Flatpak or Snap, returns the command
@@ -252,39 +255,39 @@ def get_rare_executable(*, external: bool = False) -> List[str]:
:param external: if True return the command to invoke Rare through Flatpak or Snap, defaults to false
:return: command list
"""
- logger.debug(f"Trying to find executable: {sys.executable}, {sys.argv}")
+ logger.debug(f'Trying to find executable: {sys.executable}, {sys.argv}')
- if os.environ.get("SNAP") and external:
- return ["snap", "run", "rare"]
- if os.environ.get("container") == "flatpak" and external:
- return ["flatpak", "run", "io.github.dummerle.rare"]
+ if os.environ.get('SNAP') and external:
+ return ['snap', 'run', 'rare']
+ if os.environ.get('container') == 'flatpak' and external: # noqa: SIM112
+ return ['flatpak', 'run', 'io.github.dummerle.rare']
# lk: detect if nuitka
- if "__compiled__" in globals():
+ if '__compiled__' in globals():
executable = [sys.executable]
- elif sys.argv[0].endswith("__main__.py"):
- executable = [sys.executable, "-m", "rare"]
- elif platform.system() in {"Linux", "FreeBSD", "Darwin"}:
- if p := os.environ.get("APPIMAGE"):
+ elif sys.argv[0].endswith('__main__.py'):
+ executable = [sys.executable, '-m', 'rare']
+ elif platform.system() in {'Linux', 'FreeBSD', 'Darwin'}:
+ if p := os.environ.get('APPIMAGE'):
executable = [p]
else:
if sys.executable == os.path.abspath(sys.argv[0]):
executable = [sys.executable]
else:
executable = [os.path.abspath(sys.argv[0])]
- elif platform.system() == "Windows":
+ elif platform.system() == 'Windows':
executable = [sys.executable]
if sys.executable != os.path.abspath(sys.argv[0]):
executable.append(os.path.abspath(sys.argv[0]))
- if executable[0].endswith("python.exe"):
+ if executable[0].endswith('python.exe'):
# be sure to start console-less then
- executable[0] = executable[0].replace("python.exe", "pythonw.exe")
+ executable[0] = executable[0].replace('python.exe', 'pythonw.exe')
- if executable[0].endswith("pythonw.exe"):
- if executable[1].endswith("rare"):
- executable[1] = executable[1] + ".exe"
+ if executable[0].endswith('pythonw.exe'):
+ if executable[1].endswith('rare'):
+ executable[1] = executable[1] + '.exe'
else:
executable = [sys.executable]
@@ -292,7 +295,7 @@ def get_rare_executable(*, external: bool = False) -> List[str]:
return executable
-def create_desktop_link(app_name: str, app_title: str = "", link_name: str = "", link_type="desktop") -> bool:
+def create_desktop_link(app_name: str, app_title: str = '', link_name: str = '', link_type='desktop') -> bool:
"""
Creates a desktop or start menu shortcut link
@@ -311,55 +314,55 @@ def create_desktop_link(app_name: str, app_title: str = "", link_name: str = "",
"""
# macOS is based on Darwin
if not desktop_links_supported():
- logger.error(f"Shortcut creation is not available on {platform.system()}")
+ logger.error(f'Shortcut creation is not available on {platform.system()}')
return False
- if link_type not in ["desktop", "start_menu"]:
- logger.error(f"Invalid link type {link_type}")
+ if link_type not in ['desktop', 'start_menu']:
+ logger.error(f'Invalid link type {link_type}')
return False
- for_rare = app_name == "rare_shortcut"
+ for_rare = app_name == 'rare_shortcut'
if for_rare:
- icon_path = resources_path.joinpath("images", f"Rare.{desktop_icon_suffix()}")
- app_title = "Rare"
- link_name = "Rare"
+ icon_path = resources_path.joinpath('images', f'Rare.{desktop_icon_suffix()}')
+ app_title = 'Rare'
+ link_name = 'Rare'
else:
icon_path = desktop_icon_path(app_name)
if not app_title or not link_name:
- logger.error("Missing app_title or link_name")
+ logger.error('Missing app_title or link_name')
return False
shortcut_path = desktop_link_path(link_name, link_type)
if not shortcut_path.parent.exists():
- logger.error(f"Parent directory {shortcut_path.parent} does not exist")
+ logger.error(f'Parent directory {shortcut_path.parent} does not exist')
return False
else:
- logger.info(f"Creating shortcut for {app_title} at {shortcut_path}")
+ logger.info(f'Creating shortcut for {app_title} at {shortcut_path}')
- if platform.system() in {"Linux", "FreeBSD"}:
+ if platform.system() in {'Linux', 'FreeBSD'}:
executable = get_rare_executable(external=True)
executable = shlex.join(executable)
if not for_rare:
- executable = f"{executable} launch {app_name}"
+ executable = f'{executable} launch {app_name}'
- with shortcut_path.open(mode="w") as desktop_file:
+ with shortcut_path.open(mode='w') as desktop_file:
desktop_file.write(
- "[Desktop Entry]\n"
- f"Name={app_title}\n"
- "Type=Application\n"
- "Categories=Game;\n"
- f"Icon={icon_path}\n"
- f"Exec={executable}\n"
- "Terminal=false\n"
- "StartupWMClass=Rare\n"
+ '[Desktop Entry]\n'
+ f'Name={app_title}\n'
+ 'Type=Application\n'
+ 'Categories=Game;\n'
+ f'Icon={icon_path}\n'
+ f'Exec={executable}\n'
+ 'Terminal=false\n'
+ 'StartupWMClass=Rare\n'
)
# shortcut_path.chmod(0o755)
return True
- elif platform.system() == "Windows":
+ elif platform.system() == 'Windows':
# Add shortcut
- shell = Dispatch("WScript.Shell")
+ shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(str(shortcut_path))
executable = get_rare_executable()
@@ -370,7 +373,7 @@ def create_desktop_link(app_name: str, app_title: str = "", link_name: str = "",
executable = executable[0]
if not for_rare:
- arguments.extend(["launch", app_name])
+ arguments.extend(['launch', app_name])
shortcut.Targetpath = executable
# Maybe there is a better solution, but windows does not accept single quotes (Windows is weird)
shortcut.Arguments = shlex.join(arguments).replace("'", '"')
diff --git a/rare/utils/qt_requests.py b/rare/utils/qrequests.py
similarity index 73%
rename from rare/utils/qt_requests.py
rename to rare/utils/qrequests.py
index 74b6bb7700..279b0f568a 100644
--- a/rare/utils/qt_requests.py
+++ b/rare/utils/qrequests.py
@@ -1,7 +1,8 @@
+from collections.abc import Callable
from dataclasses import dataclass, field
from email.message import Message
from logging import getLogger
-from typing import Callable, Dict, List, Tuple, TypeVar, Union
+from typing import TypeVar
import orjson
from PySide6.QtCore import QObject, QUrl, QUrlQuery, Signal, Slot
@@ -12,42 +13,42 @@
QNetworkRequest,
)
-user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36"
+user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
# user_agent = f'UELauncher/{version} Windows/10.0.19041.1.256.64bit'
-RequestHandler = TypeVar("RequestHandler", bound=Callable[[Union[Dict, bytes]], None])
+RequestHandler = TypeVar('RequestHandler', bound=Callable[[dict | bytes], None])
@dataclass
class RequestQueueItem:
method: str = None
url: QUrl = None
- payload: Dict = field(default_factory=dict)
- params: Dict = field(default_factory=dict)
- handlers: List[RequestHandler] = field(default_factory=list)
+ payload: dict = field(default_factory=dict)
+ params: dict = field(default_factory=dict)
+ handlers: list[RequestHandler] = field(default_factory=list)
def __eq__(self, other):
return self.method == other.method and self.url == other.url
-class QtRequests(QObject):
+class QRequests(QObject):
data_ready = Signal(object)
def __init__(self, cache: str = None, token: str = None, parent=None):
- super(QtRequests, self).__init__(parent=parent)
- self.logger = getLogger(f"{type(self).__name__}_{type(parent).__name__}")
+ super(QRequests, self).__init__(parent=parent)
+ self.logger = getLogger(f'{type(self).__name__}_{type(parent).__name__}')
self._manager = QNetworkAccessManager(self)
self._manager.finished.connect(self.__on_finished)
self._cache = None
if cache is not None:
- self.logger.debug("Using cache dir %s", cache)
+ self.logger.debug('Using cache dir %s', cache)
self._cache = QNetworkDiskCache(self)
self._cache.setCacheDirectory(cache)
self._manager.setCache(self._cache)
if token is not None:
- self.logger.debug("Manager is authorized")
+ self.logger.debug('Manager is authorized')
self._token = token
- self.__active_requests: Dict[QNetworkReply, RequestQueueItem] = {}
+ self.__active_requests: dict[QNetworkReply, RequestQueueItem] = {}
@staticmethod
def __prepare_query(url, params) -> QUrl:
@@ -62,7 +63,7 @@ def __prepare_request(self, item: RequestQueueItem) -> QNetworkRequest:
request = QNetworkRequest(item.url)
request.setHeader(
QNetworkRequest.KnownHeaders.ContentTypeHeader,
- "application/json; charset=UTF-8",
+ 'application/json; charset=UTF-8',
)
request.setHeader(QNetworkRequest.KnownHeaders.UserAgentHeader, user_agent)
request.setAttribute(
@@ -75,7 +76,7 @@ def __prepare_request(self, item: RequestQueueItem) -> QNetworkRequest:
QNetworkRequest.CacheLoadControl.PreferCache,
)
if self._token is not None:
- request.setRawHeader(b"Authorization", self._token.encode())
+ request.setRawHeader(b'Authorization', self._token.encode())
return request
def __post(self, item: RequestQueueItem):
@@ -86,7 +87,7 @@ def __post(self, item: RequestQueueItem):
self.__active_requests[reply] = item
def post(self, url: str, handler: RequestHandler, payload: dict):
- item = RequestQueueItem(method="post", url=QUrl(url), payload=payload, handlers=[handler])
+ item = RequestQueueItem(method='post', url=QUrl(url), payload=payload, handlers=[handler])
self.__post(item)
def __get(self, item: RequestQueueItem):
@@ -99,37 +100,37 @@ def get(
self,
url: str,
handler: RequestHandler,
- payload: Dict = None,
- params: Dict = None,
+ payload: dict = None,
+ params: dict = None,
):
url = self.__prepare_query(url, params) if params is not None else QUrl(url)
- item = RequestQueueItem(method="get", url=url, payload=payload, handlers=[handler])
+ item = RequestQueueItem(method='get', url=url, payload=payload, handlers=[handler])
self.__get(item)
def __on_error(self, error: QNetworkReply.NetworkError) -> None:
self.logger.error(error)
@staticmethod
- def __parse_content_type(header) -> Tuple[str, str]:
+ def __parse_content_type(header) -> tuple[str, str]:
# lk: this looks weird but `cgi` is deprecated, PEP 594 suggests this way of parsing MIME
m = Message()
- m["content-type"] = header
+ m['content-type'] = header
return m.get_content_type(), m.get_content_charset()
@Slot(QNetworkReply)
def __on_finished(self, reply: QNetworkReply):
item = self.__active_requests.pop(reply, None)
if item is None:
- self.logger.error("QNetworkReply: %s without associated item", reply.url().toString())
+ self.logger.error('QNetworkReply: %s without associated item', reply.url().toString())
elif reply.error() != QNetworkReply.NetworkError.NoError:
self.logger.error(reply.errorString())
else:
mimetype, charset = self.__parse_content_type(reply.header(QNetworkRequest.KnownHeaders.ContentTypeHeader))
- maintype, subtype = mimetype.split("/")
+ maintype, subtype = mimetype.split('/')
bin_data = reply.readAll().data()
- if mimetype == "application/json":
+ if mimetype == 'application/json':
data = orjson.loads(bin_data)
- elif maintype == "image":
+ elif maintype == 'image':
data = bin_data
else:
data = None
diff --git a/rare/utils/singleton.py b/rare/utils/singleton.py
index 6dad3c59c6..f1f09392cf 100644
--- a/rare/utils/singleton.py
+++ b/rare/utils/singleton.py
@@ -6,9 +6,9 @@
import sys
import tempfile
-logger = logging.getLogger("tendo.singleton")
+logger = logging.getLogger('tendo.singleton')
-if sys.platform != "win32":
+if sys.platform != 'win32':
import fcntl
@@ -16,62 +16,65 @@ class SingleInstanceException(BaseException):
pass
-class SingleInstance(object):
+class SingleInstance:
"""Class that can be instantiated only once per machine.
- If you want to prevent your script from running in parallel just instantiate SingleInstance() class. If is there another instance already running it will throw a `SingleInstanceException`.
+ If you want to prevent your script from running in parallel just instantiate SingleInstance() class. If is there
+ another instance already running it will throw a `SingleInstanceException`.
This option is very useful if you have scripts executed by crontab at small amounts of time.
Remember that this works by creating a lock file with a filename based on the full path to the script file.
- Providing a flavor_id will augment the filename with the provided flavor_id, allowing you to create multiple singleton instances from the same file. This is particularly useful if you want specific functions to have their own singleton instances.
+ Providing a flavor_id will augment the filename with the provided flavor_id, allowing you to create multiple
+ singleton instances from the same file. This is particularly useful if you want specific functions to have their
+ own singleton instances.
"""
- def __init__(self, flavor_id="", lockfile=""):
+ def __init__(self, flavor_id='', lockfile=''):
self.initialized = False
if lockfile:
self.lockfile = lockfile
else:
basename = (
- os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace("/", "-").replace(":", "").replace("\\", "-")
- + "-%s" % flavor_id
- + ".lock"
+ os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace('/', '-').replace(':', '').replace('\\', '-')
+ + f'-{flavor_id}'
+ + '.lock'
)
- self.lockfile = os.path.normpath(f"{tempfile.gettempdir()}/{basename}")
+ self.lockfile = os.path.normpath(f'{tempfile.gettempdir()}/{basename}')
- logger.debug(f"SingleInstance lockfile: {self.lockfile}")
- if sys.platform == "win32":
+ logger.debug(f'SingleInstance lockfile: {self.lockfile}')
+ if sys.platform == 'win32':
try:
# file already exists, we try to remove (in case previous
# execution was interrupted)
if os.path.exists(self.lockfile):
os.unlink(self.lockfile)
self.fd = os.open(self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR)
- except OSError:
+ except OSError as exc:
type, e, tb = sys.exc_info()
if e.errno == 13:
- logger.error("Another instance is already running, quitting.")
- raise SingleInstanceException()
+ logger.error('Another instance is already running, quitting.')
+ raise SingleInstanceException() from exc
print(e.errno)
raise
else: # non Windows
- self.fp = open(self.lockfile, "w")
+ self.fp = open(self.lockfile, 'w') # noqa: SIM115
self.fp.flush()
try:
fcntl.lockf(self.fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
- except IOError:
- logger.warning("Another instance is already running, quitting.")
- raise SingleInstanceException()
+ except OSError as exc:
+ logger.warning('Another instance is already running, quitting.')
+ raise SingleInstanceException() from exc
self.initialized = True
def __del__(self):
if not self.initialized:
return
try:
- if sys.platform == "win32":
- if hasattr(self, "fd"):
+ if sys.platform == 'win32':
+ if hasattr(self, 'fd'):
os.close(self.fd)
os.unlink(self.lockfile)
else:
@@ -83,5 +86,5 @@ def __del__(self):
if logger:
logger.warning(e)
else:
- print("Unloggable error: %s" % e)
+ print(f'Unloggable error: {e}', file=sys.stderr)
sys.exit(-1)
diff --git a/rare/utils/slot_adapters.py b/rare/utils/slot_adapters.py
index 3e97ca2b9c..78c2509111 100644
--- a/rare/utils/slot_adapters.py
+++ b/rare/utils/slot_adapters.py
@@ -1,5 +1,5 @@
import types
-from typing import Callable
+from collections.abc import Callable
from PySide6 import QtCore, QtGui, QtWidgets
diff --git a/rare/utils/steam_grades.py b/rare/utils/steam_grades.py
index 46605f3115..01449913a8 100644
--- a/rare/utils/steam_grades.py
+++ b/rare/utils/steam_grades.py
@@ -1,31 +1,27 @@
import difflib
import lzma
-import os
from datetime import datetime
from enum import Enum
from logging import getLogger
-from typing import Dict, Tuple
import orjson
import requests
from rare.lgndr.core import LegendaryCore
-from rare.utils.paths import cache_dir
-
-logger = getLogger("SteamGrades")
+from rare.utils.paths import data_dir
class ProtondbRatings(int, Enum):
# internal
- PENDING = ("pending", -2)
- FAIL = ("fail", -1)
+ PENDING = ('pending', -2)
+ FAIL = ('fail', -1)
# protondb
- NA = ("na", 0)
- BORKED = ("borked", 1)
- BRONZE = ("bronze", 2)
- SILVER = ("silver", 3)
- GOLD = ("gold", 4)
- PLATINUM = ("platinum", 5)
+ NA = ('na', 0)
+ BORKED = ('borked', 1)
+ BRONZE = ('bronze', 2)
+ SILVER = ('silver', 3)
+ GOLD = ('gold', 4)
+ PLATINUM = ('platinum', 5)
def __new__(cls, name: str, value: int):
obj = int.__new__(cls, value)
@@ -41,68 +37,69 @@ def __int__(self):
class SteamGrades:
- __steam_appids: Dict[str, str] = {}
- __steam_appids_version: int = 3
- __active_download: bool = False
- __replace_chars = ",;.:-_ "
- __steamids_url = "https://raredevs.github.io/wring/steam_appids.json.xz"
- __protondb_url = "https://www.protondb.com/api/v1/reports/summaries/"
+ _steam_appids_version: int = 3
+ _replace_chars = ',;.:-_ '
+ _steamids_url = 'https://raredevs.github.io/wring/steam_appids.json.xz'
+ _protondb_url = 'https://www.protondb.com/api/v1/reports/summaries/'
def __init__(self):
- pass
+ self.logger = getLogger(type(self).__name__)
+ self._steam_appids: dict[str, str] = {}
+ self._active_download: bool = False
def _download_steam_appids(self) -> bytes:
- if SteamGrades.__active_download:
- return b""
- SteamGrades.__active_download = True
- resp = requests.get(self.__steamids_url)
- SteamGrades.__active_download = False
+ if self._active_download:
+ return b''
+ self._active_download = True
+ resp = requests.get(self._steamids_url)
+ self._active_download = False
return resp.content
- def load_steam_appids(self) -> Dict:
- if SteamGrades.__steam_appids:
- return SteamGrades.__steam_appids
+ def load_steam_appids(self) -> dict:
+ if self._steam_appids:
+ return self._steam_appids
- file = os.path.join(cache_dir(), "steam_appids.json")
- version = SteamGrades.__steam_appids_version
+ file = data_dir().joinpath('steam_appids.json')
+ version = self._steam_appids_version
elapsed_days = 0
- if os.path.exists(file):
- mod_time = datetime.fromtimestamp(os.path.getmtime(file))
+ if file.is_file():
+ mod_time = datetime.fromtimestamp(file.stat().st_mtime)
elapsed_days = abs(datetime.now() - mod_time).days
- json = orjson.loads(open(file, "r").read())
- version = json.get("version", 0)
- if version >= SteamGrades.__steam_appids_version:
- SteamGrades.__steam_appids = json["games"]
+ with file.open('r') as fd:
+ json = orjson.loads(fd.read())
+ version = json.get('version', 0)
+ if version >= self._steam_appids_version:
+ self._steam_appids = json['games']
- if not os.path.exists(file) or elapsed_days > 3 or version < SteamGrades.__steam_appids_version:
+ if not file.is_file() or elapsed_days > 3 or version < self._steam_appids_version:
if content := self._download_steam_appids():
- text = lzma.decompress(content).decode("utf-8")
- with open(file, "w", encoding="utf-8") as f:
- f.write(text)
- json = orjson.loads(text)
- SteamGrades.__steam_appids = json["games"]
+ data = lzma.decompress(content).decode('utf-8')
+ with file.open('w', encoding='utf-8') as fd:
+ fd.write(data)
+ json = orjson.loads(data)
+ self._steam_appids = json['games']
- return SteamGrades.__steam_appids
+ return self._steam_appids
@property
- def steam_appids(self) -> Dict[str, str]:
- if not SteamGrades.__steam_appids:
- SteamGrades.__steam_appids = self.load_steam_appids()
- return SteamGrades.__steam_appids
+ def steam_appids(self) -> dict[str, str]:
+ if not self._steam_appids:
+ self.load_steam_appids()
+ return self._steam_appids
@property
- def steam_titles(self) -> Dict:
+ def steam_titles(self) -> dict:
return {v: k for k, v in self.steam_appids.items()}
def _get_steam_appid(self, title: str) -> str:
# workarounds for satisfactory
# FIXME: This has to be made smarter.
- title = title.replace("Early Access", "").replace("Experimental", "").strip()
+ title = title.replace('Early Access', '').replace('Experimental', '').strip()
# title = title.split(":")[0]
# title = title.split("-")[0]
- if title in self.steam_titles.keys():
+ if title in self.steam_titles:
steam_name = [title]
else:
steam_name = difflib.get_close_matches(title, self.steam_appids.keys(), n=1, cutoff=0.5)
@@ -110,23 +107,23 @@ def _get_steam_appid(self, title: str) -> str:
if steam_name:
return self.steam_appids[steam_name[0]]
else:
- return "0"
+ return '0'
def _get_grade(self, steam_appid: str):
- if steam_appid == "0":
- return "fail"
+ if steam_appid == '0':
+ return 'fail'
steam_appid = str(steam_appid)
- res = requests.get(f"{self.__protondb_url}/{steam_appid}.json")
+ res = requests.get(f'{self._protondb_url}/{steam_appid}.json')
try:
app = orjson.loads(res.text)
except orjson.JSONDecodeError as e:
- logger.error(repr(e))
- logger.error("Failed to get ProtonDB response for %s", steam_appid)
- return "fail"
+ self.logger.error(repr(e))
+ self.logger.error('Failed to get ProtonDB response for %s', steam_appid)
+ return 'fail'
- return app.get("tier", "fail")
+ return app.get('tier', 'fail')
- def get_rating(self, core: LegendaryCore, app_name: str, steam_appid: str = None) -> Tuple[str, str]:
+ def get_rating(self, core: LegendaryCore, app_name: str, steam_appid: str = None) -> tuple[str, str]:
game = core.get_game(app_name)
try:
if steam_appid is None:
@@ -135,8 +132,13 @@ def get_rating(self, core: LegendaryCore, app_name: str, steam_appid: str = None
raise RuntimeError
grade = self._get_grade(steam_appid)
except Exception as e:
- logger.error(repr(e))
- logger.error("Failed to get ProtonDB rating for %s", game.app_title)
- return "0", "fail"
+ self.logger.error(repr(e))
+ self.logger.error('Failed to get ProtonDB rating for %s', game.app_title)
+ return '0', 'fail'
else:
return steam_appid, grade
+
+
+steam_grades = SteamGrades()
+
+__all__ = ['steam_grades']
diff --git a/rare/utils/steam_shortcuts.py b/rare/utils/steam_shortcuts.py
index 4510d37ee1..0555f7abb6 100644
--- a/rare/utils/steam_shortcuts.py
+++ b/rare/utils/steam_shortcuts.py
@@ -4,7 +4,6 @@
from dataclasses import asdict
from logging import getLogger
from pathlib import Path
-from typing import Dict, List, Optional
import vdf
@@ -17,68 +16,68 @@
image_wide_path,
)
-if platform.system() == "Windows":
+if platform.system() == 'Windows':
# noinspection PyUnresolvedReferences
import winreg # pylint: disable=E0401
-logger = getLogger("SteamShortcuts")
+logger = getLogger('SteamShortcuts')
-steam_client_install_paths = [os.path.expanduser("~/.local/share/Steam")]
+steam_client_install_paths = [os.path.expanduser('~/.local/share/Steam')]
-def find_steam() -> Optional[str]:
- if platform.system() == "Windows":
+def find_steam() -> str | None:
+ if platform.system() == 'Windows':
# Find the Steam install directory or raise an error
try: # 32-bit
- key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Valve\\Steams")
+ key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Valve\\Steams')
except FileNotFoundError:
try: # 64-bit
- key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432Node\\Valve\\Steam")
+ key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432Node\\Valve\\Steam')
except FileNotFoundError:
return None
- return winreg.QueryValueEx(key, "InstallPath")[0]
+ return winreg.QueryValueEx(key, 'InstallPath')[0]
# return the first valid path
- elif platform.system() in {"Linux", "FreeBSD"}:
+ elif platform.system() in {'Linux', 'FreeBSD'}:
for path in steam_client_install_paths:
- if os.path.isdir(path) and os.path.isfile(os.path.join(path, "steam.sh")):
+ if os.path.isdir(path) and os.path.isfile(os.path.join(path, 'steam.sh')):
return path
return None
-def find_steam_users(steam_path: str) -> List[SteamUser]:
+def find_steam_users(steam_path: str) -> list[SteamUser]:
_users = []
- vdf_path = os.path.join(steam_path, "config", "loginusers.vdf")
+ vdf_path = os.path.join(steam_path, 'config', 'loginusers.vdf')
if not os.path.exists(vdf_path):
return _users
- with open(vdf_path, "r", encoding="utf-8") as f:
- users = vdf.load(f).get("users", {})
+ with open(vdf_path, encoding='utf-8') as f:
+ users = vdf.load(f).get('users', {})
for long_id, user in users.items():
_users.append(SteamUser(long_id, user))
return _users
-def _load_shortcuts(steam_path: str, user: SteamUser) -> Dict[str, SteamShortcut]:
+def _load_shortcuts(steam_path: str, user: SteamUser) -> dict[str, SteamShortcut]:
_shortcuts = {}
- vdf_path = os.path.join(steam_path, "userdata", str(user.short_id), "config", "shortcuts.vdf")
+ vdf_path = os.path.join(steam_path, 'userdata', str(user.short_id), 'config', 'shortcuts.vdf')
if not os.path.exists(vdf_path):
return _shortcuts
- with open(vdf_path, "rb") as f:
- shortcuts = vdf.binary_load(f).get("shortcuts", {})
+ with open(vdf_path, 'rb') as f:
+ shortcuts = vdf.binary_load(f).get('shortcuts', {})
for idx, shortcut in shortcuts.items():
_shortcuts[idx] = SteamShortcut.from_dict(shortcut)
return _shortcuts
-def _save_shortcuts(steam_path: str, user: SteamUser, shortcuts: Dict[str, SteamShortcut]) -> None:
+def _save_shortcuts(steam_path: str, user: SteamUser, shortcuts: dict[str, SteamShortcut]) -> None:
_shortcuts = {k: asdict(v) for k, v in shortcuts.items()}
- vdf_path = os.path.join(steam_path, "userdata", str(user.short_id), "config", "shortcuts.vdf")
- with open(vdf_path, "wb") as f:
- vdf.binary_dump({"shortcuts": _shortcuts}, f)
+ vdf_path = os.path.join(steam_path, 'userdata', str(user.short_id), 'config', 'shortcuts.vdf')
+ with open(vdf_path, 'wb') as f:
+ vdf.binary_dump({'shortcuts': _shortcuts}, f)
-__steam_dir: Optional[str] = None
-__steam_user: Optional[SteamUser] = None
-__steam_shortcuts: Optional[Dict] = None
+__steam_dir: str | None = None
+__steam_user: SteamUser | None = None
+__steam_shortcuts: dict | None = None
def steam_shortcuts_supported() -> bool:
@@ -93,13 +92,13 @@ def load_steam_shortcuts():
steam_dir = find_steam()
if not steam_dir:
- logger.error("Failed to find Steam install directory")
+ logger.error('Failed to find Steam install directory')
return
__steam_dir = steam_dir
steam_users = find_steam_users(steam_dir)
if not steam_users:
- logger.error("Failed to find any Steam users")
+ logger.error('Failed to find any Steam users')
return
else:
steam_user = next(
@@ -107,7 +106,7 @@ def load_steam_shortcuts():
sorted(steam_users, key=lambda x: x.last_login, reverse=True)[0],
)
logger.info(
- "Found most recently logged-in user %s(%s) (%s)",
+ 'Found most recently logged-in user %s(%s) (%s)',
steam_user.account_name,
steam_user.persona_name,
steam_user.last_login,
@@ -118,9 +117,11 @@ def load_steam_shortcuts():
def save_steam_shortcuts():
+ if not (__steam_dir and __steam_user):
+ return
logger.info(
- "%s Steam shortcuts for user %s(%s)",
- "Saving" if __steam_shortcuts else "Removing",
+ '%s Steam shortcuts for user %s(%s)',
+ 'Saving' if __steam_shortcuts else 'Removing',
__steam_user.account_name,
__steam_user.persona_name,
)
@@ -131,7 +132,7 @@ def steam_shortcut_exists(app_name: str) -> bool:
return SteamShortcut.calculate_appid(app_name) in {s.appid for s in __steam_shortcuts.values()}
-def remove_steam_shortcut(app_name: str) -> Optional[SteamShortcut]:
+def remove_steam_shortcut(app_name: str) -> SteamShortcut | None:
global __steam_shortcuts
if not steam_shortcut_exists(app_name):
@@ -149,29 +150,31 @@ def add_steam_shortcut(app_name: str, app_title: str) -> SteamShortcut:
global __steam_shortcuts
if steam_shortcut_exists(app_name):
- logger.info("Removing old Steam shortcut for %s", app_name)
+ logger.info('Removing old Steam shortcut for %s', app_name)
remove_steam_shortcut(app_name)
command = get_rare_executable(external=True)
- arguments = ["launch", app_name]
+ arguments = ['launch', app_name]
if len(command) > 1:
arguments = command[1:] + arguments
shortcut = SteamShortcut.create(
app_name=app_name,
- app_title=f"{app_title} (Rare)",
+ app_title=f'{app_title} (Rare)',
executable=command[0],
start_dir=os.path.dirname(command[0]),
icon=desktop_icon_path(app_name).as_posix(),
launch_options=arguments,
)
- key = int(max(__steam_shortcuts.keys(), default="0"))
+ key = int(max(__steam_shortcuts.keys(), default='0'))
__steam_shortcuts[str(key + 1)] = shortcut
return shortcut
def add_steam_coverart(app_name: str, shortcut: SteamShortcut):
- steam_grid_dir = os.path.join(__steam_dir, "userdata", str(__steam_user.short_id), "config", "grid")
+ if not __steam_dir:
+ return
+ steam_grid_dir = os.path.join(__steam_dir, 'userdata', str(__steam_user.short_id), 'config', 'grid')
if not os.path.exists(steam_grid_dir):
os.mkdir(steam_grid_dir)
shutil.copy(image_wide_path(app_name), os.path.join(steam_grid_dir, shortcut.game_hero))
@@ -181,9 +184,11 @@ def add_steam_coverart(app_name: str, shortcut: SteamShortcut):
def remove_steam_coverart(shortcut: SteamShortcut):
- steam_grid_dir = os.path.join(__steam_dir, "userdata", str(__steam_user.short_id), "config", "grid")
+ if not __steam_dir:
+ return
+ steam_grid_dir = os.path.join(__steam_dir, 'userdata', str(__steam_user.short_id), 'config', 'grid')
if not os.path.exists(steam_grid_dir):
- logger.warning("Path does not exist %s", steam_grid_dir)
+ logger.warning('Path does not exist %s', steam_grid_dir)
return
Path(steam_grid_dir).joinpath(shortcut.game_hero).unlink(missing_ok=True)
Path(steam_grid_dir).joinpath(shortcut.game_logo).unlink(missing_ok=True)
@@ -191,7 +196,7 @@ def remove_steam_coverart(shortcut: SteamShortcut):
Path(steam_grid_dir).joinpath(shortcut.grid_tall).unlink(missing_ok=True)
-if __name__ == "__main__":
+if __name__ == '__main__':
load_steam_shortcuts()
print(__steam_dir)
@@ -206,11 +211,11 @@ def print_shortcuts():
print_shortcuts()
- add_steam_shortcut("test1", "Test1")
- add_steam_shortcut("test2", "Test2")
- add_steam_shortcut("test3", "Test3")
- add_steam_shortcut("test1", "Test1")
+ add_steam_shortcut('test1', 'Test1')
+ add_steam_shortcut('test2', 'Test2')
+ add_steam_shortcut('test3', 'Test3')
+ add_steam_shortcut('test1', 'Test1')
- remove_steam_shortcut("test2")
+ remove_steam_shortcut('test2')
print_shortcuts()
diff --git a/rare/utils/workarounds.py b/rare/utils/workarounds.py
index 3eaa75cf08..ee3dec7f79 100644
--- a/rare/utils/workarounds.py
+++ b/rare/utils/workarounds.py
@@ -1,175 +1,102 @@
import platform
-from typing import Dict, Set, Union
+from logging import getLogger
+import requests
+from orjson import orjson
from PySide6.QtWidgets import QApplication
from rare.utils import config_helper as config
+from rare.utils.paths import data_dir
-__os_compat: Set = {"Linux", "Darwin", "FreeBSD"}
-__os_native: Set = {"Windows"}
-__os_all: Set = {*__os_compat, *__os_native}
-
-
-def __screen_height() -> int:
- return QApplication.instance().primaryScreen().geometry().height()
-
-
-def __screen_width() -> int:
- return QApplication.instance().primaryScreen().geometry().width()
-
-
-# Keeps a dictionary of workarounds.
-# Can use the following placeholders: res_width, res_height
-__workarounds: Dict[str, Dict[str, Dict[str, Dict[str, Union[str, Set]]]]] = {
- # XCOM2
- "3be3c4d681bc46b3b8b26c5df3ae0a18": {
- "options": {
- "override_exe": {
- "value": "Binaries/Win64/XCom2.exe",
- "os": __os_all,
- },
- },
- },
- # Civilization VI
- "Kinglet": {
- "options": {
- "override_exe": {
- "value": "Base/Binaries/Win64EOS/CivilizationVI.exe",
- "os": __os_all,
- },
- },
- },
- # Bioshock 2 Remastered
- "b22ce34b4ce0408c97a888554447479b": {
- "options": {
- "override_exe": {
- "value": "Build/FinalEpic/Bioshock2HD.exe",
- "os": __os_all,
- },
- },
- },
- # Bioshock 1 Remastered
- "bc2c95c6ff564a16b26644f1d3ac3c55": {
- "options": {
- "override_exe": {
- "value": "Build/FinalEpic/BioshockHD.exe",
- "os": __os_all,
- },
- },
- },
- # Eternal Threads
- "ff1d9bf6b1304cb9a12b8754afa78ae5": {
- "options": {
- "override_exe": {
- "value": "EternalThreadsBuild/EternalThreads.exe",
- "os": __os_compat,
- },
- },
- },
- # Celeste
- "Salt": {
- "options": {
- "start_params": {
- "value": "/gldevice:OpenGL",
- "os": __os_compat,
- },
- },
- },
- # Borderlands: The Pre Sequel
- "Turkey": {
- "options": {
- "start_params": {
- "value": "-NoLauncher",
- "os": __os_compat,
- },
- },
- },
- # Borderlands 2
- "Dodo": {
- "options": {
- "start_params": {
- "value": "-NoLauncher",
- "os": __os_compat,
- },
- # "override_exe": { "value": "Binaries/Win32/Borderlands2.exe", "os": __os_compat, },
- },
- },
- # Tiny Tina's Assault on Dragon Keep: A Wonderlands One shot Adventure
- "9e296d276ad447108f12c654c3341d59": {
- "options": {
- "start_params": {
- "value": "-NoLauncher",
- "os": __os_compat,
- },
- },
- },
- # Brothers: A Tale of Two Sons
- "Tamarind": {
- "options": {
- "start_params": {
- # value set at runtime
- "value": "ResX={res_width} ResY={res_height} -nomovies -nosplash",
- "os": __os_compat,
- },
- "override_exe": {
- "value": "Binaries/Win32/Brothers.exe",
- "os": __os_compat,
- },
- },
- },
- # Borderlands: The Pre Sequel
- "9c203b6ed35846e8a4a9ff1e314f6593": {
- "options": {
- "start_params": {
- "value": "/autorun /ed /autoquit",
- "os": __os_compat,
- },
- },
- },
- # F1® Manager 2024
- "03c9fe3b2869452ba8433ee7708a3e93": {
- "options": {
- "override_exe": {
- "value": "F1Manager24/Binaries/Win64/F1Manage",
- "os": __os_all,
- },
- },
- },
- # Cities Skylines
- "bcbc03d8812a44c18f41cf7d5f849265": {
- "options": {
- "override_exe": {
- "value": "Cities.exe",
- "os": __os_all,
- },
- },
- },
-}
-
-
-def __subst(text: str) -> str:
- return text.format(
- res_width=__screen_width(),
- res_height=__screen_height(),
- )
+
+class Workarounds:
+ _workarounds_url = 'https://raredevs.github.io/wring/workarounds.json'
+ _workarounds_version_url = 'https://raredevs.github.io/wring/workarounds_version.json'
+
+ def __init__(self):
+ self.logger = getLogger(type(self).__name__)
+ self._workarounds: dict[str, dict[str, dict[str, dict[str, str | tuple]]]] = {}
+ self._active_download: bool = False
+
+ def _download_workarounds(self) -> bytes:
+ if self._active_download:
+ return b''
+ self._active_download = True
+ resp = requests.get(self._workarounds_url)
+ self._active_download = False
+ return resp.content
+
+ def load_workarounds(self) -> dict[str, dict[str, dict[str, dict[str, str | tuple]]]]:
+ if self._workarounds:
+ return self._workarounds
+
+ try:
+ resp = requests.get(self._workarounds_version_url, timeout=1)
+ data = resp.content.decode('utf-8')
+ remote_version = orjson.loads(data).get('version', 1)
+ except requests.exceptions.Timeout:
+ remote_version = 1
+
+ file = data_dir().joinpath('workarounds.json')
+
+ if file.is_file():
+ json = orjson.loads(file.open('r').read())
+ version = json.get('version', 1)
+ if version >= remote_version:
+ self._workarounds = json.get('workarounds', {})
+ else:
+ version = 0
+
+ if not file.is_file() or version < remote_version:
+ if content := self._download_workarounds():
+ data = content.decode('utf-8')
+ with file.open('w', encoding='utf-8') as fd:
+ fd.write(data)
+ json = orjson.loads(data)
+ self._workarounds = json.get('workarounds', {})
+
+ return self._workarounds
+
+ def get(self, app_name: str) -> dict:
+ if not self._workarounds:
+ self.load_workarounds()
+ return self._workarounds.get(app_name, {})
+
+ @staticmethod
+ def screen_height() -> int:
+ return QApplication.instance().primaryScreen().geometry().height()
+
+ @staticmethod
+ def screen_width() -> int:
+ return QApplication.instance().primaryScreen().geometry().width()
+
+ @staticmethod
+ def subst(text: str) -> str:
+ return text.format(
+ res_width=Workarounds.screen_width(),
+ res_height=Workarounds.screen_height(),
+ )
+
+
+workarounds = Workarounds()
def apply_workarounds(app_name: str):
- if workaround := __workarounds.get(app_name):
+ if wa := workarounds.get(app_name):
# apply options
- for opt in (options := workaround.get("options", {})):
+ for opt in (options := wa.get('options', {})):
if config.get_option(app_name, opt, None) is not None:
continue
- if platform.system() not in options[opt].get("os", set()):
+ if platform.system() not in options[opt].get('os', tuple()):
continue
- config.set_option(app_name, opt, __subst(options[opt]["value"]))
+ config.set_option(app_name, opt, Workarounds.subst(options[opt]['value']))
# apply environment
- for var in (environ := workaround.get("environ", {})):
+ for var in (environ := wa.get('environ', {})):
if config.get_envvar(app_name, var, None) is not None:
continue
- if platform.system() not in environ[var].get("os", set()):
+ if platform.system() not in environ[var].get('os', tuple()):
continue
- config.set_envvar(app_name, var, __subst(environ[var]["value"]))
+ config.set_envvar(app_name, var, Workarounds.subst(environ[var]['value']))
-__all__ = ["apply_workarounds"]
+__all__ = ['apply_workarounds', 'workarounds']
diff --git a/rare/utils/wrapper_exe.py b/rare/utils/wrapper_exe.py
new file mode 100644
index 0000000000..7bab3b24b8
--- /dev/null
+++ b/rare/utils/wrapper_exe.py
@@ -0,0 +1,74 @@
+from datetime import datetime
+
+import requests
+from orjson import orjson
+
+from rare.utils import config_helper as config
+from rare.utils.paths import data_dir, runtime_assets_json
+
+
+def version_tuple(version: str) -> tuple:
+ return tuple(version.lstrip('v').split('.'))
+
+
+_github_api_url = 'https://api.github.com/repos/Etaash-mathamsetty/heroic-epic-integration/releases/latest'
+
+
+def download_wrapper_exe():
+ wrapper_exe = data_dir().joinpath('EpicGamesLauncher.exe')
+
+ if not wrapper_exe.exists():
+ config.remove_envvar('default', 'LEGENDARY_WRAPPER_EXE')
+
+ runtime_assets = {
+ wrapper_exe.name: {
+ 'version': 'v0.0',
+ 'date': datetime.isoformat(datetime.min),
+ }
+ }
+ version = runtime_assets[wrapper_exe.name]['version']
+
+ if runtime_assets_json().exists():
+ runtime_assets = orjson.loads(runtime_assets_json().open('r').read())
+ version = runtime_assets.get(wrapper_exe.name, {}).get('version', 'v0.0')
+
+ try:
+ resp = requests.get(_github_api_url, timeout=5)
+ data = resp.content.decode('utf-8')
+ latest_release = orjson.loads(data)
+ except requests.exceptions.Timeout:
+ return
+
+ remote_assets = latest_release['assets']
+ remote_version = latest_release['tag_name']
+
+ if version_tuple(version) >= version_tuple(remote_version):
+ if wrapper_exe.exists() and config.get_envvar('default', 'LEGENDARY_WRAPPER_EXE', '') == str(wrapper_exe):
+ return
+
+ if wrapper_exe.exists():
+ config.set_envvar('default', 'LEGENDARY_WRAPPER_EXE', str(wrapper_exe))
+ return
+
+ download_url = remote_assets[0]['browser_download_url']
+ try:
+ resp = requests.get(download_url, timeout=5)
+ wrapper_exe.write_bytes(resp.content)
+ config.set_envvar('default', 'LEGENDARY_WRAPPER_EXE', str(wrapper_exe))
+ except requests.exceptions.Timeout:
+ return
+
+ runtime_assets[wrapper_exe.name] = {
+ 'version': remote_version,
+ 'date': datetime.isoformat(datetime.fromisoformat(remote_assets[0]['created_at'].replace('Z', '+00:00'))),
+ }
+
+ runtime_assets_json().write_text(orjson.dumps(runtime_assets).decode('utf-8'), encoding='utf-8')
+ return
+
+
+if __name__ == '__main__':
+ download_wrapper_exe()
+
+
+__all__ = ['download_wrapper_exe']
diff --git a/rare/widgets/button_edit.py b/rare/widgets/button_edit.py
index 505aff6ece..019055019a 100644
--- a/rare/widgets/button_edit.py
+++ b/rare/widgets/button_edit.py
@@ -12,7 +12,7 @@ def __init__(self, icon_name, placeholder_text: str, parent=None):
self.setObjectName(type(self).__name__)
self.button = QPushButton(self)
- self.button.setObjectName(f"{type(self).__name__}Button")
+ self.button.setObjectName(f'{type(self).__name__}Button')
self.button.setIcon(qta_icon(icon_name))
self.button.setCursor(Qt.CursorShape.ArrowCursor)
self.button.clicked.connect(self.buttonClicked)
diff --git a/rare/widgets/collapsible_widget.py b/rare/widgets/collapsible_widget.py
index 0546a0216a..e1ed8095ec 100644
--- a/rare/widgets/collapsible_widget.py
+++ b/rare/widgets/collapsible_widget.py
@@ -1,5 +1,4 @@
from abc import abstractmethod
-from typing import Optional
from PySide6.QtCore import (
QAbstractAnimation,
@@ -22,7 +21,7 @@
from rare.utils.misc import qta_icon
-class CollapsibleBase(object):
+class CollapsibleBase:
"""
References:
# Adapted from c++ version
@@ -39,13 +38,13 @@ def __init__(self, parent=None):
def setup(self, animation_duration: int = 200):
self.animation_duration = animation_duration
- self.content_area: Optional[QWidget] = None
- self.content_toggle_animation: Optional[QPropertyAnimation] = None
+ self.content_area: QWidget | None = None
+ self.content_toggle_animation: QPropertyAnimation | None = None
# let the entire widget grow and shrink with its content
self.toggle_animation = QParallelAnimationGroup(self)
- self.toggle_animation.addAnimation(QPropertyAnimation(self, b"minimumHeight"))
- self.toggle_animation.addAnimation(QPropertyAnimation(self, b"maximumHeight"))
+ self.toggle_animation.addAnimation(QPropertyAnimation(self, b'minimumHeight'))
+ self.toggle_animation.addAnimation(QPropertyAnimation(self, b'maximumHeight'))
@abstractmethod
def isChecked(self) -> bool:
@@ -89,7 +88,7 @@ def setWidget(self, widget: QWidget):
self.content_area.setMaximumHeight(0)
self.content_area.setMinimumHeight(0)
- self.content_toggle_animation = QPropertyAnimation(self.content_area, b"maximumHeight")
+ self.content_toggle_animation = QPropertyAnimation(self.content_area, b'maximumHeight')
self.toggle_animation.addAnimation(self.content_toggle_animation)
self.addToLayout(self.content_area)
collapsed_height = self.sizeHint().height()
@@ -115,7 +114,7 @@ def __init__(self, animation_duration: int = 200, parent=None):
self.toggle_button = QToolButton(self)
self.toggle_button.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
- self.toggle_button.setIcon(qta_icon("fa.arrow-right", "fa5s.arrow-right"))
+ self.toggle_button.setIcon(qta_icon('fa.arrow-right', 'fa5s.arrow-right'))
self.toggle_button.setCheckable(True)
self.toggle_button.setChecked(False)
@@ -157,7 +156,7 @@ def sizeHint(self) -> QSize:
return super(CollapsibleFrame, self).sizeHint()
def animationStart(self, checked):
- arrow_type = qta_icon("fa.arrow-down", "fa5s.arrow-down") if checked else qta_icon("fa.arrow-right", "fa5s.arrow-right")
+ arrow_type = qta_icon('fa.arrow-down', 'fa5s.arrow-down') if checked else qta_icon('fa.arrow-right', 'fa5s.arrow-right')
self.toggle_button.setIcon(arrow_type)
super(CollapsibleFrame, self).animationStart(checked)
diff --git a/rare/widgets/dialogs.py b/rare/widgets/dialogs.py
index dc5643bb84..4bca2b4df3 100644
--- a/rare/widgets/dialogs.py
+++ b/rare/widgets/dialogs.py
@@ -26,7 +26,7 @@ def game_title(text: str, app_title: str) -> str:
def dialog_title(text: str) -> str:
- return f"{text} - {QCoreApplication.instance().applicationName()}"
+ return f'{text} - {QCoreApplication.instance().applicationName()}'
class BaseDialog(QDialog):
@@ -82,8 +82,8 @@ def __init__(self, parent=None):
self.subtitle_label.setVisible(False)
self.reject_button = QPushButton(self)
- self.reject_button.setText(self.tr("Cancel"))
- self.reject_button.setIcon(qta_icon("fa.remove", "fa5s.times"))
+ self.reject_button.setText(self.tr('Cancel'))
+ self.reject_button.setIcon(qta_icon('fa.remove', 'fa5s.times'))
self.reject_button.setAutoDefault(False)
self.reject_button.clicked.connect(self.reject)
@@ -118,7 +118,7 @@ def close(self):
raise RuntimeError(f"Don't use `close()` with {type(self).__name__}")
def setSubtitle(self, text: str):
- self.subtitle_label.setText(f"{text}")
+ self.subtitle_label.setText(f'{text}')
self.subtitle_label.setVisible(True)
def setCentralWidget(self, widget: QWidget):
@@ -226,16 +226,16 @@ def closeEvent(self, a0: QCloseEvent) -> None:
super(BaseDialog, self).closeEvent(a0)
-__all__ = ["dialog_title", "game_title", "BaseDialog", "ButtonDialog", "ActionDialog"]
+__all__ = ['dialog_title', 'game_title', 'BaseDialog', 'ButtonDialog', 'ActionDialog']
class TestDialog(BaseDialog):
def __init__(self, parent=None):
super(TestDialog, self).__init__(parent=parent)
- self.accept_button = QPushButton("accept", self)
- self.reject_button = QPushButton("reject", self)
- self.action_button = QPushButton("action", self)
+ self.accept_button = QPushButton('accept', self)
+ self.reject_button = QPushButton('reject', self)
+ self.action_button = QPushButton('action', self)
self.button_box = QDialogButtonBox(Qt.Orientation.Horizontal, self)
self.button_box.addButton(self.accept_button, QDialogButtonBox.ButtonRole.AcceptRole)
self.button_box.addButton(self.reject_button, QDialogButtonBox.ButtonRole.RejectRole)
@@ -253,13 +253,13 @@ def setWindowTitle(self, a0):
super().setWindowTitle(dialog_title(a0))
def close(self):
- print("in close")
+ print('in close')
super().close()
def closeEvent(self, a0: QCloseEvent) -> None:
- print("in closeEvent")
+ print('in closeEvent')
if a0.spontaneous():
- print("is spontaneous")
+ print('is spontaneous')
a0.ignore()
return
if self.reject_close:
@@ -270,31 +270,31 @@ def closeEvent(self, a0: QCloseEvent) -> None:
# super().closeEvent(a0)
def done(self, a0):
- print(f"in done {a0}")
+ print(f'in done {a0}')
return
super().done(a0)
def accept(self):
- print("in accept")
+ print('in accept')
self._on_accept()
# return
# super().accept()
def reject(self):
- print("in reject")
+ print('in reject')
self._on_reject()
# return
# super().reject()
def _on_close(self):
- print("in _on_close")
+ print('in _on_close')
def _on_accept(self):
- print("in _on_accepted")
+ print('in _on_accepted')
# self.close()
def _on_reject(self):
- print("in _on_rejected")
+ print('in _on_rejected')
self.close()
def keyPressEvent(self, a0: QKeyEvent) -> None:
@@ -309,5 +309,5 @@ def test_dialog():
sys.exit(ret)
-if __name__ == "__main__":
+if __name__ == '__main__':
test_dialog()
diff --git a/rare/widgets/elide_label.py b/rare/widgets/elide_label.py
index d5d4d96007..63e0fa025f 100644
--- a/rare/widgets/elide_label.py
+++ b/rare/widgets/elide_label.py
@@ -1,16 +1,14 @@
-from typing import Union
-
from PySide6.QtCore import Qt
from PySide6.QtGui import QFontMetrics, QResizeEvent
from PySide6.QtWidgets import QLabel
class ElideLabel(QLabel):
- def __init__(self, text="", parent=None):
+ def __init__(self, text='', parent=None):
super(ElideLabel, self).__init__(parent=parent)
self.__text = text
self.__fm = QFontMetrics(self.font())
- self.__tooltip = ""
+ self.__tooltip = ''
self.setFixedHeight(True)
self.setWordWrap(True)
self.setText(text)
@@ -29,7 +27,7 @@ def __setElideText(self, a0: str):
if self.__fm.boundingRect(elided_text).width() < self.__fm.boundingRect(self.__text).width():
super(ElideLabel, self).setToolTip(self.__text)
else:
- super(ElideLabel, self).setToolTip("")
+ super(ElideLabel, self).setToolTip('')
super(ElideLabel, self).setText(elided_text)
def setToolTip(self, a0: str) -> None:
@@ -40,7 +38,7 @@ def resizeEvent(self, a0: QResizeEvent) -> None:
self.__setElideText(self.__text)
super(ElideLabel, self).resizeEvent(a0)
- def setFixedHeight(self, h: Union[int, bool]) -> None:
+ def setFixedHeight(self, h: int | bool) -> None:
if isinstance(h, bool):
# FIXME: figure out 'else' case
super(ElideLabel, self).setFixedHeight(self.__fm.height() if h else 16777215)
diff --git a/rare/widgets/flow_layout.py b/rare/widgets/flow_layout.py
index b4777bacd4..d9db452702 100644
--- a/rare/widgets/flow_layout.py
+++ b/rare/widgets/flow_layout.py
@@ -1,4 +1,4 @@
-from typing import List, Optional, overload
+from typing import overload
from PySide6.QtCore import QPoint, QRect, QSize, Qt
from PySide6.QtWidgets import QLayout, QLayoutItem, QSizePolicy, QStyle, QWidget
@@ -10,7 +10,7 @@ def __init__(self, parent=None):
self.setObjectName(type(self).__name__)
self._hspacing = -1
self._vspacing = -1
- self._items: List[QLayoutItem] = []
+ self._items: list[QLayoutItem] = []
def __del__(self):
del self._items[:]
@@ -70,12 +70,12 @@ def verticalSpacing(self):
def count(self) -> int:
return len(self._items)
- def itemAt(self, index: int) -> Optional[QLayoutItem]:
+ def itemAt(self, index: int) -> QLayoutItem | None:
if 0 <= index < len(self._items):
return self._items[index]
return None
- def takeAt(self, index: int) -> Optional[QLayoutItem]:
+ def takeAt(self, index: int) -> QLayoutItem | None:
if 0 <= index < len(self._items):
item = self._items.pop(index)
self.invalidate()
diff --git a/rare/widgets/image_widget.py b/rare/widgets/image_widget.py
index 74d14b52c4..5e23f824db 100644
--- a/rare/widgets/image_widget.py
+++ b/rare/widgets/image_widget.py
@@ -1,5 +1,5 @@
+from contextlib import suppress
from enum import Enum
-from typing import Dict, Optional, Tuple, Union
from PySide6.QtCore import QRectF, QSize, Qt
from PySide6.QtGui import (
@@ -17,11 +17,11 @@
from PySide6.QtWidgets import QWidget
from rare.models.image import ImageSize
-from rare.utils.qt_requests import QtRequests
+from rare.utils.qrequests import QRequests
from .loading_widget import LoadingWidget
-OverlayPath = Tuple[QPainterPath, Union[QColor, QLinearGradient]]
+OverlayPath = tuple[QPainterPath, QColor | QLinearGradient]
class ImageWidget(QWidget):
@@ -29,16 +29,16 @@ class Border(Enum):
Rounded = 0
Squared = 1
- _rounded_overlay: Optional[OverlayPath] = None
- _squared_overlay: Optional[OverlayPath] = None
+ _rounded_overlay: OverlayPath | None = None
+ _squared_overlay: OverlayPath | None = None
def __init__(self, parent=None) -> None:
super(ImageWidget, self).__init__(parent=parent)
- self._pixmap: Optional[QPixmap] = None
+ self._pixmap: QPixmap | None = None
self._opacity: float = 1.0
self._transform: QTransform = None
self._smooth_transform: bool = False
- self._image_size: Optional[ImageSize.Preset] = None
+ self._image_size: ImageSize.Preset | None = None
self.setObjectName(type(self).__name__)
self.setContentsMargins(0, 0, 0, 0)
@@ -65,7 +65,10 @@ def setPixmap(self, pixmap: QPixmap) -> None:
)
else:
self.paint_image = self.paint_image_empty
- self.update()
+ # FIXME: this suppresss the RuntimeError raised by Qt if the widget has already
+ # deleted. Temporary until I look into it again.
+ with suppress(RuntimeError):
+ self.update()
def sizeHint(self) -> QSize:
return self._image_size.size if self._image_size else super(ImageWidget, self).sizeHint()
@@ -160,11 +163,11 @@ def paintEvent(self, a0: QPaintEvent) -> None:
class LoadingImageWidget(ImageWidget):
- def __init__(self, manager: QtRequests, parent=None):
+ def __init__(self, manager: QRequests, parent=None):
super(LoadingImageWidget, self).__init__(parent=parent)
self.manager = manager
- def fetchPixmap(self, url: str, params: Dict = None):
+ def fetchPixmap(self, url: str, params: dict = None):
self.setPixmap(QPixmap())
self.manager.get(url, self._on_image_ready, params=params)
@@ -182,22 +185,16 @@ def _on_image_ready(self, data):
class LoadingSpinnerImageWidget(LoadingImageWidget):
- def __init__(self, manager: QtRequests, parent=None):
+ def __init__(self, manager: QRequests, parent=None):
super(LoadingSpinnerImageWidget, self).__init__(manager, parent=parent)
self.spinner = LoadingWidget(parent=self)
self.spinner.setVisible(False)
- def fetchPixmap(self, url: str, params: Dict = None):
+ def fetchPixmap(self, url: str, params: dict = None):
self.spinner.setFixedSize(self._image_size.size)
self.spinner.start()
params = (
- {
- "resize": 1,
- "w": self._image_size.base.size.width(),
- "h": self._image_size.base.size.height(),
- }
- if not params
- else params
+ params if params else {'resize': 1, 'w': self._image_size.base.size.width(), 'h': self._image_size.base.size.height()}
)
super().fetchPixmap(url, params=params)
@@ -206,4 +203,4 @@ def _on_image_ready(self, data):
self.spinner.stop()
-__all__ = ["ImageSize", "ImageWidget", "LoadingImageWidget", "LoadingSpinnerImageWidget"]
+__all__ = ['ImageSize', 'ImageWidget', 'LoadingImageWidget', 'LoadingSpinnerImageWidget']
diff --git a/rare/widgets/indicator_edit.py b/rare/widgets/indicator_edit.py
index 107b2d2b97..feec79732b 100644
--- a/rare/widgets/indicator_edit.py
+++ b/rare/widgets/indicator_edit.py
@@ -1,7 +1,7 @@
import os
+from collections.abc import Callable
from enum import Enum, IntEnum
from logging import getLogger
-from typing import Callable, Dict, Optional, Tuple
from PySide6.QtCore import (
QDir,
@@ -32,7 +32,7 @@
from rare.utils.misc import qta_icon
-logger = getLogger("IndicatorEdit")
+logger = getLogger('IndicatorEdit')
class IndicatorReasonsCommon(IntEnum):
@@ -72,18 +72,18 @@ class IndicatorReasonsStrings(QObject):
def __init__(self, parent=None):
super(IndicatorReasonsStrings, self).__init__(parent=parent)
self.__text = {
- IndicatorReasonsCommon.VALID: self.tr("Ok!"),
- IndicatorReasonsCommon.INVALID: self.tr("Unknown error occurred"),
- IndicatorReasonsCommon.IS_EMPTY: self.tr("Value can not be empty"),
- IndicatorReasonsCommon.WRONG_FORMAT: self.tr("Wrong format"),
- IndicatorReasonsCommon.WRONG_PATH: self.tr("Wrong file or directory"),
- IndicatorReasonsCommon.DIR_NOT_EMPTY: self.tr("Directory is not empty"),
- IndicatorReasonsCommon.DIR_NOT_EXISTS: self.tr("Directory does not exist"),
- IndicatorReasonsCommon.FILE_NOT_EXISTS: self.tr("File does not exist"),
- IndicatorReasonsCommon.GAME_NOT_INSTALLED: self.tr("Game is not installed"),
- IndicatorReasonsCommon.GAME_NOT_EXISTS: self.tr("Game does not exist"),
+ IndicatorReasonsCommon.VALID: self.tr('Ok!'),
+ IndicatorReasonsCommon.INVALID: self.tr('Unknown error occurred'),
+ IndicatorReasonsCommon.IS_EMPTY: self.tr('Value can not be empty'),
+ IndicatorReasonsCommon.WRONG_FORMAT: self.tr('Wrong format'),
+ IndicatorReasonsCommon.WRONG_PATH: self.tr('Wrong file or directory'),
+ IndicatorReasonsCommon.DIR_NOT_EMPTY: self.tr('Directory is not empty'),
+ IndicatorReasonsCommon.DIR_NOT_EXISTS: self.tr('Directory does not exist'),
+ IndicatorReasonsCommon.FILE_NOT_EXISTS: self.tr('File does not exist'),
+ IndicatorReasonsCommon.GAME_NOT_INSTALLED: self.tr('Game is not installed'),
+ IndicatorReasonsCommon.GAME_NOT_EXISTS: self.tr('Game does not exist'),
IndicatorReasonsCommon.PERM_NO_WRITE: self.tr("No 'write' access to folder"),
- IndicatorReasonsCommon.UNDEFINED: self.tr("Unset"),
+ IndicatorReasonsCommon.UNDEFINED: self.tr('Unset'),
}
def __getitem__(self, item: int) -> str:
@@ -92,10 +92,10 @@ def __getitem__(self, item: int) -> str:
def __setitem__(self, key: int, value: str):
self.__text[key] = value
- def extend(self, reasons: Dict):
- for k in self.__text.keys():
- if k in reasons.keys():
- raise RuntimeError(f"{reasons} contains existing values")
+ def extend(self, reasons: dict):
+ for k in self.__text:
+ if k in reasons:
+ raise RuntimeError(f'{reasons} contains existing values')
self.__text.update(reasons)
@@ -104,7 +104,7 @@ class EditFuncRunnableSignals(QObject):
class EditFuncRunnable(QRunnable):
- def __init__(self, func: Callable[[str], Tuple[bool, str, int]], args: str):
+ def __init__(self, func: Callable[[str], tuple[bool, str, int]], args: str):
super(EditFuncRunnable, self).__init__()
self.setAutoDelete(True)
self.signals = EditFuncRunnableSignals()
@@ -118,9 +118,9 @@ def run(self):
self.signals.deleteLater()
@staticmethod
- def __wrap_edit_function(func: Callable[[str], Tuple[bool, str, int]]):
+ def __wrap_edit_function(func: Callable[[str], tuple[bool, str, int]]):
if func:
- return lambda text: func(os.path.expanduser(text) if text.startswith("~") else text)
+ return lambda text: func(os.path.expanduser(text) if text.startswith('~') else text)
else:
return func
@@ -131,10 +131,10 @@ class IndicatorLineEdit(QWidget):
def __init__(
self,
- text: str = "",
- placeholder: str = "",
+ text: str = '',
+ placeholder: str = '',
completer: QCompleter = None,
- edit_func: Callable[[str], Tuple[bool, str, int]] = None,
+ edit_func: Callable[[str], tuple[bool, str, int]] = None,
save_func: Callable[[str], None] = None,
horiz_policy: QSizePolicy.Policy = QSizePolicy.Policy.Expanding,
parent=None,
@@ -142,18 +142,18 @@ def __init__(
super(IndicatorLineEdit, self).__init__(parent=parent)
self.setObjectName(type(self).__name__)
layout = QHBoxLayout(self)
- layout.setObjectName(f"{self.objectName()}Layout")
+ layout.setObjectName(f'{self.objectName()}Layout')
layout.setContentsMargins(0, 0, 0, 0)
layout.setSizeConstraint(QHBoxLayout.SizeConstraint.SetDefaultConstraint)
# Add line_edit
self.line_edit = QLineEdit(self)
- self.line_edit.setObjectName(f"{type(self).__name__}Edit")
- self.line_edit.setPlaceholderText(placeholder if placeholder else self.tr("Use global/default settings"))
- self.line_edit.setToolTip(placeholder if placeholder else "")
+ self.line_edit.setObjectName(f'{type(self).__name__}Edit')
+ self.line_edit.setPlaceholderText(placeholder if placeholder else self.tr('Use global/default settings'))
+ self.line_edit.setToolTip(placeholder if placeholder else '')
self.line_edit.setSizePolicy(horiz_policy, QSizePolicy.Policy.Fixed)
# Add informative label
self.info_label = QLabel(self)
- self.info_label.setObjectName(f"{self.objectName()}Label")
+ self.info_label.setObjectName(f'{self.objectName()}Label')
self.info_label.setFrameStyle(QLabel.Shape.StyledPanel | QLabel.Shadow.Plain)
self.info_label.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
line_edit_layout = QHBoxLayout(self.line_edit)
@@ -166,7 +166,7 @@ def __init__(
layout.addWidget(self.line_edit)
if edit_func is not None:
self.indicator_label = QLabel(self)
- self.indicator_label.setPixmap(qta_icon("ei.info-circle", color="gray").pixmap(16, 16))
+ self.indicator_label.setPixmap(qta_icon('ei.info-circle', color='gray').pixmap(16, 16))
self.indicator_label.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
layout.addWidget(self.indicator_label)
@@ -174,7 +174,7 @@ def __init__(
self.__threadpool = QThreadPool(self)
self.__threadpool.setMaxThreadCount(1)
- self.__thread: Optional[EditFuncRunnable] = None
+ self.__thread: EditFuncRunnable | None = None
self.is_valid = False
self.edit_func = edit_func
@@ -210,7 +210,7 @@ def setInfo(self, text: str):
self.info_label.setVisible(bool(text))
self.info_label.setText(text)
- def setCompleter(self, completer: Optional[QCompleter]):
+ def setCompleter(self, completer: QCompleter | None):
if old := self.line_edit.completer():
old.deleteLater()
if not completer:
@@ -228,7 +228,7 @@ def reasons(self):
return self.__reasons
@reasons.setter
- def reasons(self, reasons: Dict):
+ def reasons(self, reasons: dict):
self.__reasons.extend(reasons)
def refresh(self):
@@ -236,8 +236,8 @@ def refresh(self):
self.__threadpool.waitForDone()
def __indicator(self, valid, reason: int = 0):
- color = "gray" if reason == IndicatorReasonsCommon.UNDEFINED else "green" if valid else "red"
- self.indicator_label.setPixmap(qta_icon("ei.info-circle", color=color).pixmap(16, 16))
+ color = 'gray' if reason == IndicatorReasonsCommon.UNDEFINED else 'green' if valid else 'red'
+ self.indicator_label.setPixmap(qta_icon('ei.info-circle', color=color).pixmap(16, 16))
if not valid:
self.indicator_label.setToolTip(self.__reasons[reason])
else:
@@ -276,33 +276,33 @@ class CustomIconType(Enum):
Executable = -2
icons = {
- CustomIconType.Unknown: ("mdi.file-cancel", "fa5.file-excel"), # Unknown
+ CustomIconType.Unknown: ('mdi.file-cancel', 'fa5.file-excel'), # Unknown
QAbstractFileIconProvider.IconType.Computer: (
- "mdi.desktop-classic",
- "fa5s.desktop",
+ 'mdi.desktop-classic',
+ 'fa5s.desktop',
), # Computer
QAbstractFileIconProvider.IconType.Desktop: (
- "mdi.desktop-mac",
- "fa5s.desktop",
+ 'mdi.desktop-mac',
+ 'fa5s.desktop',
), # Desktop
QAbstractFileIconProvider.IconType.Trashcan: (
- "mdi.trash-can",
- "fa5s.trash",
+ 'mdi.trash-can',
+ 'fa5s.trash',
), # Trashcan
QAbstractFileIconProvider.IconType.Network: (
- "mdi.server-network",
- "fa5s.server",
+ 'mdi.server-network',
+ 'fa5s.server',
), # Network
QAbstractFileIconProvider.IconType.Drive: (
- "mdi.harddisk",
- "fa5s.desktop",
+ 'mdi.harddisk',
+ 'fa5s.desktop',
), # Drive
QAbstractFileIconProvider.IconType.Folder: (
- "mdi.folder",
- "fa5.folder",
+ 'mdi.folder',
+ 'fa5.folder',
), # Folder
- QAbstractFileIconProvider.IconType.File: ("mdi.file", "fa5.file"), # File
- CustomIconType.Executable: ("mdi.cog", "fa5s.cog"), # Executable
+ QAbstractFileIconProvider.IconType.File: ('mdi.file', 'fa5.file'), # File
+ CustomIconType.Executable: ('mdi.cog', 'fa5s.cog'), # Executable
}
def __init__(self):
@@ -310,7 +310,7 @@ def __init__(self):
self.setOptions(QAbstractFileIconProvider.Option.DontUseCustomDirectoryIcons)
self.icon_types = {}
for idx, (icn, fallback) in PathEditIconProvider.icons.items():
- self.icon_types.update({idx: qta_icon(icn, fallback, color="#eeeeee")})
+ self.icon_types.update({idx: qta_icon(icn, fallback, color='#eeeeee')})
def icon(self, info_type):
if isinstance(info_type, QFileInfo):
@@ -329,17 +329,17 @@ def icon(self, info_type):
class PathEdit(IndicatorLineEdit):
def __init__(
self,
- path: str = "",
+ path: str = '',
file_mode: QFileDialog.FileMode = QFileDialog.FileMode.AnyFile,
file_filter: QDir.Filter = 0,
- name_filters: Tuple[str, ...] = None,
- placeholder: str = "",
- edit_func: Callable[[str], Tuple[bool, str, int]] = None,
+ name_filters: tuple[str, ...] = None,
+ placeholder: str = '',
+ edit_func: Callable[[str], tuple[bool, str, int]] = None,
save_func: Callable[[str], None] = None,
horiz_policy: QSizePolicy.Policy = QSizePolicy.Policy.Expanding,
parent=None,
):
- self.__root_path = path if path else os.path.expanduser("~/")
+ self.__root_path = path if path else os.path.expanduser('~/')
self.__completer = QCompleter()
self.__completer_model = QFileSystemModel(self.__completer)
try:
@@ -370,11 +370,11 @@ def __init__(
self.setObjectName(type(self).__name__)
self.line_edit.setMinimumSize(QSize(250, 0))
self.path_select = QPushButton(self)
- self.path_select.setObjectName(f"{type(self).__name__}Button")
+ self.path_select.setObjectName(f'{type(self).__name__}Button')
layout = self.layout()
layout.addWidget(self.path_select)
- self.path_select.setText(self.tr("Browse..."))
+ self.path_select.setText(self.tr('Browse...'))
self.__file_mode = file_mode
self.__file_filter = file_filter
@@ -393,7 +393,7 @@ def __set_path(self):
dlg_path = self.line_edit.text()
if not dlg_path or not os.path.isabs(dlg_path):
dlg_path = self.__root_path
- dlg = QFileDialog(self, self.tr("Choose path"), dlg_path)
+ dlg = QFileDialog(self, self.tr('Choose path'), dlg_path)
dlg.setOption(QFileDialog.Option.DontUseCustomDirectoryIcons)
dlg.setIconProvider(PathEditIconProvider())
dlg.setFileMode(self.__file_mode)
@@ -402,7 +402,7 @@ def __set_path(self):
if self.__file_filter:
dlg.setFilter(self.__file_filter)
if self.__name_filter:
- dlg.setNameFilter(" ".join(self.__name_filter))
+ dlg.setNameFilter(' '.join(self.__name_filter))
if dlg.exec_():
name = dlg.selectedFiles()[0]
self.__completer_model.setRootPath(name)
@@ -410,7 +410,7 @@ def __set_path(self):
class ColumnCompleter(QCompleter):
- def __init__(self, items: Optional[Dict[str, str]], parent=None):
+ def __init__(self, items: dict[str, str] | None, parent=None):
super(ColumnCompleter, self).__init__(parent)
self._treeview = QTreeView()
@@ -427,7 +427,7 @@ def __init__(self, items: Optional[Dict[str, str]], parent=None):
self.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
# self.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
- def setModel(self, items: Dict[str, str]):
+ def setModel(self, items: dict[str, str]):
model = QStandardItemModel(len(items), 2, self)
for idx, item in enumerate(items.items()):
app_title, app_name = item
diff --git a/rare/widgets/library_layout.py b/rare/widgets/library_layout.py
index f40d2842bd..df0cb2ddab 100644
--- a/rare/widgets/library_layout.py
+++ b/rare/widgets/library_layout.py
@@ -1,4 +1,4 @@
-from typing import Callable
+from collections.abc import Callable
from PySide6.QtCore import QPoint, QRect, Qt
from PySide6.QtWidgets import QSizePolicy
diff --git a/rare/widgets/loading_widget.py b/rare/widgets/loading_widget.py
index adabecf1d8..ab61707ae9 100644
--- a/rare/widgets/loading_widget.py
+++ b/rare/widgets/loading_widget.py
@@ -8,7 +8,7 @@ def __init__(self, autostart=False, parent=None):
super(LoadingWidget, self).__init__(parent=parent)
self.setObjectName(type(self).__name__)
self.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
- self.movie = QMovie(":/images/loader.webp", parent=self)
+ self.movie = QMovie(':/images/loader.webp', parent=self)
# The animation's exact size is 94x94
self.setFixedSize(96, 96)
self.setMovie(self.movie)
diff --git a/rare/widgets/rare_app.py b/rare/widgets/rare_app.py
index c31f1d8191..bdbb925011 100644
--- a/rare/widgets/rare_app.py
+++ b/rare/widgets/rare_app.py
@@ -48,7 +48,7 @@ def deleteLater(self):
@Slot(object, object, object)
def _on_exception(self, exc_type, exc_value, exc_tb):
- message = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
+ message = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
if self._handler(exc_type, exc_value, exc_tb):
return
self.logger.fatal(message)
@@ -56,10 +56,10 @@ def _on_exception(self, exc_type, exc_value, exc_tb):
QMessageBox.Icon.Critical,
exc_type.__name__,
self.tr(
- "An error has occurred!\n\n"
- "You can report this issue at\n"
- "https://github.com/RareDevs/Rare/issues\n\n"
- "Be sure to include the detailed text in your report."
+ 'An error has occurred!\n\n'
+ 'You can report this issue at\n'
+ 'https://github.com/RareDevs/Rare/issues\n\n'
+ 'Be sure to include the detailed text in your report.'
),
detailedText=message,
buttons=QMessageBox.StandardButton.Ignore | QMessageBox.StandardButton.Abort,
@@ -78,9 +78,9 @@ def __init__(self, args: Namespace, log_file: str):
self.setQuitOnLastWindowClosed(False)
self.setAttribute(Qt.ApplicationAttribute.AA_DontUseNativeDialogs, True)
- self.setDesktopFileName("rare")
- self.setApplicationName("Rare")
- self.setOrganizationName("Rare")
+ self.setDesktopFileName('rare')
+ self.setApplicationName('Rare')
+ self.setOrganizationName('Rare')
# Create directories after QStandardPaths has been initialized
paths.create_dirs()
@@ -91,33 +91,33 @@ def __init__(self, args: Namespace, log_file: str):
# Set up common logging channel to stderr
logging.basicConfig(
- format="[%(name)s] %(levelname)s: %(message)s",
+ format='[%(name)s] %(levelname)s: %(message)s',
level=logging.DEBUG if args.debug else logging.INFO,
stream=sys.stderr,
)
- start_time = time.strftime("%y-%m-%d--%H-%M") # year-month-day-hour-minute
+ start_time = time.strftime('%y-%m-%d--%H-%M') # year-month-day-hour-minute
file_handler = logging.FileHandler(
filename=os.path.join(paths.log_dir(), log_file.format(start_time)),
- encoding="utf-8",
+ encoding='utf-8',
)
- file_handler.setFormatter(fmt=logging.Formatter("[%(name)s] %(levelname)s: %(message)s"))
+ file_handler.setFormatter(fmt=logging.Formatter('[%(name)s] %(levelname)s: %(message)s'))
file_handler.setLevel(logging.DEBUG if args.debug else logging.INFO)
logging.root.addHandler(file_handler)
logging.getLogger().setLevel(logging.DEBUG if args.debug else logging.INFO)
# keep requests, asyncio and pillow quiet
- logging.getLogger("requests").setLevel(logging.WARNING)
- logging.getLogger("urllib3").setLevel(logging.WARNING)
- logging.getLogger("asyncio").setLevel(logging.WARNING)
+ logging.getLogger('requests').setLevel(logging.WARNING)
+ logging.getLogger('urllib3').setLevel(logging.WARNING)
+ logging.getLogger('asyncio').setLevel(logging.WARNING)
self.logger.info(
- f"Launching Rare version {rare.__version__} Codename: {rare.__codename__}\n"
- f" - Using Legendary {legendary.__version__} Codename: {legendary.__codename__} as backend\n"
- f" - Operating System: {platform.system()}, Python version: {platform.python_version()}\n"
- f" - Running {sys.executable} {' '.join(sys.argv)}\n"
- f" - Qt version: {QT_VERSION_STR}, PySide6 version: {PYSIDE_VERSION_STR}"
+ f'Launching Rare version {rare.__version__} Codename: {rare.__codename__}\n'
+ f' - Using Legendary {legendary.__version__} Codename: {legendary.__codename__} as backend\n'
+ f' - Operating System: {platform.system()}, Python version: {platform.python_version()}\n'
+ f' - Running {sys.executable} {" ".join(sys.argv)}\n'
+ f' - Qt version: {QT_VERSION_STR}, PySide6 version: {PYSIDE_VERSION_STR}'
)
self.settings = RareAppSettings(self)
@@ -128,29 +128,29 @@ def __init__(self, args: Namespace, log_file: str):
# Style
# 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())
+ self.setProperty('rareDefaultQtStyle', self.style().objectName())
if color_scheme := self.settings.get_value(app_settings.color_scheme):
- self.settings.set_value(app_settings.style_sheet, "")
+ self.settings.set_value(app_settings.style_sheet, '')
set_color_pallete(str(color_scheme))
elif style_sheet := self.settings.get_value(app_settings.style_sheet):
- self.settings.set_value(app_settings.color_scheme, "")
+ self.settings.set_value(app_settings.color_scheme, '')
set_style_sheet(str(style_sheet))
else:
self.setStyleSheet(get_static_style())
- self.setWindowIcon(QIcon(":/images/icon.png"))
+ self.setWindowIcon(QIcon(':/images/icon.png'))
def load_translator(self, lang: str):
# translator for qt stuff
locale = QLocale(lang)
- self.logger.info("Using locale: %s", locale.name())
+ self.logger.info('Using locale: %s', locale.name())
translations = {
- "qtbase": QLibraryInfo.location(QLibraryInfo.LibraryPath.TranslationsPath),
- "rare": os.path.join(paths.resources_path, "languages"),
+ 'qtbase': QLibraryInfo.location(QLibraryInfo.LibraryPath.TranslationsPath),
+ 'rare': os.path.join(paths.resources_path, 'languages'),
}
for filename, path in translations.items():
translator = QTranslator(self)
- if translator.load(locale, filename, "_", path):
- self.logger.debug("Loaded translation file: %s", translator.filePath())
+ if translator.load(locale, filename, '_', path):
+ self.logger.debug('Loaded translation file: %s', translator.filePath())
self.installTranslator(translator)
else:
self.logger.info("Couldn't find translation for locale: %s", locale.name())
diff --git a/rare/widgets/side_tab.py b/rare/widgets/side_tab.py
index 11bbd9b475..6581737d91 100644
--- a/rare/widgets/side_tab.py
+++ b/rare/widgets/side_tab.py
@@ -1,5 +1,5 @@
from logging import getLogger
-from typing import Protocol, Union
+from typing import Protocol
from PySide6.QtCore import (
QSize,
@@ -23,7 +23,7 @@
from rare.utils.misc import qta_icon
-logger = getLogger("SideTab")
+logger = getLogger('SideTab')
class SideTabBar(QTabBar):
@@ -56,7 +56,7 @@ def paintEvent(self, event):
painter.restore()
-class SideTabContents(object):
+class SideTabContents:
# str: title
set_title = Signal(str)
implements_scrollarea: bool = False
@@ -78,8 +78,8 @@ def sizeHint(self) -> QSize:
class SideTabContainer(QWidget):
def __init__(
self,
- widget: Union[QWidget, SideTabContentsProtocol],
- title: str = "",
+ widget: QWidget | SideTabContentsProtocol,
+ title: str = '',
parent: QWidget = None,
):
super(SideTabContainer, self).__init__(parent=parent)
@@ -89,13 +89,13 @@ def __init__(
if widget.layout():
widget.layout().setAlignment(Qt.AlignmentFlag.AlignTop)
widget.layout().setContentsMargins(0, 0, 3, 0)
- if hasattr(widget, "set_title"):
+ if hasattr(widget, 'set_title'):
widget.set_title.connect(self.setTitle)
layout = QVBoxLayout(self)
layout.addWidget(self.title)
- if not hasattr(widget, "implements_scrollarea") or not widget.implements_scrollarea:
+ if not hasattr(widget, 'implements_scrollarea') or not widget.implements_scrollarea:
scrollarea = QScrollArea(self)
scrollarea.setSizeAdjustPolicy(QScrollArea.SizeAdjustPolicy.AdjustToContents)
scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
@@ -113,7 +113,7 @@ def __init__(
layout.setAlignment(Qt.AlignmentFlag.AlignTop)
def setTitle(self, text: str) -> None:
- self.title.setText(f"{text}
")
+ self.title.setText(f'{text}
')
self.title.setVisible(bool(text))
@@ -128,8 +128,8 @@ def __init__(self, show_back: bool = False, padding: int = -1, parent=None):
if show_back:
super(SideTabWidget, self).addTab(
QWidget(self),
- qta_icon("mdi.keyboard-backspace", "ei.backward"),
- self.tr("Back"),
+ qta_icon('mdi.keyboard-backspace', 'ei.backward'),
+ self.tr('Back'),
)
self.tabBarClicked.connect(self.back_func)
@@ -138,6 +138,6 @@ def back_func(self, tab):
if not tab:
self.back_clicked.emit()
- def addTab(self, widget: Union[QWidget, SideTabContentsProtocol], a1: str, title: str = "") -> int:
+ def addTab(self, widget: QWidget | SideTabContentsProtocol, a1: str, title: str = '') -> int:
container = SideTabContainer(widget, title, parent=self)
return super(SideTabWidget, self).addTab(container, a1)
diff --git a/rare/widgets/sliding_stack.py b/rare/widgets/sliding_stack.py
index 8a7d51d93b..5264d843a0 100644
--- a/rare/widgets/sliding_stack.py
+++ b/rare/widgets/sliding_stack.py
@@ -75,7 +75,7 @@ def slideInWidget(self, newwidget):
offsetx, offsety = self.frameRect().width(), self.frameRect().height()
self.widget(_next).setGeometry(self.frameRect())
- if not self.m_direction == Qt.Orientation.Horizontal:
+ if self.m_direction != Qt.Orientation.Horizontal:
if _now < _next:
offsetx, offsety = 0, -offsety
else:
@@ -97,10 +97,10 @@ def slideInWidget(self, newwidget):
animgroup = QParallelAnimationGroup(self, finished=self.animationDoneSlot)
- for index, start, end in zip((_now, _next), (pnow, pnext - offset), (pnow + offset, pnext)):
+ for index, start, end in zip((_now, _next), (pnow, pnext - offset), (pnow + offset, pnext), strict=False):
animation = QPropertyAnimation(
self.widget(index),
- b"pos",
+ b'pos',
duration=self.m_speed,
easingCurve=self.m_animationtype,
startValue=start,