1
0
Fork 0
mirror of synced 2024-06-26 10:11:19 +12:00

Merge branch 'main' into dev

This commit is contained in:
Dummerle 2021-09-04 22:49:08 +02:00 committed by GitHub
commit f7f7f83101
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 2501 additions and 2548 deletions

6
.github/GenPKG.sh vendored
View file

@ -1,6 +0,0 @@
export PYTHONPATH=$PWD
version=$(python3 rare --version)
cd .github
git clone https://aur.archlinux.org/rare.git
cd ..
sed -i "s/.*pkgver=.*/pkgver=$version/" .github/rare/PKGBUILD

1
.github/rare vendored

@ -1 +0,0 @@
Subproject commit f689dacef1b8902da34aae7eff85a1bbe8e0a0a4

View file

@ -76,7 +76,7 @@ jobs:
- name: Build Appimage
run: |
appimage-builder --skip-test
mv Rare-*.AppImage ../Rare.AppImage
mv Rare-*.AppImage Rare.AppImage
- name: Upload to GitHub
uses: svenstaro/upload-release-action@2.2.1
@ -87,5 +87,65 @@ jobs:
tag: ${{ github.ref }}
overwrite: true
cx_freeze:
runs-on: "windows-latest"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Dependencies
run: pip3 install -r requirements.txt
- name: cx_freeze
run: pip3 install --upgrade cx_freeze wheel
- name: prepare Files
run: mv rare/__main__.py rare/Rare.py
- name: Build
run: python freeze.py bdist_msi
- name: Rename File
run: mv dist/* dist/Rare.msi
- name: Upload to GitHub
uses: svenstaro/upload-release-action@2.2.1
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: dist/Rare.msi
asset_name: Rare.msi
tag: ${{ github.ref }}
overwrite: true
mac_os:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Rare Dependencies
run: pip install -r requirements.txt
- name: install pyinstaller
run: pip install pyinstaller
- name: copy files
run: mv rare/__main__.py __main__.py
- name: run pyinstaller
run: pyinstaller -F --name Rare --add-data "rare/languages/*:rare/languages" --add-data "rare/resources/colors/*:rare/resources/colors" --add-data "rare/resources/images/*:rare/resources/images/" --add-data "rare/resources/stylesheets/RareStyle/*:rare/resources/stylesheets/RareStyle/" --windowed --icon rare/resources/images/Rare.icns __main__.py
- name: upload to GitHub
uses: svenstaro/upload-release-action@2.2.1
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: dist/Rare.app
asset_name: Rare_MacOS.app
tag: ${{ github.ref }}
overwrite: true

67
.github/workflows/tests.yml vendored Normal file
View file

@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
workflow_dispatch:
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: 'python'
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View file

@ -2,9 +2,8 @@
## A frontend for legendary, the open source Epic Games Launcher alternative
Rare is a GUI for Legendary, a command line aternative to Epic Games launcher. It is currently considered beta software.
You will probably run into issues, so it is recommend to make a backup. If you run into an issue, please report it by
creating an issue on github or on Discord: https://discord.gg/YvmABK9YSk
Rare is a GUI for Legendary, a command line alternative to Epic Games launcher. If you run into an issue, please report
it by creating an issue on GitHub or on Discord: https://discord.gg/YvmABK9YSk
![Discord Shield](https://discordapp.com/api/guilds/826881530310819914/widget.png?style=shield)
@ -12,13 +11,14 @@ 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.
Execute `pip install Rare` for all users Or `pip install Rare --user` for only one user.
Linux: execute `rare` in your terminal.
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
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.
@ -32,16 +32,16 @@ There are some AUR packages available:
- [rare](https://aur.archlinux.org/packages/rare) - for stable releases
- [rare-git](https://aur.archlinux.org/packages/rare-git) - for the latest features, which are not in a stable release
#### Debian based
There is a `.deb` package available from
the [releases page](https://github.com/Dummerle/Rare/releases): `sudo apt install ./Rare.deb`
#### Other
Install via `pip`.
## Run from source
### Packages
In [releases page](https://github.com/Dummerle/Rare/releases) are AppImages for Linux and a msi file for windows
available
### Run from source
1. Run `pip install -r requirements.txt` to get dependencies. If you use `pacman` you can
run `sudo pacman --needed -S python-wheel python-setuptools python-pyqt5 python-qtawesome python-requests python-pillow`

View file

@ -293,6 +293,37 @@ class LegendaryCore:
def _get_installed_game(self, app_name) -> InstalledGame:
return self.lgd.get_installed_game(app_name)
def get_non_asset_library_items(self, skip_ue=True) -> (List[Game], Dict[str, List[Game]]):
"""
Gets a list of Games without assets for installation, for instance Games delivered via
third-party stores that do not have assets for installation
:param skip_ue: Ingore Unreal Marketplace entries
:return: List of Games and DLC that do not have assets
"""
_ret = []
_dlc = defaultdict(list)
# get all the appnames we have to ignore
ignore = set(i.app_name for i in self.get_assets())
for libitem in self.egs.get_library_items():
if libitem['namespace'] == 'ue' and skip_ue:
continue
if libitem['appName'] in ignore:
continue
game = self.lgd.get_game_meta(libitem['appName'])
if not game:
eg_meta = self.egs.get_game_info(libitem['namespace'], libitem['catalogItemId'])
game = Game(app_name=libitem['appName'], app_version=None,
app_title=eg_meta['title'], asset_info=None, metadata=eg_meta)
self.lgd.set_game_meta(game.app_name, game)
if game.is_dlc:
_dlc[game.metadata['mainGameItem']['id']].append(game)
elif not any(i['path'] == 'mods' for i in game.metadata.get('categories', [])):
_ret.append(game)
return _ret, _dlc
def get_launch_parameters(self, app_name: str, offline: bool = False,
user: str = None, extra_args: list = None,
wine_bin: str = None, wine_pfx: str = None,

82
freeze.py Normal file
View file

@ -0,0 +1,82 @@
import sys
from cx_Freeze import setup, Executable
from rare import __version__
# Packages to include
python_packages = [
"PyQt5",
'requests',
'PIL',
'qtawesome',
'psutil',
'pypresence',
'win32com'
]
# 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": False}
build_options["bdist_msi"] = bdist_msi_options
else:
name = 'Rare'
src_files += [
'LICENSE',
'README.md',
'rare/resources/images/Rare.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/Rare.py',
targetName=name,
icon='rare/resources/images/Rare.ico',
base=base,
shortcutName=shortcutName,
shortcutDir=shortcutDir,
),
],
)

View file

@ -1,6 +1,6 @@
import os
__version__ = "1.5.0"
__version__ = "1.6.2"
resources_path = os.path.join(os.path.dirname(__file__), "resources")
languages_path = os.path.join(os.path.dirname(__file__), "languages")

View file

@ -36,7 +36,7 @@ def main():
if args.version:
print(__version__)
exit(0)
return
try:
# this object only allows one instance per machine
me = singleton.SingleInstance()
@ -49,14 +49,16 @@ def main():
else:
file.write("start")
file.close()
exit(0)
return
if args.subparser == "launch":
args.silent = True
# start app
# Import start now, to not import everything
# fix error in cx_freeze
import multiprocessing
multiprocessing.freeze_support()
from rare.app import start
start(args)

View file

@ -4,8 +4,9 @@ import os
import sys
import time
import qtawesome
from PyQt5.QtCore import QSettings, QTranslator
from PyQt5.QtGui import QIcon
from PyQt5.QtGui import QIcon, QPalette
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QStyleFactory
from custom_legendary.core import LegendaryCore
@ -72,8 +73,8 @@ class App(QApplication):
# Translator
self.translator = QTranslator()
lang = settings.value("language", get_lang(), type=str)
if os.path.exists(languages_path + lang + ".qm"):
self.translator.load(languages_path + lang + ".qm")
if os.path.exists(p := os.path.join(languages_path, lang + ".qm")):
self.translator.load(p)
logger.info("Your language is supported: " + lang)
elif not lang == "en":
logger.info("Your language is not supported")
@ -89,6 +90,8 @@ class App(QApplication):
custom_palette = load_color_scheme(os.path.join(resources_path, "colors", color + ".scheme"))
if custom_palette is not None:
self.setPalette(custom_palette)
qtawesome.set_defaults(color=custom_palette.color(QPalette.Text))
elif style := settings.value("style_sheet", False):
settings.setValue("color_scheme", "")
stylesheet = open(os.path.join(resources_path, "stylesheets", style, "stylesheet.qss")).read()
@ -96,6 +99,7 @@ class App(QApplication):
if os.name == "nt":
style_resource_path = style_resource_path.replace('\\', '/')
self.setStyleSheet(stylesheet.replace("@path@", style_resource_path))
qtawesome.set_defaults(color="white")
# 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)
@ -126,7 +130,8 @@ class App(QApplication):
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)
QSystemTrayIcon.Information, 4000) if (
x[0] and QSettings().value("notification", True, bool)) else None)
if not self.args.silent:
self.mainwindow.show()

View file

@ -27,6 +27,6 @@ class TabButtonWidget(QToolButton):
super(TabButtonWidget, self).__init__()
self.setText("Icon")
self.setPopupMode(QToolButton.InstantPopup)
self.setIcon(icon(button_icon, color="white", scale_factor=1.25))
self.setIcon(icon(button_icon, scale_factor=1.25))
self.setToolTip(tool_tip)
self.setIconSize(QSize(25, 25))

View file

@ -1,5 +1,5 @@
from PyQt5.QtCore import QSize, pyqtSignal
from PyQt5.QtWidgets import QMenu, QTabWidget, QWidget, QWidgetAction
from PyQt5.QtWidgets import QMenu, QTabWidget, QWidget, QWidgetAction, QShortcut
from qtawesome import icon
from custom_legendary.core import LegendaryCore
@ -55,7 +55,7 @@ class TabWidget(QTabWidget):
account_button.menu().addAction(account_action)
self.tabBar().setTabButton(disabled_tab + 1, self.tabBar().RightSide, account_button)
self.addTab(self.settings, icon("fa.gear", color='white'),
self.addTab(self.settings, icon("fa.gear"),
"(!)" if self.settings.about.update_available else "")
# Signals
@ -100,12 +100,22 @@ class TabWidget(QTabWidget):
# Open game list on click on Games tab button
self.tabBarClicked.connect(self.mouse_clicked)
self.setIconSize(QSize(25, 25))
# shortcuts
QShortcut("Alt+1", self).activated.connect(lambda: self.setCurrentIndex(0))
QShortcut("Alt+2", self).activated.connect(lambda: self.setCurrentIndex(1))
QShortcut("Alt+3", self).activated.connect(lambda: self.setCurrentIndex(2))
QShortcut("Alt+4", self).activated.connect(lambda: self.setCurrentIndex(5))
self.downloadTab.dl_status.connect(
self.games_tab.default_widget.game_list.installing_widget.image_widget.set_status)
def mouse_clicked(self, tab_num):
if tab_num == 0:
self.games_tab.layout.setCurrentIndex(0)
if tab_num == 3:
self.store.load()
self.store.load()
# TODO; maybe pass InstallOptionsModel only, not split arguments
def install_game(self, options: InstallOptionsModel, update=False, silent=False):
@ -125,6 +135,7 @@ class TabWidget(QTabWidget):
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))
self.setCurrentIndex(1)
self.downloadTab.install_game(download_item)
self.games_tab.default_widget.game_list.start_download(download_item.options.app_name)
def game_imported(self, app_name: str):
igame = self.core.get_installed_game(app_name)
@ -159,6 +170,7 @@ class TabWidget(QTabWidget):
if update_list[0]:
self.games_tab.default_widget.game_list.update_list(update_list[1])
self.cloud_saves.reload(update_list[1])
self.games_tab.default_widget.game_list.installing_widget.setVisible(False)
downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys())
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))

View file

@ -86,7 +86,9 @@ class SyncWidget(QGroupBox):
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))
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)

View file

@ -21,6 +21,7 @@ class DownloadTab(QWidget):
finished = pyqtSignal(tuple)
thread: QThread
dl_queue = []
dl_status = pyqtSignal(int)
def __init__(self, core: LegendaryCore, updates: list, parent):
super(DownloadTab, self).__init__(parent=parent)
@ -120,16 +121,6 @@ class DownloadTab(QWidget):
pass
elif text == "finish":
self.installing_game.setText(self.tr("Download finished. Reload library"))
if QSettings().value("notification", True, bool):
try:
from notifypy import Notify
except ModuleNotFoundError:
logger.warning("No Notification Module found")
else:
notification = Notify()
notification.title = self.tr("Installation finished")
notification.message = self.tr("Finished Download of game {}").format(self.active_game.app_title)
notification.send()
# QMessageBox.information(self, "Info", "Download finished")
logger.info("Download finished: " + self.active_game.app_title)
@ -184,6 +175,7 @@ class DownloadTab(QWidget):
self.downloaded.setText(
self.tr("Downloaded") + f": {get_size(ui_update.total_downloaded)} / {get_size(self.analysis.dl_size)}")
self.time_left.setText(self.tr("Time left: ") + self.get_time(ui_update.estimated_time_left))
self.dl_status.emit(int(100 * ui_update.total_downloaded / self.analysis.dl_size))
def get_time(self, seconds: int) -> str:
return str(datetime.timedelta(seconds=seconds))

View file

@ -20,14 +20,14 @@ class DlWidget(QWidget):
self.layout = QHBoxLayout()
self.left_layout = QVBoxLayout()
self.move_up_button = QPushButton(icon("fa.arrow-up", color="white"), "")
self.move_up_button = QPushButton(icon("fa.arrow-up"), "")
if index == 0:
self.move_up_button.setDisabled(True)
self.move_up_button.clicked.connect(lambda: self.move_up.emit(self.app_name))
self.move_up_button.setFixedWidth(20)
self.left_layout.addWidget(self.move_up_button)
self.move_down_buttton = QPushButton(icon("fa.arrow-down", color="white"), "")
self.move_down_buttton = QPushButton(icon("fa.arrow-down"), "")
self.move_down_buttton.clicked.connect(lambda: self.move_down.emit(self.app_name))
self.left_layout.addWidget(self.move_down_buttton)
self.move_down_buttton.setFixedWidth(20)

View file

@ -95,12 +95,13 @@ class GameListHeadBar(QWidget):
self.filter.addItems([self.tr("All"),
self.tr("Installed only"),
self.tr("Offline Games"),
self.tr("32 Bit Games")])
self.tr("32 Bit Games"),
self.tr("Installable Games")])
self.filter.currentIndexChanged.connect(self.filter_changed)
self.layout.addWidget(self.filter)
self.layout.addStretch(1)
self.import_game = QPushButton(icon("mdi.import", color="white"), self.tr("Import Game"))
self.import_game = QPushButton(icon("mdi.import"), self.tr("Import Game"))
self.layout.addWidget(self.import_game)
self.layout.addStretch(1)
@ -109,24 +110,23 @@ class GameListHeadBar(QWidget):
self.search_bar.setObjectName("search_bar")
self.search_bar.setFrame(False)
icon_label = QLabel()
icon_label.setPixmap(icon("fa.search", color="white").pixmap(QSize(20, 20)))
icon_label.setPixmap(icon("fa.search").pixmap(QSize(20, 20)))
self.layout.addWidget(icon_label)
self.search_bar.setMinimumWidth(200)
self.search_bar.setPlaceholderText(self.tr("Search Game"))
self.layout.addWidget(self.search_bar)
self.layout.addStretch(2)
checked = QSettings().value("icon_view", True, bool)
self.view = SelectViewWidget(checked)
self.layout.addWidget(self.view)
self.layout.addStretch(1)
self.refresh_list = QPushButton()
self.refresh_list.setIcon(icon("fa.refresh", color="white")) # Reload icon
self.refresh_list.setIcon(icon("fa.refresh")) # Reload icon
self.layout.addWidget(self.refresh_list)
self.setLayout(self.layout)
def filter_changed(self, i):
self.filter_changed_signal.emit(["", "installed", "offline", "32bit"][i])
self.filter_changed_signal.emit(["", "installed", "offline", "32bit", "installable"][i])

View file

@ -27,7 +27,7 @@ class InfoTabs(QTabWidget):
self.setTabBar(SideTabBar())
self.setTabPosition(QTabWidget.West)
self.addTab(QWidget(), icon("mdi.keyboard-backspace", color="white"), self.tr("Back"))
self.addTab(QWidget(), icon("mdi.keyboard-backspace"), self.tr("Back"))
self.tabBarClicked.connect(lambda x: self.parent().layout.setCurrentIndex(0) if x == 0 else None)
self.info = GameInfo(core, self)
@ -60,7 +60,6 @@ class InfoTabs(QTabWidget):
self.parent().layout.setCurrentIndex(0)
class GameInfo(QWidget, Ui_GameInfo):
igame: InstalledGame
game: Game

View file

@ -25,7 +25,7 @@ class UninstalledTabInfo(QTabWidget):
self.setTabBar(SideTabBar())
self.setTabPosition(QTabWidget.West)
self.addTab(QWidget(), icon("mdi.keyboard-backspace", color="white"), self.tr("Back"))
self.addTab(QWidget(), icon("mdi.keyboard-backspace"), self.tr("Back"))
self.tabBarClicked.connect(lambda x: self.parent().layout.setCurrentIndex(0) if x == 0 else None)
self.info = UninstalledInfo(core, self)
@ -46,7 +46,10 @@ class UninstalledTabInfo(QTabWidget):
def update_game(self, app_name):
self.info.update_game(app_name)
self.model.clear()
self.model.load(json.load(open(os.path.expanduser(f"~/.config/legendary/metadata/{app_name}.json"), "r")))
try:
self.model.load(json.load(open(os.path.expanduser(f"~/.config/legendary/metadata/{app_name}.json"), "r")))
except: # ignore if no metadata
pass
def keyPressEvent(self, e: QKeyEvent):
if e.key() == Qt.Key_Escape:
@ -62,6 +65,8 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.setupUi(self)
self.core = core
self.no_install_label.setVisible(False)
if platform.system() != "Windows":
self.steam_worker = SteamWorker(self.core)
self.steam_worker.rating_signal.connect(self.grade.setText)
@ -71,6 +76,8 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.lbl_grade.setVisible(False)
self.grade.setVisible(False)
self.non_asset_games = [i.app_name for i in self.core.get_non_asset_library_items()[0]]
self.install_size.setEnabled(False)
self.lbl_install_size.setEnabled(False)
self.install_path.setEnabled(False)
@ -83,6 +90,7 @@ 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>")
pixmap = get_pixmap(app_name)
@ -96,6 +104,13 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.install_size.setText("N/A")
self.install_path.setText("N/A")
if app_name in self.non_asset_games:
self.no_install_label.setVisible(True)
self.install_button.setEnabled(False)
else:
self.no_install_label.setVisible(False)
self.install_button.setEnabled(True)
if platform.system() != "Windows":
self.grade.setText(self.tr("Loading"))
self.steam_worker.set_app_name(app_name)

View file

@ -9,6 +9,7 @@ from rare import data_dir
from rare.components.tabs.games.game_widgets.base_installed_widget import BaseInstalledWidget
from rare.components.tabs.games.game_widgets.installed_icon_widget import GameWidgetInstalled
from rare.components.tabs.games.game_widgets.installed_list_widget import InstalledListWidget
from rare.components.tabs.games.game_widgets.installing_game_widget import InstallingGameWidget
from rare.components.tabs.games.game_widgets.uninstalled_icon_widget import IconWidgetUninstalled
from rare.components.tabs.games.game_widgets.uninstalled_list_widget import ListWidgetUninstalled
from rare.utils.extra_widgets import FlowLayout
@ -34,7 +35,7 @@ class GameList(QStackedWidget):
self.core = core
self.setObjectName("list_widget")
self.offline = offline
self.installing_widget = InstallingGameWidget()
self.settings = QSettings()
icon_view = self.settings.value("icon_view", True, bool)
self.procs = []
@ -77,11 +78,16 @@ class GameList(QStackedWidget):
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]]
self.no_assets = [i.app_name for i in self.core.get_non_asset_library_items()[0]]
else:
self.bit32 = []
self.mac_games = []
self.installed = sorted(self.core.get_installed_list(), key=lambda x: x.title)
self.installing_widget = InstallingGameWidget()
self.icon_layout.addWidget(self.installing_widget)
self.installing_widget.setVisible(False)
# Installed Games
for igame in self.installed:
icon_widget, list_widget = self.add_installed_widget(igame)
@ -92,13 +98,13 @@ class GameList(QStackedWidget):
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):
for game in games:
if not game.app_name in installed:
self.uninstalled_games.append(game)
# add uninstalled games
for game in self.uninstalled_games:
self.uninstalled_games += self.core.get_non_asset_library_items()[0]
for game in sorted(self.uninstalled_games, key=lambda x: x.app_title):
icon_widget, list_widget = self.add_uninstalled_widget(game)
self.icon_layout.addWidget(icon_widget)
@ -124,6 +130,10 @@ class GameList(QStackedWidget):
if filter_games := self.settings.value("filter", "", str):
self.filter(filter_games)
def start_download(self, app_name):
self.installing_widget.set_game(self.core.get_game(app_name))
self.installing_widget.setVisible(True)
def add_uninstalled_widget(self, game):
pixmap = get_uninstalled_pixmap(game.app_name)
if pixmap.isNull():
@ -256,6 +266,8 @@ class GameList(QStackedWidget):
w.setVisible(w.game.app_name in self.bit32)
elif filter == "mac":
w.setVisible(w.game.app_name in self.mac_games)
elif filter == "installable":
w.setVisible(w.game.app_name not in self.no_assets)
else:
# All visible
w.setVisible(True)
@ -348,6 +360,7 @@ class GameList(QStackedWidget):
i_widget, list_widget = self.widgets[name]
self.icon_layout.addWidget(i_widget)
self.list_layout.addWidget(list_widget)
self.installing_widget.setVisible(False)
def _update_games(self):
# new layouts to remove from old layout
@ -355,6 +368,7 @@ class GameList(QStackedWidget):
# QWidget().setLayout(self.list_layout)
list_layout = QVBoxLayout()
icon_layout.addWidget(self.installing_widget)
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)

View file

@ -50,7 +50,7 @@ class GameWidgetInstalled(BaseInstalledWidget):
# Info Button
self.menu_btn = QPushButton()
self.menu_btn.setIcon(icon("ei.info-circle", color="white"))
self.menu_btn.setIcon(icon("ei.info-circle"))
# self.menu_btn.setObjectName("installed_menu_button")
self.menu_btn.setIconSize(QSize(18, 18))
self.menu_btn.enterEvent = lambda x: self.info_label.setText("Information")

View file

@ -1,4 +1,3 @@
import os
from logging import getLogger
from PyQt5.QtCore import QProcess, pyqtSignal, Qt
@ -34,7 +33,7 @@ class InstalledListWidget(BaseInstalledWidget):
self.image.setPixmap(self.pixmap)
self.layout.addWidget(self.image)
play_icon = icon("ei.play", color="white")
play_icon = icon("ei.play")
self.title_widget = QLabel(f"<h1>{self.igame.title}</h1>")
self.app_name_label = QLabel(self.igame.app_name)
self.launch_button = QPushButton(play_icon, self.tr("Launch"))

View file

@ -0,0 +1,80 @@
from PyQt5.QtCore import Qt, QRect
from PyQt5.QtGui import QPaintEvent, QPainter, QPixmap, QPen, QFont, QColor
from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QLabel, QHBoxLayout, QWidget
from custom_legendary.models.game import Game
from rare.utils.utils import get_pixmap, get_uninstalled_pixmap, optimal_text_background, text_color_for_background
class InstallingGameWidget(QGroupBox):
def __init__(self):
super(InstallingGameWidget, self).__init__()
self.setObjectName("game_widget_icon")
self.setLayout(QVBoxLayout())
self.pixmap = QPixmap()
w = 200
# self.pixmap = self.pixmap.scaled(w, int(w * 4 / 3), transformMode=Qt.SmoothTransformation)
self.image_widget = PaintWidget()
self.image_widget.setFixedSize(w, int(w * 4 / 3))
self.layout().addWidget(self.image_widget)
self.title_label = QLabel(f"<h4>Error</h4>")
self.title_label.setAutoFillBackground(False)
self.title_label.setWordWrap(True)
self.title_label.setFixedWidth(175)
minilayout = QHBoxLayout()
self.setObjectName("game_widget")
minilayout.addWidget(self.title_label)
self.layout().addLayout(minilayout)
def set_game(self, game: Game):
self.title_label.setText(f"<h4>{game.app_title}</h4>")
self.pixmap = get_pixmap(game.app_name)
w = 200
self.pixmap = self.pixmap.scaled(w, int(w * 4 / 3), transformMode=Qt.SmoothTransformation)
self.image_widget.set_game(self.pixmap, game.app_name)
class PaintWidget(QWidget):
def __init__(self):
super(PaintWidget, self).__init__()
def set_game(self, pixmap: QPixmap, app_name):
self.image = pixmap
self.setFixedSize(self.image.size())
self.new_image = get_uninstalled_pixmap(app_name)
self.status = 0
pixel_list = []
for x in range(self.image.width()):
for y in range(self.image.height()):
# convert pixmap to qimage, get pixel and remove alpha channel
pixel_list.append(self.image.toImage().pixelColor(x, y).getRgb()[:-1])
self.rgb_tuple = optimal_text_background(pixel_list)
def paintEvent(self, a0: QPaintEvent) -> None:
painter = QPainter()
painter.begin(self)
painter.drawPixmap(self.rect(), self.image)
w = self.image.width() * (1 - self.status / 100)
painter.drawPixmap(self.image.width() - w, 0, w, self.image.height(),
self.new_image.copy(QRect(self.image.width() - w, 0, w, self.image.height())))
# Draw Circle
pen = QPen(QColor(*self.rgb_tuple), 3)
painter.setPen(pen)
painter.setBrush(QColor(*self.rgb_tuple))
painter.drawEllipse(int(self.width() / 2) - 20, int(self.height() / 2) - 20, 40, 40)
# draw text
painter.setPen(QColor(*text_color_for_background(self.rgb_tuple)))
painter.setFont(QFont(None, 16))
painter.drawText(a0.rect(), Qt.AlignCenter, str(self.status) + "%")
painter.end()
def set_status(self, s: int):
self.status = s
self.repaint()

View file

@ -23,7 +23,7 @@ class ImportWidget(QWidget):
self.game_list = [i.app_name for i in self.core.get_game_list()]
self.main_layout = QHBoxLayout()
self.back_button = QPushButton(icon("mdi.keyboard-backspace", color="white"), self.tr("Back"))
self.back_button = QPushButton(icon("mdi.keyboard-backspace"), self.tr("Back"))
self.right_layout = QVBoxLayout()
self.right_layout.addWidget(self.back_button)
self.right_layout.addStretch(1)

View file

@ -42,7 +42,7 @@ class LegendarySettings(QStackedWidget, Ui_legendary_settings):
self.back_button.clicked.connect(lambda: self.setCurrentIndex(0))
self.path_info.setText(self.tr(r"EGL path is at C:\ProgramData\Epic\EpicGamesLauncher\Data\Manifests"))
self.path_info.setText(self.tr("EGL path is at C:\\ProgramData\\Epic\\EpicGamesLauncher\\Data\\Manifests"))
path = os.path.expanduser("~/")
if self.core.egl.programdata_path:
path = self.core.egl.programdata_path

View file

@ -45,6 +45,7 @@ class RareSettings(QWidget, Ui_RareSettings):
language = self.settings.value("language", get_lang(), type=str)
self.logdir = os.path.join(cache_dir, "logs")
# Select Image directory
# self.img_dir = PathEdit(self.img_dir_path, file_type=QFileDialog.DirectoryOnly, save_func=self.save_path)
# self.img_dir_layout.addWidget(self.img_dir)

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -14,6 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_GameInfo(object):
def setupUi(self, GameInfo):
GameInfo.setObjectName("GameInfo")
GameInfo.resize(571, 326)
self.layout_game_info = QtWidgets.QGridLayout(GameInfo)
self.layout_game_info.setObjectName("layout_game_info")
self.layout_game_info_form = QtWidgets.QGridLayout()
@ -182,15 +183,20 @@ class Ui_GameInfo(object):
self.install_button.setStyleSheet("")
self.install_button.setObjectName("install_button")
self.uninstalled_layout.addWidget(self.install_button)
self.no_install_label = QtWidgets.QLabel(self.uninstalled_page)
self.no_install_label.setWordWrap(True)
self.no_install_label.setObjectName("no_install_label")
self.uninstalled_layout.addWidget(self.no_install_label)
self.game_actions_stack.addWidget(self.uninstalled_page)
self.layout_game_info_form.addWidget(self.game_actions_stack, 6, 1, 1, 1, QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.layout_game_info_form.addWidget(self.game_actions_stack, 6, 1, 1, 1,
QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
self.layout_game_info.addLayout(self.layout_game_info_form, 2, 1, 1, 1)
self.image = QtWidgets.QLabel(GameInfo)
self.image.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.image.setFrameShadow(QtWidgets.QFrame.Sunken)
self.image.setText("")
self.image.setObjectName("image")
self.layout_game_info.addWidget(self.image, 2, 0, 1, 1, QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.layout_game_info.addWidget(self.image, 2, 0, 1, 1, QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
self.game_title = QtWidgets.QLabel(GameInfo)
self.game_title.setText("error")
self.game_title.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
@ -198,7 +204,7 @@ class Ui_GameInfo(object):
self.layout_game_info.addWidget(self.game_title, 0, 0, 1, 3)
self.retranslateUi(GameInfo)
self.game_actions_stack.setCurrentIndex(0)
self.game_actions_stack.setCurrentIndex(1)
self.verify_widget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(GameInfo)
@ -216,6 +222,8 @@ class Ui_GameInfo(object):
self.repair_button.setText(_translate("GameInfo", "Repair Instalation"))
self.uninstall_button.setText(_translate("GameInfo", "Uninstall Game"))
self.install_button.setText(_translate("GameInfo", "Install Game"))
self.no_install_label.setText(
_translate("GameInfo", "It is not possible to install the Game. It could be from a third party store"))
if __name__ == "__main__":

View file

@ -2,16 +2,24 @@
<ui version="4.0">
<class>GameInfo</class>
<widget class="QWidget" name="GameInfo">
<property name="windowTitle">
<string>Game Info</string>
</property>
<layout class="QGridLayout" name="layout_game_info">
<item row="2" column="1">
<layout class="QGridLayout" name="layout_game_info_form">
<property name="leftMargin">
<number>6</number>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>571</width>
<height>326</height>
</rect>
</property>
<property name="topMargin">
<property name="windowTitle">
<string>Game Info</string>
</property>
<layout class="QGridLayout" name="layout_game_info">
<item row="2" column="1">
<layout class="QGridLayout" name="layout_game_info_form">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
@ -248,29 +256,29 @@
</size>
</property>
<property name="currentIndex">
<number>0</number>
<number>1</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>
<item>
<widget class="QStackedWidget" name="verify_widget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_verify_button">
<layout class="QVBoxLayout" name="layout_verify_button">
<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>
<item>
<widget class="QStackedWidget" name="verify_widget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_verify_button">
<layout class="QVBoxLayout" name="layout_verify_button">
<property name="spacing">
<number>0</number>
</property>
@ -343,18 +351,28 @@
</layout>
</widget>
<widget class="QWidget" name="uninstalled_page">
<layout class="QVBoxLayout" name="uninstalled_layout">
<item>
<widget class="QPushButton" name="install_button">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Install Game</string>
</property>
</widget>
</item>
</layout>
<layout class="QVBoxLayout" name="uninstalled_layout">
<item>
<widget class="QPushButton" name="install_button">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Install Game</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="no_install_label">
<property name="text">
<string>It is not possible to install the Game. It could be from a third party store</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>

View file

@ -14,51 +14,13 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_RareSettings(object):
def setupUi(self, RareSettings):
RareSettings.setObjectName("RareSettings")
RareSettings.resize(544, 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.groupBox = QtWidgets.QGroupBox(RareSettings)
self.groupBox.setObjectName("groupBox")
self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox)
self.verticalLayout.setObjectName("verticalLayout")
self.desktop_link = QtWidgets.QPushButton(self.groupBox)
self.desktop_link.setObjectName("desktop_link")
self.verticalLayout.addWidget(self.desktop_link)
self.startmenu_link = QtWidgets.QPushButton(self.groupBox)
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.confirm_start = QtWidgets.QCheckBox(self.settings_group)
self.confirm_start.setObjectName("confirm_start")
self.behavior_layout.addWidget(self.confirm_start, 2, 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_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.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.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.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)
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.rare_layout.addWidget(self.settings_group, 2, 0, 1, 1, QtCore.Qt.AlignTop)
RareSettings.resize(694, 532)
self.gridLayout = QtWidgets.QGridLayout(RareSettings)
self.gridLayout.setObjectName("gridLayout")
self.verticalLayout_4 = QtWidgets.QVBoxLayout()
self.verticalLayout_4.setObjectName("verticalLayout_4")
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)
@ -70,11 +32,36 @@ class Ui_RareSettings(object):
self.log_dir_clean_button.setObjectName("log_dir_clean_button")
self.log_dir_layout.addWidget(self.log_dir_clean_button)
self.log_dir_size_label = QtWidgets.QLabel(self.log_dir_group)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.log_dir_size_label.sizePolicy().hasHeightForWidth())
self.log_dir_size_label.setText("")
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.verticalLayout_4.addWidget(self.log_dir_group)
self.rpc_layout = QtWidgets.QVBoxLayout()
self.rpc_layout.setObjectName("rpc_layout")
self.verticalLayout_4.addLayout(self.rpc_layout)
self.groupBox = QtWidgets.QGroupBox(RareSettings)
self.groupBox.setObjectName("groupBox")
self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox)
self.verticalLayout.setObjectName("verticalLayout")
self.desktop_link = QtWidgets.QPushButton(self.groupBox)
self.desktop_link.setObjectName("desktop_link")
self.verticalLayout.addWidget(self.desktop_link)
self.startmenu_link = QtWidgets.QPushButton(self.groupBox)
self.startmenu_link.setObjectName("startmenu_link")
self.verticalLayout.addWidget(self.startmenu_link)
self.verticalLayout_4.addWidget(self.groupBox)
self.gridLayout.addLayout(self.verticalLayout_4, 0, 1, 1, 1)
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.interface_group = QtWidgets.QGroupBox(RareSettings)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
@ -84,6 +71,16 @@ class Ui_RareSettings(object):
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)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.interface_layout.addItem(spacerItem, 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("")
@ -104,23 +101,40 @@ class Ui_RareSettings(object):
self.interface_info.setWordWrap(True)
self.interface_info.setObjectName("interface_info")
self.interface_layout.addWidget(self.interface_info, 3, 0, 1, 3)
self.lang_label = QtWidgets.QLabel(self.interface_group)
self.lang_label.setObjectName("lang_label")
self.interface_layout.addWidget(self.lang_label, 0, 0, 1, 1, QtCore.Qt.AlignRight)
self.lang_select = QtWidgets.QComboBox(self.interface_group)
self.lang_select.setObjectName("lang_select")
self.interface_layout.addWidget(self.lang_select, 0, 1, 1, 1)
self.color_label = QtWidgets.QLabel(self.interface_group)
self.color_label.setObjectName("color_label")
self.interface_layout.addWidget(self.color_label, 1, 0, 1, 1, QtCore.Qt.AlignRight)
self.rare_layout.addWidget(self.interface_group, 1, 0, 1, 1, QtCore.Qt.AlignTop)
self.img_dir_group = QtWidgets.QGroupBox(RareSettings)
self.img_dir_group.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, QtCore.Qt.AlignTop)
self.verticalLayout_3.addWidget(self.interface_group)
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.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.notification = QtWidgets.QCheckBox(self.settings_group)
self.notification.setObjectName("notification")
self.behavior_layout.addWidget(self.notification, 4, 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.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.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)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.behavior_layout.addItem(spacerItem1, 2, 1, 2, 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.verticalLayout_3.addWidget(self.settings_group)
self.gridLayout.addLayout(self.verticalLayout_3, 0, 0, 1, 1)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.rare_layout.addItem(spacerItem2, 3, 0, 1, 2)
self.gridLayout.addItem(spacerItem2, 1, 0, 1, 2)
self.retranslateUi(RareSettings)
QtCore.QMetaObject.connectSlotsByName(RareSettings)
@ -128,20 +142,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_sync_cloud.setText(_translate("RareSettings", "Automatically sync with cloud"))
self.sys_tray.setText(_translate("RareSettings", "Exit to System tray"))
self.save_size.setText(_translate("RareSettings", "Restore window size on application startup"))
self.notification.setText(_translate("RareSettings", "Show notification on download completion"))
self.auto_update.setText(_translate("RareSettings", "Update games on application startup"))
self.log_games.setText(_translate("RareSettings", "Open console for game output"))
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.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.interface_group.setTitle(_translate("RareSettings", "Interface"))
self.color_select.setItemText(0, _translate("RareSettings", "None"))
self.style_label.setText(_translate("RareSettings", "Style Sheet"))
@ -149,7 +157,17 @@ class Ui_RareSettings(object):
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.img_dir_group.setTitle(_translate("RareSettings", "Image Cache Directory"))
self.interface_info.setText(_translate("RareSettings", "Restart Rare to apply."))
self.settings_group.setTitle(_translate("RareSettings", "Behavior"))
self.save_size.setText(_translate("RareSettings", "Restore window size on application startup"))
self.notification.setText(_translate("RareSettings", "Show notification on download completion"))
self.auto_sync_cloud.setText(_translate("RareSettings", "Automatically sync with cloud"))
self.auto_update.setText(_translate("RareSettings", "Update games on application startup"))
self.sys_tray.setText(_translate("RareSettings", "Exit to System tray"))
self.log_games.setText(_translate("RareSettings", "Show console for game debug"))
self.confirm_start.setText(_translate("RareSettings", "Confirm game launch"))
if __name__ == "__main__":

View file

@ -13,134 +13,251 @@
<property name="windowTitle">
<string>RareSettings</string>
</property>
<layout class="QGridLayout" name="rare_layout">
<item row="1" column="1">
<layout class="QVBoxLayout" name="rpc_layout"/>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<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="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="rpc_layout"/>
</item>
<item>
<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>
</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="2" column="0">
<widget class="QCheckBox" name="confirm_start">
<property name="text">
<string>Confirm game launch</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="3" column="0">
<widget class="QCheckBox" name="auto_sync_cloud">
<property name="text">
<string>Automatically sync with cloud</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="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="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="6" column="0">
<widget class="QCheckBox" name="log_games">
<property name="text">
<string>Open console for game output</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>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<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="2" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="style_label">
<property name="text">
<string>Style Sheet</string>
</property>
</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="0" column="1">
<widget class="QComboBox" name="lang_select"/>
</item>
<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="1">
<widget class="QComboBox" name="style_select">
<item>
<property name="text">
<string>None</string>
</property>
</item>
</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="1" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="color_label">
<property name="text">
<string>Color Scheme</string>
</property>
</widget>
</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>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="settings_group">
<property name="title">
<string>Behavior</string>
</property>
<layout class="QGridLayout" name="behavior_layout">
<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="4" column="0">
<widget class="QCheckBox" name="notification">
<property name="text">
<string>Show notification on download completion</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="1" column="0">
<widget class="QCheckBox" name="auto_update">
<property name="text">
<string>Update games on application startup</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="log_games">
<property name="text">
<string>Show console for game debug</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="2" column="0">
<widget class="QCheckBox" name="confirm_start">
<property name="text">
<string>Confirm game launch</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<spacer name="verticalSpacer_2">
<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>
</item>

View file

@ -88,6 +88,8 @@ class FlowLayout(QLayout):
lineheight = 0
for item in self._items:
widget = item.widget()
if not widget.isVisible():
continue
hspace = self.horizontalSpacing()
if hspace == -1:
hspace = widget.style().layoutSpacing(
@ -229,9 +231,9 @@ class SelectViewWidget(QWidget):
self.list_view = QPushButton()
if icon_view:
self.icon_view_button.setIcon(icon("mdi.view-grid-outline", color="orange"))
self.list_view.setIcon(icon("fa5s.list", color="white"))
self.list_view.setIcon(icon("fa5s.list"))
else:
self.icon_view_button.setIcon(icon("mdi.view-grid-outline", color="white"))
self.icon_view_button.setIcon(icon("mdi.view-grid-outline"))
self.list_view.setIcon(icon("fa5s.list", color="orange"))
self.icon_view_button.clicked.connect(self.icon)
@ -248,12 +250,12 @@ class SelectViewWidget(QWidget):
def icon(self):
self.icon_view_button.setIcon(icon("mdi.view-grid-outline", color="orange"))
self.list_view.setIcon(icon("fa5s.list", color="white"))
self.list_view.setIcon(icon("fa5s.list"))
self.icon_view = False
self.toggled.emit()
def list(self):
self.icon_view_button.setIcon(icon("mdi.view-grid-outline", color="white"))
self.icon_view_button.setIcon(icon("mdi.view-grid-outline"))
self.list_view.setIcon(icon("fa5s.list", color="orange"))
self.icon_view = True
self.toggled.emit()

View file

@ -87,6 +87,13 @@ def uninstall(app_name: str, core: LegendaryCore, options=None):
except Exception as e:
logger.warning(f'Removing game failed: {e!r}, please remove {igame.install_path} manually.')
logger.info("Removing sections in config file")
if core.lgd.config.has_section(app_name):
core.lgd.config.remove_section(app_name)
if core.lgd.config.has_section(app_name + ".env"):
core.lgd.config.remove_section(app_name + ".env")
core.lgd.save_config()
class VerifyThread(QThread):
status = pyqtSignal(tuple)

View file

@ -46,7 +46,9 @@ def get_rating(app_name: str, core: LegendaryCore):
grades = {}
if not grades.get(app_name):
steam_id = get_steam_id(core.get_game(app_name).app_title)
game = core.get_game(app_name)
steam_id = get_steam_id(game.app_title)
grade = get_grade(steam_id)
grades[app_name] = {
"steam_id": steam_id,
@ -118,7 +120,6 @@ def get_steam_id(title: str):
return 0
def check_time(): # this function check if it's time to update
global file
text = open(file, 'r')

View file

@ -1,9 +1,11 @@
import json
import math
import os
import platform
import shutil
import sys
from logging import getLogger
from typing import Tuple
import requests
from PIL import Image, UnidentifiedImageError
@ -21,7 +23,7 @@ from rare import languages_path, resources_path, image_dir
from custom_legendary.core import LegendaryCore
logger = getLogger("Utils")
s = QSettings("Rare", "Rare")
settings = QSettings("Rare", "Rare")
@ -35,7 +37,10 @@ def download_images(signal: pyqtSignal, core: LegendaryCore):
dlc_list = []
for i in dlcs.values():
dlc_list.append(i[0])
game_list = games + dlc_list
no_assets = core.get_non_asset_library_items()[0]
game_list = games + dlc_list + no_assets
for i, game in enumerate(game_list):
try:
download_image(game)
@ -377,3 +382,38 @@ def get_uninstalled_pixmap(app_name: str) -> QPixmap:
pixmap = QPixmap()
return pixmap
def optimal_text_background(image: list) -> Tuple[int, int, int]:
"""
Finds an optimal background color for text on the image by calculating the
average color of the image and inverting it.
The image list is supposed to be a one-dimensional list of arbitrary length
containing RGB tuples, ranging from 0 to 255.
"""
# cursed, I know
average = map(lambda value: value / len(image), map(sum, zip(*image)))
inverted = map(lambda value: 255 - value, average)
return tuple(inverted)
def text_color_for_background(background: Tuple[int, int, int]) -> Tuple[int,
int,
int]:
"""
Calculates whether a black or white text color would fit better for the
given background, and returns that color. This is done by calculating the
luminance and simple comparing of bounds
"""
# see https://alienryderflex.com/hsp.html
(red, green, blue) = background
luminance = math.sqrt(
0.299 * red ** 2 +
0.587 * green ** 2 +
0.114 * blue ** 2)
if luminance < 127:
return 255, 255, 255
else:
return 0, 0, 0

View file

@ -2,7 +2,6 @@ requests
Pillow
PyQt5
QtAwesome
notify-py
psutil
pypresence
pywin32; platform_system == "Windows"

View file

@ -14,7 +14,6 @@ requirements = [
"wheel",
"PyQt5",
"QtAwesome",
"notify-py",
"psutil",
"pypresence",
'pywin32; platform_system == "Windows"'