1
0
Fork 0
mirror of synced 2024-06-02 02:34:40 +12:00

Merge branch 'main' of github.com:Dummerle/Rare

This commit is contained in:
Dummerle 2021-04-10 16:04:58 +02:00
commit 089bde1210
28 changed files with 560 additions and 267 deletions

View file

@ -1,15 +1,26 @@
import sys
from argparse import ArgumentParser
from rare import __version__
from rare.utils import singleton
def main():
if "--version" in sys.argv:
from rare import __version__
parser = ArgumentParser()
parser.add_argument("-V", "--version", action="store_true")
parser.add_argument("-S", "--silent", action="store_true")
args = parser.parse_args()
if args.version:
print(__version__)
exit(0)
try:
# this object only allows one instance pre machine
me = singleton.SingleInstance()
except singleton.SingleInstanceException:
print("Rare is already running")
exit(0)
from rare.App import start
start()
from rare.app import start
start(args)
if __name__ == '__main__':

View file

@ -30,8 +30,11 @@ logger = logging.getLogger("Rare")
class App(QApplication):
def __init__(self):
def __init__(self, args):
super(App, self).__init__(sys.argv)
self.args = args
# add some options
# init Legendary
try:
self.core = LegendaryCore()
@ -92,9 +95,9 @@ class App(QApplication):
logger.info("Show App")
def start():
def start(args):
while True:
app = App()
app = App(args)
exit_code = app.exec_()
# if not restart
if exit_code != -133742:

View file

@ -69,7 +69,9 @@ class LaunchDialog(QDialog):
self.setLayout(self.layout)
def login(self):
self.hide()
if LoginDialog(core=self.core).login():
self.show()
self.login_thread.start()
else:
exit(0)

View file

@ -8,17 +8,29 @@ from rare.components.tab_widget import TabWidget
class MainWindow(QMainWindow):
def __init__(self, core):
super(MainWindow, self).__init__()
self.setGeometry(0, 0, 1200, 800)
settings = QSettings()
width, height = 1200, 800
if settings.value("save_size", False):
width, height = settings.value("window_size", (1200, 800), tuple)
self.setGeometry(0, 0, width, height)
self.setWindowTitle("Rare - GUI for legendary")
self.tab_widget = TabWidget(core)
self.setCentralWidget(self.tab_widget)
self.show()
def closeEvent(self, e: QCloseEvent):
if QSettings().value("sys_tray", True, bool):
settings = QSettings()
if settings.value("sys_tray", True, bool):
self.hide()
e.ignore()
elif self.tab_widget.downloadTab.active_game is not None and QMessageBox.question(self, "Close", self.tr("There is a download active. Do you really want to exit app?"), QMessageBox.Yes, QMessageBox.No) == QMessageBox.Yes:
e.accept()
else:
e.accept()
return
elif self.tab_widget.downloadTab.active_game is not None:
if not QMessageBox.question(self, "Close", self.tr("There is a download active. Do you really want to exit app?"), QMessageBox.Yes, QMessageBox.No) == QMessageBox.Yes:
e.ignore()
return
if settings.value("save_size", False, bool):
size = self.size().width(), self.size().height()
settings.setValue("window_size", size)
e.accept()

View file

@ -1,10 +1,8 @@
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QTabBar, QToolButton, QMenu, QWidgetAction
from PyQt5.QtWidgets import QTabBar, QToolButton
from qtawesome import icon
from rare.components.tabs.account import MiniWidget
class TabBar(QTabBar):
def __init__(self, expanded):
@ -25,14 +23,10 @@ class TabBar(QTabBar):
class TabButtonWidget(QToolButton):
def __init__(self, core):
def __init__(self, core, button_icon: str, tool_tip: str):
super(TabButtonWidget, self).__init__()
self.setText("Icon")
self.setPopupMode(QToolButton.InstantPopup)
self.setIcon(icon("mdi.account-circle", color="white", scale_factor=1.25))
self.setToolTip("Account")
self.setIcon(icon(button_icon, color="white", scale_factor=1.25))
self.setToolTip(tool_tip)
self.setIconSize(QSize(25, 25))
self.setMenu(QMenu())
action = QWidgetAction(self)
action.setDefaultWidget(MiniWidget(core))
self.menu().addAction(action)

View file

@ -1,8 +1,11 @@
import webbrowser
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QTabWidget, QWidget
from PyQt5.QtWidgets import QMenu, QTabWidget, QWidget, QWidgetAction
from qtawesome import icon
from rare.components.tab_utils import TabBar, TabButtonWidget
from rare.components.tabs.account import MiniWidget
from rare.components.tabs.cloud_saves import SyncSaves
from rare.components.tabs.downloads.__init__ import DownloadTab
from rare.components.tabs.games import GameTab
@ -34,6 +37,11 @@ class TabWidget(QTabWidget):
self.cloud_saves = SyncSaves(core)
self.addTab(self.cloud_saves, "Cloud Saves")
self.cloud_saves.finished.connect(self.finished_sync)
self.game_list.default_widget.game_list.sync_cloud.connect(
lambda app_name: self.cloud_saves.sync_game(app_name, True))
# Space Tab
self.addTab(QWidget(), "")
self.setTabEnabled(disabled_tab, False)
@ -45,7 +53,17 @@ class TabWidget(QTabWidget):
self.addTab(self.settings, icon("fa.gear", color='white'), "(!)" if self.settings.about.update_available else "")
self.setIconSize(QSize(25, 25))
self.tabBar().setTabButton(3, self.tabBar().RightSide, TabButtonWidget(core))
store_button = TabButtonWidget(core, 'fa.shopping-cart', 'Epic Games Store')
store_button.pressed.connect(lambda: webbrowser.open("https://www.epicgames.com/store"))
self.tabBar().setTabButton(3, self.tabBar().RightSide, store_button)
account_action = QWidgetAction(self)
account_action.setDefaultWidget(MiniWidget(core))
account_button = TabButtonWidget(core, 'mdi.account-circle', 'Account')
account_button.setMenu(QMenu())
account_button.menu().addAction(account_action)
self.tabBar().setTabButton(4, self.tabBar().RightSide, account_button)
def dl_finished(self):
self.game_list.default_widget.game_list.update_list()
@ -54,3 +72,8 @@ class TabWidget(QTabWidget):
def resizeEvent(self, event):
self.tabBar().setMinimumWidth(self.width())
super(TabWidget, self).resizeEvent(event)
def finished_sync(self, app_name):
self.game_list.default_widget.game_list.widgets[app_name][0].info_text = ""
self.game_list.default_widget.game_list.widgets[app_name][0].info_label.setText("")
self.game_list.default_widget.game_list.widgets[app_name][1].info_label.setText("")

View file

@ -37,4 +37,4 @@ class MiniWidget(QWidget):
if reply == QMessageBox.Yes:
self.core.lgd.invalidate_userdata()
# restart app
QCoreApplication.instance().exit_action(-133742) # restart exit code
QCoreApplication.instance().exit(-133742) # restart exit code

View file

@ -3,11 +3,11 @@ from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtWidgets import *
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import SaveGameStatus
from rare.components.dialogs.path_input_dialog import PathInputDialog
from rare.components.tabs.cloud_saves.sync_widget import SyncWidget
from rare.utils.extra_widgets import WaitingSpinner
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import SaveGameStatus
logger = getLogger("Sync Saves")
@ -25,6 +25,7 @@ class LoadThread(QThread):
class SyncSaves(QScrollArea):
finished = pyqtSignal(str)
def __init__(self, core: LegendaryCore):
super(SyncSaves, self).__init__()
@ -32,7 +33,7 @@ class SyncSaves(QScrollArea):
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.load_saves()
def load_saves(self):
def load_saves(self, app_name=None, auto=False):
self.widget = QWidget()
layout = QVBoxLayout()
layout.addWidget(WaitingSpinner())
@ -42,11 +43,11 @@ class SyncSaves(QScrollArea):
self.setWidget(self.widget)
self.start_thread = LoadThread(self.core)
self.start_thread.signal.connect(self.setup_ui)
self.start_thread.signal.connect(lambda x: self.setup_ui(x, app_name, auto))
self.start_thread.start()
self.igames = self.core.get_installed_list()
def setup_ui(self, saves: list):
def setup_ui(self, saves: list, app_name, auto=False):
self.start_thread.disconnect()
self.main_layout = QVBoxLayout()
self.title = QLabel(
@ -93,10 +94,41 @@ class SyncSaves(QScrollArea):
self.setWidgetResizable(True)
self.setWidget(self.widget)
def reload(self):
if auto:
self.save(app_name, True)
def reload(self, app_name, auto=False):
self.finished.emit(app_name)
self.setWidget(QWidget())
self.load_saves()
self.update()
self.load_saves(auto)
def sync_game(self, app_name, from_game_finish_auto=True):
self.setWidget(QWidget())
self.load_saves(app_name, from_game_finish_auto)
def save(self, app_name, from_game_finish_auto=True):
for w in self.widgets:
if w.igame.app_name == app_name:
widget = w
break
else:
logger.warning("An Error occurred. Game does not support cloud saves")
return
if widget.res == SaveGameStatus.SAME_AGE:
logger.info("Game is up to date")
elif widget.res == SaveGameStatus.LOCAL_NEWER:
widget.upload()
elif widget.res == SaveGameStatus.REMOTE_NEWER:
if from_game_finish_auto:
if QMessageBox.question(self, "Really", self.tr("You finished playing game, but Remote game is newer. "
"Do you want to download anyway? This could remove "
"your game progress. Please check your save path or "
"make a backup"),
QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes:
widget.download()
else:
logger.info("Cancel Download")
def sync_all(self):
logger.info("Sync all Games")

View file

@ -38,7 +38,7 @@ class _DownloadThread(QThread):
class SyncWidget(QGroupBox):
reload = pyqtSignal()
reload = pyqtSignal(str)
def __init__(self, igame: InstalledGame, save, core: LegendaryCore):
super(SyncWidget, self).__init__(igame.title)
@ -155,7 +155,7 @@ class SyncWidget(QGroupBox):
self.igame.save_path = path
self.core.lgd.set_installed_game(self.igame.app_name, self.igame)
self.save_path_text.setText(self.igame.save_path)
self.reload.emit()
self.reload.emit(self.game.app_name)
def upload(self):
self.logger.info("Uploading Saves for game " + self.igame.title)
@ -169,7 +169,7 @@ class SyncWidget(QGroupBox):
def uploaded(self):
self.info_text.setText(self.tr("Upload finished"))
self.upload_button.setDisabled(False)
self.reload.emit()
self.reload.emit(self.game.app_name)
def download(self):
if not os.path.exists(self.igame.save_path):
@ -187,4 +187,4 @@ class SyncWidget(QGroupBox):
self.upload_button.setDisabled(True)
self.download_button.setDisabled(True)
self.download_button.setStyleSheet("QPushButton{background-color: black}")
self.reload.emit()
self.reload.emit(self.game.app_name)

View file

@ -211,6 +211,8 @@ class DownloadTab(QWidget):
if self.active_game.app_name in self.update_widgets.keys():
self.update_widgets[self.active_game.app_name].setVisible(False)
self.update_widgets.pop(self.active_game.app_name)
if len(self.update_widgets) == 0:
self.update_text.setVisible(True)
self.active_game = None
@ -272,7 +274,11 @@ class UpdateWidget(QWidget):
self.layout.addWidget(self.title)
self.update_button = QPushButton(self.tr("Update Game"))
self.update_button.clicked.connect(lambda: self.update.emit(game.app_name))
self.update_button.clicked.connect(self.update_game)
self.layout.addWidget(self.update_button)
self.setLayout(self.layout)
def update_game(self):
self.update_button.setDisabled(True)
self.update.emit(self.game.app_name)

View file

@ -41,8 +41,8 @@ class DlWidget(QWidget):
self.size = QHBoxLayout()
self.size.addWidget(QLabel(self.tr("Download size: {} GB").format(dl_size / 1024 ** 3)))
self.size.addWidget(QLabel(self.tr("Install size: {} GB").format(install_size / 1024 ** 3)))
self.size.addWidget(QLabel(self.tr("Download size: {} GB").format(round(dl_size / 1024 ** 3, 2))))
self.size.addWidget(QLabel(self.tr("Install size: {} GB").format(round(install_size / 1024 ** 3, 2))))
self.right_layout.addLayout(self.size)
self.delete = QPushButton(self.tr("Remove Download"))

View file

@ -3,7 +3,7 @@ import os
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QPixmap, QKeyEvent
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QLabel, QHBoxLayout, QTabWidget, QMessageBox, \
QProgressBar, QStackedWidget, QGroupBox
QProgressBar, QStackedWidget, QGroupBox, QScrollArea
from qtawesome import icon
from rare.components.dialogs.uninstall_dialog import UninstallDialog
@ -42,7 +42,7 @@ class InfoTabs(QTabWidget):
self.parent().layout.setCurrentIndex(0)
class GameInfo(QWidget):
class GameInfo(QScrollArea):
igame: InstalledGame
game: Game
update_list = pyqtSignal()
@ -51,12 +51,14 @@ class GameInfo(QWidget):
def __init__(self, core: LegendaryCore):
super(GameInfo, self).__init__()
self.widget = QWidget()
self.core = core
self.layout = QVBoxLayout()
self.setWidgetResizable(True)
# TODO More Information: Image text settings needs_verification platform
top_layout = QHBoxLayout()
# No Game at start. Game is set when clicked info
self.image = QLabel()
top_layout.addWidget(self.image)
@ -86,7 +88,6 @@ class GameInfo(QWidget):
top_layout.addLayout(right_layout)
top_layout.addStretch()
self.game_actions = GameActions()
self.game_actions.uninstall_button.clicked.connect(self.uninstall)
@ -96,7 +97,8 @@ class GameInfo(QWidget):
self.layout.addLayout(top_layout)
self.layout.addWidget(self.game_actions)
self.layout.addStretch()
self.setLayout(self.layout)
self.widget.setLayout(self.layout)
self.setWidget(self.widget)
def uninstall(self):
infos = UninstallDialog(self.game).get_information()

View file

@ -1,23 +1,29 @@
import os
from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QComboBox, QFileDialog, QPushButton, QMessageBox, QLineEdit
from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QComboBox, QFileDialog, QPushButton, QMessageBox, QLineEdit, \
QScrollArea, QCheckBox
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame, Game
from rare.components.tabs.settings.linux import LinuxSettings
from rare.components.tabs.settings.settings_widget import SettingsWidget
from rare.utils.extra_widgets import PathEdit
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame, Game
class GameSettings(QWidget):
class GameSettings(QScrollArea):
game: Game
igame: InstalledGame
# variable to no update when changing game
change = False
def __init__(self, core: LegendaryCore):
super(GameSettings, self).__init__()
self.core = core
self.widget = QWidget()
self.settings = QSettings()
self.setWidgetResizable(True)
self.layout = QVBoxLayout()
self.title = QLabel("Error")
self.layout.addWidget(self.title)
@ -33,11 +39,26 @@ class GameSettings(QWidget):
self.layout.addWidget(self.skip_update_widget)
self.skip_update.currentIndexChanged.connect(lambda x: self.update_combobox(x, "skip_update_check"))
self.launch_params = QLineEdit("")
self.launch_params.setPlaceholderText(self.tr("Start parameter"))
self.launch_params_accept_button = QPushButton(self.tr("Save"))
self.launch_params_widget = SettingsWidget(self.tr("Launch parameters"), self.launch_params,
self.launch_params_accept_button)
self.layout.addWidget(self.launch_params_widget)
self.launch_params_accept_button.clicked.connect(lambda: self.save_line_edit("start_params", self.launch_params.text()))
self.cloud_sync = QCheckBox("Sync with cloud")
self.cloud_sync_widget = SettingsWidget(self.tr("Auto sync with cloud"), self.cloud_sync)
self.layout.addWidget(self.cloud_sync_widget)
self.cloud_sync.stateChanged.connect(lambda: self.settings.setValue(f"{self.game.app_name}/auto_sync_cloud",
self.cloud_sync.isChecked()))
self.layout.addWidget(self.offline_widget)
self.wrapper = QLineEdit("")
self.wrapper.setPlaceholderText("Wrapper")
self.wrapper_save_button = QPushButton(self.tr("Save"))
self.wrapper_save_button.clicked.connect(self.update_wrapper)
self.wrapper_save_button.clicked.connect(lambda: self.save_line_edit("wrapper", self.wrapper.text()))
self.wrapper_widget = SettingsWidget(self.tr("Wrapper (e.g. optirun)"), self.wrapper, self.wrapper_save_button)
self.layout.addWidget(self.wrapper_widget)
@ -71,17 +92,18 @@ class GameSettings(QWidget):
# startparams, skip_update_check
self.layout.addStretch(1)
self.setLayout(self.layout)
self.widget.setLayout(self.layout)
self.setWidget(self.widget)
def update_wrapper(self):
wrapper = self.wrapper.text()
if wrapper != "":
def save_line_edit(self, option, value):
if value != "":
if not self.game.app_name in self.core.lgd.config.sections():
self.core.lgd.config[self.game.app_name] = {}
self.core.lgd.config.set(self.game.app_name, "wrapper", wrapper)
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(f"{self.game.app_name}", "wrapper", fallback="") != "":
self.core.lgd.config.remove_option(self.game.app_name, "wrapper")
if self.game.app_name in self.core.lgd.config.sections() and self.core.lgd.config.get(
f"{self.game.app_name}", option, fallback="") != "":
self.core.lgd.config.remove_option(self.game.app_name, option)
if self.core.lgd.config[self.game.app_name] == {}:
self.core.lgd.config.remove_section(self.game.app_name)
self.core.lgd.save_config()
@ -96,10 +118,10 @@ class GameSettings(QWidget):
if self.core.lgd.config[self.game.app_name] == {}:
self.core.lgd.config.remove_section(self.game.app_name)
elif i == 1:
self.core.lgd.config[self.game.app_name] = {}
self.core.lgd.config.add_section(self.game.app_name)
self.core.lgd.config.set(self.game.app_name, option, "true")
elif i == 2:
self.core.lgd.config[self.game.app_name] = {}
self.core.lgd.config.add_section(self.game.app_name)
self.core.lgd.config.set(self.game.app_name, option, "false")
self.core.lgd.save_config()
@ -189,7 +211,6 @@ class GameSettings(QWidget):
wrapper = self.core.lgd.config.get(self.game.app_name, "wrapper", fallback="")
self.wrapper.setText(wrapper)
self.title.setText(f"<h2>{self.game.app_title}</h2>")
if os.name != "nt":
self.linux_settings.update_game(app_name)
@ -207,6 +228,15 @@ class GameSettings(QWidget):
self.select_proton.setCurrentIndex(0)
self.proton_prefix_widget.setVisible(False)
self.wrapper_widget.setVisible(True)
if not self.game.supports_cloud_saves:
self.cloud_sync_widget.setVisible(False)
else:
self.cloud_sync_widget.setVisible(True)
sync_cloud = self.settings.value(f"{self.game.app_name}/auto_sync_cloud", True, bool)
self.cloud_sync.setChecked(sync_cloud)
self.launch_params.setText(self.core.lgd.config.get(self.game.app_name, "start_params", fallback=""))
self.change = True

View file

@ -21,6 +21,7 @@ class GameList(QStackedWidget):
install_game = pyqtSignal(InstallOptions)
show_game_info = pyqtSignal(str)
update_game = pyqtSignal()
sync_cloud = pyqtSignal(str)
def __init__(self, core: LegendaryCore):
super(GameList, self).__init__()
@ -158,6 +159,16 @@ class GameList(QStackedWidget):
self.widgets[app_name][0].info_label.setText("")
self.widgets[app_name][1].launch_button.setDisabled(False)
self.widgets[app_name][1].launch_button.setText(self.tr("Launch"))
if self.widgets[app_name][0].game.supports_cloud_saves:
if not self.settings.value(f"{app_name}/auto_sync_cloud", True, bool) \
and not self.settings.value("auto_sync_cloud", True, bool):
logger.info("Auto saves disabled")
return
self.sync_cloud.emit(app_name)
self.widgets[app_name][0].info_text = self.tr("Sync CLoud saves")
self.widgets[app_name][0].info_label.setText(self.tr("Sync CLoud saves"))
self.widgets[app_name][1].info_label.setText(self.tr("Sync CLoud saves"))
def launch(self, app_name):
self.widgets[app_name][0].info_text = self.tr("Game running")

View file

@ -3,9 +3,9 @@ from logging import getLogger
from PyQt5.QtCore import pyqtSignal, QProcess, QSettings
from PyQt5.QtWidgets import QGroupBox, QMessageBox
from rare.utils import legendary_utils
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame
from rare.utils import legendary_utils
logger = getLogger("Game")
@ -25,12 +25,8 @@ class BaseInstalledWidget(QGroupBox):
self.game_running = False
self.update_available = self.core.get_asset(self.game.app_name, True).build_version != igame.version
self.setContentsMargins(0, 0, 0, 0)
# self.setStyleSheet("border-radius: 5px")
def launch(self, offline=False, skip_version_check=False):
if QSettings().value("confirm_start", False, bool):
if not QMessageBox.question(self, "Launch", self.tr("Do you want to launch {}").format(self.game.app_title),

View file

@ -82,12 +82,12 @@ class GameWidgetInstalled(BaseInstalledWidget):
self.setFixedWidth(self.sizeHint().width())
def enterEvent(self, a0: QEvent) -> None:
if self.update_available:
self.info_label.setText(self.tr("Start game without version check"))
elif not self.running:
self.info_label.setText("Start Game")
else:
if self.game_running:
self.info_label.setText(self.tr("Game running"))
elif self.update_available:
self.info_label.setText(self.tr("Start game without version check"))
else:
self.info_label.setText("Start Game")
def leaveEvent(self, a0: QEvent) -> None:
if self.running:
@ -105,4 +105,11 @@ class GameWidgetInstalled(BaseInstalledWidget):
# right
elif e.button() == 2:
pass
pass # self.showMenu(e)
"""def showMenu(self, event):
menu = QMenu()
desktop_link = menu.addAction("Add Desktop link")
action = menu.exec_(self.mapToGlobal(event.pos()))
if action == desktop_link:
print("LOL")"""

View file

@ -62,6 +62,9 @@ class InstalledListWidget(BaseInstalledWidget):
self.childLayout.addWidget(self.version_label)
self.childLayout.addWidget(self.size_label)
self.info_label = QLabel("")
self.childLayout.addWidget(self.info_label)
self.childLayout.addStretch(1)
self.layout.addLayout(self.childLayout)
self.layout.addStretch(1)

View file

@ -44,6 +44,7 @@ class ImportWidget(QWidget):
self.gb_layout.addWidget(self.import_game_info)
self.override_app_name_label = QLabel(self.tr("Override app name (Only if imported game from legendary or the app could not find the app name)"))
self.override_app_name_label.setWordWrap(True)
self.app_name_input = QLineEdit()
self.app_name_input.setFixedHeight(32)
minilayout = QHBoxLayout()

View file

@ -6,6 +6,13 @@ from rare import __version__
from rare.utils.utils import get_latest_version
def versiontuple(v):
try:
return tuple(map(int, (v.split("."))))
except:
return tuple((9, 9, 9)) # It is a beta version and newer
class About(QWidget):
def __init__(self):
super(About, self).__init__()
@ -17,14 +24,15 @@ class About(QWidget):
self.version = QLabel("Version: " + __version__)
self.layout.addWidget(self.version)
latest_tag = get_latest_version()
self.update_available = latest_tag != __version__
if latest_tag != __version__:
self.update_available = versiontuple(latest_tag) > versiontuple(__version__)
if self.update_available:
print(f"Update available: {__version__} -> {latest_tag}")
self.update_available = QLabel(self.tr("Update available: {} -> {}").format(__version__, latest_tag))
self.layout.addWidget(self.update_available)
self.open_browser = QPushButton(self.tr("Download latest release"))
self.layout.addWidget(self.open_browser)
self.open_browser.clicked.connect(lambda: webbrowser.open("https://github.com/Dummerle/Rare/releases/latest"))
self.open_browser.clicked.connect(
lambda: webbrowser.open("https://github.com/Dummerle/Rare/releases/latest"))
self.dev = QLabel(self.tr("Developer:") + "<a href='https://github.com/Dummerle'>Dummerle</a>")
self.dev.setToolTip("Github")

View file

@ -13,6 +13,7 @@ class DxvkWidget(QGroupBox):
def __init__(self, core: LegendaryCore):
super(DxvkWidget, self).__init__()
self.core = core
self.setObjectName("settings_widget")
self.dxvk_settings = {
"fps": [False, "Fps"],
"gpuload": [False, self.tr("GPU usage")],

View file

@ -26,9 +26,9 @@ class RareSettings(QScrollArea):
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setWidgetResizable(True)
self.layout = QVBoxLayout()
settings = QSettings()
img_dir = settings.value("img_dir", os.path.expanduser("~/.cache/rare/images/"), type=str)
language = settings.value("language", get_lang(), type=str)
self.settings = QSettings()
img_dir = self.settings.value("img_dir", os.path.expanduser("~/.cache/rare/images/"), type=str)
language = self.settings.value("language", get_lang(), type=str)
# select Image dir
self.select_path = PathEdit(img_dir, type_of_file=QFileDialog.DirectoryOnly)
self.select_path.text_edit.textChanged.connect(lambda t: self.save_path_button.setDisabled(False))
@ -50,36 +50,48 @@ class RareSettings(QScrollArea):
self.layout.addWidget(self.lang_widget)
self.exit_to_sys_tray = QCheckBox(self.tr("Hide to System Tray Icon"))
self.exit_to_sys_tray.setChecked(settings.value("sys_tray", True, bool))
self.exit_to_sys_tray.stateChanged.connect(lambda x: self.update_checkbox(x, "sys_tray"))
self.exit_to_sys_tray.setChecked(self.settings.value("sys_tray", True, bool))
self.exit_to_sys_tray.stateChanged.connect(lambda: self.settings.setValue("sys_tray", self.exit_to_sys_tray.isChecked()))
self.sys_tray_widget = SettingsWidget(self.tr("Exit to System Tray Icon"), self.exit_to_sys_tray)
self.layout.addWidget(self.sys_tray_widget)
self.game_start_accept = QCheckBox(self.tr("Confirm launch of game"))
self.game_start_accept.stateChanged.connect(lambda x: self.update_checkbox(x, "confirm_start"))
self.game_start_accept.stateChanged.connect(lambda x: self.settings.setValue("confirm_start", self.game_start_accept.isChecked()))
self.game_start_accept_widget = SettingsWidget(self.tr("Confirm launch of game"), self.game_start_accept)
self.layout.addWidget(self.game_start_accept_widget)
self.cloud_sync = QCheckBox(self.tr("Sync with cloud"))
self.cloud_sync.setChecked(self.settings.value("auto_sync_cloud", True, bool))
self.cloud_sync_widget = SettingsWidget(self.tr("Auto sync with cloud"), self.cloud_sync)
self.layout.addWidget(self.cloud_sync_widget)
self.cloud_sync.stateChanged.connect(lambda: self.settings.setValue(f"auto_sync_cloud",
self.cloud_sync.isChecked()))
self.save_size = QCheckBox(self.tr("Save size"))
self.save_size.setChecked(self.settings.value("save_size", False, bool))
self.save_size_widget = SettingsWidget(self.tr("Save size of window after restart"), self.save_size)
self.layout.addWidget(self.save_size_widget)
self.save_size.stateChanged.connect(self.save_window_size)
self.layout.addWidget(self.save_size_widget)
self.layout.addStretch()
self.widget.setLayout(self.layout)
self.setWidget(self.widget)
def update_checkbox(self, checked, setting_name):
settings = QSettings()
settings.setValue(setting_name, checked != 0)
def save_window_size(self):
self.settings.setValue("save_size", self.save_size.isChecked())
self.settings.remove("window_size")
def save_path(self):
self.save_path_button.setDisabled(True)
self.update_path()
def update_lang(self, i: int):
settings = QSettings()
settings.setValue("language", languages[i][0])
self.settings.setValue("language", languages[i][0])
self.lang_widget.info_text.setText(self.tr("Restart Application to activate changes"))
def update_path(self):
settings = QSettings()
old_path = settings.value("img_dir", type=str)
old_path = self.settings.value("img_dir", type=str)
new_path = self.select_path.text()
if old_path != new_path:
@ -92,4 +104,4 @@ class RareSettings(QScrollArea):
for i in os.listdir(old_path):
shutil.move(os.path.join(old_path, i), os.path.join(new_path, i))
os.rmdir(old_path)
settings.setValue("img_dir", new_path)
self.settings.setValue("img_dir", new_path)

View file

@ -5,9 +5,6 @@ class SettingsWidget(QGroupBox):
def __init__(self, text: str, widget: QWidget, accept_button: QPushButton = None):
super(SettingsWidget, self).__init__()
self.setObjectName("settings_widget")
self.setStyleSheet("""QGroupBox{border: 1px solid gray;
border-radius: 3px;
margin-top: 1ex;}""")
self.layout = QVBoxLayout()
self.info_text = QLabel("")
self.setTitle(text)

View file

@ -6,7 +6,7 @@ from qtawesome import icon
class TrayIcon(QSystemTrayIcon):
def __init__(self, parent):
super(TrayIcon, self).__init__(parent)
self.setIcon(icon("ei.cogs", color="white"))
self.setIcon(icon("fa.gamepad", color="white")) # TODO change icon to logo
self.setVisible(True)
self.setToolTip("Rare")

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -30,18 +30,27 @@ QTabBar::tab:hover#main_tab_bar {
}
QGroupBox{
QGroupBox {
padding: 4px;
margin: 8px;
}
QGroupBox#game_widget_icon{
QGroupBox#settings_widget {
border: 1px solid gray;
font-size: 13px;
border-radius: 3px;
margin-top: 1ex;
padding-top: 4px;
padding-bottom: 4px;
}
QGroupBox#game_widget_icon {
border: none;
padding: 0;
margin: 0;
}
QGroupBox#group{
QGroupBox#group {
font-size: 15px;
font-weight: bold;
border: 1px solid white;
@ -101,12 +110,12 @@ QCheckBox {
color: #F0F0F0;
background-color: none;
}
QCheckBox::indicator{
QCheckBox::indicator {
}
#list_widget {
border-top: 2px solid white;
}

88
rare/utils/singleton.py Normal file
View file

@ -0,0 +1,88 @@
# https://github.com/pycontribs/tendo/blob/master/tendo/singleton.py
import logging
import os
import sys
import tempfile
logger = logging.getLogger("tendo.singleton")
if sys.platform != "win32":
import fcntl
class SingleInstanceException(BaseException):
pass
class SingleInstance(object):
"""Class that can be instantiated only once per machine.
If you want to prevent your script from running in parallel just instantiate SingleInstance() class. If is there another instance already running it will throw a `SingleInstanceException`.
This option is very useful if you have scripts executed by crontab at small amounts of time.
Remember that this works by creating a lock file with a filename based on the full path to the script file.
Providing a flavor_id will augment the filename with the provided flavor_id, allowing you to create multiple singleton instances from the same file. This is particularly useful if you want specific functions to have their own singleton instances.
"""
def __init__(self, flavor_id="", lockfile=""):
self.initialized = False
if lockfile:
self.lockfile = lockfile
else:
basename = os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace(
"/", "-").replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock'
self.lockfile = os.path.normpath(
tempfile.gettempdir() + '/' + basename)
logger.debug("SingleInstance lockfile: " + self.lockfile)
if sys.platform == 'win32':
try:
# file already exists, we try to remove (in case previous
# execution was interrupted)
if os.path.exists(self.lockfile):
os.unlink(self.lockfile)
self.fd = os.open(
self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR)
except OSError:
type, e, tb = sys.exc_info()
if e.errno == 13:
logger.error(
"Another instance is already running, quitting.")
raise SingleInstanceException()
print(e.errno)
raise
else: # non Windows
self.fp = open(self.lockfile, 'w')
self.fp.flush()
try:
fcntl.lockf(self.fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
logger.warning(
"Another instance is already running, quitting.")
raise SingleInstanceException()
self.initialized = True
def __del__(self):
if not self.initialized:
return
try:
if sys.platform == 'win32':
if hasattr(self, 'fd'):
os.close(self.fd)
os.unlink(self.lockfile)
else:
fcntl.lockf(self.fp, fcntl.LOCK_UN)
# os.close(self.fp)
if os.path.isfile(self.lockfile):
os.unlink(self.lockfile)
except Exception as e:
if logger:
logger.warning(e)
else:
print("Unloggable error: %s" % e)
sys.exit(-1)

2
start.sh Normal file → Executable file
View file

@ -1,2 +1,2 @@
export PYTHONPATH=$PWD
python3 Rare
python3 rare