|
@ -45,6 +45,7 @@ AppDir:
|
|||
# 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:${APPDIR}/usr/lib/python3.9/site-packages'
|
||||
PYTHONNOUSERSITE: 1
|
||||
|
||||
test:
|
||||
fedora:
|
||||
|
|
38
README.md
|
@ -147,12 +147,24 @@ Depending on your operating system and the `python` distribution, the following
|
|||
|
||||
### Run from source
|
||||
|
||||
1. Clone the repo: `git clone https://github.com/Dummerle/Rare
|
||||
1. Clone the repo: `git clone https://github.com/RareDevs/Rare`
|
||||
2. Change your working directory to the project folder: `cd Rare`
|
||||
3. Run `pip install -r requirements.txt` to install all required dependencies.
|
||||
* If you want to be able to use the automatic login and Discord pypresence, run `pip install -r requirements-full.txt`
|
||||
* If you are on Arch you can run `sudo pacman --needed -S python-wheel python-setuptools python-pyqt5 python-qtawesome python-requests python-orjson` and `yay -S legendary`
|
||||
* If you are on FreeBSD you have to install py39-qt5 from the packages: `sudo pkg install py39-qt5`
|
||||
* If you want to be able to use the automatic login and Discord pypresence, run
|
||||
```shell
|
||||
pip install -r requirements-full.txt
|
||||
```
|
||||
* If you are on Arch you can run
|
||||
```shell
|
||||
sudo pacman --needed -S python-wheel python-setuptools python-pyqt5 python-qtawesome python-requests python-orjson
|
||||
```
|
||||
```
|
||||
yay -S legendary
|
||||
```
|
||||
* If you are on FreeBSD you have to install py39-qt5 from the packages
|
||||
```shell
|
||||
sudo pkg install py39-qt5
|
||||
```
|
||||
4. Run `python3 -m rare`
|
||||
|
||||
|
||||
|
@ -165,10 +177,16 @@ There are several options to contribute.
|
|||
|
||||
More information is available in CONTRIBUTING.md.
|
||||
|
||||
## Images
|
||||
## Screenshots
|
||||
| Game covers | Vertical list |
|
||||
|----------------------------------------------|----------------------------------------------|
|
||||
| ![alt text](Screenshots/RareLibraryIcon.png) | ![alt text](Screenshots/RareLibraryList.png) |
|
||||
|
||||
| Game details | Game settings |
|
||||
|-------------------------------------------|-----------------------------------------------|
|
||||
| ![alt text](Screenshots/RareGameInfo.png) | ![alt text](Screenshots/RareGameSettings.png) |
|
||||
|
||||
| Downloads | Application settings |
|
||||
|--------------------------------------------|-------------------------------------------|
|
||||
| ![alt text](Screenshots/RareDownloads.png) | ![alt text](Screenshots/RareSettings.png) |
|
||||
|
||||
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/Rare.png?raw=true)
|
||||
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/GameInfo.png?raw=true)
|
||||
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/RareSettings.png?raw=true)
|
||||
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/RareDownloads.png?raw=true)
|
||||
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/GameSettings.png?raw=true)
|
||||
|
|
Before Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 512 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 93 KiB |
BIN
Screenshots/RareGameInfo.png
Normal file
After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
BIN
Screenshots/RareLibraryIcon.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
Screenshots/RareLibraryList.png
Normal file
After Width: | Height: | Size: 358 KiB |
|
@ -52,7 +52,9 @@ class DownloadWidget(ImageWidget):
|
|||
# lk: trade some possible delay and start-up time
|
||||
# lk: for faster rendering. Gradients are expensive
|
||||
# lk: so pre-generate the image
|
||||
super(DownloadWidget, self).setPixmap(self.prepare_pixmap(pixmap))
|
||||
if not pixmap.isNull():
|
||||
pixmap = self.prepare_pixmap(pixmap)
|
||||
super(DownloadWidget, self).setPixmap(pixmap)
|
||||
|
||||
def paint_image_empty(self, painter: QPainter, a0: QPaintEvent) -> None:
|
||||
# when pixmap object is not available yet, show a gray rectangle
|
||||
|
|
|
@ -270,8 +270,7 @@ class GameInfo(QWidget, SideTabContents):
|
|||
@pyqtSlot()
|
||||
def __update_widget(self):
|
||||
""" React to state updates from RareGame """
|
||||
# self.image.setPixmap(self.image_manager.get_pixmap(self.rgame.app_name, True))
|
||||
self.image.setPixmap(self.rgame.pixmap)
|
||||
self.image.setPixmap(self.rgame.get_pixmap(True))
|
||||
|
||||
self.ui.lbl_version.setDisabled(self.rgame.is_non_asset)
|
||||
self.ui.version.setDisabled(self.rgame.is_non_asset)
|
||||
|
|
|
@ -18,7 +18,7 @@ class ProgressLabel(QLabel):
|
|||
|
||||
def __center_on_parent(self):
|
||||
fm = QFontMetrics(self.font())
|
||||
rect = fm.boundingRect(f" {self.text()} ")
|
||||
rect = fm.boundingRect(" 100% ")
|
||||
rect.moveCenter(self.parent().contentsRect().center())
|
||||
self.setGeometry(rect)
|
||||
|
||||
|
|
|
@ -9,12 +9,10 @@ from PyQt5.QtGui import (
|
|||
QLinearGradient,
|
||||
QPixmap,
|
||||
QImage,
|
||||
QResizeEvent,
|
||||
)
|
||||
|
||||
from rare.models.game import RareGame
|
||||
from rare.utils.misc import format_size
|
||||
from rare.widgets.image_widget import ImageWidget
|
||||
from .game_widget import GameWidget
|
||||
from .list_widget import ListWidget
|
||||
|
||||
|
@ -70,23 +68,6 @@ class ListGameWidget(GameWidget):
|
|||
refactored to be used in downloads and/or dlcs
|
||||
"""
|
||||
|
||||
def event(self, e: QEvent) -> bool:
|
||||
if e.type() == QEvent.LayoutRequest:
|
||||
if self.progress_label.isVisible():
|
||||
width = int(self._pixmap.width() / self._pixmap.devicePixelRatioF())
|
||||
origin = self.width() - width
|
||||
fill_rect = QRect(origin, 0, width, self.sizeHint().height())
|
||||
self.progress_label.setGeometry(fill_rect)
|
||||
return ImageWidget.event(self, e)
|
||||
|
||||
def resizeEvent(self, a0: QResizeEvent) -> None:
|
||||
if self.progress_label.isVisible():
|
||||
width = int(self._pixmap.width() / self._pixmap.devicePixelRatioF())
|
||||
origin = self.width() - width
|
||||
fill_rect = QRect(origin, 0, width, self.sizeHint().height())
|
||||
self.progress_label.setGeometry(fill_rect)
|
||||
ImageWidget.resizeEvent(self, a0)
|
||||
|
||||
def prepare_pixmap(self, pixmap: QPixmap) -> QPixmap:
|
||||
device: QImage = QImage(
|
||||
pixmap.size().width() * 3,
|
||||
|
@ -112,7 +93,9 @@ class ListGameWidget(GameWidget):
|
|||
# lk: trade some possible delay and start-up time
|
||||
# lk: for faster rendering. Gradients are expensive
|
||||
# lk: so pre-generate the image
|
||||
super(ListGameWidget, self).setPixmap(self.prepare_pixmap(pixmap))
|
||||
if not pixmap.isNull():
|
||||
pixmap = self.prepare_pixmap(pixmap)
|
||||
super(ListGameWidget, self).setPixmap(pixmap)
|
||||
|
||||
def paint_image_cover(self, painter: QPainter, a0: QPaintEvent) -> None:
|
||||
painter.setOpacity(self._opacity)
|
||||
|
|
|
@ -26,7 +26,7 @@ class ListWidget(object):
|
|||
self.size_label = None
|
||||
|
||||
def setupUi(self, widget: QWidget):
|
||||
self.title_label = QLabel(parent=widget)
|
||||
self.title_label = ElideLabel(parent=widget)
|
||||
self.title_label.setObjectName(f"{type(self).__name__}TitleLabel")
|
||||
self.title_label.setWordWrap(False)
|
||||
|
||||
|
@ -41,13 +41,13 @@ class ListWidget(object):
|
|||
self.install_btn = QPushButton(parent=widget)
|
||||
self.install_btn.setObjectName(f"{type(self).__name__}Button")
|
||||
self.install_btn.setIcon(icon("ri.install-line"))
|
||||
self.install_btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
self.install_btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
self.install_btn.setFixedWidth(120)
|
||||
|
||||
self.launch_btn = QPushButton(parent=widget)
|
||||
self.launch_btn.setObjectName(f"{type(self).__name__}Button")
|
||||
self.launch_btn.setIcon(icon("ei.play-alt"))
|
||||
self.launch_btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
self.launch_btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
self.launch_btn.setFixedWidth(120)
|
||||
|
||||
# lk: do not focus on button
|
||||
|
@ -71,19 +71,19 @@ class ListWidget(object):
|
|||
self.size_label.setFixedWidth(60)
|
||||
|
||||
# Create layouts
|
||||
top_layout = QHBoxLayout()
|
||||
top_layout.setAlignment(Qt.AlignLeft)
|
||||
left_layout = QVBoxLayout()
|
||||
left_layout.setAlignment(Qt.AlignLeft)
|
||||
|
||||
bottom_layout = QHBoxLayout()
|
||||
bottom_layout.setAlignment(Qt.AlignRight)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout = QHBoxLayout()
|
||||
layout.setSpacing(0)
|
||||
layout.setContentsMargins(3, 3, 3, 3)
|
||||
|
||||
# Layout the widgets
|
||||
# (from inner to outer)
|
||||
top_layout.addWidget(self.title_label, stretch=1)
|
||||
left_layout.addWidget(self.title_label, stretch=1)
|
||||
|
||||
bottom_layout.addWidget(self.developer_label, stretch=0, alignment=Qt.AlignLeft)
|
||||
bottom_layout.addItem(QSpacerItem(20, 0, QSizePolicy.Fixed, QSizePolicy.Minimum))
|
||||
|
@ -94,15 +94,17 @@ class ListWidget(object):
|
|||
bottom_layout.addWidget(self.status_label, stretch=0, alignment=Qt.AlignLeft)
|
||||
bottom_layout.addItem(QSpacerItem(20, 0, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
||||
bottom_layout.addWidget(self.tooltip_label, stretch=0, alignment=Qt.AlignRight)
|
||||
bottom_layout.addWidget(self.install_btn, stretch=0, alignment=Qt.AlignRight)
|
||||
bottom_layout.addWidget(self.launch_btn, stretch=0, alignment=Qt.AlignRight)
|
||||
|
||||
layout.addLayout(top_layout)
|
||||
layout.addLayout(bottom_layout)
|
||||
left_layout.addLayout(bottom_layout)
|
||||
|
||||
layout.addLayout(left_layout)
|
||||
layout.addWidget(self.install_btn, stretch=0, alignment=Qt.AlignRight)
|
||||
layout.addWidget(self.launch_btn, stretch=0, alignment=Qt.AlignRight)
|
||||
|
||||
widget.setLayout(layout)
|
||||
|
||||
widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
|
||||
widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
widget.setFixedHeight(widget.sizeHint().height())
|
||||
widget.leaveEvent(None)
|
||||
|
||||
self.translateUi(widget)
|
||||
|
|
|
@ -149,7 +149,7 @@ class LegendarySettings(QWidget, Ui_LegendarySettings):
|
|||
def locale_edit_cb(text: str) -> Tuple[bool, str, int]:
|
||||
if text:
|
||||
if re.match("^[a-zA-Z]{2,3}[-_][a-zA-Z]{2,3}$", text):
|
||||
language, country = text.replace("_", "-").split("-")
|
||||
language, country = text.split("-" if "-" in text else "_")
|
||||
text = "-".join([language.lower(), country.upper()])
|
||||
if bool(re.match("^[a-z]{2,3}-[A-Z]{2,3}$", text)):
|
||||
return True, text, IndicatorReasonsCommon.VALID
|
||||
|
|
|
@ -29,6 +29,7 @@ DETACHED_APP_NAMES = {
|
|||
"09e442f830a341f698b4da42abd98c9b", # Fortnite Festival
|
||||
"d8f7763e07d74c209d760a679f9ed6ac", # Lego Fortnite
|
||||
"Fortnite_Studio", # Unreal Editor for Fortnite
|
||||
"dcfccf8d965a4f2281dddf9fead042de", # Homeworld Remastered Collection (issue#376)
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,7 +40,7 @@ class PreLaunchThread(QRunnable):
|
|||
pre_launch_command_finished = pyqtSignal(int) # exit_code
|
||||
error_occurred = pyqtSignal(str)
|
||||
|
||||
def __init__(self, core: LegendaryCore, args: InitArgs, rgame: RareGameSlim, sync_action=None):
|
||||
def __init__(self, args: InitArgs, rgame: RareGameSlim, sync_action=None):
|
||||
super(PreLaunchThread, self).__init__()
|
||||
self.signals = self.Signals()
|
||||
self.logger = getLogger(type(self).__name__)
|
||||
|
@ -321,7 +322,7 @@ class RareLauncher(RareApp):
|
|||
self.stop()
|
||||
|
||||
def start_prepare(self, sync_action=None):
|
||||
worker = PreLaunchThread(self.core, self.args, self.rgame, sync_action)
|
||||
worker = PreLaunchThread(self.args, self.rgame, sync_action)
|
||||
worker.signals.ready_to_launch.connect(self.launch_game)
|
||||
worker.signals.error_occurred.connect(self.error_occurred)
|
||||
# worker.signals.started_pre_launch_command(None)
|
||||
|
|
10
rare/main.py
|
@ -24,10 +24,6 @@ def main() -> int:
|
|||
# fix cx_freeze
|
||||
multiprocessing.freeze_support()
|
||||
|
||||
# insert legendary for installed via pip/setup.py submodule to path
|
||||
# if not __name__ == "__main__":
|
||||
# sys.path.insert(0, os.path.join(os.path.dirname(__file__), "legendary"))
|
||||
|
||||
# CLI Options
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
|
@ -121,12 +117,6 @@ def main() -> int:
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# run from source
|
||||
# insert raw legendary submodule
|
||||
# sys.path.insert(
|
||||
# 0, os.path.join(pathlib.Path(__file__).parent.absolute(), "legendary")
|
||||
# )
|
||||
|
||||
# insert source directory if running `main.py` as python script
|
||||
# Required by AppImage
|
||||
if "__compiled__" not in globals():
|
||||
|
|
|
@ -479,6 +479,10 @@ class RareGame(RareGameSlim):
|
|||
return True
|
||||
return False
|
||||
|
||||
def get_pixmap(self, color=True) -> QPixmap:
|
||||
QPixmapCache.clear()
|
||||
return self.image_manager.get_pixmap(self.app_name, color)
|
||||
|
||||
def set_pixmap(self):
|
||||
self.pixmap = self.image_manager.get_pixmap(self.app_name, self.is_installed)
|
||||
QPixmapCache.clear()
|
||||
|
|
|
@ -163,7 +163,7 @@ css.QPushButton[css_name(IconWidget, "Button")].hover.borderColor.setValue("gray
|
|||
|
||||
# ListGameWidget
|
||||
from rare.components.tabs.games.game_widgets.list_widget import ListWidget
|
||||
css.QLabel[css_name(ListWidget,"TitleLabel")].fontWeight.setValue("bold")
|
||||
css.QLabel[css_name(ListWidget, "TitleLabel")].fontWeight.setValue("bold")
|
||||
list_status_label_props = {
|
||||
"color": "white",
|
||||
"backgroundColor": "rgba(0, 0, 0, 75%)",
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'rare/ui/utils/pathedit.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, QtWidgets
|
||||
|
||||
|
||||
class Ui_PathEdit(object):
|
||||
def setupUi(self, PathEdit):
|
||||
PathEdit.setObjectName("PathEdit")
|
||||
PathEdit.resize(385, 30)
|
||||
PathEdit.setWindowTitle("PathEdit")
|
||||
self.layout_pathedit = QtWidgets.QHBoxLayout(PathEdit)
|
||||
self.layout_pathedit.setContentsMargins(0, 0, 0, 0)
|
||||
self.layout_pathedit.setObjectName("layout_pathedit")
|
||||
self.text_edit = QtWidgets.QLineEdit(PathEdit)
|
||||
self.text_edit.setMinimumSize(QtCore.QSize(300, 0))
|
||||
self.text_edit.setObjectName("text_edit")
|
||||
self.layout_pathedit.addWidget(self.text_edit)
|
||||
self.path_select = QtWidgets.QToolButton(PathEdit)
|
||||
self.path_select.setObjectName("path_select")
|
||||
self.layout_pathedit.addWidget(self.path_select)
|
||||
|
||||
self.retranslateUi(PathEdit)
|
||||
|
||||
def retranslateUi(self, PathEdit):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
self.text_edit.setPlaceholderText(_translate("PathEdit", "Default"))
|
||||
self.path_select.setText(_translate("PathEdit", "Browse..."))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
PathEdit = QtWidgets.QWidget()
|
||||
ui = Ui_PathEdit()
|
||||
ui.setupUi(PathEdit)
|
||||
PathEdit.show()
|
||||
sys.exit(app.exec_())
|
|
@ -1,53 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PathEdit</class>
|
||||
<widget class="QWidget" name="PathEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>385</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">PathEdit</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="layout_pathedit">
|
||||
<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="QLineEdit" name="text_edit">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="path_select">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|