Merge branch 'main' of github.com:Dummerle/Rare
This commit is contained in:
commit
089bde1210
|
@ -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__':
|
||||
|
|
|
@ -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:
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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("")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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"))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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")"""
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")],
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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
88
rare/utils/singleton.py
Normal 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)
|
Loading…
Reference in a new issue