1
0
Fork 0
mirror of synced 2024-07-03 05:31:23 +12:00

Update shop branch (#98)

This commit is contained in:
Dummerle 2021-08-22 22:43:41 +02:00 committed by GitHub
parent df5da90292
commit f2a077a5c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
84 changed files with 5732 additions and 3004 deletions

View file

@ -5,10 +5,9 @@ on:
types: [ published ] types: [ published ]
jobs: jobs:
pypy-deploy: pypi-deploy:
if: "!github.event.release.prerelease" if: "!github.event.release.prerelease"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python - name: Set up Python
@ -27,49 +26,6 @@ jobs:
python setup.py sdist bdist_wheel python setup.py sdist bdist_wheel
twine upload dist/* twine upload dist/*
aur-publish:
if: "!github.event.release.prerelease"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Generate PKGBUILD
run:
./.github/GenPKG.sh
- name: Upload
uses: KSXGitHub/github-actions-deploy-aur@v2.2.3
with:
pkgname: rare
pkgbuild: ./.github/rare/PKGBUILD
commit_username: Dummerle
commit_email: ${{ secrets.MAIL }}
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
commit_message: Update AUR package
cx-freeze-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9.5'
- name: Install python deps
run: |
pip3 install cx_Freeze setuptools wheel
pip3 install -r requirements.txt
- name: Build
run: python3 freeze.py bdist_msi
- name: Copy File
run: cp dist/*.msi Rare.msi
- name: Upload files to GitHub
uses: svenstaro/upload-release-action@2.2.1
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: Rare.msi
asset_name: Rare.msi
tag: ${{ github.ref }}
overwrite: true
deb-package: deb-package:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -79,7 +35,6 @@ jobs:
sudo apt install python3-all python3-stdeb dh-python python3-setuptools python3-wheel sudo apt install python3-all python3-stdeb dh-python python3-setuptools python3-wheel
sudo pip install -r requirements.txt sudo pip install -r requirements.txt
- name: run python setup - name: run python setup
run: | run: |
python3 setup.py sdist python3 setup.py sdist
@ -97,4 +52,40 @@ jobs:
tag: ${{ github.ref }} tag: ${{ github.ref }}
overwrite: true overwrite: true
appimage:
if: "!github.event.release.prerelease"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: install Deps
run: |
sudo apt update
sudo apt install python3 python3-pip python3-setuptools patchelf desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse
- name: install appimage-builder
run: |
sudo wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /usr/local/bin/appimagetool
sudo chmod +x /usr/local/bin/appimagetool
sudo pip3 install appimage-builder
- name: Prepare Build directory
run: |
mkdir build
cp AppImageBuilder.yml build/
cd build
- name: Build Appimage
run: |
appimage-builder --skip-test
mv Rare-*.AppImage ../Rare.AppImage
- name: Upload to GitHub
uses: svenstaro/upload-release-action@2.2.1
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: Rare.AppImage
asset_name: Rare.AppImage
tag: ${{ github.ref }}
overwrite: true

85
AppImageBuilder.yml Normal file
View file

@ -0,0 +1,85 @@
# appimage-builder recipe see https://appimage-builder.readthedocs.io for details
version: 1
script:
# Remove any previous build
- rm -rf AppDir Rare | true
# Make usr and icons dirs
- mkdir -p AppDir/usr/src
- mkdir -p AppDir/usr/share/icons/hicolor/256x256/apps/
- git clone https://github.com/Dummerle/Rare
- cp -r Rare/rare AppDir/usr/src/rare
- cp -r Rare/custom_legendary AppDir/usr/src/
- cp AppDir/usr/src/rare/__main__.py AppDir/usr/src/__main__.py
- cp AppDir/usr/src/rare/resources/images/Rare.png AppDir/usr/share/icons/hicolor/256x256/apps/
# Install application dependencies
- python3 -m pip install --system --ignore-installed --prefix=/usr --root=AppDir -r Rare/requirements.txt
AppDir:
path: AppDir
app_info:
id: org.dummerle.rare
name: Rare
icon: Rare
version: 1.5.0
exec: usr/bin/python3
exec_args: $APPDIR/usr/src/__main__.py $@
apt:
arch: amd64
allow_unauthenticated: true
sources:
- sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy main restricted
- sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-updates main restricted
- sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy universe
- sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-updates universe
- sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy multiverse
- sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-updates multiverse
- sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-backports main restricted
universe multiverse
- sourceline: deb http://security.ubuntu.com/ubuntu groovy-security main restricted
- sourceline: deb http://security.ubuntu.com/ubuntu groovy-security universe
- sourceline: deb http://security.ubuntu.com/ubuntu groovy-security multiverse
include:
- python3
- python3-pkg-resources
- python3-distutils
- python3-pyqt5
runtime:
env:
# Set python home
# See https://docs.python.org/3/using/cmdline.html#envvar-PYTHONHOME
PYTHONHOME: '${APPDIR}/usr'
# Path to the site-packages dir or other modules dirs
# See https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH
PYTHONPATH: '${APPDIR}/usr/lib/python3.8/site-packages'
files:
include: [ ]
exclude: [ ]
test:
fedora:
image: appimagecrafters/tests-env:fedora-30
command: ./AppRun
use_host_x: true
debian-stable:
image: appimagecrafters/tests-env:debian-stable
command: ./AppRun
use_host_x: true
archlinux-latest:
image: appimagecrafters/tests-env:archlinux-latest
command: ./AppRun
use_host_x: true
centos-7:
image: appimagecrafters/tests-env:centos-7
command: ./AppRun
use_host_x: true
ubuntu-xenial:
image: appimagecrafters/tests-env:ubuntu-xenial
command: ./AppRun
use_host_x: true
AppImage:
arch: x86_64
update-information: gh-releases-zsync|Dummerle|Rare|latest|Rare-*x86_64.AppImage.zsync

View file

@ -1,5 +1,5 @@
include README.md include README.md
include rare/languages/*.qm include rare/languages/*.qm
include rare/styles/* include rare/resources/images/*
include rare/styles/qss/* recursive-include rare/resources/stylesheets/* *
include rare/styles/colors/* include rare/resources/colors/*

View file

@ -12,20 +12,17 @@ creating an issue on github or on Discord: https://discord.gg/YvmABK9YSk
### Installation via pip (recommend) ### Installation via pip (recommend)
Execute `pip install Rare` for all users Or `pip install Rare --user` for only one user. Then execute `rare` in your Execute `pip install Rare` for all users Or `pip install Rare --user` for only one user.
terminal or cmd
Linux: execute `rare` in your terminal.
Windows: execute `pythonw -m rare` in cmd
It is possible to create a desktop link, or a start menu link. Execute the command above with `--desktop-shortcut` or `--startmenu-shortcut` option
**Note**: On Linux must be `/home/user/.local/bin` in PATH and on Windows must be `PythonInstallationDirectory\Scripts` **Note**: On Linux must be `/home/user/.local/bin` in PATH and on Windows must be `PythonInstallationDirectory\Scripts`
in PATH. in PATH.
### Windows Simple
Download Rare.exe from the [releases page](https://github.com/Dummerle/Rare/releases) and execute it.
**Note:**
Using the exe file could cause errors with Windows Defender or other Anti Virus. Sometimes it is not possible to
download games and sometimes the app crashes. In this case please use pip
### Linux ### Linux
#### Arch based #### Arch based
@ -38,7 +35,7 @@ There are some AUR packages available:
#### Debian based #### Debian based
There is a `.deb` package available from There is a `.deb` package available from
the [releases page](https://github.com/Dummerle/Rare/releases): `sudo dpkg i Rare.deb` the [releases page](https://github.com/Dummerle/Rare/releases): `sudo apt install ./Rare.deb`
#### Other #### Other
@ -56,16 +53,16 @@ Install via `pip`.
based [HeroicGamesLauncher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) uses. based [HeroicGamesLauncher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) uses.
- Rare supports all major platforms (Windows, Linux, Mac) unlike the alternatives. - Rare supports all major platforms (Windows, Linux, Mac) unlike the alternatives.
**Note** Mac should work too, but I have no Mac and I can't test it.
## Features ## Features
- Launch, install and uninstall games - Launch, install and uninstall games
- Authentication(Import from existing installation and via Browser) - Authentication(Import from existing installation or via Browser)
- Download progress bar, queue - Download progress bar and queue
- Settings (Legendary and games) - Settings (Legendary and games)
- Sync Cloud Saves - Sync Cloud Saves
- Translations (English, German and French) - Translations (English, German and French)
- Create desktop shortcut for each game (Note: not supported on Mac yet)
- Display rating from [ProtonDB](https://www.protondb.com/) for each game
## Planned Features ## Planned Features
@ -88,3 +85,4 @@ More Information is in CONTRIBUTING.md
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/GameInfo.png?raw=true) ![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/GameInfo.png?raw=true)
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/RareSettings.png?raw=true) ![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/RareSettings.png?raw=true)
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/RareDownloads.png?raw=true) ![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/RareDownloads.png?raw=true)
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/Settings.png?raw=true)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 KiB

After

Width:  |  Height:  |  Size: 558 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 75 KiB

BIN
Screenshots/Settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View file

@ -526,7 +526,7 @@ class LegendaryCLI:
status_queue = MPQueue() status_queue = MPQueue()
logger.info('Preparing download...') logger.info('Preparing download...')
try: try:
dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download( dlm, analysis, game, igame, repair, repair_file, res = self.core.prepare_download(
app_name=args.app_name, app_name=args.app_name,
base_path=args.base_path, base_path=args.base_path,
force=args.force, force=args.force,
@ -556,6 +556,15 @@ class LegendaryCLI:
logger.fatal(e) logger.fatal(e)
exit(1) exit(1)
if res.failures:
for i in res.failures:
logger.fatal(i)
exit(1)
if res.warnings:
for warn in res.warnings:
logger.warning(warn)
logger.info(f'Install size: {analysis.install_size / 1024 / 1024:.02f} MiB') logger.info(f'Install size: {analysis.install_size / 1024 / 1024:.02f} MiB')
compression = (1 - (analysis.dl_size / analysis.uncompressed_dl_size)) * 100 compression = (1 - (analysis.dl_size / analysis.uncompressed_dl_size)) * 100
logger.info(f'Download size: {analysis.dl_size / 1024 / 1024:.02f} MiB ' logger.info(f'Download size: {analysis.dl_size / 1024 / 1024:.02f} MiB '

View file

@ -1000,16 +1000,7 @@ class LegendaryCore:
if res.warnings or res.failures: if res.warnings or res.failures:
self.log.info('Installation requirements check returned the following results:') self.log.info('Installation requirements check returned the following results:')
if res.warnings: return dlm, anlres, game, igame, repair, repair_file, res
for warn in sorted(res.warnings):
self.log.warning(warn)
if res.failures:
for msg in sorted(res.failures):
self.log.fatal(msg)
raise RuntimeError('Installation cannot proceed, exiting.')
return dlm, anlres, game, igame, repair, repair_file
@staticmethod @staticmethod
def check_installation_conditions(analysis: AnalysisResult, def check_installation_conditions(analysis: AnalysisResult,

View file

@ -1,74 +0,0 @@
import sys
from cx_Freeze import setup, Executable
from rare import __version__
# Packages to include
python_packages = []
# Modules to include
python_modules = []
base = None
name = None
build_options = {}
build_exe_options = {}
shortcutName = None
shortcutDir = None
bdist_msi_options = None
src_files = []
external_so_files = []
if sys.platform == 'win32':
base = 'Win32GUI'
name = 'Rare.exe'
shortcut_table = [
('DesktopShortcut', # Shortcut
'DesktopFolder', # Directory
'Rare', # Name
'TARGETDIR', # Component
'[TARGETDIR]' + name, # Target
None, # Arguments
'A gui for Legendary.', # Description
None, # Hotkey
None, # Icon
None, # IconIndex
None, # ShowCmd
'TARGETDIR' # Working Directory
)]
msi_data = {"Shortcut": shortcut_table}
bdist_msi_options = {'data': msi_data, "all_users": True}
build_options["bdist_msi"] = bdist_msi_options
else:
name = 'Rare'
src_files += [
'LICENSE',
'README.md',
'rare/styles/Logo.ico',
]
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options["packages"] = python_packages
build_exe_options["include_files"] = src_files + external_so_files
build_exe_options["includes"] = python_modules
build_exe_options["excludes"] = ["setuptools", "tkinter", "pkg_resources"]
# Set options
build_options["build_exe"] = build_exe_options
setup(name='Rare',
version=__version__,
description='A gui for Legendary.',
options=build_options,
executables=[
Executable('rare/__main__.py',
targetName=name,
icon='rare/styles/Logo.ico',
base=base,
shortcutName=shortcutName,
shortcutDir=shortcutDir,
),
],
)

View file

@ -1,9 +1,9 @@
import os import os
__version__ = "1.4.1" __version__ = "1.5.0"
style_path = os.path.join(os.path.dirname(__file__), "styles/") resources_path = os.path.join(os.path.dirname(__file__), "resources")
lang_path = os.path.join(os.path.dirname(__file__), "languages/") languages_path = os.path.join(os.path.dirname(__file__), "languages")
# Cache Directory: Store images # Cache Directory: Store images
if p := os.getenv("XDG_CACHE_HOME"): if p := os.getenv("XDG_CACHE_HOME"):
@ -11,7 +11,8 @@ if p := os.getenv("XDG_CACHE_HOME"):
elif os.name == "nt": elif os.name == "nt":
cache_dir = os.path.expandvars("%APPDATA%/rare/cache") cache_dir = os.path.expandvars("%APPDATA%/rare/cache")
else: else:
cache_dir = os.path.expanduser("~/.cache/rare/cache") cache_dir = os.path.expanduser("~/.cache/rare/")
if not os.path.exists(cache_dir): if not os.path.exists(cache_dir):
os.makedirs(cache_dir) os.makedirs(cache_dir)
@ -24,3 +25,6 @@ else:
data_dir = os.path.expanduser("~/.local/share/rare/") data_dir = os.path.expanduser("~/.local/share/rare/")
if not os.path.exists(data_dir): if not os.path.exists(data_dir):
os.makedirs(data_dir) os.makedirs(data_dir)
image_dir = os.path.join(data_dir, "images")

View file

@ -9,7 +9,8 @@ from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QStyleFactory from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QStyleFactory
from custom_legendary.core import LegendaryCore from custom_legendary.core import LegendaryCore
from rare import lang_path, style_path, cache_dir from rare import languages_path, resources_path
from rare.components.dialogs.launch_dialog import LaunchDialog from rare.components.dialogs.launch_dialog import LaunchDialog
from rare.components.main_window import MainWindow from rare.components.main_window import MainWindow
from rare.components.tray_icon import TrayIcon from rare.components.tray_icon import TrayIcon
@ -50,13 +51,21 @@ class App(QApplication):
self.core.lgd.config.add_section("Legendary") self.core.lgd.config.add_section("Legendary")
self.core.lgd.save_config() self.core.lgd.save_config()
# workaround if egl sync enabled, but no programdata path # workaround if egl sync enabled, but no programdata_path
if self.core.egl_sync_enabled and not os.path.exists(self.core.egl.programdata_path): # programdata_path might be unset if logging in through the browser
self.core.lgd.config.remove_option("Legendary", "egl-sync") if self.core.egl_sync_enabled:
self.core.lgd.save_config() if self.core.egl.programdata_path is None:
self.core.lgd.config.remove_option("Legendary", "egl_sync")
self.core.lgd.save_config()
else:
if not os.path.exists(self.core.egl.programdata_path):
self.core.lgd.config.remove_option("Legendary", "egl_sync")
self.core.lgd.save_config()
# set Application name for settings # set Application name for settings
self.mainwindow = None self.mainwindow = None
self.tray_icon = None
self.launch_dialog = None
self.setApplicationName("Rare") self.setApplicationName("Rare")
self.setOrganizationName("Rare") self.setOrganizationName("Rare")
settings = QSettings() settings = QSettings()
@ -64,8 +73,8 @@ class App(QApplication):
# Translator # Translator
self.translator = QTranslator() self.translator = QTranslator()
lang = settings.value("language", get_lang(), type=str) lang = settings.value("language", get_lang(), type=str)
if os.path.exists(lang_path + lang + ".qm"): if os.path.exists(languages_path + lang + ".qm"):
self.translator.load(lang_path + lang + ".qm") self.translator.load(languages_path + lang + ".qm")
logger.info("Your language is supported: " + lang) logger.info("Your language is supported: " + lang)
elif not lang == "en": elif not lang == "en":
logger.info("Your language is not supported") logger.info("Your language is not supported")
@ -78,33 +87,47 @@ class App(QApplication):
settings.setValue("style_sheet", "RareStyle") settings.setValue("style_sheet", "RareStyle")
if color := settings.value("color_scheme", False): if color := settings.value("color_scheme", False):
settings.setValue("style_sheet", "") settings.setValue("style_sheet", "")
custom_palette = load_color_scheme(os.path.join(style_path, "colors", color + ".scheme")) custom_palette = load_color_scheme(os.path.join(resources_path, "colors", color + ".scheme"))
if custom_palette is not None: if custom_palette is not None:
self.setPalette(custom_palette) self.setPalette(custom_palette)
elif style := settings.value("style_sheet", False): elif style := settings.value("style_sheet", False):
settings.setValue("color_scheme", "") settings.setValue("color_scheme", "")
self.setStyleSheet(open(os.path.join(style_path, "qss", style + ".qss")).read()) stylesheet = open(os.path.join(resources_path, "stylesheets", style, "stylesheet.qss")).read()
self.setWindowIcon(QIcon(os.path.join(style_path, "Logo.png"))) style_resource_path = os.path.join(resources_path, "stylesheets", style, "")
if os.name == "nt":
style_resource_path = style_resource_path.replace('\\', '/')
self.setStyleSheet(stylesheet.replace("@path@", style_resource_path))
# lk: for qresources stylesheets, not an ideal solution for modability,
# lk: too many extra steps and I don't like binary files in git, even as strings.
# importlib.import_module("rare.resources.stylesheets." + style)
# resource = QFile(f":/{style}/stylesheet.qss")
# resource.open(QIODevice.ReadOnly)
# self.setStyleSheet(QTextStream(resource).readAll())
self.setWindowIcon(QIcon(os.path.join(resources_path, "images", "Rare.png")))
# launch app # launch app
self.launch_dialog = LaunchDialog(self.core, args.offline) self.launch_dialog = LaunchDialog(self.core, args.offline)
self.launch_dialog.quit_app.connect(self.launch_dialog.close)
self.launch_dialog.quit_app.connect(lambda ec: exit(ec))
self.launch_dialog.start_app.connect(self.start_app) self.launch_dialog.start_app.connect(self.start_app)
self.launch_dialog.start_app.connect(self.launch_dialog.close)
if not args.silent or args.subparser == "launch": if not args.silent or args.subparser == "launch":
self.launch_dialog.show() self.launch_dialog.login()
def start_app(self, offline=False): def start_app(self, offline=False):
self.args.offline = offline self.args.offline = offline
self.mainwindow = MainWindow(self.core, self.args) self.mainwindow = MainWindow(self.core, self.args)
self.launch_dialog.close() self.mainwindow.quit_app.connect(self.exit_app)
self.tray_icon = TrayIcon(self) self.tray_icon = TrayIcon(self)
self.tray_icon.exit_action.triggered.connect(lambda: exit(0)) self.tray_icon.exit_action.triggered.connect(self.exit_app)
self.tray_icon.start_rare.triggered.connect(self.mainwindow.show) self.tray_icon.start_rare.triggered.connect(self.mainwindow.show)
self.tray_icon.activated.connect(self.tray) self.tray_icon.activated.connect(self.tray)
if not offline: if not offline:
self.mainwindow.tab_widget.downloadTab.finished.connect(lambda update: self.tray_icon.showMessage( self.mainwindow.tab_widget.downloadTab.finished.connect(lambda x: self.tray_icon.showMessage(
self.tr("Download finished"), self.tr("Download finished. Game is playable now"), self.tr("Download finished"),
QSystemTrayIcon.Information, 4000) if update else None) self.tr("Download finished. {} is playable now").format(self.core.get_game(x[1]).app_title),
QSystemTrayIcon.Information, 4000) if x[0] else None)
if not self.args.silent: if not self.args.silent:
self.mainwindow.show() self.mainwindow.show()
@ -114,13 +137,21 @@ class App(QApplication):
self.mainwindow.show() self.mainwindow.show()
logger.info("Show App") logger.info("Show App")
def exit_app(self, exit_code=0):
if self.tray_icon is not None:
self.tray_icon.deleteLater()
if self.mainwindow is not None:
self.mainwindow.close()
self.processEvents()
self.exit(exit_code)
def start(args): def start(args):
while True: while True:
app = App(args) app = App(args)
exit_code = app.exec_() exit_code = app.exec_()
# if not restart # if not restart
if exit_code != -133742:
break
# restart app # restart app
del app del app
if exit_code != -133742:
break

View file

@ -53,6 +53,9 @@ class InstallDialog(QDialog, Ui_InstallDialog):
self.install_dir_label.setVisible(False) self.install_dir_label.setVisible(False)
self.install_dir_edit.setVisible(False) self.install_dir_edit.setVisible(False)
self.warn_label.setVisible(False)
self.warn_message.setVisible(False)
if self.core.lgd.config.has_option("Legendary", "max_workers"): if self.core.lgd.config.has_option("Legendary", "max_workers"):
max_workers = self.core.lgd.config.get("Legendary", "max_workers") max_workers = self.core.lgd.config.get("Legendary", "max_workers")
else: else:
@ -92,7 +95,9 @@ class InstallDialog(QDialog, Ui_InstallDialog):
self.reject_close = True self.reject_close = True
self.resize(self.minimumSize()) self.resize(self.minimumSize())
self.setFixedSize(self.size()) # self.setFixedSize(self.size())
self.verify_clicked()
def execute(self): def execute(self):
if self.silent: if self.silent:
@ -151,9 +156,6 @@ class InstallDialog(QDialog, Ui_InstallDialog):
def on_worker_result(self, dl_item: InstallDownloadModel): def on_worker_result(self, dl_item: InstallDownloadModel):
self.dl_item.download = dl_item self.dl_item.download = dl_item
# TODO: Check available size and act accordingly
# TODO: (show message in label | color it | disable install unless ignore)
# TODO: Find a way to get the installation size delta and show it
download_size = self.dl_item.download.analysis.dl_size download_size = self.dl_item.download.analysis.dl_size
install_size = self.dl_item.download.analysis.install_size install_size = self.dl_item.download.analysis.install_size
if download_size: if download_size:
@ -240,8 +242,11 @@ class InstallInfoWorker(QRunnable):
# reset_sdl=, # reset_sdl=,
sdl_prompt=lambda app_name, title: self.dl_item.options.sdl_list sdl_prompt=lambda app_name, title: self.dl_item.options.sdl_list
)) ))
self.signals.result.emit(download) if not download.res.failures:
except RuntimeError as e: self.signals.result.emit(download)
else:
self.signals.failed.emit("\n".join(str(i) for i in download.res.failures))
except Exception as e:
self.signals.failed.emit(str(e)) self.signals.failed.emit(str(e))
self.signals.finished.emit() self.signals.finished.emit()

View file

@ -1,16 +1,15 @@
import json
import os import os
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal, QSettings from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtWidgets import QDialog from PyQt5.QtWidgets import QDialog
from requests.exceptions import ConnectionError from requests.exceptions import ConnectionError
from custom_legendary.core import LegendaryCore from custom_legendary.core import LegendaryCore
from rare import data_dir, cache_dir from rare import image_dir
from rare.components.dialogs.login import LoginDialog from rare.components.dialogs.login import LoginDialog
from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog
from rare.utils import steam_grades
from rare.utils.utils import download_images from rare.utils.utils import download_images
logger = getLogger("Login") logger = getLogger("Login")
@ -28,134 +27,64 @@ class ImageThread(QThread):
self.download_progess.emit(100) self.download_progess.emit(100)
class SteamThread(QThread):
progress = pyqtSignal(int)
action = pyqtSignal(str)
def __init__(self, core: LegendaryCore, parent):
super(SteamThread, self).__init__(parent)
self.core = core
def run(self) -> None:
gamelist = self.core.get_game_list(True)
if not os.path.exists(os.path.join(data_dir, "game_list.json")):
self.action.emit(self.tr("Getting data from ProtonDB"))
steam_grades.upgrade_all([(i.app_title, i.app_name) for i in gamelist], self.progress)
self.progress.emit(99)
self.action.emit(self.tr("Checking Games for data"))
grades = json.load(open(os.path.join(data_dir, "game_list.json")))
ids = json.load(open(os.path.join(cache_dir, "steam_ids.json")))
for game in gamelist:
if not grades.get(game.app_name):
steam_id = steam_grades.get_steam_id(game.app_title, ids)
grade = steam_grades.get_grade(steam_id)
grades[game.app_name] = {
"steam_id": steam_id,
"grade": grade
}
if not grades[game.app_name].get("steam_id"):
grades[game.app_name]["steam_id"] = steam_grades.get_steam_id(game.app_title)
if not grades[game.app_name].get("grade"):
grades[game.app_name]["grade"] = steam_grades.get_grade(game.app_title)
with open(os.path.join(data_dir, "game_list.json"), "w") as f:
f.write(json.dumps(grades))
f.close()
self.action.emit("Ready")
self.progress.emit(100)
class LoginThread(QThread):
login = pyqtSignal()
start_app = pyqtSignal(bool) # offline
def __init__(self, core: LegendaryCore):
super(LoginThread, self).__init__()
self.core = core
def run(self):
logger.info("Try if you are logged in")
try:
if self.core.login():
logger.info("You are logged in")
self.start_app.emit(False)
else:
self.run()
except ValueError:
logger.info("You are not logged in. Open Login Window")
self.login.emit()
except ConnectionError as e:
logger.warning(e)
self.start_app.emit(True)
class LaunchDialog(QDialog, Ui_LaunchDialog): class LaunchDialog(QDialog, Ui_LaunchDialog):
quit_app = pyqtSignal(int)
start_app = pyqtSignal(bool) start_app = pyqtSignal(bool)
finished = False
def __init__(self, core: LegendaryCore, offline): def __init__(self, core: LegendaryCore, offline=False, parent=None):
super(LaunchDialog, self).__init__() super(LaunchDialog, self).__init__(parent=parent)
self.setupUi(self) self.setupUi(self)
if os.name == "nt": self.setAttribute(Qt.WA_DeleteOnClose, True)
self.finished = True
self.steam_info.setVisible(False)
self.steam_prog_bar.setVisible(False)
self.core = core self.core = core
if not offline: self.offline = offline
self.login_thread = LoginThread(core) self.image_thread = None
self.login_thread.login.connect(self.login)
self.login_thread.start_app.connect(self.launch)
self.login_thread.start()
else:
self.launch(offline)
def login(self): def login(self):
self.hide() do_launch = True
if LoginDialog(core=self.core).login(): try:
self.show() if self.offline:
self.login_thread.start() pass
else:
exit(0)
def launch(self, offline=False):
# self.core = core
if not os.path.exists(p := QSettings().value("img_dir", os.path.join(data_dir, "images"))):
os.makedirs(p)
self.offline = offline
if not offline:
self.image_info.setText(self.tr("Downloading Images"))
self.img_thread = ImageThread(self.core, self)
self.img_thread.download_progess.connect(self.update_image_progbar)
self.img_thread.finished.connect(self.finish)
self.img_thread.start()
# not disabled and not windows
if (not QSettings().value("disable_protondb", False, bool)) and (not os.name == "nt"):
self.steam_thread = SteamThread(self.core, self)
self.steam_thread.progress.connect(self.update_steam_prog_bar)
self.steam_thread.action.connect(lambda x: self.steam_info.setText(x))
self.steam_thread.finished.connect(self.finish)
self.steam_thread.start()
else: else:
self.finished = True if self.core.login():
self.steam_info.setVisible(False) logger.info("You are logged in")
self.steam_prog_bar.setVisible(False) else:
raise ValueError("You are not logged in. Open Login Window")
except ValueError as e:
logger.info(str(e))
do_launch = LoginDialog(core=self.core, parent=self).login()
except ConnectionError as e:
logger.warning(e)
self.offline = True
finally:
if do_launch:
self.show()
self.launch()
else:
self.quit_app.emit(0)
def update_steam_prog_bar(self, value): def launch(self):
self.steam_prog_bar.setValue(value) # self.core = core
if not os.path.exists(image_dir):
os.makedirs(image_dir)
if not self.offline:
self.image_info.setText(self.tr("Downloading Images"))
self.image_thread = ImageThread(self.core, self)
self.image_thread.download_progess.connect(self.update_image_progbar)
self.image_thread.finished.connect(self.finish)
self.image_thread.finished.connect(lambda: self.image_info.setText(self.tr("Ready")))
self.image_thread.finished.connect(self.image_thread.quit)
self.image_thread.finished.connect(self.image_thread.deleteLater)
self.image_thread.start()
else:
self.finish()
def update_image_progbar(self, i: int): def update_image_progbar(self, i: int):
self.image_prog_bar.setValue(i) self.image_prog_bar.setValue(i)
def finish(self): def finish(self):
if self.finished: self.image_info.setText(self.tr("Starting..."))
self.image_info.setText(self.tr("Starting...")) self.image_prog_bar.setValue(100)
self.image_prog_bar.setValue(100) self.start_app.emit(self.offline)
self.steam_prog_bar.setValue(100)
self.start_app.emit(self.offline)
else:
self.finished = True

View file

@ -1,96 +1,102 @@
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QStackedLayout, QWidget, QPushButton from dataclasses import dataclass
from logging import getLogger
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog
from custom_legendary.core import LegendaryCore from custom_legendary.core import LegendaryCore
# Login Opportunities: Browser, Import
from rare.components.dialogs.login.browser_login import BrowserLogin from rare.components.dialogs.login.browser_login import BrowserLogin
from rare.components.dialogs.login.import_widget import ImportWidget from rare.components.dialogs.login.import_login import ImportLogin
from rare.ui.components.dialogs.login.login_dialog import Ui_LoginDialog
logger = getLogger("Login")
class LoginDialog(QDialog): @dataclass
class LoginPages:
landing: int
browser: int
import_egl: int
success: int
class LoginDialog(QDialog, Ui_LoginDialog):
logged_in: bool = False logged_in: bool = False
pages = LoginPages(landing=0, browser=1, import_egl=2, success=3)
def __init__(self, core: LegendaryCore): def __init__(self, core: LegendaryCore, parent=None):
super(LoginDialog, self).__init__() super(LoginDialog, self).__init__(parent=parent)
self.setupUi(self)
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint)
self.core = core self.core = core
self.setWindowTitle("Rare - Login")
self.setFixedWidth(350)
self.setFixedHeight(450)
self.init_ui() self.browser_page = BrowserLogin(self.core, self.login_stack)
self.login_stack.insertWidget(self.pages.browser, self.browser_page)
self.browser_page.success.connect(self.login_successful)
self.browser_page.changed.connect(
lambda: self.next_button.setEnabled(self.browser_page.is_valid())
)
self.import_page = ImportLogin(self.core, self.login_stack)
self.login_stack.insertWidget(self.pages.import_egl, self.import_page)
self.import_page.success.connect(self.login_successful)
self.import_page.changed.connect(
lambda: self.next_button.setEnabled(self.import_page.is_valid())
)
def init_ui(self): self.next_button.setEnabled(False)
self.layout = QStackedLayout() self.back_button.setEnabled(False)
self.landing_widget = QWidget() self.login_browser_radio.clicked.connect(lambda: self.next_button.setEnabled(True))
self.landing_layout = QVBoxLayout() self.login_import_radio.clicked.connect(lambda: self.next_button.setEnabled(True))
self.exit_button.clicked.connect(self.close)
self.back_button.clicked.connect(self.back_clicked)
self.next_button.clicked.connect(self.next_clicked)
self.title = QLabel(f"<h1>{self.tr('Welcome to Rare')}</h1>") self.login_stack.setCurrentIndex(self.pages.landing)
self.landing_layout.addWidget(self.title)
self.info_text = QLabel(self.tr("Select one option to Login"))
self.landing_layout.addWidget(self.info_text)
self.browser_login = OptionWidget(self.tr("Use Browser"), self.resize(self.minimumSizeHint())
self.tr("This opens your default browser. Login and copy the text")) self.setFixedSize(self.size())
self.landing_layout.addWidget(self.browser_login) def back_clicked(self):
self.browser_login.button.clicked.connect(lambda: self.layout.setCurrentIndex(1)) self.back_button.setEnabled(False)
self.next_button.setEnabled(True)
self.login_stack.setCurrentIndex(self.pages.landing)
self.import_login = OptionWidget("Import from existing installation", def next_clicked(self):
"Import an existing login session from an Epic Games Launcher installation. You will get logged out there") if self.login_stack.currentIndex() == self.pages.landing:
self.import_login.button.clicked.connect(lambda: self.layout.setCurrentIndex(2)) if self.login_browser_radio.isChecked():
self.landing_layout.addWidget(self.import_login) self.login_stack.setCurrentIndex(self.pages.browser)
self.next_button.setEnabled(False)
self.close_button = QPushButton("Exit App") if self.login_import_radio.isChecked():
self.close_button.clicked.connect(self.close) self.login_stack.setCurrentIndex(self.pages.import_egl)
self.landing_layout.addWidget(self.close_button) self.next_button.setEnabled(self.import_page.is_valid())
self.back_button.setEnabled(True)
self.landing_widget.setLayout(self.landing_layout) elif self.login_stack.currentIndex() == self.pages.browser:
self.layout.addWidget(self.landing_widget) self.browser_page.do_login()
elif self.login_stack.currentIndex() == self.pages.import_egl:
self.browser_widget = BrowserLogin(self.core) self.import_page.do_login()
self.browser_widget.success.connect(self.success) else:
self.browser_widget.back.clicked.connect(lambda: self.layout.setCurrentIndex(0)) self.close()
self.layout.addWidget(self.browser_widget)
self.import_widget = ImportWidget(self.core)
self.import_widget.back.clicked.connect(lambda: self.layout.setCurrentIndex(0))
self.import_widget.success.connect(self.success)
self.layout.addWidget(self.import_widget)
self.layout.addWidget(LoginSuccessfulWidget())
self.setLayout(self.layout)
def login(self): def login(self):
self.exec_() self.exec_()
return self.logged_in return self.logged_in
def success(self): def login_successful(self):
if self.core.login(): try:
self.logged_in = True if self.core.login():
self.layout.setCurrentIndex(3) self.logged_in = True
# time.sleep(1) self.welcome_label.setText(
self.close() self.welcome_label.text().replace("</h1>", f", {self.core.lgd.userdata['displayName']}</h1>")
)
self.exit_button.setVisible(False)
class OptionWidget(QWidget): self.back_button.setVisible(False)
def __init__(self, btn_text: str, info_text: str): self.login_stack.setCurrentIndex(self.pages.success)
super(OptionWidget, self).__init__() else:
self.layout = QVBoxLayout() raise ValueError("Login failed.")
self.text = QLabel(info_text) except ValueError as e:
self.text.setWordWrap(True) logger.error(str(e))
self.button = QPushButton(btn_text) self.next_button.setEnabled(False)
self.logged_in = False
self.layout.addWidget(self.button)
self.layout.addWidget(self.text)
self.setLayout(self.layout)
class LoginSuccessfulWidget(QWidget):
def __init__(self):
super(LoginSuccessfulWidget, self).__init__()
self.layout = QVBoxLayout()
self.layout.addWidget(QLabel("Login Successful"))
self.setLayout(self.layout)

View file

@ -1,54 +1,49 @@
import json import json
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import pyqtSignal from PyQt5.QtCore import pyqtSignal, QUrl
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel, QLineEdit from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QWidget
from custom_legendary.core import LegendaryCore from custom_legendary.core import LegendaryCore
from rare.ui.components.dialogs.login.browser_login import Ui_BrowserLogin
logger = getLogger("BrowserLogin") logger = getLogger("BrowserLogin")
class BrowserLogin(QWidget): class BrowserLogin(QWidget, Ui_BrowserLogin):
success = pyqtSignal() success = pyqtSignal()
url: str = "https://www.epicgames.com/id/login?redirectUrl=https%3A%2F%2Fwww.epicgames.com%2Fid%2Fapi%2Fredirect" changed = pyqtSignal()
login_url = "https://www.epicgames.com/id/login?redirectUrl=https%3A%2F%2Fwww.epicgames.com%2Fid%2Fapi%2Fredirect"
def __init__(self, core: LegendaryCore, parent=None):
super(BrowserLogin, self).__init__(parent=parent)
self.setupUi(self)
def __init__(self, core: LegendaryCore):
super(BrowserLogin, self).__init__()
self.layout = QVBoxLayout()
self.core = core self.core = core
self.back = QPushButton("Back") # TODO Icon self.open_button.clicked.connect(self.open_browser)
self.layout.addWidget(self.back) self.sid_edit.textChanged.connect(self.changed.emit)
self.info_text = QLabel(self.tr( def is_valid(self):
"Opens a browser. You login and copy the json code in the field below. Click <a href='{}'>here</a> to open Browser").format( return len(self.sid_edit.text()) == 32
self.url))
self.info_text.setWordWrap(True)
self.info_text.setOpenExternalLinks(True)
self.layout.addWidget(self.info_text)
self.input_field = QLineEdit() def do_login(self):
self.input_field.setPlaceholderText(self.tr("Insert SID here")) self.status_label.setText(self.tr("Logging in..."))
self.layout.addWidget(self.input_field) sid = self.sid_edit.text()
self.mini_info = QLabel("")
self.login_btn = QPushButton(self.tr("Login"))
self.login_btn.clicked.connect(self.login)
self.layout.addWidget(self.login_btn)
self.setLayout(self.layout)
def login(self):
self.mini_info.setText(self.tr("Loading..."))
sid = self.input_field.text()
# when the text copied # when the text copied
if sid.startswith("{") and sid.endswith("}"): if sid.startswith("{") and sid.endswith("}"):
sid = json.loads(sid)["sid"] sid = json.loads(sid)["sid"]
token = self.core.auth_sid(sid) try:
if self.core.auth_code(token): token = self.core.auth_sid(sid)
logger.info(f"Successfully logged in as {self.core.lgd.userdata['displayName']}") if self.core.auth_code(token):
self.success.emit() logger.info(f"Successfully logged in as {self.core.lgd.userdata['displayName']}")
else: self.success.emit()
self.mini_info.setText("Login failed") else:
self.status_label.setText(self.tr("Login failed."))
logger.warning("Failed to login through browser")
except Exception as e:
logger.warning(e)
def open_browser(self):
QDesktopServices.openUrl(QUrl(self.login_url))

View file

@ -0,0 +1,95 @@
import os
from getpass import getuser
from logging import getLogger
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QWidget, QFileDialog
from custom_legendary.core import LegendaryCore
from rare.ui.components.dialogs.login.import_login import Ui_ImportLogin
logger = getLogger("ImportLogin")
class ImportLogin(QWidget, Ui_ImportLogin):
success = pyqtSignal()
changed = pyqtSignal()
if os.name == "nt":
localappdata = os.path.expandvars("%LOCALAPPDATA%")
else:
localappdata = os.path.join("drive_c/users", getuser(), "Local Settings/Application Data")
appdata_path = os.path.join(localappdata, "EpicGamesLauncher/Saved/Config/Windows")
found = False
def __init__(self, core: LegendaryCore, parent=None):
super(ImportLogin, self).__init__(parent=parent)
self.setupUi(self)
self.core = core
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. ")
if os.name == "nt":
if not self.core.egl.appdata_path and os.path.exists(self.egl_data_path):
self.core.egl.appdata_path = self.appdata_path
if not self.core.egl.appdata_path:
self.status_label.setText(self.text_egl_notfound)
else:
self.status_label.setText(self.text_egl_found)
self.found = True
else:
self.info_label.setText(self.tr(
"Please select the Wine prefix"
" where Epic Games Launcher is installed. ") + self.info_label.text()
)
prefixes = self.get_wine_prefixes()
if len(prefixes):
self.prefix_combo.addItems(prefixes)
self.status_label.setText(self.tr("Select the Wine prefix you want to import."))
else:
self.status_label.setText(self.text_egl_notfound)
self.prefix_tool.clicked.connect(self.prefix_path)
self.prefix_combo.editTextChanged.connect(self.changed.emit)
def get_wine_prefixes(self):
possible_prefixes = [
os.path.expanduser("~/.wine"),
os.path.expanduser("~/Games/epic-games-store"),
]
prefixes = []
for prefix in possible_prefixes:
if os.path.exists(os.path.join(prefix, self.appdata_path)):
prefixes.append(prefix)
return prefixes
def prefix_path(self):
prefix_dialog = QFileDialog(self, self.tr("Choose path"), os.path.expanduser("~/"))
prefix_dialog.setFileMode(QFileDialog.DirectoryOnly)
if prefix_dialog.exec_():
names = prefix_dialog.selectedFiles()
self.prefix_combo.setCurrentText(names[0])
def is_valid(self):
if os.name == "nt":
return self.found
else:
return os.path.exists(os.path.join(self.prefix_combo.currentText(), self.appdata_path))
def do_login(self):
self.status_label.setText(self.tr("Loading..."))
if os.name == "nt":
pass
else:
self.core.egl.appdata_path = os.path.join(self.prefix_combo.currentText(), self.appdata_path)
try:
if self.core.auth_import():
logger.info(f"Logged in as {self.core.lgd.userdata['displayName']}")
self.success.emit()
else:
self.status_label.setText(self.tr("Login failed."))
logger.warning("Failed to import existing session.")
except Exception as e:
self.status_label.setText(self.tr("Login failed. ") + str(e))
logger.warning("Failed to import existing session: " + str(e))

View file

@ -1,103 +0,0 @@
import os
from getpass import getuser
from logging import getLogger
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel, QButtonGroup, QRadioButton
from custom_legendary.core import LegendaryCore
logger = getLogger("Import")
class ImportWidget(QWidget):
success = pyqtSignal()
def __init__(self, core: LegendaryCore):
super(ImportWidget, self).__init__()
self.layout = QVBoxLayout()
self.core = core
self.back = QPushButton("Back")
self.layout.addWidget(self.back)
self.title = QLabel("<h3>Import existing Login session</h3>")
self.title.setWordWrap(True)
self.layout.addWidget(self.title)
self.infoText = QLabel(
"Found Installations here. \nPlease select prefix, where Epic Games Launcher is installed\nNote: You will get logged out there")
self.infoText.setWordWrap(True)
self.layout.addWidget(self.infoText)
self.import_button = QPushButton(self.tr("Import"))
self.data_path = ""
if os.name == "nt":
if not self.core.egl.appdata_path and os.path.exists(
os.path.expandvars("%LOCALAPPDATA%/EpicGamesLauncher/Saved/Config/Windows")):
self.core.egl.appdata_path = os.path.expandvars("%LOCALAPPDATA%/EpicGamesLauncher/Saved/Config/Windows")
if not self.core.egl.appdata_path:
self.text = QLabel(self.tr("Could not find EGL program data"))
else:
self.text = QLabel(self.tr("Found EGL program Data. Do you want to import them?"))
self.layout.addWidget(self.text)
# Linux
else:
self.radio_buttons = []
prefixes = self.get_wine_prefixes()
if len(prefixes) == 0:
self.infoText.setText(self.tr("Could not find any Epic Games login data"))
self.import_button.setDisabled(True)
else:
self.btn_group = QButtonGroup()
for i in prefixes:
radio_button = QRadioButton(i)
self.radio_buttons.append(radio_button)
self.btn_group.addButton(radio_button)
self.layout.addWidget(radio_button)
radio_button.toggled.connect(self.toggle_radiobutton)
self.login_text = QLabel("")
self.layout.addWidget(self.login_text)
self.layout.addWidget(self.import_button)
self.import_button.clicked.connect(self.import_login_data)
self.setLayout(self.layout)
def toggle_radiobutton(self):
if self.sender().isChecked():
self.data_path = self.sender().text()
def get_wine_prefixes(self):
possible_prefixes = [
os.path.expanduser("~/.wine"),
os.path.expanduser("~/Games/epic-games-store")
]
prefixes = []
for i in possible_prefixes:
if os.path.exists(os.path.join(i, "drive_c/users", getuser(),
"Local Settings/Application Data/EpicGamesLauncher/Saved/Config/Windows")):
prefixes.append(i)
return prefixes
def import_login_data(self):
self.import_button.setText(self.tr("Loading..."))
self.import_button.setDisabled(True)
if os.name != "nt":
self.core.egl.appdata_path = os.path.join(self.data_path,
f"drive_c/users/{getuser()}/Local Settings/Application Data/EpicGamesLauncher/Saved/Config/Windows")
try:
if self.core.auth_import():
logger.info(f"Logged in as {self.core.lgd.userdata['displayName']}")
self.success.emit()
else:
logger.warning("Failed to import existing session")
except Exception as e:
logger.warning(e)
logger.warning("Error: No valid session found")
self.login_text.setText(self.tr("Error: No valid session found"))
self.import_button.setText(self.tr("Import"))
self.import_button.setDisabled(False)

View file

@ -1,3 +1,4 @@
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QHBoxLayout, QPushButton, QVBoxLayout, QLabel, QDialog, QFileDialog from PyQt5.QtWidgets import QHBoxLayout, QPushButton, QVBoxLayout, QLabel, QDialog, QFileDialog
from rare.utils.extra_widgets import PathEdit from rare.utils.extra_widgets import PathEdit
@ -7,7 +8,7 @@ class PathInputDialog(QDialog):
def __init__(self, title_text, text, path="Select Directory"): def __init__(self, title_text, text, path="Select Directory"):
super().__init__() super().__init__()
self.path = "" self.path = ""
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setWindowTitle(title_text) self.setWindowTitle(title_text)
self.info_label = QLabel(text) self.info_label = QLabel(text)
self.info_label.setWordWrap(True) self.info_label.setWordWrap(True)

View file

@ -1,3 +1,4 @@
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog, QLabel, QVBoxLayout, QCheckBox, QFormLayout, QHBoxLayout, QPushButton from PyQt5.QtWidgets import QDialog, QLabel, QVBoxLayout, QCheckBox, QFormLayout, QHBoxLayout, QPushButton
from qtawesome import icon from qtawesome import icon
@ -9,6 +10,7 @@ class UninstallDialog(QDialog):
super(UninstallDialog, self).__init__() super(UninstallDialog, self).__init__()
self.setWindowTitle("Uninstall Game") self.setWindowTitle("Uninstall Game")
self.info = 0 self.info = 0
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.layout = QVBoxLayout() self.layout = QVBoxLayout()
self.info_text = QLabel(self.tr("Do you really want to uninstall {}").format(game.app_title)) self.info_text = QLabel(self.tr("Do you really want to uninstall {}").format(game.app_title))
self.layout.addWidget(self.info_text) self.layout.addWidget(self.info_text)

View file

@ -0,0 +1,57 @@
from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import QPlainTextEdit, QWidget, QPushButton, QFileDialog, QVBoxLayout
class ConsoleWindow(QWidget):
def __init__(self):
super(ConsoleWindow, self).__init__()
self.layout = QVBoxLayout()
self.setGeometry(0, 0, 600, 400)
self.console = Console()
self.layout.addWidget(self.console)
self.save_button = QPushButton(self.tr("Save output to file"))
self.layout.addWidget(self.save_button)
self.save_button.clicked.connect(self.save)
self.setLayout(self.layout)
def save(self):
file, ok = QFileDialog.getSaveFileName(self, "Save output", "", "Log Files (*.log);;All Files (*)")
if ok:
if "." not in file:
file += ".log"
with open(file, "w") as f:
f.write(self.console.toPlainText())
f.close()
self.save_button.setText(self.tr("Saved"))
def log(self, text):
self.console.log(text)
def error(self, text):
self.console.error(text)
class Console(QPlainTextEdit):
def __init__(self):
super().__init__()
self.setReadOnly(True)
self._cursor_output = self.textCursor()
def log(self, text):
self._cursor_output.insertText(text)
self.scroll_to_last_line()
def error(self, text):
self._cursor_output.insertHtml(f"<font color=\"Red\">{text}</font>")
self.scroll_to_last_line()
def scroll_to_last_line(self):
cursor = self.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.movePosition(QTextCursor.Up if cursor.atBlockStart() else
QTextCursor.StartOfLine)
self.setTextCursor(cursor)

View file

View file

@ -1,7 +1,7 @@
import os import os
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import QSettings, QTimer from PyQt5.QtCore import Qt, QSettings, QTimer, pyqtSignal
from PyQt5.QtGui import QCloseEvent from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QApplication from PyQt5.QtWidgets import QMainWindow, QMessageBox, QApplication
@ -14,9 +14,11 @@ logger = getLogger("Window")
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
quit_app = pyqtSignal(int)
def __init__(self, core: LegendaryCore, args): def __init__(self, core: LegendaryCore, args):
super(MainWindow, self).__init__() super(MainWindow, self).__init__()
self.setAttribute(Qt.WA_DeleteOnClose)
self.settings = QSettings() self.settings = QSettings()
self.core = core self.core = core
self.offline = args.offline self.offline = args.offline
@ -29,6 +31,7 @@ class MainWindow(QMainWindow):
self.setWindowTitle("Rare - GUI for legendary") self.setWindowTitle("Rare - GUI for legendary")
self.tab_widget = TabWidget(core, self, args.offline) self.tab_widget = TabWidget(core, self, args.offline)
self.tab_widget.quit_app.connect(self.quit_app.emit)
self.setCentralWidget(self.tab_widget) self.setCentralWidget(self.tab_widget)
if not args.offline: if not args.offline:
self.rpc = DiscordRPC(core) self.rpc = DiscordRPC(core)

View file

@ -17,6 +17,7 @@ from rare.utils.models import InstallQueueItemModel, InstallOptionsModel
class TabWidget(QTabWidget): class TabWidget(QTabWidget):
quit_app = pyqtSignal(int)
delete_presence = pyqtSignal() delete_presence = pyqtSignal()
def __init__(self, core: LegendaryCore, parent, offline): def __init__(self, core: LegendaryCore, parent, offline):
@ -45,8 +46,10 @@ class TabWidget(QTabWidget):
self.addTab(self.account, "") self.addTab(self.account, "")
self.setTabEnabled(disabled_tab + 1, False) self.setTabEnabled(disabled_tab + 1, False)
self.mini_widget = MiniWidget(core)
self.mini_widget.quit_app.connect(self.quit_app.emit)
account_action = QWidgetAction(self) account_action = QWidgetAction(self)
account_action.setDefaultWidget(MiniWidget(core)) account_action.setDefaultWidget(self.mini_widget)
account_button = TabButtonWidget(core, 'mdi.account-circle', 'Account') account_button = TabButtonWidget(core, 'mdi.account-circle', 'Account')
account_button.setMenu(QMenu()) account_button.setMenu(QMenu())
account_button.menu().addAction(account_action) account_button.menu().addAction(account_action)
@ -71,13 +74,23 @@ class TabWidget(QTabWidget):
# show uninstalled info # show uninstalled info
self.games_tab.default_widget.game_list.show_uninstalled_info.connect(self.games_tab.show_uninstalled) self.games_tab.default_widget.game_list.show_uninstalled_info.connect(self.games_tab.show_uninstalled)
# install dlc # install dlc
self.games_tab.game_info.dlc_tab.install_dlc.connect(self.install_game) self.games_tab.game_info.dlc_tab.install_dlc.connect(
lambda app_name, update: self.install_game(
InstallOptionsModel(app_name=app_name),
update=update))
# install game # install game
self.games_tab.uninstalled_info_widget.info.install_game.connect(self.install_game) self.games_tab.uninstalled_info_widget.info.install_game.connect(
lambda app_name: self.install_game(
InstallOptionsModel(app_name=app_name)))
# repair game # repair game
self.games_tab.game_info.info.verify_game.connect(lambda app_name: self.start_download( self.games_tab.game_info.info.verify_game.connect(
InstallOptionsModel(app_name, core.get_installed_game(app_name).install_path, repair=True))) lambda app_name: self.install_game(
InstallOptionsModel(app_name=app_name,
base_path=core.get_installed_game(app_name).install_path,
repair=True),
silent=True)
)
# Finished sync # Finished sync
self.cloud_saves.finished.connect(self.finished_sync) self.cloud_saves.finished.connect(self.finished_sync)
@ -87,17 +100,18 @@ class TabWidget(QTabWidget):
# Open game list on click on Games tab button # Open game list on click on Games tab button
self.tabBarClicked.connect(self.mouse_clicked) self.tabBarClicked.connect(self.mouse_clicked)
self.setIconSize(QSize(25, 25)) self.setIconSize(QSize(25, 25))
def mouse_clicked(self, tab_num): def mouse_clicked(self, tab_num):
if tab_num == 0: if tab_num == 0:
self.games_tab.layout.setCurrentIndex(0) self.games_tab.layout.setCurrentIndex(0)
if tab_num == 3: if tab_num == 3:
self.store.load() self.store.load()
def install_game(self, app_name, disable_path=False): # TODO; maybe pass InstallOptionsModel only, not split arguments
def install_game(self, options: InstallOptionsModel, update=False, silent=False):
install_dialog = InstallDialog(self.core, install_dialog = InstallDialog(self.core,
InstallQueueItemModel(options=InstallOptionsModel(app_name=app_name)), InstallQueueItemModel(options=options),
update=disable_path, parent=self) update=update, silent=silent, parent=self)
install_dialog.result_ready.connect(self.on_install_dialog_closed) install_dialog.result_ready.connect(self.on_install_dialog_closed)
install_dialog.execute() install_dialog.execute()
@ -106,11 +120,11 @@ class TabWidget(QTabWidget):
self.setCurrentIndex(1) self.setCurrentIndex(1)
self.start_download(download_item) self.start_download(download_item)
def start_download(self, options): def start_download(self, download_item: InstallQueueItemModel):
downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys()) + 1 downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys()) + 1
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else "")) self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))
self.setCurrentIndex(1) self.setCurrentIndex(1)
self.downloadTab.install_game(options) self.downloadTab.install_game(download_item)
def game_imported(self, app_name: str): def game_imported(self, app_name: str):
igame = self.core.get_installed_game(app_name) igame = self.core.get_installed_game(app_name)

View file

@ -1,12 +1,14 @@
import webbrowser import webbrowser
from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox, QLabel, QPushButton from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox, QLabel, QPushButton
from custom_legendary.core import LegendaryCore from custom_legendary.core import LegendaryCore
class MiniWidget(QWidget): class MiniWidget(QWidget):
quit_app = pyqtSignal(int)
def __init__(self, core: LegendaryCore): def __init__(self, core: LegendaryCore):
super(MiniWidget, self).__init__() super(MiniWidget, self).__init__()
self.layout = QVBoxLayout() self.layout = QVBoxLayout()
@ -39,5 +41,4 @@ class MiniWidget(QWidget):
if reply == QMessageBox.Yes: if reply == QMessageBox.Yes:
self.core.lgd.invalidate_userdata() self.core.lgd.invalidate_userdata()
# restart app self.quit_app.emit(-133742) # restart exit code
QCoreApplication.instance().exit(-133742) # restart exit code

View file

@ -2,7 +2,7 @@ import os
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QSettings from PyQt5.QtCore import QThread, pyqtSignal, Qt, QSettings
from PyQt5.QtWidgets import QVBoxLayout, QPushButton, QHBoxLayout, QLabel, QGroupBox from PyQt5.QtWidgets import QVBoxLayout, QPushButton, QHBoxLayout, QLabel, QGroupBox, QMessageBox
from custom_legendary.core import LegendaryCore from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame, SaveGameStatus from custom_legendary.models.game import InstalledGame, SaveGameStatus
@ -77,13 +77,19 @@ class SyncWidget(QGroupBox):
self.logger.error(e) self.logger.error(e)
return return
if '%' in save_path or '{' in save_path: if '%' in save_path or '{' in save_path:
status = self.tr("Path not found") # status = self.tr("Path not found")
self.logger.info("Could not find save path") self.logger.info("Could not find save path")
igame.save_path = ""
else: else:
igame.save_path = save_path igame.save_path = save_path
if not os.path.exists(igame.save_path): if not igame.save_path:
igame.save_path = os.path.expanduser(f"~/{igame.app_name}/")
QMessageBox.warning(self, "Savepath error", self.tr("Please edit save path of game {} manually in Cload saves tab").format(igame.title))
if igame.save_path and not os.path.exists(igame.save_path):
os.makedirs(igame.save_path) os.makedirs(igame.save_path)
self.core.lgd.set_installed_game(self.igame.app_name, self.igame)
self.res, (self.dt_local, dt_remote) = self.core.check_savegame_state(igame.save_path, save) self.res, (self.dt_local, dt_remote) = self.core.check_savegame_state(igame.save_path, save)
if self.res == SaveGameStatus.NO_SAVE: if self.res == SaveGameStatus.NO_SAVE:
@ -110,9 +116,7 @@ class SyncWidget(QGroupBox):
elif self.res == SaveGameStatus.REMOTE_NEWER: elif self.res == SaveGameStatus.REMOTE_NEWER:
status = self.tr("Cloud save is newer") status = self.tr("Cloud save is newer")
self.download_button = QPushButton(self.tr("Download Cloud saves")) self.download_button = QPushButton(self.tr("Download Cloud saves"))
self.download_button.setStyleSheet(""" self.download_button.setObjectName("success")
QPushButton{ background-color: lime}
""")
self.upload_button = QPushButton(self.tr("Upload Saves")) self.upload_button = QPushButton(self.tr("Upload Saves"))
self.logger.info(f'Cloud save for "{igame.title}" is newer:') self.logger.info(f'Cloud save for "{igame.title}" is newer:')
self.logger.info(f'- Cloud save date: {dt_remote.strftime("%Y-%m-%d %H:%M:%S")}') self.logger.info(f'- Cloud save date: {dt_remote.strftime("%Y-%m-%d %H:%M:%S")}')
@ -126,9 +130,7 @@ class SyncWidget(QGroupBox):
elif self.res == SaveGameStatus.LOCAL_NEWER: elif self.res == SaveGameStatus.LOCAL_NEWER:
status = self.tr("Local save is newer") status = self.tr("Local save is newer")
self.upload_button = QPushButton(self.tr("Upload saves")) self.upload_button = QPushButton(self.tr("Upload saves"))
self.upload_button.setStyleSheet(""" self.download_button.setObjectName("success")
QPushButton{ background-color: lime}
""")
self.download_button = QPushButton(self.tr("Download saves")) self.download_button = QPushButton(self.tr("Download saves"))
self.logger.info(f'Local save for "{igame.title}" is newer') self.logger.info(f'Local save for "{igame.title}" is newer')
if dt_remote: if dt_remote:

View file

@ -147,9 +147,6 @@ class DownloadTab(QWidget):
if len(self.update_widgets) == 0: if len(self.update_widgets) == 0:
self.update_text.setVisible(True) self.update_text.setVisible(True)
for i in self.update_widgets.values():
i.update_button.setDisabled(False)
self.finished.emit((True, app_name)) self.finished.emit((True, app_name))
self.reset_infos() self.reset_infos()
@ -165,7 +162,7 @@ class DownloadTab(QWidget):
elif text == "stop": elif text == "stop":
self.reset_infos() self.reset_infos()
self.active_game = None self.active_game = None
self.finished.emit((False, None)) self.finished.emit((False, ""))
if self.dl_queue: if self.dl_queue:
self.start_installation(self.dl_queue[0]) self.start_installation(self.dl_queue[0])

View file

@ -1,4 +1,5 @@
import os import os
import platform
import queue import queue
import subprocess import subprocess
import sys import sys
@ -158,7 +159,7 @@ class DownloadThread(QThread):
def _handle_postinstall(self, postinstall, igame): def _handle_postinstall(self, postinstall, igame):
print('This game lists the following prequisites to be installed:') print('This game lists the following prequisites to be installed:')
print(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}') print(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
if os.name == 'nt': if platform.system() == "Windows":
if QMessageBox.question(self, "", "Do you want to install the prequisites", if QMessageBox.question(self, "", "Do you want to install the prequisites",
QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
self.core.prereq_installed(igame.app_name) self.core.prereq_installed(igame.app_name)

View file

@ -1,8 +1,8 @@
import json
import os import os
import platform
from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QPixmap, QKeyEvent from PyQt5.QtGui import QKeyEvent
from PyQt5.QtWidgets import QWidget, QTabWidget, QMessageBox from PyQt5.QtWidgets import QWidget, QTabWidget, QMessageBox
from qtawesome import icon from qtawesome import icon
@ -14,7 +14,8 @@ from rare.components.tabs.games.game_info.game_settings import GameSettings
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
from rare.utils.extra_widgets import SideTabBar from rare.utils.extra_widgets import SideTabBar
from rare.utils.legendary_utils import VerifyThread from rare.utils.legendary_utils import VerifyThread
from rare.utils.utils import IMAGE_DIR, get_size from rare.utils.steam_grades import SteamWorker
from rare.utils.utils import get_size, get_pixmap
class InfoTabs(QTabWidget): class InfoTabs(QTabWidget):
@ -44,17 +45,21 @@ class InfoTabs(QTabWidget):
self.settings.update_game(app_name) self.settings.update_game(app_name)
# DLC Tab: Disable if no dlcs available # DLC Tab: Disable if no dlcs available
if len(dlcs[self.core.get_game(app_name).asset_info.catalog_item_id]) == 0: if dlcs:
self.setTabEnabled(3, False) if len(dlcs[self.core.get_game(app_name).asset_info.catalog_item_id]) == 0:
self.setTabEnabled(3, False)
else:
self.setTabEnabled(3, True)
self.dlc_tab.update_dlcs(app_name, dlcs)
else: else:
self.setTabEnabled(3, True) self.setTabEnabled(3, False)
self.dlc_tab.update_dlcs(app_name, dlcs)
def keyPressEvent(self, e: QKeyEvent): def keyPressEvent(self, e: QKeyEvent):
if e.key() == Qt.Key_Escape: if e.key() == Qt.Key_Escape:
self.parent().layout.setCurrentIndex(0) self.parent().layout.setCurrentIndex(0)
class GameInfo(QWidget, Ui_GameInfo): class GameInfo(QWidget, Ui_GameInfo):
igame: InstalledGame igame: InstalledGame
game: Game game: Game
@ -68,6 +73,7 @@ class GameInfo(QWidget, Ui_GameInfo):
self.setupUi(self) self.setupUi(self)
self.core = core self.core = core
self.ratings = {"platinum": self.tr("Platinum"), self.ratings = {"platinum": self.tr("Platinum"),
"gold": self.tr("Gold"), "gold": self.tr("Gold"),
"silver": self.tr("Silver"), "silver": self.tr("Silver"),
@ -79,10 +85,14 @@ class GameInfo(QWidget, Ui_GameInfo):
else: else:
self.grade_table = {} self.grade_table = {}
if os.name == "nt": if platform.system() == "Windows":
self.lbl_grade.setVisible(False) self.lbl_grade.setVisible(False)
self.grade.setVisible(False) self.grade.setVisible(False)
if platform.system() != "Windows":
self.steam_worker = SteamWorker(self.core)
self.steam_worker.rating_signal.connect(self.grade.setText)
self.game_actions_stack.setCurrentIndex(0) self.game_actions_stack.setCurrentIndex(0)
self.game_actions_stack.resize(self.game_actions_stack.minimumSize()) self.game_actions_stack.resize(self.game_actions_stack.minimumSize())
@ -133,22 +143,12 @@ class GameInfo(QWidget, Ui_GameInfo):
def update_game(self, app_name): def update_game(self, app_name):
self.game = self.core.get_game(app_name) self.game = self.core.get_game(app_name)
self.igame = self.core.get_installed_game(app_name) self.igame = self.core.get_installed_game(app_name)
self.game_title.setText(f"<h2>{self.game.app_title}</h2>") self.game_title.setText(f"<h2>{self.game.app_title}</h2>")
if os.path.exists(f"{IMAGE_DIR}/{self.game.app_name}/FinalArt.png"): pixmap = get_pixmap(app_name)
pixmap = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/FinalArt.png") w = 200
elif os.path.exists(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxTall.png"): pixmap = pixmap.scaled(w, int(w * 4 / 3))
pixmap = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxTall.png") self.image.setPixmap(pixmap)
elif os.path.exists(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxLogo.png"):
pixmap = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxLogo.png")
else:
# logger.warning(f"No Image found: {self.game.title}")
pixmap = None
if pixmap:
w = 200
pixmap = pixmap.scaled(w, int(w * 4 / 3))
self.image.setPixmap(pixmap)
self.app_name.setText(self.game.app_name) self.app_name.setText(self.game.app_name)
self.version.setText(self.game.app_version) self.version.setText(self.game.app_version)
@ -156,12 +156,10 @@ class GameInfo(QWidget, Ui_GameInfo):
self.install_size.setText(get_size(self.igame.install_size)) self.install_size.setText(get_size(self.igame.install_size))
self.install_path.setText(self.igame.install_path) self.install_path.setText(self.igame.install_path)
if os.name != "nt" and self.grade_table: if platform.system() != "Windows":
try: self.grade.setText(self.tr("Loading"))
grade = self.grade_table[app_name]["grade"] self.steam_worker.set_app_name(app_name)
except KeyError: self.steam_worker.start()
grade = "fail"
self.grade.setText(self.ratings[grade])
if len(self.verify_threads.keys()) == 0 or not self.verify_threads.get(app_name): if len(self.verify_threads.keys()) == 0 or not self.verify_threads.get(app_name):
self.verify_widget.setCurrentIndex(0) self.verify_widget.setCurrentIndex(0)

View file

@ -1,4 +1,5 @@
import os import os
import platform
from PyQt5.QtCore import QSettings from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QWidget, QFileDialog, QMessageBox from PyQt5.QtWidgets import QWidget, QFileDialog, QMessageBox
@ -14,7 +15,9 @@ def find_proton_wrappers():
possible_proton_wrappers = [] possible_proton_wrappers = []
compatibilitytools_dirs = [ compatibilitytools_dirs = [
os.path.expanduser("~/.steam/steam/steamapps/common"), os.path.expanduser("~/.steam/steam/steamapps/common"),
"/usr/share/steam/compatibilitytools.d" "/usr/share/steam/compatibilitytools.d",
os.path.expanduser("~/.steam/compatibilitytools.d"),
os.path.expanduser("~/.steam/root/compatibilitytools.d")
] ]
for c in compatibilitytools_dirs: for c in compatibilitytools_dirs:
if os.path.exists(c): if os.path.exists(c):
@ -63,7 +66,7 @@ class GameSettings(QWidget, Ui_GameSettings):
) )
self.wrapper_button.setEnabled(False) self.wrapper_button.setEnabled(False)
if os.name != "nt": if platform.system() != "Windows":
self.possible_proton_wrappers = find_proton_wrappers() self.possible_proton_wrappers = find_proton_wrappers()
self.proton_wrapper.addItems(self.possible_proton_wrappers) self.proton_wrapper.addItems(self.possible_proton_wrappers)
@ -85,11 +88,11 @@ class GameSettings(QWidget, Ui_GameSettings):
self.core.lgd.config.add_section(self.game.app_name) self.core.lgd.config.add_section(self.game.app_name)
self.core.lgd.config.set(self.game.app_name, option, value) self.core.lgd.config.set(self.game.app_name, option, value)
else: else:
if self.game.app_name in self.core.lgd.config.sections() and self.core.lgd.config.get( if self.core.lgd.config.has_section(self.game.app_name) and self.core.lgd.config.get(
f"{self.game.app_name}", option, fallback=None) is not None: f"{self.game.app_name}", option, fallback=None) is not None:
self.core.lgd.config.remove_option(self.game.app_name, option) self.core.lgd.config.remove_option(self.game.app_name, option)
if not self.core.lgd.config[self.game.app_name]: if not self.core.lgd.config[self.game.app_name]:
self.core.lgd.config.remove_section(self.game.app_name) self.core.lgd.config.remove_section(self.game.app_name)
self.core.lgd.save_config() self.core.lgd.save_config()
self.sender().setEnabled(False) self.sender().setEnabled(False)
@ -115,9 +118,6 @@ class GameSettings(QWidget, Ui_GameSettings):
if self.change: if self.change:
# Dont use Proton # Dont use Proton
if i == 0: if i == 0:
self.proton_prefix.setEnabled(False)
self.wrapper_widget.setEnabled(True)
self.linux_settings.wine_groupbox.setEnabled(True)
if f"{self.game.app_name}" in self.core.lgd.config.sections(): if f"{self.game.app_name}" in self.core.lgd.config.sections():
if self.core.lgd.config.get(f"{self.game.app_name}", "wrapper", fallback=False): if self.core.lgd.config.get(f"{self.game.app_name}", "wrapper", fallback=False):
self.core.lgd.config.remove_option(self.game.app_name, "wrapper") self.core.lgd.config.remove_option(self.game.app_name, "wrapper")
@ -130,6 +130,13 @@ class GameSettings(QWidget, Ui_GameSettings):
self.core.lgd.config.remove_option(f"{self.game.app_name}.env", "STEAM_COMPAT_DATA_PATH") self.core.lgd.config.remove_option(f"{self.game.app_name}.env", "STEAM_COMPAT_DATA_PATH")
if not self.core.lgd.config[self.game.app_name + ".env"]: if not self.core.lgd.config[self.game.app_name + ".env"]:
self.core.lgd.config.remove_section(self.game.app_name + ".env") self.core.lgd.config.remove_section(self.game.app_name + ".env")
self.proton_prefix.setEnabled(False)
# lk: TODO: This has to be fixed properly.
# lk: It happens because of the widget update. Mask it for now behind disabling the save button
self.wrapper.setText(self.core.lgd.config.get(f"{self.game.app_name}", "wrapper", fallback=""))
self.wrapper_button.setDisabled(True)
self.wrapper_widget.setEnabled(True)
self.linux_settings.wine_groupbox.setEnabled(True)
else: else:
self.proton_prefix.setEnabled(True) self.proton_prefix.setEnabled(True)
self.wrapper_widget.setEnabled(False) self.wrapper_widget.setEnabled(False)
@ -199,7 +206,7 @@ class GameSettings(QWidget, Ui_GameSettings):
self.wrapper.setText(wrapper) self.wrapper.setText(wrapper)
self.title.setText(f"<h2>{self.game.app_title}</h2>") self.title.setText(f"<h2>{self.game.app_title}</h2>")
if os.name != "nt": if platform.system() != "Windows":
self.linux_settings.update_game(app_name) self.linux_settings.update_game(app_name)
self.linux_settings.dxvk.update_settings(app_name) self.linux_settings.dxvk.update_settings(app_name)
proton = self.core.lgd.config.get(f"{app_name}", "wrapper", fallback="").replace('"', "") proton = self.core.lgd.config.get(f"{app_name}", "wrapper", fallback="").replace('"', "")

View file

@ -1,8 +1,9 @@
import json import json
import os import os
import platform
from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QPixmap, QKeyEvent from PyQt5.QtGui import QKeyEvent
from PyQt5.QtWidgets import QWidget, QTabWidget, QTreeView from PyQt5.QtWidgets import QWidget, QTabWidget, QTreeView
from qtawesome import icon from qtawesome import icon
@ -12,7 +13,8 @@ from rare import data_dir
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
from rare.utils.extra_widgets import SideTabBar from rare.utils.extra_widgets import SideTabBar
from rare.utils.json_formatter import QJsonModel from rare.utils.json_formatter import QJsonModel
from rare.utils.utils import IMAGE_DIR from rare.utils.steam_grades import SteamWorker
from rare.utils.utils import get_pixmap
class UninstalledTabInfo(QTabWidget): class UninstalledTabInfo(QTabWidget):
@ -60,18 +62,12 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.setupUi(self) self.setupUi(self)
self.core = core self.core = core
self.ratings = {"platinum": self.tr("Platinum"), if platform.system() != "Windows":
"gold": self.tr("Gold"), self.steam_worker = SteamWorker(self.core)
"silver": self.tr("Silver"), self.steam_worker.rating_signal.connect(self.grade.setText)
"bronze": self.tr("Bronze"),
"fail": self.tr("Could not get grade"), if platform.system() == "Windows":
"pending": self.tr("Not enough reports")}
if os.path.exists(p := os.path.join(data_dir, "game_list.json")):
self.grade_table = json.load(open(p))
else:
self.grade_table = {}
if os.name == "nt":
self.lbl_grade.setVisible(False) self.lbl_grade.setVisible(False)
self.grade.setVisible(False) self.grade.setVisible(False)
@ -87,22 +83,12 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
def update_game(self, app_name): def update_game(self, app_name):
self.game = self.core.get_game(app_name) self.game = self.core.get_game(app_name)
self.game_title.setText(f"<h2>{self.game.app_title}</h2>") self.game_title.setText(f"<h2>{self.game.app_title}</h2>")
if os.path.exists(f"{IMAGE_DIR}/{self.game.app_name}/FinalArt.png"): pixmap = get_pixmap(app_name)
pixmap = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/FinalArt.png") w = 200
elif os.path.exists(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxTall.png"): pixmap = pixmap.scaled(w, int(w * 4 / 3))
pixmap = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxTall.png") self.image.setPixmap(pixmap)
elif os.path.exists(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxLogo.png"):
pixmap = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxLogo.png")
else:
# logger.warning(f"No Image found: {self.game.title}")
pixmap = None
if pixmap:
w = 200
pixmap = pixmap.scaled(w, int(w * 4 / 3))
self.image.setPixmap(pixmap)
self.app_name.setText(self.game.app_name) self.app_name.setText(self.game.app_name)
self.version.setText(self.game.app_version) self.version.setText(self.game.app_version)
@ -110,9 +96,7 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.install_size.setText("N/A") self.install_size.setText("N/A")
self.install_path.setText("N/A") self.install_path.setText("N/A")
if os.name != "nt" and self.grade_table: if platform.system() != "Windows":
try: self.grade.setText(self.tr("Loading"))
grade = self.grade_table[app_name]["grade"] self.steam_worker.set_app_name(app_name)
except KeyError: self.steam_worker.start()
grade = "fail"
self.grade.setText(self.ratings[grade])

View file

@ -1,9 +1,7 @@
import os
from logging import getLogger from logging import getLogger
import psutil import psutil
from PyQt5.QtCore import Qt, pyqtSignal, QSettings, QTimer from PyQt5.QtCore import Qt, pyqtSignal, QSettings, QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QScrollArea, QWidget, QLabel, QVBoxLayout, QStackedWidget from PyQt5.QtWidgets import QScrollArea, QWidget, QLabel, QVBoxLayout, QStackedWidget
from custom_legendary.core import LegendaryCore from custom_legendary.core import LegendaryCore
@ -15,7 +13,7 @@ from rare.components.tabs.games.game_widgets.uninstalled_icon_widget import Icon
from rare.components.tabs.games.game_widgets.uninstalled_list_widget import ListWidgetUninstalled from rare.components.tabs.games.game_widgets.uninstalled_list_widget import ListWidgetUninstalled
from rare.utils.extra_widgets import FlowLayout from rare.utils.extra_widgets import FlowLayout
from rare.utils.models import InstallOptionsModel from rare.utils.models import InstallOptionsModel
from rare.utils.utils import download_image from rare.utils.utils import download_image, get_uninstalled_pixmap, get_pixmap
logger = getLogger("Game list") logger = getLogger("Game list")
@ -39,7 +37,16 @@ class GameList(QStackedWidget):
self.settings = QSettings() self.settings = QSettings()
icon_view = self.settings.value("icon_view", True, bool) icon_view = self.settings.value("icon_view", True, bool)
self.procs = [(proc.name(), proc.pid) for proc in psutil.process_iter()] self.procs = []
for proc in psutil.process_iter():
try:
self.procs.append((proc.name(), proc.pid))
except psutil.ZombieProcess:
continue
except psutil.NoSuchProcess:
continue
except Exception:
continue
self.init_ui(icon_view) self.init_ui(icon_view)
def init_ui(self, icon_view=True): def init_ui(self, icon_view=True):
@ -65,12 +72,14 @@ class GameList(QStackedWidget):
self.list_layout = QVBoxLayout() self.list_layout = QVBoxLayout()
self.list_layout.addWidget(QLabel(self.info_text)) self.list_layout.addWidget(QLabel(self.info_text))
self.IMAGE_DIR = self.settings.value("img_dir", os.path.join(data_dir, "images"), str)
self.updates = [] self.updates = []
self.widgets = {} self.widgets = {}
if not self.offline:
self.bit32 = [i.app_name for i in self.core.get_game_and_dlc_list(True, "Win32")[0]] self.bit32 = [i.app_name for i in self.core.get_game_and_dlc_list(True, "Win32")[0]]
self.mac_games = [i.app_name for i in self.core.get_game_and_dlc_list(True, "Mac")[0]] self.mac_games = [i.app_name for i in self.core.get_game_and_dlc_list(True, "Mac")[0]]
else:
self.bit32 = []
self.mac_games = []
self.installed = sorted(self.core.get_installed_list(), key=lambda x: x.title) self.installed = sorted(self.core.get_installed_list(), key=lambda x: x.title)
# Installed Games # Installed Games
@ -78,22 +87,24 @@ class GameList(QStackedWidget):
icon_widget, list_widget = self.add_installed_widget(igame) icon_widget, list_widget = self.add_installed_widget(igame)
self.icon_layout.addWidget(icon_widget) self.icon_layout.addWidget(icon_widget)
self.list_layout.addWidget(list_widget) self.list_layout.addWidget(list_widget)
if not self.offline:
self.uninstalled_games = []
installed = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
games, self.dlcs = self.core.get_game_and_dlc_list(update_assets=not self.offline)
for game in sorted(games, key=lambda x: x.app_title):
if not game.app_name in installed:
self.uninstalled_games.append(game)
self.uninstalled_games = [] # add uninstalled games
installed = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
games, self.dlcs = self.core.get_game_and_dlc_list()
for game in sorted(games, key=lambda x: x.app_title):
if not game.app_name in installed:
self.uninstalled_games.append(game)
# add uninstalled games for game in self.uninstalled_games:
icon_widget, list_widget = self.add_uninstalled_widget(game)
for game in self.uninstalled_games: self.icon_layout.addWidget(icon_widget)
icon_widget, list_widget = self.add_uninstalled_widget(game) self.list_layout.addWidget(list_widget)
else:
self.icon_layout.addWidget(icon_widget) self.dlcs = []
self.list_layout.addWidget(list_widget)
self.icon_parent_layout.addLayout(self.icon_layout) self.icon_parent_layout.addLayout(self.icon_layout)
self.icon_parent_layout.addStretch(1) self.icon_parent_layout.addStretch(1)
@ -114,17 +125,11 @@ class GameList(QStackedWidget):
self.filter(filter_games) self.filter(filter_games)
def add_uninstalled_widget(self, game): def add_uninstalled_widget(self, game):
if os.path.exists(f"{self.IMAGE_DIR}/{game.app_name}/UninstalledArt.png"): pixmap = get_uninstalled_pixmap(game.app_name)
pixmap = QPixmap(f"{self.IMAGE_DIR}/{game.app_name}/UninstalledArt.png") if pixmap.isNull():
logger.info(game.app_title + " has a corrupt image. Reloading...")
if pixmap.isNull():
logger.info(game.app_title + " has a corrupt image.")
download_image(game, force=True)
pixmap = QPixmap(f"{self.IMAGE_DIR}/{game.app_name}/UninstalledArt.png")
else:
logger.warning(f"No Image found: {game.app_title}")
download_image(game, force=True) download_image(game, force=True)
pixmap = QPixmap(f"{self.IMAGE_DIR}/{game.app_name}/UninstalledArt.png") pixmap = get_uninstalled_pixmap(game.app_name)
icon_widget = IconWidgetUninstalled(game, self.core, pixmap) icon_widget = IconWidgetUninstalled(game, self.core, pixmap)
icon_widget.show_uninstalled_info.connect(self.show_install_info) icon_widget.show_uninstalled_info.connect(self.show_install_info)
@ -137,20 +142,12 @@ class GameList(QStackedWidget):
return icon_widget, list_widget return icon_widget, list_widget
def add_installed_widget(self, igame): def add_installed_widget(self, igame):
if os.path.exists(f"{self.IMAGE_DIR}/{igame.app_name}/FinalArt.png"): pixmap = get_pixmap(igame.app_name)
pixmap = QPixmap(f"{self.IMAGE_DIR}/{igame.app_name}/FinalArt.png")
elif os.path.exists(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png"):
pixmap = QPixmap(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png")
elif os.path.exists(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxLogo.png"):
pixmap = QPixmap(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxLogo.png")
else:
logger.warning(f"No Image found: {igame.title}")
pixmap = None
if pixmap.isNull(): if pixmap.isNull():
logger.info(igame.title + " has a corrupt image.") logger.info(igame.title + " has a corrupt image.")
download_image(igame, force=True) download_image(self.core.get_game(igame.app_name), force=True)
pixmap = QPixmap(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png") pixmap = get_pixmap(igame.app_name)
icon_widget = GameWidgetInstalled(igame, self.core, pixmap, self.offline) icon_widget = GameWidgetInstalled(igame, self.core, pixmap, self.offline)
# self.icon_layout.addWidget(icon_widget) # self.icon_layout.addWidget(icon_widget)
@ -255,7 +252,7 @@ class GameList(QStackedWidget):
w.setVisible(w.igame.can_run_offline) w.setVisible(w.igame.can_run_offline)
else: else:
w.setVisible(False) w.setVisible(False)
elif filter == "32bit": elif filter == "32bit" and self.bit32:
w.setVisible(w.game.app_name in self.bit32) w.setVisible(w.game.app_name in self.bit32)
elif filter == "mac": elif filter == "mac":
w.setVisible(w.game.app_name in self.mac_games) w.setVisible(w.game.app_name in self.mac_games)
@ -283,53 +280,11 @@ class GameList(QStackedWidget):
self.widgets.pop(widgets[0].game.app_name) self.widgets.pop(widgets[0].game.app_name)
# QWidget().setLayout(self.icon_layout) # QWidget().setLayout(self.icon_layout)
icon_layout = FlowLayout()
# QWidget().setLayout(self.list_layout)
list_layout = QVBoxLayout()
igame = self.core.get_installed_game(app_name) igame = self.core.get_installed_game(app_name)
self.add_installed_widget(igame) self.add_installed_widget(igame)
for igame in sorted(self.core.get_installed_list(), key=lambda x: x.title): self._update_games()
i_widget, l_widget = self.widgets[igame.app_name]
icon_layout.addWidget(i_widget)
list_layout.addWidget(l_widget)
self.uninstalled_names = []
installed_names = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
games, self.dlcs = self.core.get_game_and_dlc_list()
for game in sorted(games, key=lambda x: x.app_title):
if not game.app_name in installed_names:
self.uninstalled_names.append(game)
for game in self.uninstalled_names:
i_widget, list_widget = self.widgets[game.app_name]
icon_layout.addWidget(i_widget)
list_layout.addWidget(list_widget)
QWidget().setLayout(self.icon_layout)
QWidget().setLayout(self.list_layout)
self.icon_widget = QWidget()
self.list_widget = QWidget()
self.icon_widget.setLayout(icon_layout)
self.list_widget.setLayout(list_layout)
self.list_scrollarea.setWidget(QWidget())
self.icon_scrollarea.setWidget(QWidget())
self.icon_scrollarea.setWidget(self.icon_widget)
self.list_scrollarea.setWidget(self.list_widget)
self.icon_layout = icon_layout
self.list_layout = list_layout
self.icon_widget.setLayout(self.icon_layout)
self.list_widget.setLayout(self.list_layout)
self.update()
# uninstalled # uninstalled
elif not self.core.is_installed(widgets[0].game.app_name) and isinstance(widgets[0], elif not self.core.is_installed(widgets[0].game.app_name) and isinstance(widgets[0],
@ -342,8 +297,8 @@ class GameList(QStackedWidget):
game = self.core.get_game(app_name, True) game = self.core.get_game(app_name, True)
self.add_uninstalled_widget(game) self.add_uninstalled_widget(game)
self.icon_layout.addWidget(self.widgets[app_name][0]) self._update_games()
self.list_layout.addWidget(self.widgets[app_name][1])
else: else:
installed_names = [i.app_name for i in self.core.get_installed_list()] installed_names = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games # get Uninstalled games
@ -393,3 +348,49 @@ class GameList(QStackedWidget):
i_widget, list_widget = self.widgets[name] i_widget, list_widget = self.widgets[name]
self.icon_layout.addWidget(i_widget) self.icon_layout.addWidget(i_widget)
self.list_layout.addWidget(list_widget) self.list_layout.addWidget(list_widget)
def _update_games(self):
# new layouts to remove from old layout
icon_layout = FlowLayout()
# QWidget().setLayout(self.list_layout)
list_layout = QVBoxLayout()
for igame in sorted(self.core.get_installed_list(), key=lambda x: x.title):
i_widget, l_widget = self.widgets[igame.app_name]
icon_layout.addWidget(i_widget)
list_layout.addWidget(l_widget)
self.uninstalled_names = []
installed_names = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
games, self.dlcs = self.core.get_game_and_dlc_list()
for game in sorted(games, key=lambda x: x.app_title):
if not game.app_name in installed_names:
self.uninstalled_names.append(game)
for game in self.uninstalled_names:
i_widget, list_widget = self.widgets[game.app_name]
icon_layout.addWidget(i_widget)
list_layout.addWidget(list_widget)
QWidget().setLayout(self.icon_layout)
QWidget().setLayout(self.list_layout)
self.icon_widget = QWidget()
self.list_widget = QWidget()
self.icon_widget.setLayout(icon_layout)
self.list_widget.setLayout(list_layout)
self.list_scrollarea.setWidget(QWidget())
self.icon_scrollarea.setWidget(QWidget())
self.icon_scrollarea.setWidget(self.icon_widget)
self.list_scrollarea.setWidget(self.list_widget)
self.icon_layout = icon_layout
self.list_layout = list_layout
self.icon_widget.setLayout(self.icon_layout)
self.list_widget.setLayout(self.list_layout)
self.update()

View file

@ -1,4 +1,5 @@
import os import os
import platform
from logging import getLogger from logging import getLogger
from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, Qt, QByteArray from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, Qt, QByteArray
@ -8,6 +9,7 @@ from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame from custom_legendary.models.game import InstalledGame
from rare import cache_dir from rare import cache_dir
from rare.components.dialogs.uninstall_dialog import UninstallDialog from rare.components.dialogs.uninstall_dialog import UninstallDialog
from rare.components.extra.Console import ConsoleWindow
from rare.utils import legendary_utils from rare.utils import legendary_utils
from rare.utils.utils import create_desktop_link from rare.utils.utils import create_desktop_link
@ -30,8 +32,9 @@ class BaseInstalledWidget(QGroupBox):
self.game_running = False self.game_running = False
self.offline = offline self.offline = offline
self.update_available = self.core.get_asset(self.game.app_name, True).build_version != igame.version self.update_available = self.core.get_asset(self.game.app_name, True).build_version != igame.version
self.data = QByteArray()
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.settings = QSettings()
self.setContextMenuPolicy(Qt.ActionsContextMenu) self.setContextMenuPolicy(Qt.ActionsContextMenu)
launch = QAction(self.tr("Launch"), self) launch = QAction(self.tr("Launch"), self)
@ -47,9 +50,9 @@ class BaseInstalledWidget(QGroupBox):
self.create_desktop.triggered.connect(lambda: self.create_desktop_link("desktop")) self.create_desktop.triggered.connect(lambda: self.create_desktop_link("desktop"))
self.addAction(self.create_desktop) self.addAction(self.create_desktop)
if os.name == "posix": if platform.system() == "Linux":
start_menu_file = os.path.expanduser(f"~/.local/share/applications/{self.igame.title}.desktop") start_menu_file = os.path.expanduser(f"~/.local/share/applications/{self.igame.title}.desktop")
elif os.name == "nt": elif platform.system() == "Windows":
start_menu_file = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu") start_menu_file = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu")
else: else:
start_menu_file = "" start_menu_file = ""
@ -66,6 +69,10 @@ class BaseInstalledWidget(QGroupBox):
self.addAction(uninstall) self.addAction(uninstall)
def create_desktop_link(self, type_of_link): def create_desktop_link(self, type_of_link):
if platform.system() not in ["Windows", "Linux"]:
QMessageBox.warning(self, "Warning",
f"Create a Desktop link is currently not supported on {platform.system()}")
return
if type_of_link == "desktop": if type_of_link == "desktop":
path = os.path.expanduser(f"~/Desktop/") path = os.path.expanduser(f"~/Desktop/")
elif type_of_link == "start_menu": elif type_of_link == "start_menu":
@ -74,7 +81,8 @@ class BaseInstalledWidget(QGroupBox):
return return
if not (os.path.exists(os.path.expanduser(f"{path}{self.igame.title}.desktop")) if not (os.path.exists(os.path.expanduser(f"{path}{self.igame.title}.desktop"))
or os.path.exists(os.path.expanduser(f"{path}{self.igame.title}.lnk"))): or os.path.exists(os.path.expanduser(f"{path}{self.igame.title}.lnk"))):
create_desktop_link(self.igame.app_name, self.core, type_of_link) if not create_desktop_link(self.igame.app_name, self.core, type_of_link):
return
if type_of_link == "desktop": if type_of_link == "desktop":
self.create_desktop.setText(self.tr("Remove Desktop link")) self.create_desktop.setText(self.tr("Remove Desktop link"))
elif type_of_link == "start_menu": elif type_of_link == "start_menu":
@ -109,37 +117,50 @@ class BaseInstalledWidget(QGroupBox):
except Exception as e: except Exception as e:
logger.error(e) logger.error(e)
QMessageBox.warning(self, "Error", QMessageBox.warning(self, "Error",
self.tr("An error occurred while starting game. Maybe game files are missing")) str(e))
return return
if not self.proc: if not self.proc:
logger.error("Could not start process") logger.error("Could not start process")
return 1 return 1
self.game_logger = getLogger(self.game.app_name)
self.proc.finished.connect(self.finished) self.proc.finished.connect(self.finished)
self.proc.readyReadStandardOutput.connect(self.stdout)
self.proc.readyReadStandardError.connect(self.stderr) if self.settings.value("show_console", False, bool):
self.console = ConsoleWindow()
self.console.show()
self.proc.readyReadStandardOutput.connect(lambda: self.console.log(
bytes(self.proc.readAllStandardOutput()).decode("utf-8", errors="ignore")))
self.proc.readyReadStandardError.connect(lambda: self.console.error(
bytes(self.proc.readAllStandardOutput()).decode("utf-8", errors="ignore")))
else:
self.proc.readyReadStandardOutput.connect(self.stdout)
self.proc.readyReadStandardError.connect(self.stderr)
self.proc.start(params[0], params[1:]) self.proc.start(params[0], params[1:])
self.launch_signal.emit(self.igame.app_name) self.launch_signal.emit(self.igame.app_name)
self.game_running = True self.game_running = True
self.data = QByteArray()
return 0 return 0
def stdout(self): def stdout(self):
data = self.proc.readAllStandardOutput() data = self.proc.readAllStandardOutput()
stdout = bytes(data).decode("utf-8") stdout = bytes(data).decode("utf-8", errors="ignore")
self.game_logger.info(stdout) print(stdout)
def stderr(self): def stderr(self):
stderr = bytes(self.proc.readAllStandardError()).decode("utf-8") stderr = bytes(self.proc.readAllStandardError()).decode("utf-8", errors="ignore")
self.game_logger.error(stderr) print(stderr)
QMessageBox.warning(self, "Warning", stderr + f"\nSee {cache_dir}/logs/") logger.error(stderr)
# QMessageBox.warning(self, "Warning", stderr + "\nSee ~/.cache/rare/logs/")
def finished(self, exit_code): def finished(self, exit_code):
logger.info("Game exited with exit code: " + str(exit_code)) logger.info("Game exited with exit code: " + str(exit_code))
self.finish_signal.emit(self.game.app_name) self.finish_signal.emit(self.game.app_name)
self.game_running = False self.game_running = False
if self.settings.value("show_console", False, bool):
self.console.log(f"Game exited with code: {exit_code}")
def uninstall(self): def uninstall(self):
infos = UninstallDialog(self.game).get_information() infos = UninstallDialog(self.game).get_information()

View file

@ -46,8 +46,6 @@ class InstalledListWidget(BaseInstalledWidget):
self.info.setFixedWidth(80) self.info.setFixedWidth(80)
self.launch_button.clicked.connect(self.launch) self.launch_button.clicked.connect(self.launch)
if os.name != "nt":
self.wine_rating = QLabel("Wine Rating: " + self.get_rating())
self.developer_label = QLabel(self.tr("Developer: ") + self.dev) self.developer_label = QLabel(self.tr("Developer: ") + self.dev)
self.version_label = QLabel("Version: " + str(self.igame.version)) self.version_label = QLabel("Version: " + str(self.igame.version))
self.size_label = QLabel(f"{self.tr('Installed size')}: {round(self.size / (1024 ** 3), 2)} GB") self.size_label = QLabel(f"{self.tr('Installed size')}: {round(self.size / (1024 ** 3), 2)} GB")
@ -57,8 +55,6 @@ class InstalledListWidget(BaseInstalledWidget):
self.childLayout.addWidget(self.app_name_label) self.childLayout.addWidget(self.app_name_label)
self.childLayout.addWidget(self.developer_label) self.childLayout.addWidget(self.developer_label)
# if os.name != "nt":
# self.childLayout.addWidget(self.wine_rating)
self.childLayout.addWidget(self.version_label) self.childLayout.addWidget(self.version_label)
self.childLayout.addWidget(self.size_label) self.childLayout.addWidget(self.size_label)
@ -74,6 +70,3 @@ class InstalledListWidget(BaseInstalledWidget):
def launch(self): def launch(self):
if not self.game_running: if not self.game_running:
super(InstalledListWidget, self).launch(skip_version_check=self.update_available) super(InstalledListWidget, self).launch(skip_version_check=self.update_available)
def get_rating(self) -> str:
return "gold" # TODO

View file

@ -1,4 +1,4 @@
import os import platform
from PyQt5.QtWidgets import QTabWidget from PyQt5.QtWidgets import QTabWidget
@ -18,7 +18,7 @@ class SettingsTab(QTabWidget):
self.rare_settings = RareSettings() self.rare_settings = RareSettings()
self.addTab(self.rare_settings, "Rare") self.addTab(self.rare_settings, "Rare")
self.addTab(LegendarySettings(core), "Legendary") self.addTab(LegendarySettings(core), "Legendary")
if os.name != "nt": if platform.system() != "Windows":
self.addTab(LinuxSettings(core), "Linux") self.addTab(LinuxSettings(core), "Linux")
self.about = About() self.about = About()

View file

@ -1,4 +1,5 @@
import os.path import os.path
import platform
from logging import getLogger from logging import getLogger
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QStackedWidget, QVBoxLayout, QDialog, QCheckBox, QLabel, \ from PyQt5.QtWidgets import QFileDialog, QMessageBox, QStackedWidget, QVBoxLayout, QDialog, QCheckBox, QLabel, \
@ -54,7 +55,7 @@ class LegendarySettings(QStackedWidget, Ui_legendary_settings):
self.path_edit = PathEdit(path, QFileDialog.DirectoryOnly, save_func=self.save_egl_path) self.path_edit = PathEdit(path, QFileDialog.DirectoryOnly, save_func=self.save_egl_path)
self.pathedit_placeholder.addWidget(self.path_edit) self.pathedit_placeholder.addWidget(self.path_edit)
if os.name != "nt": if platform.system() != "Windows":
self.core.lgd.config.set("Legendary", "egl_programdata") self.core.lgd.config.set("Legendary", "egl_programdata")
self.core.egl.programdata_path = path self.core.egl.programdata_path = path

View file

@ -1,4 +1,5 @@
import os import os
import platform
import shutil import shutil
import subprocess import subprocess
import sys import sys
@ -35,6 +36,7 @@ class RareSettings(QWidget, Ui_RareSettings):
(self.auto_sync_cloud, "auto_sync_cloud", True), (self.auto_sync_cloud, "auto_sync_cloud", True),
(self.notification, "notification", True), (self.notification, "notification", True),
(self.save_size, "save_size", False), (self.save_size, "save_size", False),
(self.log_games, "show_console", False),
(self.image_cache, "cache_images", True) (self.image_cache, "cache_images", True)
] ]
@ -81,33 +83,18 @@ class RareSettings(QWidget, Ui_RareSettings):
self.rpc = RPCSettings() self.rpc = RPCSettings()
self.rpc_layout.addWidget(self.rpc, alignment=Qt.AlignTop) self.rpc_layout.addWidget(self.rpc, alignment=Qt.AlignTop)
self.init_checkboxes(self.checkboxes) for cb in self.checkboxes:
self.sys_tray.stateChanged.connect( widget, option, default = cb
lambda: self.settings.setValue("sys_tray", self.sys_tray.isChecked()) widget.setChecked(self.settings.value(option, default, bool))
) widget.stateChanged.connect(
self.auto_update.stateChanged.connect( lambda: self.settings.setValue(option, widget.isChecked())
lambda: self.settings.setValue("auto_update", self.auto_update.isChecked()) )
)
self.confirm_start.stateChanged.connect( if platform.system() == "Linux":
lambda: self.settings.setValue("confirm_start", self.confirm_start.isChecked())
)
self.auto_sync_cloud.stateChanged.connect(
lambda: self.settings.setValue("auto_sync_cloud", self.auto_sync_cloud.isChecked())
)
self.notification.stateChanged.connect(
lambda: self.settings.setValue("notification", self.notification.isChecked())
)
self.save_size.stateChanged.connect(
lambda: self.settings.setValue("save_size", self.save_size.isChecked())
)
self.image_cache.stateChanged.connect(
lambda: self.settings.setValue("cache_images", self.image_cache.isChecked())
)
if os.name == "posix":
self.desktop_file = os.path.expanduser("~/Desktop/Rare.desktop") self.desktop_file = os.path.expanduser("~/Desktop/Rare.desktop")
self.start_menu_link = os.path.expanduser("~/.local/share/applications/Rare.desktop") self.start_menu_link = os.path.expanduser("~/.local/share/applications/Rare.desktop")
elif os.name == "nt": elif platform.system() == "Windows":
self.desktop_file = os.path.expanduser("~/Desktop/Rare.lnk") self.desktop_file = os.path.expanduser("~/Desktop/Rare.lnk")
self.start_menu_link = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu") self.start_menu_link = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu")
else: else:
@ -132,7 +119,6 @@ class RareSettings(QWidget, Ui_RareSettings):
size += os.path.getsize(os.path.join(logdir, i)) size += os.path.getsize(os.path.join(logdir, i))
self.log_dir_size_label.setText(utils.get_size(size)) self.log_dir_size_label.setText(utils.get_size(size))
# TODO: Implement
# self.log_dir_clean_button.setVisible(False) # self.log_dir_clean_button.setVisible(False)
# self.log_dir_size_label.setVisible(False) # self.log_dir_size_label.setVisible(False)
@ -151,7 +137,7 @@ class RareSettings(QWidget, Ui_RareSettings):
def create_desktop_link(self): def create_desktop_link(self):
if not os.path.exists(self.desktop_file): if not os.path.exists(self.desktop_file):
utils.create_rare_desktop_link("start_menu") utils.create_rare_desktop_link("desktop")
self.desktop_link.setText(self.tr("Remove Desktop link")) self.desktop_link.setText(self.tr("Remove Desktop link"))
else: else:
os.remove(self.desktop_file) os.remove(self.desktop_file)
@ -178,7 +164,7 @@ class RareSettings(QWidget, Ui_RareSettings):
self.interface_info.setVisible(True) self.interface_info.setVisible(True)
def open_dir(self): def open_dir(self):
if os.name == "nt": if platform.system() == "Windows":
os.startfile(self.logdir) os.startfile(self.logdir)
else: else:
opener = "open" if sys.platform == "darwin" else "xdg-open" opener = "open" if sys.platform == "darwin" else "xdg-open"
@ -214,8 +200,3 @@ class RareSettings(QWidget, Ui_RareSettings):
os.rmdir(old_path) os.rmdir(old_path)
self.img_dir_path = new_path self.img_dir_path = new_path
self.settings.setValue("img_dir", new_path) self.settings.setValue("img_dir", new_path)
def init_checkboxes(self, checkboxes):
for cb in checkboxes:
widget, option, default = cb
widget.setChecked(self.settings.value(option, default, bool))

View file

@ -1,13 +1,15 @@
import os
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu, QAction from PyQt5.QtWidgets import QSystemTrayIcon, QMenu, QAction
from rare import style_path from rare import resources_path
class TrayIcon(QSystemTrayIcon): class TrayIcon(QSystemTrayIcon):
def __init__(self, parent): def __init__(self, parent):
super(TrayIcon, self).__init__(parent) super(TrayIcon, self).__init__(parent)
self.setIcon(QIcon(style_path + "Logo.png")) self.setIcon(QIcon(os.path.join(resources_path, "images", "Rare.png")))
self.setVisible(True) self.setVisible(True)
self.setToolTip("Rare") self.setToolTip("Rare")

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,3 @@
<svg height="736" viewBox="0 0 736 736" width="736" xmlns="http://www.w3.org/2000/svg">
<path d="m368 120c-137 0-248 111-248 248s111 248 248 248 248-111 248-248-111-248-248-248z" fill="#43474d"/>
</svg>

After

Width:  |  Height:  |  Size: 206 B

View file

@ -0,0 +1,3 @@
<svg height="736" viewBox="0 0 736 736" width="736" xmlns="http://www.w3.org/2000/svg">
<path d="m368 120c-137 0-248 111-248 248s111 248 248 248 248-111 248-248-111-248-248-248z" fill="#483d8b"/>
</svg>

After

Width:  |  Height:  |  Size: 206 B

View file

@ -0,0 +1,4 @@
<svg height="640" viewBox="0 0 320 640" width="320" xmlns="http://www.w3.org/2000/svg">
<path d="m41 236.475h238c21.4 0 32.1 25.9 17 41l-119 119c-9.4 9.4-24.6 9.4-33.9 0l-119.1-119c-15.1-15.1-4.4-41 17-41z"
fill="#eee"/>
</svg>

After

Width:  |  Height:  |  Size: 241 B

View file

@ -0,0 +1,4 @@
<svg height="320" viewBox="0 0 320 320" width="320" xmlns="http://www.w3.org/2000/svg">
<path d="m41 76.475h238c21.4 0 32.1 25.9 17 41l-119 119c-9.4 9.4-24.6 9.4-33.9 0l-119.1-119c-15.1-15.1-4.4-41 17-41z"
fill="#eee"/>
</svg>

After

Width:  |  Height:  |  Size: 240 B

View file

@ -0,0 +1,4 @@
<svg height="320" viewBox="0 0 320 320" width="320" xmlns="http://www.w3.org/2000/svg">
<path d="m279.01546 243.525h-237.999997c-21.4 0-32.1000001-25.9-17-41l118.999997-119c9.4-9.4 24.6-9.4 33.9 0l119 119c15.2 15.1 4.5 41-16.9 41z"
fill="#eee"/>
</svg>

After

Width:  |  Height:  |  Size: 266 B

View file

@ -0,0 +1,4 @@
<svg height="688" viewBox="0 0 688 688" width="688" xmlns="http://www.w3.org/2000/svg">
<path d="m520 120h-352c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-352c0-26.5-21.5-48-48-48z"
fill="#43474d"/>
</svg>

After

Width:  |  Height:  |  Size: 248 B

View file

@ -0,0 +1,4 @@
<svg height="688" viewBox="0 0 688 688" width="688" xmlns="http://www.w3.org/2000/svg">
<path d="m520 120h-352c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-352c0-26.5-21.5-48-48-48z"
fill="#483d8b"/>
</svg>

After

Width:  |  Height:  |  Size: 248 B

View file

@ -0,0 +1,355 @@
/*
$active_base = #202225;
$active_text = #eeeeee;
$widget_base = #333344;
$primary_border = #42474E;
$secondary_border = darkslategrey;
*/
* { background-color: #202225; }
* { color: #eeeeee; }
* { border-color: #483d8b; }
*::disabled,
*::drop-down:disabled {
color: #43474d;
border-color: #43474d;
background-color: #202225;
}
QLabel {
border-width: 0px;
background-color: transparent;
padding: 0px;
}
QMenu,
QListView,
QListWidget,
QFrame[frameShape="6"],
QLineEdit,
QTextEdit,
QTimeEdit,
QDateEdit,
QDateTimeEdit,
QComboBox,
QComboBox:editable,
QComboBox QAbstractItemView,
QSpinBox,
QDoubleSpinBox,
QProgressBar,
QScrollBar {
border-width: 1px;
border-style: solid;
border-radius: 2px;
padding: 2px;
}
QListView,
QListWidget,
QLineEdit,
QTextEdit,
QTimeEdit,
QDateEdit,
QDateTimeEdit,
QComboBox:editable,
QComboBox QAbstractItemView,
QSpinBox,
QDoubleSpinBox,
QProgressBar,
QScrollBar {
border-color: #2f4f4f;
background-color: #333344;
selection-background-color: #2f4f4f;
}
QLineEdit,
QTextEdit,
QTimeEdit,
QDateEdit,
QDateTimeEdit,
QComboBox
QSpinBox,
QDoubleSpinBox,
QProgressBar,
QPushButton {
height: 17px;
}
QToolButton {
height: 14px;
}
QFrame[frameShape="6"] {
border-radius: 4px;
}
QComboBox {
background-color: #3c3f41;
}
*::item:selected,
QComboBox QAbstractItemView {
selection-background-color: #2f4f4f;
}
*::drop-down,
*::drop-down:editable,
*::up-button,
*::down-button {
subcontrol-origin: border;
border-width: 1px;
border-style: solid;
border-radius: 2px;
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
}
*::drop-down {
subcontrol-position: top right;
border-color: #483d8b;
border-left-color: #5246a0; /* #483d8b lighter */
}
*::drop-down:editable,
*::up-button ,
*::down-button {
border-color: #2f4f4f;
background-color: #3c3f41;
}
*::drop-down,
*::drop-down:editable {
width: 14px;
image: url(@path@drop-down.svg);
}
*::up-button ,
*::down-button {
width: 14px; /* 16 + 2*1px border-width = 15px padding + 3px parent border */
}
*::up-button {
subcontrol-position: top right; /* position at the top right corner */
border-bottom-width: 1;
image: url(@path@sort-up.svg);
}
*::down-button {
subcontrol-position: bottom right; /* position at bottom right corner */
border-top-width: 1;
image: url(@path@sort-down.svg);
}
QProgressBar {
text-align: center;
}
QProgressBar::chunk {
width: 9.5%;
margin: 0.5%;
background-color: #2f4f4f;
}
QScrollBar {
border-radius: 6px;
padding: 1px;
}
QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical,
QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal {
border: none;
height: 0px;
background: transparent;
}
QScrollBar::add-line:vertical {
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar::sub-line:vertical {
subcontrol-position: bottom;
subcontrol-origin: margin;
}
QScrollBar::add-line:horizontal {
subcontrol-position: right;
subcontrol-origin: margin;
}
QScrollBar::sub-line:horizontal {
subcontrol-position: left;
subcontrol-origin: margin;
}
QScrollBar::handle {
border-width: 1px;
border-style: solid;
border-color: lightgray;
background-color: gray;
border-radius: 4px;
min-height: 20px;
min-width: 20px;
}
QPushButton,
QToolButton {
background-color: #3c3f41;
}
QPushButton::hover,
QToolButton::hover {
background-color: #222233;
}
QPushButton,
QToolButton {
border-width: 1px;
border-style: solid;
border-radius: 2px;
padding: 2px;
padding-left: 6px;
padding-right: 6px
}
QPushButton::menu-indicator {
subcontrol-position: right center;
subcontrol-origin: padding;
left: -2px;
border-style: none;
}
QPushButton#menu {
padding: 0px;
margin: 0px;
border-style: none;
}
QPushButton#menu_button {
background-color: transparent;
border: none;
}
QPushButton:hover#menu_button {
background-color: #334;
}
QPushButton#install_button {
background-color: #090;
}
QPushButton::hover#install_button {
background-color: #060;
}
QPushButton::disabled#install_button {
background-color: #020;
}
QPushButton#uninstall_button {
background-color: #900;
}
QPushButton::hover#uninstall_button {
background-color: #600;
}
QPushButton::disabled#uninstall_button {
background-color: #200;
}
QPushButton#success{
background-color: lime;
}
QGroupBox,
QCheckBox,
QRadioButton {
background-color: none;
}
QGroupBox::indicator,
QCheckBox::indicator,
QRadioButton::indicator {
border-color: #2f4f4f;
border-width: 1px;
border-style: solid;
}
QCheckBox::indicator,
QRadioButton::indicator {
width: 11px;
height: 11px;
}
QGroupBox::indicator:disabled,
QCheckBox::indicator:disabled,
QRadioButton::indicator:disabled {
border-color: #43474d;
}
QRadioButton::indicator {
border-radius: 5%;
}
QGroupBox::indicator,
QCheckBox::indicator {
border-radius: 2px;
}
QGroupBox::indicator:checked,
QCheckBox::indicator:checked {
border-radius: 2px;
image: url(@path@square.svg);
}
QRadioButton::indicator:checked {
border-radius: 5%;
image: url(@path@circle.svg);
}
QGroupBox::indicator:checked:disabled,
QCheckBox::indicator:checked:disabled {
image: url(@path@square-disabled.svg);
}
QRadioButton::indicator:checked:disabled {
image: url(@path@circle-disabled.svg);
}
QGroupBox,
QGroupBox#group,
QGroupBox#settings_widget {
border-width: 1px;
border-style: solid;
border-radius: 4px;
font-size: 11px;
font-weight: bold;
margin-top: 3ex;
padding: 1px;
}
QGroupBox#game_widget_icon {
border: none;
padding: 0px;
margin: 0px;
}
QSizeGrip {
image: none;
width: 4px;
height: 4px;
}
#list_widget {
border-top-width: 2px;
}
#search_bar {
padding: 3px;
border-radius: 5px;
background-color: #334;
}
QPushButton:hover#installed_menu_button {
background-color: green;
}
QTabBar#main_tab_bar {
border-bottom: none;
background-color: #2b2b2c;
}
QTabBar::tab#main_tab_bar {
border-top: 2px solid transparent;
border-bottom: none;
}
QTabBar::tab#main_tab_bar {
border-bottom: none;
padding: 5px;
}
QTabBar::tab:selected#main_tab_bar {
background-color: #202225;
border-top: 2px solid #483d8b;
}
QTabBar::tab:hover#main_tab_bar {
border-top: 2px solid #483d8b;
}
QTabBar::tab#settings_bar {
border-radius: 0;
}
QTabBar::tab:hover#settings_bar {
border-left: 2px solid white;
}
QTabBar::tab::selected#settings_bar {
background-color: #2f4f4f;
}
QTabBar::tab:disabled#settings_bar {
color: transparent;
background-color: transparent;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -1,262 +0,0 @@
QWidget {
background-color: #202225;
color: #eee;
}
QLabel {
background-color: transparent;
padding: 4px;
}
QLineEdit {
border: 1px solid darkslategrey;
border-radius: 2px;
background-color: #334;
padding: 2px;
}
QScrollBar {
border: 1px solid darkslategrey;
border-radius: 4px;
background-color: #334;
padding: 1px;
}
QScrollBar::add-line:vertical {
border: none;
background: transparent;
height: 0px;
subcontrol-position: bottom;
subcontrol-origin: margin;
}
QScrollBar::sub-line:vertical {
border: none;
background: transparent;
height: 0px;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar::add-line:horizontal {
border: none;
background: transparent;
height: 0px;
subcontrol-position: right;
subcontrol-origin: margin;
}
QScrollBar::sub-line:horizontal {
border: none;
background: green;
height: 0px;
subcontrol-position: left;
subcontrol-origin: margin;
}
QScrollBar::handle {
border: 1px solid darkslategrey;
background-color: gray;
border-radius: 4px;
min-height: 20px;
min-width: 20px;
}
QTabBar#main_tab_bar {
border-bottom: none;
background-color: #2b2b2c;
}
QTabBar::tab#main_tab_bar {
border-bottom: none;
}
QTabBar::tab#main_tab_bar {
border-bottom: none;
padding: 5px
}
QTabBar::tab:selected#main_tab_bar {
background-color: gray;
}
QTabBar::tab:hover#main_tab_bar {
border-bottom: 2px solid black;
}
QGroupBox {
border: 1px solid gray;
font-size: 13px;
font-weight: bold;
border-radius: 6px;
margin-top: 3ex;
padding-top: 0px;
padding-bottom: 0px;
padding-left: 0px;
padding-right: 0px;
}
QGroupBox#settings_widget {
border: 1px solid gray;
font-size: 13px;
font-weight: bold;
border-radius: 6px;
margin-top: 3ex;
padding-top: 0px;
padding-bottom: 0px;
padding-left: 0px;
padding-right: 0px;
}
QGroupBox#game_widget_icon {
border: none;
padding: 0;
margin: 0;
}
QGroupBox#group {
border: 1px solid gray;
font-size: 13px;
font-weight: bold;
border-radius: 6px;
margin-top: 3ex;
padding-top: 0px;
padding-bottom: 0px;
padding-left: 0px;
padding-right: 0px;
}
QToolButton {
border: 1px solid gray;
border-radius: 2px;
background-color: #3c3f41;
padding: 4px;
}
QToolButton:hover {
background-color: #223;
}
QPushButton {
border: 1px solid gray;
border-radius: 2px;
background-color: #3c3f41;
padding: 5px;
}
QPushButton:hover {
background-color: #223;
}
QPushButton::menu-indicator {
subcontrol-position: right center;
subcontrol-origin: padding;
left: -2px;
border-style: none;
}
QPushButton#menu {
padding: 0px;
margin: 0px;
border-style: none;
}
QPushButton#menu_button {
background-color: transparent;
border: none;
}
QPushButton:hover#menu_button {
background-color: #334;
}
QRadioButton {
background-color: none;
border-radius: 50%;
}
QRadioButton::indicator {
border: 1px solid gray;
border-radius: 5%;
}
QCheckBox {
background-color: none;
}
QCheckBox::indicator {
border: 1px solid gray;
border-radius: 2px;
}
QCheckBox::indicator:checked {
width: -20ex;
height: -20ex;
background: #5F5F80;
border-radius: 10px;
}
QSpinBox {
border: 1px solid darkslategrey;
border-radius: 2px;
background-color: #334;
padding: 2px;
}
/*
QCheckBox::indicator {
border: 1px solid gray;
}
*/
QComboBox {
border: 1px solid gray;
border-radius: 2px;
background-color: #3c3f41;
padding: 5px;
}
QComboBox::drop-down {
subcontrol-origin: padding;
subcontrol-position: top right;
width: 15px;
/*
border-left-width: 1px;
border-left-color: darkgray;
border-left-style: solid;
*/
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
#list_widget {
border-top: 2px solid white;
}
QPushButton:hover#installed_menu_button {
background-color: green;
}
QTabBar::tab#settings_bar {
border-radius: 0;
}
QTabBar::tab:hover#settings_bar {
border-left: 2px solid white;
}
QTabBar::tab::selected#settings_bar {
background-color: darkslategrey;
}
#search_bar {
padding: 3px;
border-radius: 5px;
background-color: #334;
}
QTabBar::tab:disabled#settings_bar {
color: transparent;
background-color: transparent;
}

View file

@ -14,16 +14,93 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_InstallDialog(object): class Ui_InstallDialog(object):
def setupUi(self, InstallDialog): def setupUi(self, InstallDialog):
InstallDialog.setObjectName("InstallDialog") InstallDialog.setObjectName("InstallDialog")
InstallDialog.resize(338, 267)
InstallDialog.setWindowTitle("Rare") InstallDialog.setWindowTitle("Rare")
self.install_dialog_layout = QtWidgets.QGridLayout(InstallDialog) self.install_dialog_layout = QtWidgets.QGridLayout(InstallDialog)
self.install_dialog_layout.setObjectName("install_dialog_layout") self.install_dialog_layout.setObjectName("install_dialog_layout")
self.force_download_check = QtWidgets.QCheckBox(InstallDialog)
self.force_download_check.setObjectName("force_download_check")
self.install_dialog_layout.addWidget(self.force_download_check, 3, 1, 1, 1)
self.install_dir_label = QtWidgets.QLabel(InstallDialog)
self.install_dir_label.setObjectName("install_dir_label")
self.install_dialog_layout.addWidget(self.install_dir_label, 1, 0, 1, 1, QtCore.Qt.AlignRight)
self.download_only_label = QtWidgets.QLabel(InstallDialog)
self.download_only_label.setObjectName("download_only_label")
self.install_dialog_layout.addWidget(self.download_only_label, 5, 0, 1, 1, QtCore.Qt.AlignRight)
self.sdl_list_label = QtWidgets.QLabel(InstallDialog)
self.sdl_list_label.setObjectName("sdl_list_label")
self.install_dialog_layout.addWidget(self.sdl_list_label, 6, 0, 1, 1, QtCore.Qt.AlignRight)
self.install_size_info_label = QtWidgets.QLabel(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.install_size_info_label.setFont(font)
self.install_size_info_label.setObjectName("install_size_info_label")
self.install_dialog_layout.addWidget(self.install_size_info_label, 8, 1, 1, 2)
self.install_dialog_label = QtWidgets.QLabel(InstallDialog)
self.install_dialog_label.setObjectName("install_dialog_label")
self.install_dialog_layout.addWidget(self.install_dialog_label, 0, 0, 1, 3)
spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.install_dialog_layout.addItem(spacerItem, 3, 2, 1, 1)
self.install_size_label = QtWidgets.QLabel(InstallDialog)
self.install_size_label.setObjectName("install_size_label")
self.install_dialog_layout.addWidget(self.install_size_label, 8, 0, 1, 1, QtCore.Qt.AlignRight)
self.download_only_info_label = QtWidgets.QLabel(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.download_only_info_label.setFont(font)
self.download_only_info_label.setObjectName("download_only_info_label")
self.install_dialog_layout.addWidget(self.download_only_info_label, 5, 2, 1, 1)
self.max_workers_spin = QtWidgets.QSpinBox(InstallDialog)
self.max_workers_spin.setObjectName("max_workers_spin")
self.install_dialog_layout.addWidget(self.max_workers_spin, 2, 1, 1, 1)
self.ignore_space_check = QtWidgets.QCheckBox(InstallDialog)
self.ignore_space_check.setObjectName("ignore_space_check")
self.install_dialog_layout.addWidget(self.ignore_space_check, 4, 1, 1, 1)
self.download_only_check = QtWidgets.QCheckBox(InstallDialog)
self.download_only_check.setText("")
self.download_only_check.setObjectName("download_only_check")
self.install_dialog_layout.addWidget(self.download_only_check, 5, 1, 1, 1)
self.max_workers_info_label = QtWidgets.QLabel(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.max_workers_info_label.setFont(font)
self.max_workers_info_label.setObjectName("max_workers_info_label")
self.install_dialog_layout.addWidget(self.max_workers_info_label, 2, 2, 1, 1)
self.warn_message = QtWidgets.QLabel(InstallDialog)
self.warn_message.setWordWrap(True)
self.warn_message.setObjectName("warn_message")
self.install_dialog_layout.addWidget(self.warn_message, 9, 1, 1, 2)
self.ignore_space_label = QtWidgets.QLabel(InstallDialog)
self.ignore_space_label.setObjectName("ignore_space_label")
self.install_dialog_layout.addWidget(self.ignore_space_label, 4, 0, 1, 1, QtCore.Qt.AlignRight)
self.install_dir_layout = QtWidgets.QHBoxLayout()
self.install_dir_layout.setObjectName("install_dir_layout")
self.install_dialog_layout.addLayout(self.install_dir_layout, 1, 1, 1, 2)
self.download_size_label = QtWidgets.QLabel(InstallDialog)
self.download_size_label.setObjectName("download_size_label")
self.install_dialog_layout.addWidget(self.download_size_label, 7, 0, 1, 1, QtCore.Qt.AlignRight)
self.force_download_label = QtWidgets.QLabel(InstallDialog) self.force_download_label = QtWidgets.QLabel(InstallDialog)
self.force_download_label.setObjectName("force_download_label") self.force_download_label.setObjectName("force_download_label")
self.install_dialog_layout.addWidget(self.force_download_label, 3, 0, 1, 1, QtCore.Qt.AlignRight) self.install_dialog_layout.addWidget(self.force_download_label, 3, 0, 1, 1, QtCore.Qt.AlignRight)
self.ignore_space_info_label = QtWidgets.QLabel(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.ignore_space_info_label.setFont(font)
self.ignore_space_info_label.setObjectName("ignore_space_info_label")
self.install_dialog_layout.addWidget(self.ignore_space_info_label, 4, 2, 1, 1)
self.max_workers_label = QtWidgets.QLabel(InstallDialog)
self.max_workers_label.setObjectName("max_workers_label")
self.install_dialog_layout.addWidget(self.max_workers_label, 2, 0, 1, 1, QtCore.Qt.AlignRight)
self.download_size_info_label = QtWidgets.QLabel(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.download_size_info_label.setFont(font)
self.download_size_info_label.setObjectName("download_size_info_label")
self.install_dialog_layout.addWidget(self.download_size_info_label, 7, 1, 1, 2)
self.button_layout = QtWidgets.QHBoxLayout() self.button_layout = QtWidgets.QHBoxLayout()
self.button_layout.setObjectName("button_layout") self.button_layout.setObjectName("button_layout")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.button_layout.addItem(spacerItem) self.button_layout.addItem(spacerItem1)
self.cancel_button = QtWidgets.QPushButton(InstallDialog) self.cancel_button = QtWidgets.QPushButton(InstallDialog)
self.cancel_button.setObjectName("cancel_button") self.cancel_button.setObjectName("cancel_button")
self.button_layout.addWidget(self.cancel_button) self.button_layout.addWidget(self.cancel_button)
@ -33,76 +110,7 @@ class Ui_InstallDialog(object):
self.install_button = QtWidgets.QPushButton(InstallDialog) self.install_button = QtWidgets.QPushButton(InstallDialog)
self.install_button.setObjectName("install_button") self.install_button.setObjectName("install_button")
self.button_layout.addWidget(self.install_button) self.button_layout.addWidget(self.install_button)
self.install_dialog_layout.addLayout(self.button_layout, 9, 0, 1, 3) self.install_dialog_layout.addLayout(self.button_layout, 11, 0, 1, 3)
self.ignore_space_info_label = QtWidgets.QLabel(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.ignore_space_info_label.setFont(font)
self.ignore_space_info_label.setObjectName("ignore_space_info_label")
self.install_dialog_layout.addWidget(self.ignore_space_info_label, 4, 2, 1, 1)
self.install_dir_label = QtWidgets.QLabel(InstallDialog)
self.install_dir_label.setObjectName("install_dir_label")
self.install_dialog_layout.addWidget(self.install_dir_label, 1, 0, 1, 1, QtCore.Qt.AlignRight)
self.ignore_space_check = QtWidgets.QCheckBox(InstallDialog)
self.ignore_space_check.setObjectName("ignore_space_check")
self.install_dialog_layout.addWidget(self.ignore_space_check, 4, 1, 1, 1)
self.max_workers_info_label = QtWidgets.QLabel(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.max_workers_info_label.setFont(font)
self.max_workers_info_label.setObjectName("max_workers_info_label")
self.install_dialog_layout.addWidget(self.max_workers_info_label, 2, 2, 1, 1)
self.max_workers_spin = QtWidgets.QSpinBox(InstallDialog)
self.max_workers_spin.setObjectName("max_workers_spin")
self.install_dialog_layout.addWidget(self.max_workers_spin, 2, 1, 1, 1)
self.download_only_info_label = QtWidgets.QLabel(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.download_only_info_label.setFont(font)
self.download_only_info_label.setObjectName("download_only_info_label")
self.install_dialog_layout.addWidget(self.download_only_info_label, 5, 2, 1, 1)
self.install_size_label = QtWidgets.QLabel(InstallDialog)
self.install_size_label.setObjectName("install_size_label")
self.install_dialog_layout.addWidget(self.install_size_label, 8, 0, 1, 1, QtCore.Qt.AlignRight)
self.install_dir_layout = QtWidgets.QHBoxLayout()
self.install_dir_layout.setObjectName("install_dir_layout")
self.install_dialog_layout.addLayout(self.install_dir_layout, 1, 1, 1, 2)
spacerItem1 = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.install_dialog_layout.addItem(spacerItem1, 3, 2, 1, 1)
self.ignore_space_label = QtWidgets.QLabel(InstallDialog)
self.ignore_space_label.setObjectName("ignore_space_label")
self.install_dialog_layout.addWidget(self.ignore_space_label, 4, 0, 1, 1, QtCore.Qt.AlignRight)
self.download_only_label = QtWidgets.QLabel(InstallDialog)
self.download_only_label.setObjectName("download_only_label")
self.install_dialog_layout.addWidget(self.download_only_label, 5, 0, 1, 1, QtCore.Qt.AlignRight)
self.max_workers_label = QtWidgets.QLabel(InstallDialog)
self.max_workers_label.setObjectName("max_workers_label")
self.install_dialog_layout.addWidget(self.max_workers_label, 2, 0, 1, 1, QtCore.Qt.AlignRight)
self.install_size_info_label = QtWidgets.QLabel(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.install_size_info_label.setFont(font)
self.install_size_info_label.setObjectName("install_size_info_label")
self.install_dialog_layout.addWidget(self.install_size_info_label, 8, 1, 1, 2)
self.download_size_label = QtWidgets.QLabel(InstallDialog)
self.download_size_label.setObjectName("download_size_label")
self.install_dialog_layout.addWidget(self.download_size_label, 7, 0, 1, 1, QtCore.Qt.AlignRight)
self.download_size_info_label = QtWidgets.QLabel(InstallDialog)
font = QtGui.QFont()
font.setItalic(True)
self.download_size_info_label.setFont(font)
self.download_size_info_label.setObjectName("download_size_info_label")
self.install_dialog_layout.addWidget(self.download_size_info_label, 7, 1, 1, 2)
self.force_download_check = QtWidgets.QCheckBox(InstallDialog)
self.force_download_check.setObjectName("force_download_check")
self.install_dialog_layout.addWidget(self.force_download_check, 3, 1, 1, 1)
self.install_dialog_label = QtWidgets.QLabel(InstallDialog)
self.install_dialog_label.setObjectName("install_dialog_label")
self.install_dialog_layout.addWidget(self.install_dialog_label, 0, 0, 1, 3)
self.download_only_check = QtWidgets.QCheckBox(InstallDialog)
self.download_only_check.setText("")
self.download_only_check.setObjectName("download_only_check")
self.install_dialog_layout.addWidget(self.download_only_check, 5, 1, 1, 1)
self.sdl_list_frame = QtWidgets.QFrame(InstallDialog) self.sdl_list_frame = QtWidgets.QFrame(InstallDialog)
self.sdl_list_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.sdl_list_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.sdl_list_frame.setFrameShadow(QtWidgets.QFrame.Raised) self.sdl_list_frame.setFrameShadow(QtWidgets.QFrame.Raised)
@ -111,32 +119,34 @@ class Ui_InstallDialog(object):
self.sdl_list_layout.setSpacing(0) self.sdl_list_layout.setSpacing(0)
self.sdl_list_layout.setObjectName("sdl_list_layout") self.sdl_list_layout.setObjectName("sdl_list_layout")
self.install_dialog_layout.addWidget(self.sdl_list_frame, 6, 1, 1, 2, QtCore.Qt.AlignTop) self.install_dialog_layout.addWidget(self.sdl_list_frame, 6, 1, 1, 2, QtCore.Qt.AlignTop)
self.sdl_list_label = QtWidgets.QLabel(InstallDialog) self.warn_label = QtWidgets.QLabel(InstallDialog)
self.sdl_list_label.setObjectName("sdl_list_label") self.warn_label.setObjectName("warn_label")
self.install_dialog_layout.addWidget(self.sdl_list_label, 6, 0, 1, 1, QtCore.Qt.AlignRight) self.install_dialog_layout.addWidget(self.warn_label, 9, 0, 1, 1, QtCore.Qt.AlignRight)
self.retranslateUi(InstallDialog) self.retranslateUi(InstallDialog)
QtCore.QMetaObject.connectSlotsByName(InstallDialog) QtCore.QMetaObject.connectSlotsByName(InstallDialog)
def retranslateUi(self, InstallDialog): def retranslateUi(self, InstallDialog):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
self.install_dir_label.setText(_translate("InstallDialog", "Install directory"))
self.download_only_label.setText(_translate("InstallDialog", "Download only"))
self.sdl_list_label.setText(_translate("InstallDialog", "Optional packs"))
self.install_size_info_label.setText(_translate("InstallDialog", "Click verify..."))
self.install_dialog_label.setText(_translate("InstallDialog", "error"))
self.install_size_label.setText(_translate("InstallDialog", "Total install size"))
self.download_only_info_label.setText(_translate("InstallDialog", "Do not try to install."))
self.max_workers_info_label.setText(_translate("InstallDialog", "Less is slower. (0: Default)"))
self.warn_message.setText(_translate("InstallDialog", "TextLabel"))
self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space"))
self.download_size_label.setText(_translate("InstallDialog", "Download size"))
self.force_download_label.setText(_translate("InstallDialog", "Force redownload")) self.force_download_label.setText(_translate("InstallDialog", "Force redownload"))
self.ignore_space_info_label.setText(_translate("InstallDialog", "Use with caution!"))
self.max_workers_label.setText(_translate("InstallDialog", "Max workers"))
self.download_size_info_label.setText(_translate("InstallDialog", "Click verify..."))
self.cancel_button.setText(_translate("InstallDialog", "Cancel")) self.cancel_button.setText(_translate("InstallDialog", "Cancel"))
self.verify_button.setText(_translate("InstallDialog", "Verify")) self.verify_button.setText(_translate("InstallDialog", "Verify"))
self.install_button.setText(_translate("InstallDialog", "Install")) self.install_button.setText(_translate("InstallDialog", "Install"))
self.ignore_space_info_label.setText(_translate("InstallDialog", "Use with caution!")) self.warn_label.setText(_translate("InstallDialog", "Warnings"))
self.install_dir_label.setText(_translate("InstallDialog", "Install directory"))
self.max_workers_info_label.setText(_translate("InstallDialog", "Less is slower. (0: Default)"))
self.download_only_info_label.setText(_translate("InstallDialog", "Do not try to install."))
self.install_size_label.setText(_translate("InstallDialog", "Total install size"))
self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space"))
self.download_only_label.setText(_translate("InstallDialog", "Download only"))
self.max_workers_label.setText(_translate("InstallDialog", "Max workers"))
self.install_size_info_label.setText(_translate("InstallDialog", "Click verify..."))
self.download_size_label.setText(_translate("InstallDialog", "Download size"))
self.download_size_info_label.setText(_translate("InstallDialog", "Click verify..."))
self.install_dialog_label.setText(_translate("InstallDialog", "error"))
self.sdl_list_label.setText(_translate("InstallDialog", "Optional packs"))
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -2,10 +2,145 @@
<ui version="4.0"> <ui version="4.0">
<class>InstallDialog</class> <class>InstallDialog</class>
<widget class="QDialog" name="InstallDialog"> <widget class="QDialog" name="InstallDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>338</width>
<height>267</height>
</rect>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string notr="true">Rare</string> <string notr="true">Rare</string>
</property> </property>
<layout class="QGridLayout" name="install_dialog_layout"> <layout class="QGridLayout" name="install_dialog_layout">
<item row="3" column="1">
<widget class="QCheckBox" name="force_download_check"/>
</item>
<item row="1" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="install_dir_label">
<property name="text">
<string>Install directory</string>
</property>
</widget>
</item>
<item row="5" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="download_only_label">
<property name="text">
<string>Download only</string>
</property>
</widget>
</item>
<item row="6" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="sdl_list_label">
<property name="text">
<string>Optional packs</string>
</property>
</widget>
</item>
<item row="8" column="1" colspan="2">
<widget class="QLabel" name="install_size_info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Click verify...</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="install_dialog_label">
<property name="text">
<string>error</string>
</property>
</widget>
</item>
<item row="3" column="2">
<spacer name="install_dialog_hspacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="8" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="install_size_label">
<property name="text">
<string>Total install size</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="download_only_info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Do not try to install.</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="max_workers_spin"/>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="ignore_space_check"/>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="download_only_check">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="max_workers_info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Less is slower. (0: Default)</string>
</property>
</widget>
</item>
<item row="9" column="1" colspan="2">
<widget class="QLabel" name="warn_message">
<property name="text">
<string>TextLabel</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="ignore_space_label">
<property name="text">
<string>Ignore free space</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<layout class="QHBoxLayout" name="install_dir_layout"/>
</item>
<item row="7" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="download_size_label">
<property name="text">
<string>Download size</string>
</property>
</widget>
</item>
<item row="3" column="0" alignment="Qt::AlignRight"> <item row="3" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="force_download_label"> <widget class="QLabel" name="force_download_label">
<property name="text"> <property name="text">
@ -13,7 +148,38 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0" colspan="3"> <item row="4" column="2">
<widget class="QLabel" name="ignore_space_info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Use with caution!</string>
</property>
</widget>
</item>
<item row="2" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="max_workers_label">
<property name="text">
<string>Max workers</string>
</property>
</widget>
</item>
<item row="7" column="1" colspan="2">
<widget class="QLabel" name="download_size_info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Click verify...</string>
</property>
</widget>
</item>
<item row="11" column="0" colspan="3">
<layout class="QHBoxLayout" name="button_layout"> <layout class="QHBoxLayout" name="button_layout">
<item> <item>
<spacer name="button_hspacer"> <spacer name="button_hspacer">
@ -51,147 +217,6 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="4" column="2">
<widget class="QLabel" name="ignore_space_info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Use with caution!</string>
</property>
</widget>
</item>
<item row="1" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="install_dir_label">
<property name="text">
<string>Install directory</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="ignore_space_check"/>
</item>
<item row="2" column="2">
<widget class="QLabel" name="max_workers_info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Less is slower. (0: Default)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="max_workers_spin"/>
</item>
<item row="5" column="2">
<widget class="QLabel" name="download_only_info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Do not try to install.</string>
</property>
</widget>
</item>
<item row="8" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="install_size_label">
<property name="text">
<string>Total install size</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<layout class="QHBoxLayout" name="install_dir_layout"/>
</item>
<item row="3" column="2">
<spacer name="install_dialog_hspacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="ignore_space_label">
<property name="text">
<string>Ignore free space</string>
</property>
</widget>
</item>
<item row="5" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="download_only_label">
<property name="text">
<string>Download only</string>
</property>
</widget>
</item>
<item row="2" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="max_workers_label">
<property name="text">
<string>Max workers</string>
</property>
</widget>
</item>
<item row="8" column="1" colspan="2">
<widget class="QLabel" name="install_size_info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Click verify...</string>
</property>
</widget>
</item>
<item row="7" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="download_size_label">
<property name="text">
<string>Download size</string>
</property>
</widget>
</item>
<item row="7" column="1" colspan="2">
<widget class="QLabel" name="download_size_info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Click verify...</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="force_download_check"/>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="install_dialog_label">
<property name="text">
<string>error</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="download_only_check">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item row="6" column="1" colspan="2" alignment="Qt::AlignTop"> <item row="6" column="1" colspan="2" alignment="Qt::AlignTop">
<widget class="QFrame" name="sdl_list_frame"> <widget class="QFrame" name="sdl_list_frame">
<property name="frameShape"> <property name="frameShape">
@ -207,10 +232,10 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="6" column="0" alignment="Qt::AlignRight"> <item row="9" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="sdl_list_label"> <widget class="QLabel" name="warn_label">
<property name="text"> <property name="text">
<string>Optional packs</string> <string>Warnings</string>
</property> </property>
</widget> </widget>
</item> </item>

View file

@ -8,7 +8,7 @@
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtWidgets
class Ui_LaunchDialog(object): class Ui_LaunchDialog(object):
@ -27,13 +27,6 @@ class Ui_LaunchDialog(object):
self.image_info = QtWidgets.QLabel(LaunchDialog) self.image_info = QtWidgets.QLabel(LaunchDialog)
self.image_info.setObjectName("image_info") self.image_info.setObjectName("image_info")
self.verticalLayout.addWidget(self.image_info) self.verticalLayout.addWidget(self.image_info)
self.steam_prog_bar = QtWidgets.QProgressBar(LaunchDialog)
self.steam_prog_bar.setProperty("value", 0)
self.steam_prog_bar.setObjectName("steam_prog_bar")
self.verticalLayout.addWidget(self.steam_prog_bar)
self.steam_info = QtWidgets.QLabel(LaunchDialog)
self.steam_info.setObjectName("steam_info")
self.verticalLayout.addWidget(self.steam_info)
self.retranslateUi(LaunchDialog) self.retranslateUi(LaunchDialog)
QtCore.QMetaObject.connectSlotsByName(LaunchDialog) QtCore.QMetaObject.connectSlotsByName(LaunchDialog)
@ -43,7 +36,6 @@ class Ui_LaunchDialog(object):
LaunchDialog.setWindowTitle(_translate("LaunchDialog", "Launching Rare")) LaunchDialog.setWindowTitle(_translate("LaunchDialog", "Launching Rare"))
self.title_label.setText(_translate("LaunchDialog", "<h2>Launching Rare</h2>")) self.title_label.setText(_translate("LaunchDialog", "<h2>Launching Rare</h2>"))
self.image_info.setText(_translate("LaunchDialog", "Downloading images")) self.image_info.setText(_translate("LaunchDialog", "Downloading images"))
self.steam_info.setText(_translate("LaunchDialog", "Getting Steam grades"))
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -35,20 +35,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QProgressBar" name="steam_prog_bar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="steam_info">
<property name="text">
<string>Getting Steam grades</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View file

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'browser_login.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_BrowserLogin(object):
def setupUi(self, BrowserLogin):
BrowserLogin.setObjectName("BrowserLogin")
BrowserLogin.resize(246, 130)
BrowserLogin.setWindowTitle("BrowserLogin")
self.browser_layout = QtWidgets.QGridLayout(BrowserLogin)
self.browser_layout.setObjectName("browser_layout")
self.open_button = QtWidgets.QPushButton(BrowserLogin)
self.open_button.setObjectName("open_button")
self.browser_layout.addWidget(self.open_button, 1, 0, 1, 1)
self.title_label = QtWidgets.QLabel(BrowserLogin)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.title_label.setFont(font)
self.title_label.setObjectName("title_label")
self.browser_layout.addWidget(self.title_label, 0, 0, 1, 2, QtCore.Qt.AlignTop)
self.sid_edit = QtWidgets.QLineEdit(BrowserLogin)
self.sid_edit.setObjectName("sid_edit")
self.browser_layout.addWidget(self.sid_edit, 1, 1, 1, 1)
self.info_label = QtWidgets.QLabel(BrowserLogin)
font = QtGui.QFont()
font.setItalic(True)
self.info_label.setFont(font)
self.info_label.setWordWrap(True)
self.info_label.setObjectName("info_label")
self.browser_layout.addWidget(self.info_label, 3, 0, 1, 2, QtCore.Qt.AlignBottom)
self.status_label = QtWidgets.QLabel(BrowserLogin)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.status_label.sizePolicy().hasHeightForWidth())
self.status_label.setSizePolicy(sizePolicy)
self.status_label.setText("")
self.status_label.setObjectName("status_label")
self.browser_layout.addWidget(self.status_label, 2, 1, 1, 1)
self.retranslateUi(BrowserLogin)
QtCore.QMetaObject.connectSlotsByName(BrowserLogin)
def retranslateUi(self, BrowserLogin):
_translate = QtCore.QCoreApplication.translate
self.open_button.setText(_translate("BrowserLogin", "Open Browser"))
self.title_label.setText(_translate("BrowserLogin", "Login through browser"))
self.sid_edit.setPlaceholderText(_translate("BrowserLogin", "Insert SID here"))
self.info_label.setText(_translate("BrowserLogin",
"Click the button to open the login page in a browser. After logging in, copy the SID code in the input above."))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
BrowserLogin = QtWidgets.QWidget()
ui = Ui_BrowserLogin()
ui.setupUi(BrowserLogin)
BrowserLogin.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BrowserLogin</class>
<widget class="QWidget" name="BrowserLogin">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>246</width>
<height>130</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">BrowserLogin</string>
</property>
<layout class="QGridLayout" name="browser_layout">
<item row="1" column="0">
<widget class="QPushButton" name="open_button">
<property name="text">
<string>Open Browser</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2" alignment="Qt::AlignTop">
<widget class="QLabel" name="title_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Login through browser</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="sid_edit">
<property name="placeholderText">
<string>Insert SID here</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2" alignment="Qt::AlignBottom">
<widget class="QLabel" name="info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Click the button to open the login page in a browser. After logging in, copy the SID
code in the input above.
</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="status_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'import_login.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ImportLogin(object):
def setupUi(self, ImportLogin):
ImportLogin.setObjectName("ImportLogin")
ImportLogin.resize(503, 173)
ImportLogin.setWindowTitle("ImportLogin")
self.import_layout = QtWidgets.QGridLayout(ImportLogin)
self.import_layout.setObjectName("import_layout")
self.info_label = QtWidgets.QLabel(ImportLogin)
font = QtGui.QFont()
font.setItalic(True)
self.info_label.setFont(font)
self.info_label.setWordWrap(True)
self.info_label.setObjectName("info_label")
self.import_layout.addWidget(self.info_label, 3, 0, 1, 3, QtCore.Qt.AlignBottom)
self.title_label = QtWidgets.QLabel(ImportLogin)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.title_label.setFont(font)
self.title_label.setObjectName("title_label")
self.import_layout.addWidget(self.title_label, 0, 0, 1, 3, QtCore.Qt.AlignTop)
self.prefix_combo = QtWidgets.QComboBox(ImportLogin)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.prefix_combo.sizePolicy().hasHeightForWidth())
self.prefix_combo.setSizePolicy(sizePolicy)
self.prefix_combo.setEditable(True)
self.prefix_combo.setObjectName("prefix_combo")
self.import_layout.addWidget(self.prefix_combo, 1, 1, 1, 1)
self.prefix_label = QtWidgets.QLabel(ImportLogin)
self.prefix_label.setObjectName("prefix_label")
self.import_layout.addWidget(self.prefix_label, 1, 0, 1, 1)
self.prefix_tool = QtWidgets.QToolButton(ImportLogin)
self.prefix_tool.setObjectName("prefix_tool")
self.import_layout.addWidget(self.prefix_tool, 1, 2, 1, 1)
self.status_label = QtWidgets.QLabel(ImportLogin)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.status_label.sizePolicy().hasHeightForWidth())
self.status_label.setSizePolicy(sizePolicy)
font = QtGui.QFont()
font.setItalic(True)
self.status_label.setFont(font)
self.status_label.setText("")
self.status_label.setObjectName("status_label")
self.import_layout.addWidget(self.status_label, 2, 1, 1, 2)
self.retranslateUi(ImportLogin)
QtCore.QMetaObject.connectSlotsByName(ImportLogin)
def retranslateUi(self, ImportLogin):
_translate = QtCore.QCoreApplication.translate
self.info_label.setText(_translate("ImportLogin", "You will get logged out from EGL in the process."))
self.title_label.setText(_translate("ImportLogin", "Import existing session from EGL"))
self.prefix_label.setText(_translate("ImportLogin", "Select path"))
self.prefix_tool.setText(_translate("ImportLogin", "Browse"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ImportLogin = QtWidgets.QWidget()
ui = Ui_ImportLogin()
ui.setupUi(ImportLogin)
ImportLogin.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImportLogin</class>
<widget class="QWidget" name="ImportLogin">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>503</width>
<height>173</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">ImportLogin</string>
</property>
<layout class="QGridLayout" name="import_layout">
<item row="3" column="0" colspan="3" alignment="Qt::AlignBottom">
<widget class="QLabel" name="info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>You will get logged out from EGL in the process.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3" alignment="Qt::AlignTop">
<widget class="QLabel" name="title_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Import existing session from EGL</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="prefix_combo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="prefix_label">
<property name="text">
<string>Select path</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="prefix_tool">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QLabel" name="status_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'login_dialog.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_LoginDialog(object):
def setupUi(self, LoginDialog):
LoginDialog.setObjectName("LoginDialog")
LoginDialog.resize(498, 311)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(LoginDialog.sizePolicy().hasHeightForWidth())
LoginDialog.setSizePolicy(sizePolicy)
self.dialog_layout = QtWidgets.QVBoxLayout(LoginDialog)
self.dialog_layout.setObjectName("dialog_layout")
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
self.dialog_layout.addItem(spacerItem)
self.welcome_label = QtWidgets.QLabel(LoginDialog)
self.welcome_label.setObjectName("welcome_label")
self.dialog_layout.addWidget(self.welcome_label)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
self.dialog_layout.addItem(spacerItem1)
self.login_stack = QtWidgets.QStackedWidget(LoginDialog)
self.login_stack.setEnabled(True)
self.login_stack.setMinimumSize(QtCore.QSize(480, 135))
self.login_stack.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.login_stack.setFrameShadow(QtWidgets.QFrame.Raised)
self.login_stack.setObjectName("login_stack")
self.login_page = QtWidgets.QWidget()
self.login_page.setEnabled(True)
self.login_page.setObjectName("login_page")
self.login_page_layout = QtWidgets.QGridLayout(self.login_page)
self.login_page_layout.setObjectName("login_page_layout")
self.login_browser_label = QtWidgets.QLabel(self.login_page)
font = QtGui.QFont()
font.setItalic(True)
self.login_browser_label.setFont(font)
self.login_browser_label.setObjectName("login_browser_label")
self.login_page_layout.addWidget(self.login_browser_label, 1, 1, 1, 1)
self.login_label = QtWidgets.QLabel(self.login_page)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.login_label.setFont(font)
self.login_label.setObjectName("login_label")
self.login_page_layout.addWidget(self.login_label, 0, 0, 1, 3, QtCore.Qt.AlignTop)
self.login_import_label = QtWidgets.QLabel(self.login_page)
font = QtGui.QFont()
font.setItalic(True)
self.login_import_label.setFont(font)
self.login_import_label.setObjectName("login_import_label")
self.login_page_layout.addWidget(self.login_import_label, 2, 1, 1, 1)
self.login_import_radio = QtWidgets.QRadioButton(self.login_page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.login_import_radio.sizePolicy().hasHeightForWidth())
self.login_import_radio.setSizePolicy(sizePolicy)
self.login_import_radio.setObjectName("login_import_radio")
self.login_page_layout.addWidget(self.login_import_radio, 2, 0, 1, 1)
self.login_browser_radio = QtWidgets.QRadioButton(self.login_page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.login_browser_radio.sizePolicy().hasHeightForWidth())
self.login_browser_radio.setSizePolicy(sizePolicy)
self.login_browser_radio.setObjectName("login_browser_radio")
self.login_page_layout.addWidget(self.login_browser_radio, 1, 0, 1, 1)
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.login_page_layout.addItem(spacerItem2, 1, 2, 2, 1)
self.login_stack.addWidget(self.login_page)
self.success_page = QtWidgets.QWidget()
self.success_page.setObjectName("success_page")
self.success_page_layout = QtWidgets.QHBoxLayout(self.success_page)
self.success_page_layout.setObjectName("success_page_layout")
self.success_label = QtWidgets.QLabel(self.success_page)
self.success_label.setObjectName("success_label")
self.success_page_layout.addWidget(self.success_label)
self.login_stack.addWidget(self.success_page)
self.dialog_layout.addWidget(self.login_stack)
self.button_layout = QtWidgets.QHBoxLayout()
self.button_layout.setObjectName("button_layout")
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.button_layout.addItem(spacerItem3)
self.exit_button = QtWidgets.QPushButton(LoginDialog)
self.exit_button.setObjectName("exit_button")
self.button_layout.addWidget(self.exit_button)
self.back_button = QtWidgets.QPushButton(LoginDialog)
self.back_button.setObjectName("back_button")
self.button_layout.addWidget(self.back_button)
self.next_button = QtWidgets.QPushButton(LoginDialog)
self.next_button.setObjectName("next_button")
self.button_layout.addWidget(self.next_button)
self.dialog_layout.addLayout(self.button_layout)
self.retranslateUi(LoginDialog)
self.login_stack.setCurrentIndex(1)
QtCore.QMetaObject.connectSlotsByName(LoginDialog)
def retranslateUi(self, LoginDialog):
_translate = QtCore.QCoreApplication.translate
LoginDialog.setWindowTitle(_translate("LoginDialog", "Welcome to Rare"))
self.welcome_label.setText(_translate("LoginDialog", "<h1>Welcome to Rare</h1>"))
self.login_browser_label.setText(_translate("LoginDialog", "Login using a browser."))
self.login_label.setText(_translate("LoginDialog", "Select login method"))
self.login_import_label.setText(_translate("LoginDialog", "Import from Epic Games Launcher"))
self.login_import_radio.setText(_translate("LoginDialog", "Import"))
self.login_browser_radio.setText(_translate("LoginDialog", "Browser"))
self.success_label.setText(_translate("LoginDialog",
"<html><head/><body><h2 style=\" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:x-large; font-weight:600;\">Login Successful!</span></h2></body></html>"))
self.exit_button.setText(_translate("LoginDialog", "Exit"))
self.back_button.setText(_translate("LoginDialog", "Back"))
self.next_button.setText(_translate("LoginDialog", "Next"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
LoginDialog = QtWidgets.QDialog()
ui = Ui_LoginDialog()
ui.setupUi(LoginDialog)
LoginDialog.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,224 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoginDialog</class>
<widget class="QDialog" name="LoginDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>498</width>
<height>311</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Welcome to Rare</string>
</property>
<layout class="QVBoxLayout" name="dialog_layout">
<item>
<spacer name="login_vspacer_top">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="welcome_label">
<property name="text">
<string>&lt;h1&gt;Welcome to Rare&lt;/h1&gt;</string>
</property>
</widget>
</item>
<item>
<spacer name="login_vspacer_bottom">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QStackedWidget" name="login_stack">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>480</width>
<height>135</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="login_page">
<property name="enabled">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="login_page_layout">
<item row="1" column="1">
<widget class="QLabel" name="login_browser_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Login using a browser.</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3" alignment="Qt::AlignTop">
<widget class="QLabel" name="login_label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Select login method</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="login_import_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Import from Epic Games Launcher</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="login_import_radio">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Import</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="login_browser_radio">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Browser</string>
</property>
</widget>
</item>
<item row="1" column="2" rowspan="2">
<spacer name="login_hspacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="success_page">
<layout class="QHBoxLayout" name="success_page_layout">
<item>
<widget class="QLabel" name="success_label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;h2 style=&quot;
margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px;
-qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot;
font-size:x-large; font-weight:600;&quot;&gt;Login Successful!&lt;/span&gt;&lt;/h2&gt;&lt;/body&gt;&lt;/html&gt;
</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="button_layout">
<item>
<spacer name="button_hspacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="exit_button">
<property name="text">
<string>Exit</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="back_button">
<property name="text">
<string>Back</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="next_button">
<property name="text">
<string>Next</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -170,7 +170,7 @@ class Ui_GameInfo(object):
self.repair_button.setObjectName("repair_button") self.repair_button.setObjectName("repair_button")
self.installed_layout.addWidget(self.repair_button) self.installed_layout.addWidget(self.repair_button)
self.uninstall_button = QtWidgets.QPushButton(self.installed_page) self.uninstall_button = QtWidgets.QPushButton(self.installed_page)
self.uninstall_button.setStyleSheet("background-color: #900") self.uninstall_button.setStyleSheet("")
self.uninstall_button.setObjectName("uninstall_button") self.uninstall_button.setObjectName("uninstall_button")
self.installed_layout.addWidget(self.uninstall_button) self.installed_layout.addWidget(self.uninstall_button)
self.game_actions_stack.addWidget(self.installed_page) self.game_actions_stack.addWidget(self.installed_page)
@ -179,7 +179,7 @@ class Ui_GameInfo(object):
self.uninstalled_layout = QtWidgets.QVBoxLayout(self.uninstalled_page) self.uninstalled_layout = QtWidgets.QVBoxLayout(self.uninstalled_page)
self.uninstalled_layout.setObjectName("uninstalled_layout") self.uninstalled_layout.setObjectName("uninstalled_layout")
self.install_button = QtWidgets.QPushButton(self.uninstalled_page) self.install_button = QtWidgets.QPushButton(self.uninstalled_page)
self.install_button.setStyleSheet("background-color: #090") self.install_button.setStyleSheet("")
self.install_button.setObjectName("install_button") self.install_button.setObjectName("install_button")
self.uninstalled_layout.addWidget(self.install_button) self.uninstalled_layout.addWidget(self.install_button)
self.game_actions_stack.addWidget(self.uninstalled_page) self.game_actions_stack.addWidget(self.uninstalled_page)

View file

@ -250,17 +250,17 @@
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="installed_page" native="true"> <widget class="QWidget" name="installed_page">
<layout class="QVBoxLayout" name="installed_layout"> <layout class="QVBoxLayout" name="installed_layout">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
<property name="topMargin"> <property name="topMargin">
<number>0</number> <number>0</number>
</property> </property>
<property name="rightMargin"> <property name="rightMargin">
<number>0</number> <number>0</number>
</property> </property>
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -333,7 +333,7 @@
<item> <item>
<widget class="QPushButton" name="uninstall_button"> <widget class="QPushButton" name="uninstall_button">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">background-color: #900</string> <string notr="true"/>
</property> </property>
<property name="text"> <property name="text">
<string>Uninstall Game</string> <string>Uninstall Game</string>
@ -347,7 +347,7 @@
<item> <item>
<widget class="QPushButton" name="install_button"> <widget class="QPushButton" name="install_button">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">background-color: #090</string> <string notr="true"/>
</property> </property>
<property name="text"> <property name="text">
<string>Install Game</string> <string>Install Game</string>

View file

@ -14,12 +14,93 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_RareSettings(object): class Ui_RareSettings(object):
def setupUi(self, RareSettings): def setupUi(self, RareSettings):
RareSettings.setObjectName("RareSettings") RareSettings.setObjectName("RareSettings")
RareSettings.resize(544, 532) RareSettings.resize(694, 532)
self.rare_layout = QtWidgets.QGridLayout(RareSettings) self.rare_layout = QtWidgets.QGridLayout(RareSettings)
self.rare_layout.setObjectName("rare_layout") self.rare_layout.setObjectName("rare_layout")
self.rpc_layout = QtWidgets.QVBoxLayout() self.settings_group = QtWidgets.QGroupBox(RareSettings)
self.rpc_layout.setObjectName("rpc_layout") self.settings_group.setObjectName("settings_group")
self.rare_layout.addLayout(self.rpc_layout, 1, 1, 1, 1) self.behavior_layout = QtWidgets.QGridLayout(self.settings_group)
self.behavior_layout.setObjectName("behavior_layout")
self.confirm_start = QtWidgets.QCheckBox(self.settings_group)
self.confirm_start.setObjectName("confirm_start")
self.behavior_layout.addWidget(self.confirm_start, 2, 0, 1, 1)
self.notification = QtWidgets.QCheckBox(self.settings_group)
self.notification.setObjectName("notification")
self.behavior_layout.addWidget(self.notification, 4, 0, 1, 1)
self.auto_update = QtWidgets.QCheckBox(self.settings_group)
self.auto_update.setObjectName("auto_update")
self.behavior_layout.addWidget(self.auto_update, 1, 0, 1, 1)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.behavior_layout.addItem(spacerItem, 2, 1, 2, 1)
self.auto_update = QtWidgets.QCheckBox(self.settings_group)
self.auto_update.setObjectName("auto_update")
self.behavior_layout.addWidget(self.auto_update, 1, 0, 1, 1)
self.save_size = QtWidgets.QCheckBox(self.settings_group)
self.save_size.setObjectName("save_size")
self.behavior_layout.addWidget(self.save_size, 5, 0, 1, 1)
self.auto_sync_cloud = QtWidgets.QCheckBox(self.settings_group)
self.auto_sync_cloud.setObjectName("auto_sync_cloud")
self.behavior_layout.addWidget(self.auto_sync_cloud, 3, 0, 1, 1)
self.notification = QtWidgets.QCheckBox(self.settings_group)
self.notification.setObjectName("notification")
self.behavior_layout.addWidget(self.notification, 4, 0, 1, 1)
self.sys_tray = QtWidgets.QCheckBox(self.settings_group)
self.sys_tray.setObjectName("sys_tray")
self.behavior_layout.addWidget(self.sys_tray, 0, 0, 1, 1)
self.log_games = QtWidgets.QCheckBox(self.settings_group)
self.log_games.setObjectName("log_games")
self.behavior_layout.addWidget(self.log_games, 6, 0, 1, 1)
self.confirm_start = QtWidgets.QCheckBox(self.settings_group)
self.confirm_start.setObjectName("confirm_start")
self.behavior_layout.addWidget(self.confirm_start, 2, 0, 1, 1)
self.sys_tray = QtWidgets.QCheckBox(self.settings_group)
self.sys_tray.setObjectName("sys_tray")
self.behavior_layout.addWidget(self.sys_tray, 0, 0, 1, 1)
self.image_cache = QtWidgets.QCheckBox(self.settings_group)
self.image_cache.setObjectName("image_cache")
self.behavior_layout.addWidget(self.image_cache, 6, 0, 1, 1)
self.rare_layout.addWidget(self.settings_group, 2, 0, 1, 1, QtCore.Qt.AlignTop)
self.interface_group = QtWidgets.QGroupBox(RareSettings)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.interface_group.sizePolicy().hasHeightForWidth())
self.interface_group.setSizePolicy(sizePolicy)
self.interface_group.setObjectName("interface_group")
self.interface_layout = QtWidgets.QGridLayout(self.interface_group)
self.interface_layout.setObjectName("interface_layout")
self.style_label = QtWidgets.QLabel(self.interface_group)
self.style_label.setObjectName("style_label")
self.interface_layout.addWidget(self.style_label, 2, 0, 1, 1, QtCore.Qt.AlignRight)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.interface_layout.addItem(spacerItem1, 1, 2, 1, 1)
self.lang_select = QtWidgets.QComboBox(self.interface_group)
self.lang_select.setObjectName("lang_select")
self.interface_layout.addWidget(self.lang_select, 0, 1, 1, 1)
self.color_select = QtWidgets.QComboBox(self.interface_group)
self.color_select.setObjectName("color_select")
self.color_select.addItem("")
self.interface_layout.addWidget(self.color_select, 1, 1, 1, 1)
self.style_select = QtWidgets.QComboBox(self.interface_group)
self.style_select.setObjectName("style_select")
self.style_select.addItem("")
self.interface_layout.addWidget(self.style_select, 2, 1, 1, 1)
self.lang_label = QtWidgets.QLabel(self.interface_group)
self.lang_label.setObjectName("lang_label")
self.interface_layout.addWidget(self.lang_label, 0, 0, 1, 1, QtCore.Qt.AlignRight)
self.color_label = QtWidgets.QLabel(self.interface_group)
self.color_label.setObjectName("color_label")
self.interface_layout.addWidget(self.color_label, 1, 0, 1, 1, QtCore.Qt.AlignRight)
self.interface_info = QtWidgets.QLabel(self.interface_group)
font = QtGui.QFont()
font.setItalic(True)
self.interface_info.setFont(font)
self.interface_info.setWordWrap(True)
self.interface_info.setObjectName("interface_info")
self.interface_layout.addWidget(self.interface_info, 3, 0, 1, 3)
self.rare_layout.addWidget(self.interface_group, 1, 0, 1, 1, QtCore.Qt.AlignTop)
self.groupBox = QtWidgets.QGroupBox(RareSettings) self.groupBox = QtWidgets.QGroupBox(RareSettings)
self.groupBox.setObjectName("groupBox") self.groupBox.setObjectName("groupBox")
self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox) self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox)
@ -31,34 +112,11 @@ class Ui_RareSettings(object):
self.startmenu_link.setObjectName("startmenu_link") self.startmenu_link.setObjectName("startmenu_link")
self.verticalLayout.addWidget(self.startmenu_link) self.verticalLayout.addWidget(self.startmenu_link)
self.rare_layout.addWidget(self.groupBox, 2, 1, 1, 1, QtCore.Qt.AlignTop) self.rare_layout.addWidget(self.groupBox, 2, 1, 1, 1, QtCore.Qt.AlignTop)
self.settings_group = QtWidgets.QGroupBox(RareSettings) self.rpc_layout = QtWidgets.QVBoxLayout()
self.settings_group.setObjectName("settings_group") self.rpc_layout.setObjectName("rpc_layout")
self.behavior_layout = QtWidgets.QGridLayout(self.settings_group) self.rare_layout.addLayout(self.rpc_layout, 1, 1, 1, 1)
self.behavior_layout.setObjectName("behavior_layout") spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.notification = QtWidgets.QCheckBox(self.settings_group) self.rare_layout.addItem(spacerItem2, 3, 0, 1, 2)
self.notification.setObjectName("notification")
self.behavior_layout.addWidget(self.notification, 4, 0, 1, 1)
self.auto_update = QtWidgets.QCheckBox(self.settings_group)
self.auto_update.setObjectName("auto_update")
self.behavior_layout.addWidget(self.auto_update, 1, 0, 1, 1)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.behavior_layout.addItem(spacerItem, 2, 1, 2, 1)
self.save_size = QtWidgets.QCheckBox(self.settings_group)
self.save_size.setObjectName("save_size")
self.behavior_layout.addWidget(self.save_size, 5, 0, 1, 1)
self.auto_sync_cloud = QtWidgets.QCheckBox(self.settings_group)
self.auto_sync_cloud.setObjectName("auto_sync_cloud")
self.behavior_layout.addWidget(self.auto_sync_cloud, 3, 0, 1, 1)
self.confirm_start = QtWidgets.QCheckBox(self.settings_group)
self.confirm_start.setObjectName("confirm_start")
self.behavior_layout.addWidget(self.confirm_start, 2, 0, 1, 1)
self.sys_tray = QtWidgets.QCheckBox(self.settings_group)
self.sys_tray.setObjectName("sys_tray")
self.behavior_layout.addWidget(self.sys_tray, 0, 0, 1, 1)
self.image_cache = QtWidgets.QCheckBox(self.settings_group)
self.image_cache.setObjectName("image_cache")
self.behavior_layout.addWidget(self.image_cache, 6, 0, 1, 1)
self.rare_layout.addWidget(self.settings_group, 2, 0, 1, 1, QtCore.Qt.AlignTop)
self.log_dir_group = QtWidgets.QGroupBox(RareSettings) self.log_dir_group = QtWidgets.QGroupBox(RareSettings)
self.log_dir_group.setObjectName("log_dir_group") self.log_dir_group.setObjectName("log_dir_group")
self.log_dir_layout = QtWidgets.QVBoxLayout(self.log_dir_group) self.log_dir_layout = QtWidgets.QVBoxLayout(self.log_dir_group)
@ -74,50 +132,12 @@ class Ui_RareSettings(object):
self.log_dir_size_label.setWordWrap(True) self.log_dir_size_label.setWordWrap(True)
self.log_dir_size_label.setObjectName("log_dir_size_label") self.log_dir_size_label.setObjectName("log_dir_size_label")
self.log_dir_layout.addWidget(self.log_dir_size_label) self.log_dir_layout.addWidget(self.log_dir_size_label)
self.rare_layout.addWidget(self.log_dir_group, 0, 1, 1, 1, QtCore.Qt.AlignTop) self.rare_layout.addWidget(self.log_dir_group, 0, 1, 1, 1)
self.interface_group = QtWidgets.QGroupBox(RareSettings)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.interface_group.sizePolicy().hasHeightForWidth())
self.interface_group.setSizePolicy(sizePolicy)
self.interface_group.setObjectName("interface_group")
self.interface_layout = QtWidgets.QGridLayout(self.interface_group)
self.interface_layout.setObjectName("interface_layout")
self.color_select = QtWidgets.QComboBox(self.interface_group)
self.color_select.setObjectName("color_select")
self.color_select.addItem("")
self.interface_layout.addWidget(self.color_select, 1, 1, 1, 1)
self.style_label = QtWidgets.QLabel(self.interface_group)
self.style_label.setObjectName("style_label")
self.interface_layout.addWidget(self.style_label, 2, 0, 1, 1, QtCore.Qt.AlignRight)
self.style_select = QtWidgets.QComboBox(self.interface_group)
self.style_select.setObjectName("style_select")
self.style_select.addItem("")
self.interface_layout.addWidget(self.style_select, 2, 1, 1, 1)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.interface_layout.addItem(spacerItem1, 1, 2, 1, 1)
self.interface_info = QtWidgets.QLabel(self.interface_group)
font = QtGui.QFont()
font.setItalic(True)
self.interface_info.setFont(font)
self.interface_info.setWordWrap(True)
self.interface_info.setObjectName("interface_info")
self.interface_layout.addWidget(self.interface_info, 3, 0, 1, 3)
self.lang_label = QtWidgets.QLabel(self.interface_group)
self.lang_label.setObjectName("lang_label")
self.interface_layout.addWidget(self.lang_label, 0, 0, 1, 1, QtCore.Qt.AlignRight)
self.lang_select = QtWidgets.QComboBox(self.interface_group)
self.lang_select.setObjectName("lang_select")
self.interface_layout.addWidget(self.lang_select, 0, 1, 1, 1)
self.color_label = QtWidgets.QLabel(self.interface_group)
self.color_label.setObjectName("color_label")
self.interface_layout.addWidget(self.color_label, 1, 0, 1, 1, QtCore.Qt.AlignRight)
self.rare_layout.addWidget(self.interface_group, 1, 0, 1, 1, QtCore.Qt.AlignTop)
self.img_dir_group = QtWidgets.QGroupBox(RareSettings) self.img_dir_group = QtWidgets.QGroupBox(RareSettings)
self.img_dir_group.setObjectName("img_dir_group") self.img_dir_group.setObjectName("img_dir_group")
self.img_dir_layout = QtWidgets.QVBoxLayout(self.img_dir_group) self.img_dir_layout = QtWidgets.QVBoxLayout(self.img_dir_group)
self.img_dir_layout.setObjectName("img_dir_layout") self.img_dir_layout.setObjectName("img_dir_layout")
self.rare_layout.addWidget(self.img_dir_group, 0, 0, 1, 1)
self.label = QtWidgets.QLabel(self.img_dir_group) self.label = QtWidgets.QLabel(self.img_dir_group)
self.label.setWordWrap(True) self.label.setWordWrap(True)
self.label.setObjectName("label") self.label.setObjectName("label")
@ -132,10 +152,14 @@ class Ui_RareSettings(object):
def retranslateUi(self, RareSettings): def retranslateUi(self, RareSettings):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
RareSettings.setWindowTitle(_translate("RareSettings", "RareSettings")) RareSettings.setWindowTitle(_translate("RareSettings", "RareSettings"))
self.groupBox.setTitle(_translate("RareSettings", "Shortcuts"))
self.desktop_link.setText(_translate("RareSettings", "Create Desktop link"))
self.startmenu_link.setText(_translate("RareSettings", "Create start menu link"))
self.settings_group.setTitle(_translate("RareSettings", "Behavior")) self.settings_group.setTitle(_translate("RareSettings", "Behavior"))
self.confirm_start.setText(_translate("RareSettings", "Confirm game launch"))
self.auto_update.setText(_translate("RareSettings", "Update games on application startup"))
self.save_size.setText(_translate("RareSettings", "Restore window size on application startup"))
self.auto_sync_cloud.setText(_translate("RareSettings", "Automatically sync with cloud"))
self.notification.setText(_translate("RareSettings", "Show notification on download completion"))
self.sys_tray.setText(_translate("RareSettings", "Exit to System tray"))
self.log_games.setText(_translate("RareSettings", "Show console for game debug"))
self.notification.setText(_translate("RareSettings", "Show notification on download completion")) self.notification.setText(_translate("RareSettings", "Show notification on download completion"))
self.auto_update.setText(_translate("RareSettings", "Update games on application startup")) self.auto_update.setText(_translate("RareSettings", "Update games on application startup"))
self.save_size.setText(_translate("RareSettings", "Restore window size on application startup")) self.save_size.setText(_translate("RareSettings", "Restore window size on application startup"))
@ -147,12 +171,18 @@ class Ui_RareSettings(object):
self.log_dir_open_button.setText(_translate("RareSettings", "Open Log directory")) self.log_dir_open_button.setText(_translate("RareSettings", "Open Log directory"))
self.log_dir_clean_button.setText(_translate("RareSettings", "Clean Log directory")) self.log_dir_clean_button.setText(_translate("RareSettings", "Clean Log directory"))
self.interface_group.setTitle(_translate("RareSettings", "Interface")) self.interface_group.setTitle(_translate("RareSettings", "Interface"))
self.color_select.setItemText(0, _translate("RareSettings", "None"))
self.style_label.setText(_translate("RareSettings", "Style Sheet")) self.style_label.setText(_translate("RareSettings", "Style Sheet"))
self.color_select.setItemText(0, _translate("RareSettings", "None"))
self.style_select.setItemText(0, _translate("RareSettings", "None")) self.style_select.setItemText(0, _translate("RareSettings", "None"))
self.interface_info.setText(_translate("RareSettings", "Restart Rare to apply."))
self.lang_label.setText(_translate("RareSettings", "Language")) self.lang_label.setText(_translate("RareSettings", "Language"))
self.color_label.setText(_translate("RareSettings", "Color Scheme")) self.color_label.setText(_translate("RareSettings", "Color Scheme"))
self.interface_info.setText(_translate("RareSettings", "Restart Rare to apply."))
self.groupBox.setTitle(_translate("RareSettings", "Shortcuts"))
self.desktop_link.setText(_translate("RareSettings", "Create Desktop link"))
self.startmenu_link.setText(_translate("RareSettings", "Create start menu link"))
self.log_dir_group.setTitle(_translate("RareSettings", "Logs"))
self.log_dir_open_button.setText(_translate("RareSettings", "Open Log directory"))
self.log_dir_clean_button.setText(_translate("RareSettings", "Clean Log directory"))
self.img_dir_group.setTitle(_translate("RareSettings", "Image Cache Directory")) self.img_dir_group.setTitle(_translate("RareSettings", "Image Cache Directory"))
self.label.setText(_translate("RareSettings", self.label.setText(_translate("RareSettings",
"To change image directory, edit XDG_DATA_HOME variable. To change cache directory edit XDG_CACHE_HOME variable")) "To change image directory, edit XDG_DATA_HOME variable. To change cache directory edit XDG_CACHE_HOME variable"))

View file

@ -1,270 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RareSettings</class>
<widget class="QWidget" name="RareSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>544</width>
<height>532</height>
</rect>
</property>
<property name="windowTitle">
<string>RareSettings</string>
</property>
<layout class="QGridLayout" name="rare_layout">
<item row="1" column="1">
<layout class="QVBoxLayout" name="rpc_layout"/>
</item>
<item row="2" column="1" alignment="Qt::AlignTop">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Shortcuts</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="desktop_link">
<property name="text">
<string>Create Desktop link</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="startmenu_link">
<property name="text">
<string>Create start menu link</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0" alignment="Qt::AlignTop">
<widget class="QGroupBox" name="settings_group">
<property name="title">
<string>Behavior</string>
</property>
<layout class="QGridLayout" name="behavior_layout">
<item row="4" column="0">
<widget class="QCheckBox" name="notification">
<property name="text">
<string>Show notification on download completion</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="auto_update">
<property name="text">
<string>Update games on application startup</string>
</property>
</widget>
</item>
<item row="2" column="1" rowspan="2">
<spacer name="settings_hspacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="save_size">
<property name="text">
<string>Restore window size on application startup</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="auto_sync_cloud">
<property name="text">
<string>Automatically sync with cloud</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="confirm_start">
<property name="text">
<string>Confirm game launch</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="sys_tray">
<property name="text">
<string>Exit to System tray</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="image_cache">
<property name="text">
<string>Cache images in store</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1" alignment="Qt::AlignTop">
<widget class="QGroupBox" name="log_dir_group">
<property name="title">
<string>Logs</string>
</property>
<layout class="QVBoxLayout" name="log_dir_layout">
<item>
<widget class="QPushButton" name="log_dir_open_button">
<property name="text">
<string>Open Log directory</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="log_dir_clean_button">
<property name="text">
<string>Clean Log directory</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="log_dir_size_label">
<property name="text">
<string notr="true"/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" alignment="Qt::AlignTop">
<widget class="QGroupBox" name="interface_group">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Interface</string>
</property>
<layout class="QGridLayout" name="interface_layout">
<item row="1" column="1">
<widget class="QComboBox" name="color_select">
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="style_label">
<property name="text">
<string>Style Sheet</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="style_select">
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item row="1" column="2">
<spacer name="interface_hspacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="3">
<widget class="QLabel" name="interface_info">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Restart Rare to apply.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="lang_label">
<property name="text">
<string>Language</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="lang_select"/>
</item>
<item row="1" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="color_label">
<property name="text">
<string>Color Scheme</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0" alignment="Qt::AlignTop">
<widget class="QGroupBox" name="img_dir_group">
<property name="title">
<string>Image Cache Directory</string>
</property>
<layout class="QVBoxLayout" name="img_dir_layout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>To change image directory, edit XDG_DATA_HOME variable. To change cache directory edit
XDG_CACHE_HOME variable
</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0" colspan="2">
<spacer name="rare_vspacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -9,7 +9,8 @@ from PyQt5.QtWidgets import QLayout, QStyle, QSizePolicy, QLabel, QFileDialog, Q
QStyleOptionTab, QStylePainter, QTabBar, QLineEdit, QToolButton QStyleOptionTab, QStylePainter, QTabBar, QLineEdit, QToolButton
from qtawesome import icon from qtawesome import icon
from rare import style_path, cache_dir from rare import resources_path
from rare.ui.utils.pathedit import Ui_PathEdit from rare.ui.utils.pathedit import Ui_PathEdit
from rare.utils.qt_requests import QtRequestManager from rare.utils.qt_requests import QtRequestManager
@ -162,7 +163,7 @@ class PathEdit(QWidget, Ui_PathEdit):
dlg_path = self.text_edit.text() dlg_path = self.text_edit.text()
if not dlg_path: if not dlg_path:
dlg_path = os.path.expanduser("~/") dlg_path = os.path.expanduser("~/")
dlg = QFileDialog(self, self.tr("Choose Path"), dlg_path) dlg = QFileDialog(self, self.tr("Choose path"), dlg_path)
dlg.setFileMode(self.file_type) dlg.setFileMode(self.file_type)
if self.type_filter: if self.type_filter:
dlg.setFilter([self.type_filter]) dlg.setFilter([self.type_filter])
@ -212,7 +213,7 @@ class WaitingSpinner(QLabel):
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
""") """)
self.movie = QMovie(os.path.join(style_path, "loader.gif")) self.movie = QMovie(os.path.join(resources_path, "images", "loader.gif"))
self.setMovie(self.movie) self.setMovie(self.movie)
self.movie.start() self.movie.start()

View file

@ -1,9 +1,11 @@
import os import os
import platform
import shutil import shutil
from logging import getLogger from logging import getLogger
from sys import stdout from sys import stdout
from PyQt5.QtCore import QProcess, QProcessEnvironment, QThread, pyqtSignal from PyQt5.QtCore import QProcess, QProcessEnvironment, QThread, pyqtSignal
from PyQt5.QtWidgets import QMessageBox
from custom_legendary.core import LegendaryCore from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import VerifyResult from custom_legendary.models.game import VerifyResult
@ -55,13 +57,13 @@ def uninstall(app_name: str, core: LegendaryCore, options=None):
igame = core.get_installed_game(app_name) igame = core.get_installed_game(app_name)
# remove shortcuts link # remove shortcuts link
if os.name == "posix": if platform.system() == "Linux":
if os.path.exists(os.path.expanduser(f"~/Desktop/{igame.title}.desktop")): if os.path.exists(os.path.expanduser(f"~/Desktop/{igame.title}.desktop")):
os.remove(os.path.expanduser(f"~/Desktop/{igame.title}.desktop")) os.remove(os.path.expanduser(f"~/Desktop/{igame.title}.desktop"))
if os.path.exists(os.path.expanduser(f"~/.local/share/applications/{igame.title}.desktop")): if os.path.exists(os.path.expanduser(f"~/.local/share/applications/{igame.title}.desktop")):
os.remove(os.path.expanduser(f"~/.local/share/applications/{igame.title}.desktop")) os.remove(os.path.expanduser(f"~/.local/share/applications/{igame.title}.desktop"))
elif os.name == "nt": elif platform.system() == "Windows":
if os.path.exists(os.path.expanduser(f"~/Desktop/{igame.title.split(':')[0]}.lnk")): if os.path.exists(os.path.expanduser(f"~/Desktop/{igame.title.split(':')[0]}.lnk")):
os.remove(os.path.expanduser(f"~/Desktop/{igame.title.split(':')[0]}.lnk")) os.remove(os.path.expanduser(f"~/Desktop/{igame.title.split(':')[0]}.lnk"))
elif os.path.exists( elif os.path.exists(
@ -116,23 +118,30 @@ class VerifyThread(QThread):
logger.info(f'Verifying "{igame.title}" version "{manifest.meta.build_version}"') logger.info(f'Verifying "{igame.title}" version "{manifest.meta.build_version}"')
repair_file = [] repair_file = []
for result, path, result_hash in validate_files(igame.install_path, file_list): try:
self.status.emit((self.num, self.total, self.app_name)) for result, path, result_hash in validate_files(igame.install_path, file_list):
self.num += 1 self.status.emit((self.num, self.total, self.app_name))
self.num += 1
if result == VerifyResult.HASH_MATCH: if result == VerifyResult.HASH_MATCH:
repair_file.append(f'{result_hash}:{path}') repair_file.append(f'{result_hash}:{path}')
continue continue
elif result == VerifyResult.HASH_MISMATCH: elif result == VerifyResult.HASH_MISMATCH:
logger.error(f'File does not match hash: "{path}"') logger.error(f'File does not match hash: "{path}"')
repair_file.append(f'{result_hash}:{path}') repair_file.append(f'{result_hash}:{path}')
failed.append(path) failed.append(path)
elif result == VerifyResult.FILE_MISSING: elif result == VerifyResult.FILE_MISSING:
logger.error(f'File is missing: "{path}"') logger.error(f'File is missing: "{path}"')
missing.append(path) missing.append(path)
else: else:
logger.error(f'Other failure (see log), treating file as missing: "{path}"') logger.error(f'Other failure (see log), treating file as missing: "{path}"')
missing.append(path) missing.append(path)
except OSError as e:
QMessageBox.warning(None, "Error", self.tr("Path does not exist"))
logger.error(str(e))
except ValueError as e:
QMessageBox.warning(None, "Error", self.tr("No files to validate"))
logger.error(str(e))
stdout.write(f'Verification progress: {self.num}/{self.total} ({self.num * 100 / self.total:.01f}%)\t\n') stdout.write(f'Verification progress: {self.num}/{self.total} ({self.num * 100 / self.total:.01f}%)\t\n')
@ -148,7 +157,7 @@ class VerifyThread(QThread):
self.summary.emit((0, 0, self.app_name)) self.summary.emit((0, 0, self.app_name))
else: else:
logger.error(f'Verification failed, {len(failed)} file(s) corrupted, {len(missing)} file(s) are missing.') logger.error(f'Verification finished, {len(failed)} file(s) corrupted, {len(missing)} file(s) are missing.')
self.summary.emit((len(failed), len(missing), self.app_name)) self.summary.emit((len(failed), len(missing), self.app_name))

View file

@ -1,36 +1,40 @@
import os import os
from dataclasses import field, dataclass
from multiprocessing import Queue
from custom_legendary.downloader.manager import DLManager
from custom_legendary.models.downloading import AnalysisResult, ConditionCheckResult
from custom_legendary.models.game import Game, InstalledGame
@dataclass
class InstallOptionsModel: class InstallOptionsModel:
def __init__(self, app_name: str, base_path: str = os.path.expanduser("~/legendary"), app_name: str
max_workers: int = os.cpu_count() * 2, repair: bool = False, no_install: bool = False, base_path: str = os.path.expanduser("~/legendary")
ignore_space_req: bool = False, force: bool = False, sdl_list: list = [''] max_workers: int = os.cpu_count() * 2
): repair: bool = False
self.app_name = app_name no_install: bool = False
self.base_path = base_path ignore_space_req: bool = False
self.max_workers = max_workers force: bool = False
self.repair = repair sdl_list: list = field(default_factory=lambda: [''])
self.no_install = no_install
self.ignore_space_req = ignore_space_req
self.force = force
self.sdl_list = sdl_list
@dataclass
class InstallDownloadModel: class InstallDownloadModel:
def __init__(self, dlmanager, analysis, game, igame, repair: bool, repair_file: str): dlmanager: DLManager
self.dlmanager = dlmanager analysis: AnalysisResult
self.analysis = analysis game: Game
self.game = game igame: InstalledGame
self.igame = igame repair: bool
self.repair = repair repair_file: str
self.repair_file = repair_file res: ConditionCheckResult
@dataclass
class InstallQueueItemModel: class InstallQueueItemModel:
def __init__(self, status_q=None, download: InstallDownloadModel = None, options: InstallOptionsModel = None): status_q: Queue = None
self.status_q = status_q download: InstallDownloadModel = None
self.download = download options: InstallOptionsModel = None
self.options = options
def __bool__(self): def __bool__(self):
return (self.status_q is not None) and (self.download is not None) and (self.options is not None) return (self.status_q is not None) and (self.download is not None) and (self.options is not None)

View file

@ -4,16 +4,62 @@ import os
from datetime import date from datetime import date
import requests import requests
from PyQt5.QtCore import pyqtSignal from PyQt5.QtCore import QThread, pyqtSignal
from custom_legendary.core import LegendaryCore
from rare import cache_dir, data_dir
from rare import data_dir, cache_dir from rare import data_dir, cache_dir
replace_chars = ",;.:-_ " replace_chars = ",;.:-_ "
file = os.path.join(data_dir, "game_list.json") file = os.path.join(cache_dir, "game_list.json")
url = "https://api.steampowered.com/ISteamApps/GetAppList/v2/" url = "https://api.steampowered.com/ISteamApps/GetAppList/v2/"
class SteamWorker(QThread):
app_name = ""
rating_signal = pyqtSignal(str)
def __init__(self, core: LegendaryCore):
super(SteamWorker, self).__init__()
self.core = core
self.ratings = {"platinum": self.tr("Platinum"),
"gold": self.tr("Gold"),
"silver": self.tr("Silver"),
"bronze": self.tr("Bronze"),
"fail": self.tr("Could not get grade"),
"pending": self.tr("Could not get grade")
}
def set_app_name(self, app_name: str):
self.app_name = app_name
def run(self) -> None:
self.rating_signal.emit(self.ratings[get_rating(self.app_name, self.core)])
def get_rating(app_name: str, core: LegendaryCore):
if os.path.exists(p := os.path.join(data_dir, "steam_ids.json")):
grades = json.load(open(p))
else:
grades = {}
if not grades.get(app_name):
steam_id = get_steam_id(core.get_game(app_name).app_title)
grade = get_grade(steam_id)
grades[app_name] = {
"steam_id": steam_id,
"grade": grade
}
with open(os.path.join(data_dir, "steam_ids.json"), "w") as f:
f.write(json.dumps(grades))
f.close()
return grade
else:
return grades[app_name].get("grade")
# you should iniciate the module with the game's steam code # you should iniciate the module with the game's steam code
def get_grade(steam_code): def get_grade(steam_code):
if steam_code == 0: if steam_code == 0:
@ -30,73 +76,47 @@ def get_grade(steam_code):
def load_json() -> dict: def load_json() -> dict:
if not os.path.exists(p := os.path.join(cache_dir, "steam_ids.json")): if not os.path.exists(file):
response = requests.get(url) response = requests.get(url)
steam_ids = json.loads(response.text)["applist"]["apps"] steam_ids = json.loads(response.text)["applist"]["apps"]
ids = {} ids = {}
for game in steam_ids: for game in steam_ids:
ids[game["name"]] = game["appid"] ids[game["name"]] = game["appid"]
with open(os.path.expanduser(p), "w") as f: with open(file, "w") as f:
f.write(json.dumps(ids)) f.write(json.dumps(ids))
f.close() f.close()
return ids return ids
else: else:
return json.loads(open(os.path.join(cache_dir, "steam_ids.json"), "r").read()) return json.loads(open(file, "r").read())
def upgrade_all(games, progress: pyqtSignal = None): def get_steam_id(title: str):
ids = load_json() # workarounds for satisfactory
data = {}
for i, (title, app_name) in enumerate(games):
title = title.replace("Early Access", "").replace("Experimental", "").strip()
data[app_name] = {}
steam_id = get_steam_id(title, ids)
data[app_name] = {
"steam_id": steam_id,
"grade": get_grade(steam_id)}
if progress:
progress.emit(int(i / len(games) * 100))
with open(os.path.join(data_dir, "game_list.json"), "w") as f:
f.write(json.dumps(data))
f.close()
def get_steam_id(title: str, json_data=None):
title = title.replace("Early Access", "").replace("Experimental", "").strip() title = title.replace("Early Access", "").replace("Experimental", "").strip()
if not json_data: if not os.path.exists(file):
if not os.path.exists(p := os.path.join(cache_dir, "steam_ids.json")): response = requests.get(url)
response = requests.get(url) ids = {}
ids = {} steam_ids = json.loads(response.text)["applist"]["apps"]
steam_ids = json.loads(response.text)["applist"]["apps"] for game in steam_ids:
for game in steam_ids: ids[game["name"]] = game["appid"]
ids[game["name"]] = game["appid"]
with open(os.path.expanduser(p), "w") as f: with open(file, "w") as f:
f.write(json.dumps(steam_ids)) f.write(json.dumps(ids))
f.close() f.close()
else:
ids = json.loads(open(os.path.join(cache_dir, "steam_ids.json"), "r").read())
else: else:
ids = json_data ids = json.loads(open(file, "r").read())
steam_name = difflib.get_close_matches(title, ids.keys(), n=1) if title in ids.keys():
steam_name = [title]
else:
steam_name = difflib.get_close_matches(title, ids.keys(), n=1)
if steam_name: if steam_name:
return ids[steam_name[0]] return ids[steam_name[0]]
else: else:
return 0 return 0
# print(x)
# for game in steam_ids:
# num = difflib.SequenceMatcher(None, game["name"], title).ratio()
# if num > most_similar[2] and num > 0.5:
# most_similar = (game["appid"], game["name"], num)
# print(time.time()-t)
# name = difflib.get_close_matches(steam_ids.keys(), title)
# return most_similar
def check_time(): # this function check if it's time to update def check_time(): # this function check if it's time to update

View file

@ -1,5 +1,6 @@
import json import json
import os import os
import platform
import shutil import shutil
import sys import sys
from logging import getLogger from logging import getLogger
@ -7,25 +8,26 @@ from logging import getLogger
import requests import requests
from PIL import Image, UnidentifiedImageError from PIL import Image, UnidentifiedImageError
from PyQt5.QtCore import pyqtSignal, QLocale, QSettings from PyQt5.QtCore import pyqtSignal, QLocale, QSettings
from PyQt5.QtGui import QPalette, QColor from PyQt5.QtGui import QPalette, QColor, QPixmap
# Windows # Windows
if os.name == "nt":
if platform.system() == "Windows":
from win32com.client import Dispatch from win32com.client import Dispatch
from rare import lang_path, style_path, data_dir from rare import languages_path, resources_path, image_dir
# Mac not supported # Mac not supported
from custom_legendary.core import LegendaryCore from custom_legendary.core import LegendaryCore
logger = getLogger("Utils") logger = getLogger("Utils")
s = QSettings("Rare", "Rare") s = QSettings("Rare", "Rare")
IMAGE_DIR = s.value("img_dir", os.path.join(data_dir, "images"), type=str)
def download_images(signal: pyqtSignal, core: LegendaryCore): def download_images(signal: pyqtSignal, core: LegendaryCore):
if not os.path.isdir(IMAGE_DIR): if not os.path.isdir(image_dir):
os.makedirs(IMAGE_DIR) os.makedirs(image_dir)
logger.info("Create Image dir") logger.info("Create Image dir")
# Download Images # Download Images
@ -38,60 +40,60 @@ def download_images(signal: pyqtSignal, core: LegendaryCore):
try: try:
download_image(game) download_image(game)
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
shutil.rmtree(f"{IMAGE_DIR}/{game.app_name}") shutil.rmtree(f"{image_dir}/{game.app_name}")
download_image(game) download_image(game)
signal.emit(i / len(game_list) * 100) signal.emit(i / len(game_list) * 100)
def download_image(game, force=False): def download_image(game, force=False):
if force and os.path.exists(f"{IMAGE_DIR}/{game.app_name}"): if force and os.path.exists(f"{image_dir}/{game.app_name}"):
shutil.rmtree(f"{IMAGE_DIR}/{game.app_name}") shutil.rmtree(f"{image_dir}/{game.app_name}")
if not os.path.isdir(f"{IMAGE_DIR}/" + game.app_name): if not os.path.isdir(f"{image_dir}/" + game.app_name):
os.mkdir(f"{IMAGE_DIR}/" + game.app_name) os.mkdir(f"{image_dir}/" + game.app_name)
# to git picture updates # to git picture updates
if not os.path.isfile(f"{IMAGE_DIR}/{game.app_name}/image.json"): if not os.path.isfile(f"{image_dir}/{game.app_name}/image.json"):
json_data = {"DieselGameBoxTall": None, "DieselGameBoxLogo": None, "Thumbnail": None} json_data = {"DieselGameBoxTall": None, "DieselGameBoxLogo": None, "Thumbnail": None}
else: else:
json_data = json.load(open(f"{IMAGE_DIR}/{game.app_name}/image.json", "r")) json_data = json.load(open(f"{image_dir}/{game.app_name}/image.json", "r"))
# Download # Download
for image in game.metadata["keyImages"]: for image in game.metadata["keyImages"]:
if image["type"] == "DieselGameBoxTall" or image["type"] == "DieselGameBoxLogo" or image["type"] == "Thumbnail": if image["type"] == "DieselGameBoxTall" or image["type"] == "DieselGameBoxLogo" or image["type"] == "Thumbnail":
if image["type"] not in json_data.keys(): if image["type"] not in json_data.keys():
json_data[image["type"]] = None json_data[image["type"]] = None
if json_data[image["type"]] != image["md5"] or not os.path.isfile( if json_data[image["type"]] != image["md5"] or not os.path.isfile(
f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png"): f"{image_dir}/{game.app_name}/{image['type']}.png"):
# Download # Download
json_data[image["type"]] = image["md5"] json_data[image["type"]] = image["md5"]
# os.remove(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png") # os.remove(f"{image_dir}/{game.app_name}/{image['type']}.png")
json.dump(json_data, open(f"{IMAGE_DIR}/{game.app_name}/image.json", "w")) json.dump(json_data, open(f"{image_dir}/{game.app_name}/image.json", "w"))
logger.info(f"Download Image for Game: {game.app_title}") logger.info(f"Download Image for Game: {game.app_title}")
url = image["url"] url = image["url"]
with open(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png", "wb") as f: with open(f"{image_dir}/{game.app_name}/{image['type']}.png", "wb") as f:
f.write(requests.get(url).content) f.write(requests.get(url).content)
try: try:
img = Image.open(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png") img = Image.open(f"{image_dir}/{game.app_name}/{image['type']}.png")
img = img.resize((200, int(200 * 4 / 3))) img = img.resize((200, int(200 * 4 / 3)))
img.save(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png") img.save(f"{image_dir}/{game.app_name}/{image['type']}.png")
except UnidentifiedImageError as e: except UnidentifiedImageError as e:
logger.warning(e) logger.warning(e)
# scale and grey # scale and grey
if not os.path.isfile(f'{IMAGE_DIR}/' + game.app_name + '/UninstalledArt.png'): if not os.path.exists(os.path.join(image_dir, game.app_name + '/UninstalledArt.png')):
if os.path.isfile(f'{IMAGE_DIR}/' + game.app_name + '/DieselGameBoxTall.png'): if os.path.exists(os.path.join(image_dir, f"{game.app_name}/DieselGameBoxTall.png")):
# finalArt = Image.open(f'{IMAGE_DIR}/' + game.app_name + '/DieselGameBoxTall.png') # finalArt = Image.open(f'{image_dir}/' + game.app_name + '/DieselGameBoxTall.png')
# finalArt.save(f'{IMAGE_DIR}/{game.app_name}/FinalArt.png') # finalArt.save(f'{image_dir}/{game.app_name}/FinalArt.png')
# And same with the grayscale one # And same with the grayscale one
bg = Image.open(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png") bg = Image.open(os.path.join(image_dir, f"{game.app_name}/DieselGameBoxTall.png"))
uninstalledArt = bg.convert('L') uninstalledArt = bg.convert('L')
uninstalledArt = uninstalledArt.resize((200, int(200 * 4 / 3))) uninstalledArt = uninstalledArt.resize((200, int(200 * 4 / 3)))
uninstalledArt.save(f'{IMAGE_DIR}/{game.app_name}/UninstalledArt.png') uninstalledArt.save(f'{image_dir}/{game.app_name}/UninstalledArt.png')
elif os.path.isfile(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png"): elif os.path.isfile(f"{image_dir}/{game.app_name}/DieselGameBoxLogo.png"):
bg: Image.Image = Image.open(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png") bg: Image.Image = Image.open(f"{image_dir}/{game.app_name}/DieselGameBoxLogo.png")
bg = bg.resize((int(bg.size[1] * 3 / 4), bg.size[1])) bg = bg.resize((int(bg.size[1] * 3 / 4), bg.size[1]))
logo = Image.open(f'{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png').convert('RGBA') logo = Image.open(f'{image_dir}/{game.app_name}/DieselGameBoxLogo.png').convert('RGBA')
wpercent = ((bg.size[0] * (3 / 4)) / float(logo.size[0])) wpercent = ((bg.size[0] * (3 / 4)) / float(logo.size[0]))
hsize = int((float(logo.size[1]) * float(wpercent))) hsize = int((float(logo.size[1]) * float(wpercent)))
logo = logo.resize((int(bg.size[0] * (3 / 4)), hsize), Image.ANTIALIAS) logo = logo.resize((int(bg.size[0] * (3 / 4)), hsize), Image.ANTIALIAS)
@ -102,16 +104,16 @@ def download_image(game, force=False):
# finalArt = bg.copy() # finalArt = bg.copy()
# finalArt.paste(logo, (pasteX, pasteY), logo) # finalArt.paste(logo, (pasteX, pasteY), logo)
# Write out the file # Write out the file
# finalArt.save(f'{IMAGE_DIR}/' + game.app_name + '/FinalArt.png') # finalArt.save(f'{image_dir}/' + game.app_name + '/FinalArt.png')
logoCopy = logo.copy() logoCopy = logo.copy()
logoCopy.putalpha(int(256 * 3 / 4)) logoCopy.putalpha(int(256 * 3 / 4))
logo.paste(logoCopy, logo) logo.paste(logoCopy, logo)
uninstalledArt = bg.copy() uninstalledArt = bg.copy()
uninstalledArt.paste(logo, (pasteX, pasteY), logo) uninstalledArt.paste(logo, (pasteX, pasteY), logo)
uninstalledArt = uninstalledArt.convert('L') uninstalledArt = uninstalledArt.convert('L')
uninstalledArt.save(f'{IMAGE_DIR}/' + game.app_name + '/UninstalledArt.png') uninstalledArt.save(f'{image_dir}/' + game.app_name + '/UninstalledArt.png')
else: else:
logger.warning(f"File {IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png doesn't exist") logger.warning(f"File {image_dir}/{game.app_name}/DieselGameBoxTall.png doesn't exist")
def get_lang(): def get_lang():
@ -180,23 +182,23 @@ def load_color_scheme(path: str):
def get_color_schemes(): def get_color_schemes():
colors = [] colors = []
for file in os.listdir(os.path.join(style_path, "colors")): for file in os.listdir(os.path.join(resources_path, "colors")):
if file.endswith(".scheme") and os.path.isfile(os.path.join(style_path, "colors", file)): if file.endswith(".scheme") and os.path.isfile(os.path.join(resources_path, "colors", file)):
colors.append(file.replace(".scheme", "")) colors.append(file.replace(".scheme", ""))
return colors return colors
def get_style_sheets(): def get_style_sheets():
styles = [] styles = []
for file in os.listdir(os.path.join(style_path, "qss")): for folder in os.listdir(os.path.join(resources_path, "stylesheets")):
if file.endswith(".qss") and os.path.isfile(os.path.join(style_path, "qss", file)): if os.path.isfile(os.path.join(resources_path, "stylesheets", folder, "stylesheet.qss")):
styles.append(file.replace(".qss", "")) styles.append(folder)
return styles return styles
def get_possible_langs(): def get_possible_langs():
langs = ["en"] langs = ["en"]
for i in os.listdir(lang_path): for i in os.listdir(languages_path):
if i.endswith(".qm"): if i.endswith(".qm"):
langs.append(i.split(".")[0]) langs.append(i.split(".")[0])
return langs return langs
@ -205,7 +207,7 @@ def get_possible_langs():
def get_latest_version(): def get_latest_version():
try: try:
resp = requests.get("https://api.github.com/repos/Dummerle/Rare/releases/latest") resp = requests.get("https://api.github.com/repos/Dummerle/Rare/releases/latest")
tag = json.loads(resp.content.decode("utf-8"))["tag_name"] tag = resp.json()["tag_name"]
return tag return tag
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
return "0.0.0" return "0.0.0"
@ -220,27 +222,31 @@ def get_size(b: int) -> str:
def create_rare_desktop_link(type_of_link): def create_rare_desktop_link(type_of_link):
# Linux # Linux
if os.name == "posix": if platform.system() == "Linux":
if type_of_link == "desktop": if type_of_link == "desktop":
path = os.path.expanduser(f"~/Desktop/") path = os.path.expanduser("~/Desktop/")
elif type_of_link == "start_menu": elif type_of_link == "start_menu":
path = os.path.expanduser("~/.local/share/applications/") path = os.path.expanduser("~/.local/share/applications/")
else: else:
return return
with open(f"{path}Rare.desktop", "w") as desktop_file: if p := os.environ.get("APPIMAGE"):
executable = p
else:
executable = f"{sys.executable} {os.path.abspath(sys.argv[0])}"
with open(os.path.join(path, "Rare.desktop"), "w") as desktop_file:
desktop_file.write("[Desktop Entry]\n" desktop_file.write("[Desktop Entry]\n"
f"Name=Rare\n" f"Name=Rare\n"
f"Type=Application\n" f"Type=Application\n"
f"Icon={os.path.join(style_path, 'Logo.png')}\n" f"Icon={os.path.join(resources_path, 'images', 'Rare.png')}\n"
f"Exec={os.path.abspath(sys.argv[0])}\n" f"Exec={executable}\n"
"Terminal=false\n" "Terminal=false\n"
"StartupWMClass=rare\n" "StartupWMClass=rare\n"
) )
desktop_file.close() desktop_file.close()
os.chmod(os.path.expanduser(f"{path}Rare.desktop"), 0o755) os.chmod(os.path.expanduser(os.path.join(path, "Rare.desktop")), 0o755)
elif os.name == "nt": elif platform.system() == "Windows":
# Target of shortcut # Target of shortcut
if type_of_link == "desktop": if type_of_link == "desktop":
target_folder = os.path.expanduser('~/Desktop/') target_folder = os.path.expanduser('~/Desktop/')
@ -254,20 +260,24 @@ def create_rare_desktop_link(type_of_link):
# Path to location of link file # Path to location of link file
pathLink = os.path.join(target_folder, linkName) pathLink = os.path.join(target_folder, linkName)
exexutable = sys.executable
if "python.exe" in exexutable:
exexutable = exexutable.replace("python.exe", "pythonw.exe")
# Add shortcut # Add shortcut
shell = Dispatch('WScript.Shell') shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(pathLink) shortcut = shell.CreateShortCut(pathLink)
shortcut.Targetpath = os.path.abspath(sys.argv[0]) shortcut.Targetpath = exexutable
shortcut.Arguments = "" shortcut.Arguments = os.path.abspath(sys.argv[0])
shortcut.WorkingDirectory = os.getcwd() shortcut.WorkingDirectory = os.getcwd()
# Icon # Icon
shortcut.IconLocation = os.path.join(style_path, "Logo.ico") shortcut.IconLocation = os.path.join(resources_path, "images", "Rare.ico")
shortcut.save() shortcut.save()
def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"): def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop") -> bool:
igame = core.get_installed_game(app_name) igame = core.get_installed_game(app_name)
if os.path.exists( if os.path.exists(
@ -279,20 +289,25 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"):
icon = os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.join('images'), str), icon = os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.join('images'), str),
igame.app_name, 'DieselGameBoxTall') igame.app_name, 'DieselGameBoxTall')
# Linux # Linux
if os.name == "posix": if platform.system() == "Linux":
if type_of_link == "desktop": if type_of_link == "desktop":
path = os.path.expanduser(f"~/Desktop/") path = os.path.expanduser(f"~/Desktop/")
elif type_of_link == "start_menu": elif type_of_link == "start_menu":
path = os.path.expanduser("~/.local/share/applications/") path = os.path.expanduser("~/.local/share/applications/")
else: else:
return return False
if not os.path.exists(path):
return False
if p := os.environ.get("APPIMAGE"):
executable = p
else:
executable = f"{sys.executable} {os.path.abspath(sys.argv[0])}"
with open(f"{path}{igame.title}.desktop", "w") as desktop_file: with open(f"{path}{igame.title}.desktop", "w") as desktop_file:
desktop_file.write("[Desktop Entry]\n" desktop_file.write("[Desktop Entry]\n"
f"Name={igame.title}\n" f"Name={igame.title}\n"
f"Type=Application\n" f"Type=Application\n"
f"Icon={icon}.png\n" f"Icon={icon}.png\n"
f"Exec=rare launch {app_name}\n" f"Exec={executable} launch {app_name}\n"
"Terminal=false\n" "Terminal=false\n"
"StartupWMClass=rare-game\n" "StartupWMClass=rare-game\n"
) )
@ -300,7 +315,7 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"):
os.chmod(os.path.expanduser(f"{path}{igame.title}.desktop"), 0o755) os.chmod(os.path.expanduser(f"{path}{igame.title}.desktop"), 0o755)
# Windows # Windows
elif os.name == "nt": elif platform.system() == "Windows":
# Target of shortcut # Target of shortcut
if type_of_link == "desktop": if type_of_link == "desktop":
target_folder = os.path.expanduser('~/Desktop/') target_folder = os.path.expanduser('~/Desktop/')
@ -308,7 +323,10 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"):
target_folder = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu") target_folder = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu")
else: else:
logger.warning("No valid type of link") logger.warning("No valid type of link")
return return False
if not os.path.exists(target_folder):
return False
target = os.path.abspath(sys.argv[0]) target = os.path.abspath(sys.argv[0])
# Name of link file # Name of link file
@ -324,8 +342,8 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"):
# Add shortcut # Add shortcut
shell = Dispatch('WScript.Shell') shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(pathLink) shortcut = shell.CreateShortCut(pathLink)
shortcut.Targetpath = target shortcut.Targetpath = sys.executable
shortcut.Arguments = f'launch {app_name}' shortcut.Arguments = f'{target} launch {app_name}'
shortcut.WorkingDirectory = os.getcwd() shortcut.WorkingDirectory = os.getcwd()
# Icon # Icon
@ -336,4 +354,26 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"):
shortcut.IconLocation = os.path.join(icon + ".ico") shortcut.IconLocation = os.path.join(icon + ".ico")
shortcut.save() shortcut.save()
return True
elif platform.system() == "Darwin":
return False
def get_pixmap(app_name: str) -> QPixmap:
for img in ["FinalArt.png", "DieselGameBoxTall.png", "DieselGameBoxLogo.png"]:
if os.path.exists(image := os.path.join(image_dir, app_name, img)):
pixmap = QPixmap(image)
break
else:
pixmap = QPixmap()
return pixmap
def get_uninstalled_pixmap(app_name: str) -> QPixmap:
if os.path.exists(image := os.path.join(image_dir, app_name, "UninstalledArt.png")):
pixmap = QPixmap(image)
else:
pixmap = QPixmap()
return pixmap