1
0
Fork 0
mirror of synced 2024-06-29 03:31:06 +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 ]
jobs:
pypy-deploy:
pypi-deploy:
if: "!github.event.release.prerelease"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
@ -27,49 +26,6 @@ jobs:
python setup.py sdist bdist_wheel
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:
runs-on: ubuntu-latest
steps:
@ -79,7 +35,6 @@ jobs:
sudo apt install python3-all python3-stdeb dh-python python3-setuptools python3-wheel
sudo pip install -r requirements.txt
- name: run python setup
run: |
python3 setup.py sdist
@ -96,5 +51,41 @@ jobs:
asset_name: Rare.deb
tag: ${{ github.ref }}
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 rare/languages/*.qm
include rare/styles/*
include rare/styles/qss/*
include rare/styles/colors/*
include rare/resources/images/*
recursive-include rare/resources/stylesheets/* *
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)
Execute `pip install Rare` for all users Or `pip install Rare --user` for only one user. Then execute `rare` in your
terminal or cmd
Execute `pip install Rare` for all users Or `pip install Rare --user` for only one user.
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`
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
#### Arch based
@ -38,7 +35,7 @@ There are some AUR packages available:
#### Debian based
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
@ -56,16 +53,16 @@ Install via `pip`.
based [HeroicGamesLauncher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) uses.
- 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
- Launch, install and uninstall games
- Authentication(Import from existing installation and via Browser)
- Download progress bar, queue
- Authentication(Import from existing installation or via Browser)
- Download progress bar and queue
- Settings (Legendary and games)
- Sync Cloud Saves
- 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
@ -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/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/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()
logger.info('Preparing download...')
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,
base_path=args.base_path,
force=args.force,
@ -556,6 +556,15 @@ class LegendaryCLI:
logger.fatal(e)
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')
compression = (1 - (analysis.dl_size / analysis.uncompressed_dl_size)) * 100
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:
self.log.info('Installation requirements check returned the following results:')
if res.warnings:
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
return dlm, anlres, game, igame, repair, repair_file, res
@staticmethod
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
__version__ = "1.4.1"
__version__ = "1.5.0"
style_path = os.path.join(os.path.dirname(__file__), "styles/")
lang_path = os.path.join(os.path.dirname(__file__), "languages/")
resources_path = os.path.join(os.path.dirname(__file__), "resources")
languages_path = os.path.join(os.path.dirname(__file__), "languages")
# Cache Directory: Store images
if p := os.getenv("XDG_CACHE_HOME"):
@ -11,7 +11,8 @@ if p := os.getenv("XDG_CACHE_HOME"):
elif os.name == "nt":
cache_dir = os.path.expandvars("%APPDATA%/rare/cache")
else:
cache_dir = os.path.expanduser("~/.cache/rare/cache")
cache_dir = os.path.expanduser("~/.cache/rare/")
if not os.path.exists(cache_dir):
os.makedirs(cache_dir)
@ -24,3 +25,6 @@ else:
data_dir = os.path.expanduser("~/.local/share/rare/")
if not os.path.exists(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 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.main_window import MainWindow
from rare.components.tray_icon import TrayIcon
@ -50,13 +51,21 @@ class App(QApplication):
self.core.lgd.config.add_section("Legendary")
self.core.lgd.save_config()
# workaround if egl sync enabled, but no programdata path
if self.core.egl_sync_enabled and not os.path.exists(self.core.egl.programdata_path):
self.core.lgd.config.remove_option("Legendary", "egl-sync")
self.core.lgd.save_config()
# workaround if egl sync enabled, but no programdata_path
# programdata_path might be unset if logging in through the browser
if self.core.egl_sync_enabled:
if self.core.egl.programdata_path is None:
self.core.lgd.config.remove_option("Legendary", "egl_sync")
self.core.lgd.save_config()
else:
if not os.path.exists(self.core.egl.programdata_path):
self.core.lgd.config.remove_option("Legendary", "egl_sync")
self.core.lgd.save_config()
# set Application name for settings
self.mainwindow = None
self.tray_icon = None
self.launch_dialog = None
self.setApplicationName("Rare")
self.setOrganizationName("Rare")
settings = QSettings()
@ -64,8 +73,8 @@ class App(QApplication):
# Translator
self.translator = QTranslator()
lang = settings.value("language", get_lang(), type=str)
if os.path.exists(lang_path + lang + ".qm"):
self.translator.load(lang_path + lang + ".qm")
if os.path.exists(languages_path + lang + ".qm"):
self.translator.load(languages_path + lang + ".qm")
logger.info("Your language is supported: " + lang)
elif not lang == "en":
logger.info("Your language is not supported")
@ -78,33 +87,47 @@ class App(QApplication):
settings.setValue("style_sheet", "RareStyle")
if color := settings.value("color_scheme", False):
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:
self.setPalette(custom_palette)
elif style := settings.value("style_sheet", False):
settings.setValue("color_scheme", "")
self.setStyleSheet(open(os.path.join(style_path, "qss", style + ".qss")).read())
self.setWindowIcon(QIcon(os.path.join(style_path, "Logo.png")))
stylesheet = open(os.path.join(resources_path, "stylesheets", style, "stylesheet.qss")).read()
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
self.launch_dialog = LaunchDialog(self.core, args.offline)
self.launch_dialog.quit_app.connect(self.launch_dialog.close)
self.launch_dialog.quit_app.connect(lambda ec: exit(ec))
self.launch_dialog.start_app.connect(self.start_app)
self.launch_dialog.start_app.connect(self.launch_dialog.close)
if not args.silent or args.subparser == "launch":
self.launch_dialog.show()
self.launch_dialog.login()
def start_app(self, offline=False):
self.args.offline = offline
self.mainwindow = MainWindow(self.core, self.args)
self.launch_dialog.close()
self.mainwindow.quit_app.connect(self.exit_app)
self.tray_icon = TrayIcon(self)
self.tray_icon.exit_action.triggered.connect(lambda: exit(0))
self.tray_icon.exit_action.triggered.connect(self.exit_app)
self.tray_icon.start_rare.triggered.connect(self.mainwindow.show)
self.tray_icon.activated.connect(self.tray)
if not offline:
self.mainwindow.tab_widget.downloadTab.finished.connect(lambda update: self.tray_icon.showMessage(
self.tr("Download finished"), self.tr("Download finished. Game is playable now"),
QSystemTrayIcon.Information, 4000) if update else None)
self.mainwindow.tab_widget.downloadTab.finished.connect(lambda x: self.tray_icon.showMessage(
self.tr("Download finished"),
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:
self.mainwindow.show()
@ -114,13 +137,21 @@ class App(QApplication):
self.mainwindow.show()
logger.info("Show App")
def exit_app(self, exit_code=0):
if self.tray_icon is not None:
self.tray_icon.deleteLater()
if self.mainwindow is not None:
self.mainwindow.close()
self.processEvents()
self.exit(exit_code)
def start(args):
while True:
app = App(args)
exit_code = app.exec_()
# if not restart
if exit_code != -133742:
break
# restart app
del app
if exit_code != -133742:
break

View file

@ -53,6 +53,9 @@ class InstallDialog(QDialog, Ui_InstallDialog):
self.install_dir_label.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"):
max_workers = self.core.lgd.config.get("Legendary", "max_workers")
else:
@ -92,7 +95,9 @@ class InstallDialog(QDialog, Ui_InstallDialog):
self.reject_close = True
self.resize(self.minimumSize())
self.setFixedSize(self.size())
# self.setFixedSize(self.size())
self.verify_clicked()
def execute(self):
if self.silent:
@ -151,9 +156,6 @@ class InstallDialog(QDialog, Ui_InstallDialog):
def on_worker_result(self, dl_item: InstallDownloadModel):
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
install_size = self.dl_item.download.analysis.install_size
if download_size:
@ -240,8 +242,11 @@ class InstallInfoWorker(QRunnable):
# reset_sdl=,
sdl_prompt=lambda app_name, title: self.dl_item.options.sdl_list
))
self.signals.result.emit(download)
except RuntimeError as e:
if not download.res.failures:
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.finished.emit()

View file

@ -1,16 +1,15 @@
import json
import os
from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal, QSettings
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtWidgets import QDialog
from requests.exceptions import ConnectionError
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.ui.components.dialogs.launch_dialog import Ui_LaunchDialog
from rare.utils import steam_grades
from rare.utils.utils import download_images
logger = getLogger("Login")
@ -28,134 +27,64 @@ class ImageThread(QThread):
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):
quit_app = pyqtSignal(int)
start_app = pyqtSignal(bool)
finished = False
def __init__(self, core: LegendaryCore, offline):
super(LaunchDialog, self).__init__()
def __init__(self, core: LegendaryCore, offline=False, parent=None):
super(LaunchDialog, self).__init__(parent=parent)
self.setupUi(self)
if os.name == "nt":
self.finished = True
self.steam_info.setVisible(False)
self.steam_prog_bar.setVisible(False)
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.core = core
if not offline:
self.login_thread = LoginThread(core)
self.login_thread.login.connect(self.login)
self.login_thread.start_app.connect(self.launch)
self.login_thread.start()
else:
self.launch(offline)
self.offline = offline
self.image_thread = None
def login(self):
self.hide()
if LoginDialog(core=self.core).login():
self.show()
self.login_thread.start()
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()
do_launch = True
try:
if self.offline:
pass
else:
self.finished = True
self.steam_info.setVisible(False)
self.steam_prog_bar.setVisible(False)
if self.core.login():
logger.info("You are logged in")
else:
raise ValueError("You are not logged in. Open Login Window")
except ValueError as e:
logger.info(str(e))
do_launch = LoginDialog(core=self.core, parent=self).login()
except ConnectionError as e:
logger.warning(e)
self.offline = True
finally:
if do_launch:
self.show()
self.launch()
else:
self.quit_app.emit(0)
def update_steam_prog_bar(self, value):
self.steam_prog_bar.setValue(value)
def launch(self):
# 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):
self.image_prog_bar.setValue(i)
def finish(self):
if self.finished:
self.image_info.setText(self.tr("Starting..."))
self.image_prog_bar.setValue(100)
self.steam_prog_bar.setValue(100)
self.start_app.emit(self.offline)
else:
self.finished = True
self.image_info.setText(self.tr("Starting..."))
self.image_prog_bar.setValue(100)
self.start_app.emit(self.offline)

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
# Login Opportunities: Browser, Import
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
pages = LoginPages(landing=0, browser=1, import_egl=2, success=3)
def __init__(self, core: LegendaryCore):
super(LoginDialog, self).__init__()
def __init__(self, core: LegendaryCore, parent=None):
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.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.layout = QStackedLayout()
self.next_button.setEnabled(False)
self.back_button.setEnabled(False)
self.landing_widget = QWidget()
self.landing_layout = QVBoxLayout()
self.login_browser_radio.clicked.connect(lambda: self.next_button.setEnabled(True))
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.landing_layout.addWidget(self.title)
self.info_text = QLabel(self.tr("Select one option to Login"))
self.landing_layout.addWidget(self.info_text)
self.login_stack.setCurrentIndex(self.pages.landing)
self.browser_login = OptionWidget(self.tr("Use Browser"),
self.tr("This opens your default browser. Login and copy the text"))
self.resize(self.minimumSizeHint())
self.setFixedSize(self.size())
self.landing_layout.addWidget(self.browser_login)
self.browser_login.button.clicked.connect(lambda: self.layout.setCurrentIndex(1))
def back_clicked(self):
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",
"Import an existing login session from an Epic Games Launcher installation. You will get logged out there")
self.import_login.button.clicked.connect(lambda: self.layout.setCurrentIndex(2))
self.landing_layout.addWidget(self.import_login)
self.close_button = QPushButton("Exit App")
self.close_button.clicked.connect(self.close)
self.landing_layout.addWidget(self.close_button)
self.landing_widget.setLayout(self.landing_layout)
self.layout.addWidget(self.landing_widget)
self.browser_widget = BrowserLogin(self.core)
self.browser_widget.success.connect(self.success)
self.browser_widget.back.clicked.connect(lambda: self.layout.setCurrentIndex(0))
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 next_clicked(self):
if self.login_stack.currentIndex() == self.pages.landing:
if self.login_browser_radio.isChecked():
self.login_stack.setCurrentIndex(self.pages.browser)
self.next_button.setEnabled(False)
if self.login_import_radio.isChecked():
self.login_stack.setCurrentIndex(self.pages.import_egl)
self.next_button.setEnabled(self.import_page.is_valid())
self.back_button.setEnabled(True)
elif self.login_stack.currentIndex() == self.pages.browser:
self.browser_page.do_login()
elif self.login_stack.currentIndex() == self.pages.import_egl:
self.import_page.do_login()
else:
self.close()
def login(self):
self.exec_()
return self.logged_in
def success(self):
if self.core.login():
self.logged_in = True
self.layout.setCurrentIndex(3)
# time.sleep(1)
self.close()
class OptionWidget(QWidget):
def __init__(self, btn_text: str, info_text: str):
super(OptionWidget, self).__init__()
self.layout = QVBoxLayout()
self.text = QLabel(info_text)
self.text.setWordWrap(True)
self.button = QPushButton(btn_text)
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)
def login_successful(self):
try:
if self.core.login():
self.logged_in = True
self.welcome_label.setText(
self.welcome_label.text().replace("</h1>", f", {self.core.lgd.userdata['displayName']}</h1>")
)
self.exit_button.setVisible(False)
self.back_button.setVisible(False)
self.login_stack.setCurrentIndex(self.pages.success)
else:
raise ValueError("Login failed.")
except ValueError as e:
logger.error(str(e))
self.next_button.setEnabled(False)
self.logged_in = False

View file

@ -1,54 +1,49 @@
import json
from logging import getLogger
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel, QLineEdit
from PyQt5.QtCore import pyqtSignal, QUrl
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QWidget
from custom_legendary.core import LegendaryCore
from rare.ui.components.dialogs.login.browser_login import Ui_BrowserLogin
logger = getLogger("BrowserLogin")
class BrowserLogin(QWidget):
class BrowserLogin(QWidget, Ui_BrowserLogin):
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.back = QPushButton("Back") # TODO Icon
self.layout.addWidget(self.back)
self.open_button.clicked.connect(self.open_browser)
self.sid_edit.textChanged.connect(self.changed.emit)
self.info_text = QLabel(self.tr(
"Opens a browser. You login and copy the json code in the field below. Click <a href='{}'>here</a> to open Browser").format(
self.url))
self.info_text.setWordWrap(True)
self.info_text.setOpenExternalLinks(True)
self.layout.addWidget(self.info_text)
def is_valid(self):
return len(self.sid_edit.text()) == 32
self.input_field = QLineEdit()
self.input_field.setPlaceholderText(self.tr("Insert SID here"))
self.layout.addWidget(self.input_field)
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()
def do_login(self):
self.status_label.setText(self.tr("Logging in..."))
sid = self.sid_edit.text()
# when the text copied
if sid.startswith("{") and sid.endswith("}"):
sid = json.loads(sid)["sid"]
token = self.core.auth_sid(sid)
if self.core.auth_code(token):
logger.info(f"Successfully logged in as {self.core.lgd.userdata['displayName']}")
self.success.emit()
else:
self.mini_info.setText("Login failed")
try:
token = self.core.auth_sid(sid)
if self.core.auth_code(token):
logger.info(f"Successfully logged in as {self.core.lgd.userdata['displayName']}")
self.success.emit()
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 rare.utils.extra_widgets import PathEdit
@ -7,7 +8,7 @@ class PathInputDialog(QDialog):
def __init__(self, title_text, text, path="Select Directory"):
super().__init__()
self.path = ""
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setWindowTitle(title_text)
self.info_label = QLabel(text)
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 qtawesome import icon
@ -9,6 +10,7 @@ class UninstallDialog(QDialog):
super(UninstallDialog, self).__init__()
self.setWindowTitle("Uninstall Game")
self.info = 0
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.layout = QVBoxLayout()
self.info_text = QLabel(self.tr("Do you really want to uninstall {}").format(game.app_title))
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
from logging import getLogger
from PyQt5.QtCore import QSettings, QTimer
from PyQt5.QtCore import Qt, QSettings, QTimer, pyqtSignal
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QApplication
@ -14,9 +14,11 @@ logger = getLogger("Window")
class MainWindow(QMainWindow):
quit_app = pyqtSignal(int)
def __init__(self, core: LegendaryCore, args):
super(MainWindow, self).__init__()
self.setAttribute(Qt.WA_DeleteOnClose)
self.settings = QSettings()
self.core = core
self.offline = args.offline
@ -29,6 +31,7 @@ class MainWindow(QMainWindow):
self.setWindowTitle("Rare - GUI for legendary")
self.tab_widget = TabWidget(core, self, args.offline)
self.tab_widget.quit_app.connect(self.quit_app.emit)
self.setCentralWidget(self.tab_widget)
if not args.offline:
self.rpc = DiscordRPC(core)

View file

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

View file

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

View file

@ -2,7 +2,7 @@ import os
from logging import getLogger
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.models.game import InstalledGame, SaveGameStatus
@ -77,13 +77,19 @@ class SyncWidget(QGroupBox):
self.logger.error(e)
return
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")
igame.save_path = ""
else:
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)
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)
if self.res == SaveGameStatus.NO_SAVE:
@ -110,9 +116,7 @@ class SyncWidget(QGroupBox):
elif self.res == SaveGameStatus.REMOTE_NEWER:
status = self.tr("Cloud save is newer")
self.download_button = QPushButton(self.tr("Download Cloud saves"))
self.download_button.setStyleSheet("""
QPushButton{ background-color: lime}
""")
self.download_button.setObjectName("success")
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 date: {dt_remote.strftime("%Y-%m-%d %H:%M:%S")}')
@ -126,9 +130,7 @@ class SyncWidget(QGroupBox):
elif self.res == SaveGameStatus.LOCAL_NEWER:
status = self.tr("Local save is newer")
self.upload_button = QPushButton(self.tr("Upload saves"))
self.upload_button.setStyleSheet("""
QPushButton{ background-color: lime}
""")
self.download_button.setObjectName("success")
self.download_button = QPushButton(self.tr("Download saves"))
self.logger.info(f'Local save for "{igame.title}" is newer')
if dt_remote:

View file

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

View file

@ -1,4 +1,5 @@
import os
import platform
import queue
import subprocess
import sys
@ -158,7 +159,7 @@ class DownloadThread(QThread):
def _handle_postinstall(self, postinstall, igame):
print('This game lists the following prequisites to be installed:')
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",
QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
self.core.prereq_installed(igame.app_name)

View file

@ -1,8 +1,8 @@
import json
import os
import platform
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 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.utils.extra_widgets import SideTabBar
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):
@ -44,17 +45,21 @@ class InfoTabs(QTabWidget):
self.settings.update_game(app_name)
# DLC Tab: Disable if no dlcs available
if len(dlcs[self.core.get_game(app_name).asset_info.catalog_item_id]) == 0:
self.setTabEnabled(3, False)
if dlcs:
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:
self.setTabEnabled(3, True)
self.dlc_tab.update_dlcs(app_name, dlcs)
self.setTabEnabled(3, False)
def keyPressEvent(self, e: QKeyEvent):
if e.key() == Qt.Key_Escape:
self.parent().layout.setCurrentIndex(0)
class GameInfo(QWidget, Ui_GameInfo):
igame: InstalledGame
game: Game
@ -68,6 +73,7 @@ class GameInfo(QWidget, Ui_GameInfo):
self.setupUi(self)
self.core = core
self.ratings = {"platinum": self.tr("Platinum"),
"gold": self.tr("Gold"),
"silver": self.tr("Silver"),
@ -79,10 +85,14 @@ class GameInfo(QWidget, Ui_GameInfo):
else:
self.grade_table = {}
if os.name == "nt":
if platform.system() == "Windows":
self.lbl_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.resize(self.game_actions_stack.minimumSize())
@ -133,22 +143,12 @@ class GameInfo(QWidget, Ui_GameInfo):
def update_game(self, app_name):
self.game = self.core.get_game(app_name)
self.igame = self.core.get_installed_game(app_name)
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 = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/FinalArt.png")
elif os.path.exists(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxTall.png"):
pixmap = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxTall.png")
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)
pixmap = get_pixmap(app_name)
w = 200
pixmap = pixmap.scaled(w, int(w * 4 / 3))
self.image.setPixmap(pixmap)
self.app_name.setText(self.game.app_name)
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_path.setText(self.igame.install_path)
if os.name != "nt" and self.grade_table:
try:
grade = self.grade_table[app_name]["grade"]
except KeyError:
grade = "fail"
self.grade.setText(self.ratings[grade])
if platform.system() != "Windows":
self.grade.setText(self.tr("Loading"))
self.steam_worker.set_app_name(app_name)
self.steam_worker.start()
if len(self.verify_threads.keys()) == 0 or not self.verify_threads.get(app_name):
self.verify_widget.setCurrentIndex(0)

View file

@ -1,4 +1,5 @@
import os
import platform
from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QWidget, QFileDialog, QMessageBox
@ -14,7 +15,9 @@ def find_proton_wrappers():
possible_proton_wrappers = []
compatibilitytools_dirs = [
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:
if os.path.exists(c):
@ -63,7 +66,7 @@ class GameSettings(QWidget, Ui_GameSettings):
)
self.wrapper_button.setEnabled(False)
if os.name != "nt":
if platform.system() != "Windows":
self.possible_proton_wrappers = find_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.set(self.game.app_name, option, value)
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:
self.core.lgd.config.remove_option(self.game.app_name, option)
if not self.core.lgd.config[self.game.app_name]:
self.core.lgd.config.remove_section(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.save_config()
self.sender().setEnabled(False)
@ -115,9 +118,6 @@ class GameSettings(QWidget, Ui_GameSettings):
if self.change:
# Dont use Proton
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 self.core.lgd.config.get(f"{self.game.app_name}", "wrapper", fallback=False):
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")
if not self.core.lgd.config[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:
self.proton_prefix.setEnabled(True)
self.wrapper_widget.setEnabled(False)
@ -199,7 +206,7 @@ class GameSettings(QWidget, Ui_GameSettings):
self.wrapper.setText(wrapper)
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.dxvk.update_settings(app_name)
proton = self.core.lgd.config.get(f"{app_name}", "wrapper", fallback="").replace('"', "")

View file

@ -1,8 +1,9 @@
import json
import os
import platform
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 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.utils.extra_widgets import SideTabBar
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):
@ -60,18 +62,12 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.setupUi(self)
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("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 platform.system() != "Windows":
self.steam_worker = SteamWorker(self.core)
self.steam_worker.rating_signal.connect(self.grade.setText)
if platform.system() == "Windows":
if os.name == "nt":
self.lbl_grade.setVisible(False)
self.grade.setVisible(False)
@ -87,22 +83,12 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
def update_game(self, app_name):
self.game = self.core.get_game(app_name)
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 = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/FinalArt.png")
elif os.path.exists(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxTall.png"):
pixmap = QPixmap(f"{IMAGE_DIR}/{self.game.app_name}/DieselGameBoxTall.png")
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)
pixmap = get_pixmap(app_name)
w = 200
pixmap = pixmap.scaled(w, int(w * 4 / 3))
self.image.setPixmap(pixmap)
self.app_name.setText(self.game.app_name)
self.version.setText(self.game.app_version)
@ -110,9 +96,7 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.install_size.setText("N/A")
self.install_path.setText("N/A")
if os.name != "nt" and self.grade_table:
try:
grade = self.grade_table[app_name]["grade"]
except KeyError:
grade = "fail"
self.grade.setText(self.ratings[grade])
if platform.system() != "Windows":
self.grade.setText(self.tr("Loading"))
self.steam_worker.set_app_name(app_name)
self.steam_worker.start()

View file

@ -1,9 +1,7 @@
import os
from logging import getLogger
import psutil
from PyQt5.QtCore import Qt, pyqtSignal, QSettings, QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QScrollArea, QWidget, QLabel, QVBoxLayout, QStackedWidget
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.utils.extra_widgets import FlowLayout
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")
@ -39,7 +37,16 @@ class GameList(QStackedWidget):
self.settings = QSettings()
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)
def init_ui(self, icon_view=True):
@ -65,12 +72,14 @@ class GameList(QStackedWidget):
self.list_layout = QVBoxLayout()
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.widgets = {}
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]]
if not self.offline:
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]]
else:
self.bit32 = []
self.mac_games = []
self.installed = sorted(self.core.get_installed_list(), key=lambda x: x.title)
# Installed Games
@ -78,22 +87,24 @@ class GameList(QStackedWidget):
icon_widget, list_widget = self.add_installed_widget(igame)
self.icon_layout.addWidget(icon_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 = []
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
# add uninstalled games
for game in self.uninstalled_games:
icon_widget, list_widget = self.add_uninstalled_widget(game)
for game in self.uninstalled_games:
icon_widget, list_widget = self.add_uninstalled_widget(game)
self.icon_layout.addWidget(icon_widget)
self.list_layout.addWidget(list_widget)
self.icon_layout.addWidget(icon_widget)
self.list_layout.addWidget(list_widget)
else:
self.dlcs = []
self.icon_parent_layout.addLayout(self.icon_layout)
self.icon_parent_layout.addStretch(1)
@ -114,17 +125,11 @@ class GameList(QStackedWidget):
self.filter(filter_games)
def add_uninstalled_widget(self, game):
if os.path.exists(f"{self.IMAGE_DIR}/{game.app_name}/UninstalledArt.png"):
pixmap = QPixmap(f"{self.IMAGE_DIR}/{game.app_name}/UninstalledArt.png")
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}")
pixmap = get_uninstalled_pixmap(game.app_name)
if pixmap.isNull():
logger.info(game.app_title + " has a corrupt image. Reloading...")
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.show_uninstalled_info.connect(self.show_install_info)
@ -137,20 +142,12 @@ class GameList(QStackedWidget):
return icon_widget, list_widget
def add_installed_widget(self, igame):
if os.path.exists(f"{self.IMAGE_DIR}/{igame.app_name}/FinalArt.png"):
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
pixmap = get_pixmap(igame.app_name)
if pixmap.isNull():
logger.info(igame.title + " has a corrupt image.")
download_image(igame, force=True)
pixmap = QPixmap(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png")
download_image(self.core.get_game(igame.app_name), force=True)
pixmap = get_pixmap(igame.app_name)
icon_widget = GameWidgetInstalled(igame, self.core, pixmap, self.offline)
# self.icon_layout.addWidget(icon_widget)
@ -255,7 +252,7 @@ class GameList(QStackedWidget):
w.setVisible(w.igame.can_run_offline)
else:
w.setVisible(False)
elif filter == "32bit":
elif filter == "32bit" and self.bit32:
w.setVisible(w.game.app_name in self.bit32)
elif filter == "mac":
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)
# QWidget().setLayout(self.icon_layout)
icon_layout = FlowLayout()
# QWidget().setLayout(self.list_layout)
list_layout = QVBoxLayout()
igame = self.core.get_installed_game(app_name)
self.add_installed_widget(igame)
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()
self._update_games()
# uninstalled
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)
self.add_uninstalled_widget(game)
self.icon_layout.addWidget(self.widgets[app_name][0])
self.list_layout.addWidget(self.widgets[app_name][1])
self._update_games()
else:
installed_names = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
@ -393,3 +348,49 @@ class GameList(QStackedWidget):
i_widget, list_widget = self.widgets[name]
self.icon_layout.addWidget(i_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 platform
from logging import getLogger
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 rare import cache_dir
from rare.components.dialogs.uninstall_dialog import UninstallDialog
from rare.components.extra.Console import ConsoleWindow
from rare.utils import legendary_utils
from rare.utils.utils import create_desktop_link
@ -30,8 +32,9 @@ class BaseInstalledWidget(QGroupBox):
self.game_running = False
self.offline = offline
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.settings = QSettings()
self.setContextMenuPolicy(Qt.ActionsContextMenu)
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.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")
elif os.name == "nt":
elif platform.system() == "Windows":
start_menu_file = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu")
else:
start_menu_file = ""
@ -66,6 +69,10 @@ class BaseInstalledWidget(QGroupBox):
self.addAction(uninstall)
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":
path = os.path.expanduser(f"~/Desktop/")
elif type_of_link == "start_menu":
@ -74,7 +81,8 @@ class BaseInstalledWidget(QGroupBox):
return
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"))):
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":
self.create_desktop.setText(self.tr("Remove Desktop link"))
elif type_of_link == "start_menu":
@ -109,37 +117,50 @@ class BaseInstalledWidget(QGroupBox):
except Exception as e:
logger.error(e)
QMessageBox.warning(self, "Error",
self.tr("An error occurred while starting game. Maybe game files are missing"))
str(e))
return
if not self.proc:
logger.error("Could not start process")
return 1
self.game_logger = getLogger(self.game.app_name)
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.launch_signal.emit(self.igame.app_name)
self.game_running = True
self.data = QByteArray()
return 0
def stdout(self):
data = self.proc.readAllStandardOutput()
stdout = bytes(data).decode("utf-8")
self.game_logger.info(stdout)
stdout = bytes(data).decode("utf-8", errors="ignore")
print(stdout)
def stderr(self):
stderr = bytes(self.proc.readAllStandardError()).decode("utf-8")
self.game_logger.error(stderr)
QMessageBox.warning(self, "Warning", stderr + f"\nSee {cache_dir}/logs/")
stderr = bytes(self.proc.readAllStandardError()).decode("utf-8", errors="ignore")
print(stderr)
logger.error(stderr)
# QMessageBox.warning(self, "Warning", stderr + "\nSee ~/.cache/rare/logs/")
def finished(self, exit_code):
logger.info("Game exited with exit code: " + str(exit_code))
self.finish_signal.emit(self.game.app_name)
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):
infos = UninstallDialog(self.game).get_information()

View file

@ -46,8 +46,6 @@ class InstalledListWidget(BaseInstalledWidget):
self.info.setFixedWidth(80)
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.version_label = QLabel("Version: " + str(self.igame.version))
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.developer_label)
# if os.name != "nt":
# self.childLayout.addWidget(self.wine_rating)
self.childLayout.addWidget(self.version_label)
self.childLayout.addWidget(self.size_label)
@ -74,6 +70,3 @@ class InstalledListWidget(BaseInstalledWidget):
def launch(self):
if not self.game_running:
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
@ -18,7 +18,7 @@ class SettingsTab(QTabWidget):
self.rare_settings = RareSettings()
self.addTab(self.rare_settings, "Rare")
self.addTab(LegendarySettings(core), "Legendary")
if os.name != "nt":
if platform.system() != "Windows":
self.addTab(LinuxSettings(core), "Linux")
self.about = About()

View file

@ -1,4 +1,5 @@
import os.path
import platform
from logging import getLogger
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.pathedit_placeholder.addWidget(self.path_edit)
if os.name != "nt":
if platform.system() != "Windows":
self.core.lgd.config.set("Legendary", "egl_programdata")
self.core.egl.programdata_path = path

View file

@ -1,4 +1,5 @@
import os
import platform
import shutil
import subprocess
import sys
@ -35,6 +36,7 @@ class RareSettings(QWidget, Ui_RareSettings):
(self.auto_sync_cloud, "auto_sync_cloud", True),
(self.notification, "notification", True),
(self.save_size, "save_size", False),
(self.log_games, "show_console", False),
(self.image_cache, "cache_images", True)
]
@ -81,33 +83,18 @@ class RareSettings(QWidget, Ui_RareSettings):
self.rpc = RPCSettings()
self.rpc_layout.addWidget(self.rpc, alignment=Qt.AlignTop)
self.init_checkboxes(self.checkboxes)
self.sys_tray.stateChanged.connect(
lambda: self.settings.setValue("sys_tray", self.sys_tray.isChecked())
)
self.auto_update.stateChanged.connect(
lambda: self.settings.setValue("auto_update", self.auto_update.isChecked())
)
self.confirm_start.stateChanged.connect(
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())
)
for cb in self.checkboxes:
widget, option, default = cb
widget.setChecked(self.settings.value(option, default, bool))
widget.stateChanged.connect(
lambda: self.settings.setValue(option, widget.isChecked())
)
if platform.system() == "Linux":
if os.name == "posix":
self.desktop_file = os.path.expanduser("~/Desktop/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.start_menu_link = os.path.expandvars("%appdata%/Microsoft/Windows/Start Menu")
else:
@ -132,7 +119,6 @@ class RareSettings(QWidget, Ui_RareSettings):
size += os.path.getsize(os.path.join(logdir, i))
self.log_dir_size_label.setText(utils.get_size(size))
# TODO: Implement
# self.log_dir_clean_button.setVisible(False)
# self.log_dir_size_label.setVisible(False)
@ -151,7 +137,7 @@ class RareSettings(QWidget, Ui_RareSettings):
def create_desktop_link(self):
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"))
else:
os.remove(self.desktop_file)
@ -178,7 +164,7 @@ class RareSettings(QWidget, Ui_RareSettings):
self.interface_info.setVisible(True)
def open_dir(self):
if os.name == "nt":
if platform.system() == "Windows":
os.startfile(self.logdir)
else:
opener = "open" if sys.platform == "darwin" else "xdg-open"
@ -214,8 +200,3 @@ class RareSettings(QWidget, Ui_RareSettings):
os.rmdir(old_path)
self.img_dir_path = 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.QtWidgets import QSystemTrayIcon, QMenu, QAction
from rare import style_path
from rare import resources_path
class TrayIcon(QSystemTrayIcon):
def __init__(self, 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.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):
def setupUi(self, InstallDialog):
InstallDialog.setObjectName("InstallDialog")
InstallDialog.resize(338, 267)
InstallDialog.setWindowTitle("Rare")
self.install_dialog_layout = QtWidgets.QGridLayout(InstallDialog)
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.setObjectName("force_download_label")
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.setObjectName("button_layout")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.button_layout.addItem(spacerItem)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.button_layout.addItem(spacerItem1)
self.cancel_button = QtWidgets.QPushButton(InstallDialog)
self.cancel_button.setObjectName("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.setObjectName("install_button")
self.button_layout.addWidget(self.install_button)
self.install_dialog_layout.addLayout(self.button_layout, 9, 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.install_dialog_layout.addLayout(self.button_layout, 11, 0, 1, 3)
self.sdl_list_frame = QtWidgets.QFrame(InstallDialog)
self.sdl_list_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
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.setObjectName("sdl_list_layout")
self.install_dialog_layout.addWidget(self.sdl_list_frame, 6, 1, 1, 2, QtCore.Qt.AlignTop)
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.warn_label = QtWidgets.QLabel(InstallDialog)
self.warn_label.setObjectName("warn_label")
self.install_dialog_layout.addWidget(self.warn_label, 9, 0, 1, 1, QtCore.Qt.AlignRight)
self.retranslateUi(InstallDialog)
QtCore.QMetaObject.connectSlotsByName(InstallDialog)
def retranslateUi(self, InstallDialog):
_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.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.verify_button.setText(_translate("InstallDialog", "Verify"))
self.install_button.setText(_translate("InstallDialog", "Install"))
self.ignore_space_info_label.setText(_translate("InstallDialog", "Use with caution!"))
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"))
self.warn_label.setText(_translate("InstallDialog", "Warnings"))
if __name__ == "__main__":

View file

@ -2,10 +2,145 @@
<ui version="4.0">
<class>InstallDialog</class>
<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">
<string notr="true">Rare</string>
</property>
<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">
<widget class="QLabel" name="force_download_label">
<property name="text">
@ -13,7 +148,38 @@
</property>
</widget>
</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">
<item>
<spacer name="button_hspacer">
@ -51,147 +217,6 @@
</item>
</layout>
</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">
<widget class="QFrame" name="sdl_list_frame">
<property name="frameShape">
@ -207,10 +232,10 @@
</layout>
</widget>
</item>
<item row="6" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="sdl_list_label">
<item row="9" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="warn_label">
<property name="text">
<string>Optional packs</string>
<string>Warnings</string>
</property>
</widget>
</item>

View file

@ -8,7 +8,7 @@
# 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):
@ -27,13 +27,6 @@ class Ui_LaunchDialog(object):
self.image_info = QtWidgets.QLabel(LaunchDialog)
self.image_info.setObjectName("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)
QtCore.QMetaObject.connectSlotsByName(LaunchDialog)
@ -43,7 +36,6 @@ class Ui_LaunchDialog(object):
LaunchDialog.setWindowTitle(_translate("LaunchDialog", "Launching Rare"))
self.title_label.setText(_translate("LaunchDialog", "<h2>Launching Rare</h2>"))
self.image_info.setText(_translate("LaunchDialog", "Downloading images"))
self.steam_info.setText(_translate("LaunchDialog", "Getting Steam grades"))
if __name__ == "__main__":

View file

@ -35,20 +35,6 @@
</property>
</widget>
</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>
</widget>
<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.installed_layout.addWidget(self.repair_button)
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.installed_layout.addWidget(self.uninstall_button)
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.setObjectName("uninstalled_layout")
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.uninstalled_layout.addWidget(self.install_button)
self.game_actions_stack.addWidget(self.uninstalled_page)

View file

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

View file

@ -14,12 +14,93 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_RareSettings(object):
def setupUi(self, RareSettings):
RareSettings.setObjectName("RareSettings")
RareSettings.resize(544, 532)
RareSettings.resize(694, 532)
self.rare_layout = QtWidgets.QGridLayout(RareSettings)
self.rare_layout.setObjectName("rare_layout")
self.rpc_layout = QtWidgets.QVBoxLayout()
self.rpc_layout.setObjectName("rpc_layout")
self.rare_layout.addLayout(self.rpc_layout, 1, 1, 1, 1)
self.settings_group = QtWidgets.QGroupBox(RareSettings)
self.settings_group.setObjectName("settings_group")
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.setObjectName("groupBox")
self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox)
@ -31,34 +112,11 @@ class Ui_RareSettings(object):
self.startmenu_link.setObjectName("startmenu_link")
self.verticalLayout.addWidget(self.startmenu_link)
self.rare_layout.addWidget(self.groupBox, 2, 1, 1, 1, QtCore.Qt.AlignTop)
self.settings_group = QtWidgets.QGroupBox(RareSettings)
self.settings_group.setObjectName("settings_group")
self.behavior_layout = QtWidgets.QGridLayout(self.settings_group)
self.behavior_layout.setObjectName("behavior_layout")
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.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.rpc_layout = QtWidgets.QVBoxLayout()
self.rpc_layout.setObjectName("rpc_layout")
self.rare_layout.addLayout(self.rpc_layout, 1, 1, 1, 1)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.rare_layout.addItem(spacerItem2, 3, 0, 1, 2)
self.log_dir_group = QtWidgets.QGroupBox(RareSettings)
self.log_dir_group.setObjectName("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.setObjectName("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.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.rare_layout.addWidget(self.log_dir_group, 0, 1, 1, 1)
self.img_dir_group = QtWidgets.QGroupBox(RareSettings)
self.img_dir_group.setObjectName("img_dir_group")
self.img_dir_layout = QtWidgets.QVBoxLayout(self.img_dir_group)
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.setWordWrap(True)
self.label.setObjectName("label")
@ -132,10 +152,14 @@ class Ui_RareSettings(object):
def retranslateUi(self, RareSettings):
_translate = QtCore.QCoreApplication.translate
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.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.auto_update.setText(_translate("RareSettings", "Update games 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_clean_button.setText(_translate("RareSettings", "Clean Log directory"))
self.interface_group.setTitle(_translate("RareSettings", "Interface"))
self.color_select.setItemText(0, _translate("RareSettings", "None"))
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.interface_info.setText(_translate("RareSettings", "Restart Rare to apply."))
self.lang_label.setText(_translate("RareSettings", "Language"))
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.label.setText(_translate("RareSettings",
"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
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.utils.qt_requests import QtRequestManager
@ -162,7 +163,7 @@ class PathEdit(QWidget, Ui_PathEdit):
dlg_path = self.text_edit.text()
if not dlg_path:
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)
if self.type_filter:
dlg.setFilter([self.type_filter])
@ -212,7 +213,7 @@ class WaitingSpinner(QLabel):
margin-left: 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.movie.start()

View file

@ -1,9 +1,11 @@
import os
import platform
import shutil
from logging import getLogger
from sys import stdout
from PyQt5.QtCore import QProcess, QProcessEnvironment, QThread, pyqtSignal
from PyQt5.QtWidgets import QMessageBox
from custom_legendary.core import LegendaryCore
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)
# remove shortcuts link
if os.name == "posix":
if platform.system() == "Linux":
if os.path.exists(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")):
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")):
os.remove(os.path.expanduser(f"~/Desktop/{igame.title.split(':')[0]}.lnk"))
elif os.path.exists(
@ -116,23 +118,30 @@ class VerifyThread(QThread):
logger.info(f'Verifying "{igame.title}" version "{manifest.meta.build_version}"')
repair_file = []
for result, path, result_hash in validate_files(igame.install_path, file_list):
self.status.emit((self.num, self.total, self.app_name))
self.num += 1
try:
for result, path, result_hash in validate_files(igame.install_path, file_list):
self.status.emit((self.num, self.total, self.app_name))
self.num += 1
if result == VerifyResult.HASH_MATCH:
repair_file.append(f'{result_hash}:{path}')
continue
elif result == VerifyResult.HASH_MISMATCH:
logger.error(f'File does not match hash: "{path}"')
repair_file.append(f'{result_hash}:{path}')
failed.append(path)
elif result == VerifyResult.FILE_MISSING:
logger.error(f'File is missing: "{path}"')
missing.append(path)
else:
logger.error(f'Other failure (see log), treating file as missing: "{path}"')
missing.append(path)
if result == VerifyResult.HASH_MATCH:
repair_file.append(f'{result_hash}:{path}')
continue
elif result == VerifyResult.HASH_MISMATCH:
logger.error(f'File does not match hash: "{path}"')
repair_file.append(f'{result_hash}:{path}')
failed.append(path)
elif result == VerifyResult.FILE_MISSING:
logger.error(f'File is missing: "{path}"')
missing.append(path)
else:
logger.error(f'Other failure (see log), treating file as missing: "{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')
@ -148,7 +157,7 @@ class VerifyThread(QThread):
self.summary.emit((0, 0, self.app_name))
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))

View file

@ -1,36 +1,40 @@
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:
def __init__(self, app_name: str, base_path: str = os.path.expanduser("~/legendary"),
max_workers: int = os.cpu_count() * 2, repair: bool = False, no_install: bool = False,
ignore_space_req: bool = False, force: bool = False, sdl_list: list = ['']
):
self.app_name = app_name
self.base_path = base_path
self.max_workers = max_workers
self.repair = repair
self.no_install = no_install
self.ignore_space_req = ignore_space_req
self.force = force
self.sdl_list = sdl_list
app_name: str
base_path: str = os.path.expanduser("~/legendary")
max_workers: int = os.cpu_count() * 2
repair: bool = False
no_install: bool = False
ignore_space_req: bool = False
force: bool = False
sdl_list: list = field(default_factory=lambda: [''])
@dataclass
class InstallDownloadModel:
def __init__(self, dlmanager, analysis, game, igame, repair: bool, repair_file: str):
self.dlmanager = dlmanager
self.analysis = analysis
self.game = game
self.igame = igame
self.repair = repair
self.repair_file = repair_file
dlmanager: DLManager
analysis: AnalysisResult
game: Game
igame: InstalledGame
repair: bool
repair_file: str
res: ConditionCheckResult
@dataclass
class InstallQueueItemModel:
def __init__(self, status_q=None, download: InstallDownloadModel = None, options: InstallOptionsModel = None):
self.status_q = status_q
self.download = download
self.options = options
status_q: Queue = None
download: InstallDownloadModel = None
options: InstallOptionsModel = None
def __bool__(self):
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
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
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/"
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
def get_grade(steam_code):
if steam_code == 0:
@ -30,73 +76,47 @@ def get_grade(steam_code):
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)
steam_ids = json.loads(response.text)["applist"]["apps"]
ids = {}
for game in steam_ids:
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.close()
return ids
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):
ids = load_json()
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):
def get_steam_id(title: str):
# workarounds for satisfactory
title = title.replace("Early Access", "").replace("Experimental", "").strip()
if not json_data:
if not os.path.exists(p := os.path.join(cache_dir, "steam_ids.json")):
response = requests.get(url)
ids = {}
steam_ids = json.loads(response.text)["applist"]["apps"]
for game in steam_ids:
ids[game["name"]] = game["appid"]
if not os.path.exists(file):
response = requests.get(url)
ids = {}
steam_ids = json.loads(response.text)["applist"]["apps"]
for game in steam_ids:
ids[game["name"]] = game["appid"]
with open(os.path.expanduser(p), "w") as f:
f.write(json.dumps(steam_ids))
f.close()
else:
ids = json.loads(open(os.path.join(cache_dir, "steam_ids.json"), "r").read())
with open(file, "w") as f:
f.write(json.dumps(ids))
f.close()
else:
ids = json_data
steam_name = difflib.get_close_matches(title, ids.keys(), n=1)
ids = json.loads(open(file, "r").read())
if title in ids.keys():
steam_name = [title]
else:
steam_name = difflib.get_close_matches(title, ids.keys(), n=1)
if steam_name:
return ids[steam_name[0]]
else:
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

View file

@ -1,5 +1,6 @@
import json
import os
import platform
import shutil
import sys
from logging import getLogger
@ -7,25 +8,26 @@ from logging import getLogger
import requests
from PIL import Image, UnidentifiedImageError
from PyQt5.QtCore import pyqtSignal, QLocale, QSettings
from PyQt5.QtGui import QPalette, QColor
from PyQt5.QtGui import QPalette, QColor, QPixmap
# Windows
if os.name == "nt":
if platform.system() == "Windows":
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
from custom_legendary.core import LegendaryCore
logger = getLogger("Utils")
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):
if not os.path.isdir(IMAGE_DIR):
os.makedirs(IMAGE_DIR)
if not os.path.isdir(image_dir):
os.makedirs(image_dir)
logger.info("Create Image dir")
# Download Images
@ -38,60 +40,60 @@ def download_images(signal: pyqtSignal, core: LegendaryCore):
try:
download_image(game)
except json.decoder.JSONDecodeError:
shutil.rmtree(f"{IMAGE_DIR}/{game.app_name}")
shutil.rmtree(f"{image_dir}/{game.app_name}")
download_image(game)
signal.emit(i / len(game_list) * 100)
def download_image(game, force=False):
if force and os.path.exists(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):
os.mkdir(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}")
if not os.path.isdir(f"{image_dir}/" + game.app_name):
os.mkdir(f"{image_dir}/" + game.app_name)
# 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}
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
for image in game.metadata["keyImages"]:
if image["type"] == "DieselGameBoxTall" or image["type"] == "DieselGameBoxLogo" or image["type"] == "Thumbnail":
if image["type"] not in json_data.keys():
json_data[image["type"]] = None
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
json_data[image["type"]] = image["md5"]
# 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"))
# 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"))
logger.info(f"Download Image for Game: {game.app_title}")
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)
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.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:
logger.warning(e)
# 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'):
# finalArt = Image.open(f'{IMAGE_DIR}/' + game.app_name + '/DieselGameBoxTall.png')
# finalArt.save(f'{IMAGE_DIR}/{game.app_name}/FinalArt.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.save(f'{image_dir}/{game.app_name}/FinalArt.png')
# 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 = uninstalledArt.resize((200, int(200 * 4 / 3)))
uninstalledArt.save(f'{IMAGE_DIR}/{game.app_name}/UninstalledArt.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")
uninstalledArt.save(f'{image_dir}/{game.app_name}/UninstalledArt.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 = 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]))
hsize = int((float(logo.size[1]) * float(wpercent)))
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.paste(logo, (pasteX, pasteY), logo)
# 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.putalpha(int(256 * 3 / 4))
logo.paste(logoCopy, logo)
uninstalledArt = bg.copy()
uninstalledArt.paste(logo, (pasteX, pasteY), logo)
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:
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():
@ -180,23 +182,23 @@ def load_color_scheme(path: str):
def get_color_schemes():
colors = []
for file in os.listdir(os.path.join(style_path, "colors")):
if file.endswith(".scheme") and os.path.isfile(os.path.join(style_path, "colors", file)):
for file in os.listdir(os.path.join(resources_path, "colors")):
if file.endswith(".scheme") and os.path.isfile(os.path.join(resources_path, "colors", file)):
colors.append(file.replace(".scheme", ""))
return colors
def get_style_sheets():
styles = []
for file in os.listdir(os.path.join(style_path, "qss")):
if file.endswith(".qss") and os.path.isfile(os.path.join(style_path, "qss", file)):
styles.append(file.replace(".qss", ""))
for folder in os.listdir(os.path.join(resources_path, "stylesheets")):
if os.path.isfile(os.path.join(resources_path, "stylesheets", folder, "stylesheet.qss")):
styles.append(folder)
return styles
def get_possible_langs():
langs = ["en"]
for i in os.listdir(lang_path):
for i in os.listdir(languages_path):
if i.endswith(".qm"):
langs.append(i.split(".")[0])
return langs
@ -205,7 +207,7 @@ def get_possible_langs():
def get_latest_version():
try:
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
except requests.exceptions.ConnectionError:
return "0.0.0"
@ -220,27 +222,31 @@ def get_size(b: int) -> str:
def create_rare_desktop_link(type_of_link):
# Linux
if os.name == "posix":
if platform.system() == "Linux":
if type_of_link == "desktop":
path = os.path.expanduser(f"~/Desktop/")
path = os.path.expanduser("~/Desktop/")
elif type_of_link == "start_menu":
path = os.path.expanduser("~/.local/share/applications/")
else:
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"
f"Name=Rare\n"
f"Type=Application\n"
f"Icon={os.path.join(style_path, 'Logo.png')}\n"
f"Exec={os.path.abspath(sys.argv[0])}\n"
f"Icon={os.path.join(resources_path, 'images', 'Rare.png')}\n"
f"Exec={executable}\n"
"Terminal=false\n"
"StartupWMClass=rare\n"
)
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
if type_of_link == "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
pathLink = os.path.join(target_folder, linkName)
exexutable = sys.executable
if "python.exe" in exexutable:
exexutable = exexutable.replace("python.exe", "pythonw.exe")
# Add shortcut
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(pathLink)
shortcut.Targetpath = os.path.abspath(sys.argv[0])
shortcut.Arguments = ""
shortcut.Targetpath = exexutable
shortcut.Arguments = os.path.abspath(sys.argv[0])
shortcut.WorkingDirectory = os.getcwd()
# Icon
shortcut.IconLocation = os.path.join(style_path, "Logo.ico")
shortcut.IconLocation = os.path.join(resources_path, "images", "Rare.ico")
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)
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),
igame.app_name, 'DieselGameBoxTall')
# Linux
if os.name == "posix":
if platform.system() == "Linux":
if type_of_link == "desktop":
path = os.path.expanduser(f"~/Desktop/")
elif type_of_link == "start_menu":
path = os.path.expanduser("~/.local/share/applications/")
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:
desktop_file.write("[Desktop Entry]\n"
f"Name={igame.title}\n"
f"Type=Application\n"
f"Icon={icon}.png\n"
f"Exec=rare launch {app_name}\n"
f"Exec={executable} launch {app_name}\n"
"Terminal=false\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)
# Windows
elif os.name == "nt":
elif platform.system() == "Windows":
# Target of shortcut
if type_of_link == "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")
else:
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])
# Name of link file
@ -324,8 +342,8 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"):
# Add shortcut
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(pathLink)
shortcut.Targetpath = target
shortcut.Arguments = f'launch {app_name}'
shortcut.Targetpath = sys.executable
shortcut.Arguments = f'{target} launch {app_name}'
shortcut.WorkingDirectory = os.getcwd()
# 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.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