Import Games, Toggle list/iconview Some other nice stuff
This commit is contained in:
parent
9f28666d66
commit
a616bc67a3
15 changed files with 553 additions and 141 deletions
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (2)" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9" project-jdk-type="Python SDK" />
|
||||||
</project>
|
</project>
|
|
@ -6,7 +6,7 @@ from Rare.Components.TabWidget import TabWidget
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
def __init__(self, core):
|
def __init__(self, core):
|
||||||
super(MainWindow, self).__init__()
|
super(MainWindow, self).__init__()
|
||||||
self.setGeometry(0, 0, 1000, 800)
|
self.setGeometry(0, 0, 1200, 800)
|
||||||
self.setWindowTitle("Rare - GUI for legendary")
|
self.setWindowTitle("Rare - GUI for legendary")
|
||||||
self.setCentralWidget(TabWidget(core))
|
self.setCentralWidget(TabWidget(core))
|
||||||
self.show()
|
self.show()
|
||||||
|
|
|
@ -9,6 +9,7 @@ from Rare.Components.Tabs.Account.AccountWidget import MiniWidget
|
||||||
from Rare.Components.Tabs.Downloads.DownloadTab import DownloadTab
|
from Rare.Components.Tabs.Downloads.DownloadTab import DownloadTab
|
||||||
from Rare.Components.Tabs.Games.GamesTab import GameTab
|
from Rare.Components.Tabs.Games.GamesTab import GameTab
|
||||||
from Rare.Components.Tabs.Settings.SettingsTab import SettingsTab
|
from Rare.Components.Tabs.Settings.SettingsTab import SettingsTab
|
||||||
|
from Rare.utils.Models import InstallOptions
|
||||||
|
|
||||||
|
|
||||||
class TabWidget(QTabWidget):
|
class TabWidget(QTabWidget):
|
||||||
|
@ -26,6 +27,10 @@ class TabWidget(QTabWidget):
|
||||||
self.downloadTab.finished.connect(self.game_list.default_widget.game_list.update_list)
|
self.downloadTab.finished.connect(self.game_list.default_widget.game_list.update_list)
|
||||||
self.game_list.default_widget.game_list.install_game.connect(lambda x: self.downloadTab.install_game(x))
|
self.game_list.default_widget.game_list.install_game.connect(lambda x: self.downloadTab.install_game(x))
|
||||||
|
|
||||||
|
self.game_list.game_info.info.verify_game.connect(lambda app_name: self.downloadTab.install_game(InstallOptions(app_name, core.get_installed_game(app_name).install_path, repair=True)))
|
||||||
|
|
||||||
|
self.tabBarClicked.connect(lambda x: self.game_list.layout.setCurrentIndex(0) if x==0 else None)
|
||||||
|
|
||||||
# Commented, because it is not finished
|
# Commented, because it is not finished
|
||||||
# self.cloud_saves = SyncSaves(core)
|
# self.cloud_saves = SyncSaves(core)
|
||||||
# self.addTab(self.cloud_saves, "Cloud Saves")
|
# self.addTab(self.cloud_saves, "Cloud Saves")
|
||||||
|
|
|
@ -10,6 +10,7 @@ from legendary.models.game import Game
|
||||||
from notifypy import Notify
|
from notifypy import Notify
|
||||||
|
|
||||||
from Rare.Components.Dialogs.InstallDialog import InstallInfoDialog
|
from Rare.Components.Dialogs.InstallDialog import InstallInfoDialog
|
||||||
|
from Rare.utils.LegendaryApi import VerifyThread
|
||||||
from Rare.utils.Models import InstallOptions
|
from Rare.utils.Models import InstallOptions
|
||||||
|
|
||||||
logger = getLogger("Download")
|
logger = getLogger("Download")
|
||||||
|
@ -18,11 +19,13 @@ logger = getLogger("Download")
|
||||||
class DownloadThread(QThread):
|
class DownloadThread(QThread):
|
||||||
status = pyqtSignal(str)
|
status = pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, dlm, core: LegendaryCore, igame):
|
def __init__(self, dlm, core: LegendaryCore, igame, repair=False, repair_file=None):
|
||||||
super(DownloadThread, self).__init__()
|
super(DownloadThread, self).__init__()
|
||||||
self.dlm = dlm
|
self.dlm = dlm
|
||||||
self.core = core
|
self.core = core
|
||||||
self.igame = igame
|
self.igame = igame
|
||||||
|
self.repair = repair
|
||||||
|
self.repair_file = repair_file
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
@ -43,6 +46,7 @@ class DownloadThread(QThread):
|
||||||
postinstall = self.core.install_game(self.igame)
|
postinstall = self.core.install_game(self.igame)
|
||||||
if postinstall:
|
if postinstall:
|
||||||
self._handle_postinstall(postinstall, self.igame)
|
self._handle_postinstall(postinstall, self.igame)
|
||||||
|
|
||||||
dlcs = self.core.get_dlc_for_game(self.igame.app_name)
|
dlcs = self.core.get_dlc_for_game(self.igame.app_name)
|
||||||
if dlcs:
|
if dlcs:
|
||||||
print('The following DLCs are available for this game:')
|
print('The following DLCs are available for this game:')
|
||||||
|
@ -55,6 +59,19 @@ class DownloadThread(QThread):
|
||||||
if game.supports_cloud_saves and not game.is_dlc:
|
if game.supports_cloud_saves and not game.is_dlc:
|
||||||
logger.info('This game supports cloud saves, syncing is handled by the "sync-saves" command.')
|
logger.info('This game supports cloud saves, syncing is handled by the "sync-saves" command.')
|
||||||
logger.info(f'To download saves for this game run "legendary sync-saves {game.app_name}"')
|
logger.info(f'To download saves for this game run "legendary sync-saves {game.app_name}"')
|
||||||
|
old_igame = self.core.get_installed_game(game.app_name)
|
||||||
|
if old_igame and self.repair and os.path.exists(self.repair_file):
|
||||||
|
if old_igame.needs_verification:
|
||||||
|
old_igame.needs_verification = False
|
||||||
|
self.core.install_game(old_igame)
|
||||||
|
|
||||||
|
logger.debug('Removing repair file.')
|
||||||
|
os.remove(self.repair_file)
|
||||||
|
if old_igame and old_igame.install_tags != self.igame.install_tags:
|
||||||
|
old_igame.install_tags = self.igame.install_tags
|
||||||
|
self.logger.info('Deleting now untagged files.')
|
||||||
|
self.core.uninstall_tag(old_igame)
|
||||||
|
self.core.install_game(old_igame)
|
||||||
|
|
||||||
self.status.emit("finish")
|
self.status.emit("finish")
|
||||||
|
|
||||||
|
@ -137,28 +154,51 @@ class DownloadTab(QWidget):
|
||||||
igame = self.core.get_installed_game(options.app_name)
|
igame = self.core.get_installed_game(options.app_name)
|
||||||
if igame.needs_verification and not options.repair:
|
if igame.needs_verification and not options.repair:
|
||||||
options.repair = True
|
options.repair = True
|
||||||
|
repair_file = None
|
||||||
|
if options.repair:
|
||||||
|
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{options.app_name}.repair')
|
||||||
|
|
||||||
if not game:
|
if not game:
|
||||||
QMessageBox.warning(self, "Error", "Could not find Game in your library")
|
QMessageBox.warning(self, "Error", "Could not find Game in your library")
|
||||||
return
|
return
|
||||||
|
|
||||||
if game.is_dlc:
|
if game.is_dlc:
|
||||||
|
logger.info("DLCs are currently not supported")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if game.is_dlc:
|
||||||
|
logger.info('Install candidate is DLC')
|
||||||
|
app_name = game.metadata['mainGameItem']['releaseInfo'][0]['appId']
|
||||||
|
base_game = self.core.get_game(app_name)
|
||||||
|
# check if base_game is actually installed
|
||||||
|
if not self.core.is_installed(app_name):
|
||||||
|
# download mode doesn't care about whether or not something's installed
|
||||||
|
logger.error("Base Game is not installed")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
base_game = None
|
||||||
|
|
||||||
if options.repair:
|
if options.repair:
|
||||||
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{options.app_name}.repair')
|
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{options.app_name}.repair')
|
||||||
|
|
||||||
if not self.core.is_installed(game.app_name):
|
if not self.core.is_installed(game.app_name):
|
||||||
return
|
return
|
||||||
|
|
||||||
if not os.path.exists(repair_file):
|
if not os.path.exists(repair_file):
|
||||||
logger.info("Game has not been verified yet")
|
logger.info("Game has not been verified yet")
|
||||||
|
if QMessageBox.question(self, "Verify", "Game has not been verified yet. Do you want to verify first?",
|
||||||
|
QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes:
|
||||||
|
self.verify_thread = VerifyThread(self.core, game.app_name)
|
||||||
|
self.verify_thread.finished.connect(
|
||||||
|
lambda: self.prepare_download(game, options, base_game, repair_file))
|
||||||
|
self.verify_thread.start()
|
||||||
return
|
return
|
||||||
self.repair()
|
self.prepare_download(game, options, base_game, repair_file)
|
||||||
|
|
||||||
|
def prepare_download(self, game, options, base_game, repair_file):
|
||||||
dlm, analysis, igame = self.core.prepare_download(
|
dlm, analysis, igame = self.core.prepare_download(
|
||||||
game=game,
|
game=game,
|
||||||
base_path=options.path,
|
base_path=options.path,
|
||||||
max_workers=options.max_workers)
|
max_workers=options.max_workers, base_game=base_game, repair=options.repair)
|
||||||
if not analysis.dl_size:
|
if not analysis.dl_size:
|
||||||
QMessageBox.information(self, "Warning", self.tr("Download size is 0. Game already exists"))
|
QMessageBox.information(self, "Warning", self.tr("Download size is 0. Game already exists"))
|
||||||
return
|
return
|
||||||
|
@ -182,7 +222,7 @@ class DownloadTab(QWidget):
|
||||||
self.tr("Installation failed. See logs for more information"))
|
self.tr("Installation failed. See logs for more information"))
|
||||||
return
|
return
|
||||||
self.active_game = game
|
self.active_game = game
|
||||||
self.thread = DownloadThread(dlm, self.core, igame)
|
self.thread = DownloadThread(dlm, self.core, igame, options.repair, repair_file)
|
||||||
self.thread.status.connect(self.status)
|
self.thread.status.connect(self.status)
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
|
@ -192,7 +232,7 @@ class DownloadTab(QWidget):
|
||||||
elif text == "finish":
|
elif text == "finish":
|
||||||
notification = Notify()
|
notification = Notify()
|
||||||
notification.title = self.tr("Installation finished")
|
notification.title = self.tr("Installation finished")
|
||||||
notification.message = self.tr("Download of game ")+self.active_game.app_title
|
notification.message = self.tr("Download of game ") + self.active_game.app_title
|
||||||
notification.send()
|
notification.send()
|
||||||
# QMessageBox.information(self, "Info", "Download finished")
|
# QMessageBox.information(self, "Info", "Download finished")
|
||||||
self.finished.emit()
|
self.finished.emit()
|
||||||
|
@ -204,7 +244,6 @@ class DownloadTab(QWidget):
|
||||||
print("Update ", app_name)
|
print("Update ", app_name)
|
||||||
self.install_game(InstallOptions(app_name))
|
self.install_game(InstallOptions(app_name))
|
||||||
|
|
||||||
|
|
||||||
def repair(self):
|
def repair(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,13 @@ import os
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, pyqtSignal
|
from PyQt5.QtCore import Qt, pyqtSignal
|
||||||
from PyQt5.QtGui import QPixmap
|
from PyQt5.QtGui import QPixmap
|
||||||
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QLabel, QHBoxLayout, QTabWidget, QMessageBox
|
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QLabel, QHBoxLayout, QTabWidget, QMessageBox, \
|
||||||
|
QProgressBar, QStackedWidget
|
||||||
from legendary.core import LegendaryCore
|
from legendary.core import LegendaryCore
|
||||||
from legendary.models.game import InstalledGame, Game
|
from legendary.models.game import InstalledGame, Game
|
||||||
|
|
||||||
from Rare.utils import LegendaryApi
|
from Rare.utils import LegendaryApi
|
||||||
|
from Rare.utils.LegendaryApi import VerifyThread
|
||||||
from Rare.utils.QtExtensions import SideTabBar
|
from Rare.utils.QtExtensions import SideTabBar
|
||||||
from Rare.utils.utils import IMAGE_DIR
|
from Rare.utils.utils import IMAGE_DIR
|
||||||
|
|
||||||
|
@ -27,6 +29,8 @@ class GameInfo(QWidget):
|
||||||
igame: InstalledGame
|
igame: InstalledGame
|
||||||
game: Game
|
game: Game
|
||||||
update_list = pyqtSignal()
|
update_list = pyqtSignal()
|
||||||
|
verify_game = pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, core: LegendaryCore):
|
def __init__(self, core: LegendaryCore):
|
||||||
super(GameInfo, self).__init__()
|
super(GameInfo, self).__init__()
|
||||||
self.core = core
|
self.core = core
|
||||||
|
@ -67,6 +71,8 @@ class GameInfo(QWidget):
|
||||||
self.game_actions = GameActions()
|
self.game_actions = GameActions()
|
||||||
|
|
||||||
self.game_actions.uninstall_button.clicked.connect(self.uninstall)
|
self.game_actions.uninstall_button.clicked.connect(self.uninstall)
|
||||||
|
self.game_actions.verify_button.clicked.connect(self.verify)
|
||||||
|
self.game_actions.repair_button.clicked.connect(self.repair)
|
||||||
|
|
||||||
self.layout.addLayout(top_layout)
|
self.layout.addLayout(top_layout)
|
||||||
self.layout.addWidget(self.game_actions)
|
self.layout.addWidget(self.game_actions)
|
||||||
|
@ -74,11 +80,39 @@ class GameInfo(QWidget):
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
def uninstall(self):
|
def uninstall(self):
|
||||||
if QMessageBox.question(self, "Uninstall", self.tr("Are you sure to uninstall " + self.game.app_title), QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
|
if QMessageBox.question(self, "Uninstall", self.tr("Are you sure to uninstall " + self.game.app_title),
|
||||||
|
QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
|
||||||
LegendaryApi.uninstall(self.game.app_name, self.core)
|
LegendaryApi.uninstall(self.game.app_name, self.core)
|
||||||
self.update_list.emit()
|
self.update_list.emit()
|
||||||
self.back_button.click()
|
self.back_button.click()
|
||||||
|
|
||||||
|
def repair(self):
|
||||||
|
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{self.game.app_name}.repair')
|
||||||
|
if not os.path.exists(repair_file):
|
||||||
|
QMessageBox.warning(self, "Warning", self.tr(
|
||||||
|
"Repair file does not exist or game does not need a repair. Please verify game first"))
|
||||||
|
return
|
||||||
|
self.verify_game.emit(self.game.app_name)
|
||||||
|
|
||||||
|
def verify(self):
|
||||||
|
self.game_actions.verify_widget.setCurrentIndex(1)
|
||||||
|
self.verify_thread = VerifyThread(self.core, self.game.app_name)
|
||||||
|
self.verify_thread.status.connect(lambda x: self.game_actions.verify_progress_bar.setValue(x[0] * 100 / x[1]))
|
||||||
|
self.verify_thread.summary.connect(self.finish_verify)
|
||||||
|
self.verify_thread.start()
|
||||||
|
|
||||||
|
def finish_verify(self, failed):
|
||||||
|
failed, missing = failed
|
||||||
|
if failed == 0 and missing == 0:
|
||||||
|
QMessageBox.information(self, "Summary", "Game was verified successfully. No missing or corrupt files found")
|
||||||
|
else:
|
||||||
|
ans = QMessageBox.question(self, "Summary", self.tr(
|
||||||
|
'Verification failed, {} file(s) corrupted, {} file(s) are missing. Do you want to repair them?').format(
|
||||||
|
failed, missing), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
||||||
|
if ans == QMessageBox.Yes:
|
||||||
|
self.verify_game.emit(self.game.app_name)
|
||||||
|
self.game_actions.verify_widget.setCurrentIndex(0)
|
||||||
|
|
||||||
def update_game(self, app_name):
|
def update_game(self, app_name):
|
||||||
self.game = self.core.get_game(app_name)
|
self.game = self.core.get_game(app_name)
|
||||||
self.igame = self.core.get_installed_game(app_name)
|
self.igame = self.core.get_installed_game(app_name)
|
||||||
|
@ -112,10 +146,35 @@ class GameActions(QWidget):
|
||||||
self.layout = QVBoxLayout()
|
self.layout = QVBoxLayout()
|
||||||
self.game_actions = QLabel("<h3>Game actions</h3>")
|
self.game_actions = QLabel("<h3>Game actions</h3>")
|
||||||
self.layout.addWidget(self.game_actions)
|
self.layout.addWidget(self.game_actions)
|
||||||
|
|
||||||
uninstall_layout = QHBoxLayout()
|
uninstall_layout = QHBoxLayout()
|
||||||
self.uninstall_game = QLabel(self.tr("Uninstall game"))
|
self.uninstall_game = QLabel(self.tr("Uninstall game"))
|
||||||
uninstall_layout.addWidget(self.uninstall_game)
|
uninstall_layout.addWidget(self.uninstall_game)
|
||||||
self.uninstall_button = QPushButton("Uninstall")
|
self.uninstall_button = QPushButton("Uninstall")
|
||||||
|
self.uninstall_button.setFixedWidth(250)
|
||||||
uninstall_layout.addWidget(self.uninstall_button)
|
uninstall_layout.addWidget(self.uninstall_button)
|
||||||
self.layout.addLayout(uninstall_layout)
|
self.layout.addLayout(uninstall_layout)
|
||||||
|
|
||||||
|
verify_layout = QHBoxLayout()
|
||||||
|
self.verify_game = QLabel(self.tr("Verify Game"))
|
||||||
|
verify_layout.addWidget(self.verify_game)
|
||||||
|
self.verify_widget = QStackedWidget()
|
||||||
|
self.verify_widget.setMaximumHeight(20)
|
||||||
|
self.verify_widget.setFixedWidth(250)
|
||||||
|
self.verify_button = QPushButton("Verify")
|
||||||
|
self.verify_widget.addWidget(self.verify_button)
|
||||||
|
self.verify_progress_bar = QProgressBar()
|
||||||
|
self.verify_progress_bar.setMaximum(100)
|
||||||
|
self.verify_widget.addWidget(self.verify_progress_bar)
|
||||||
|
verify_layout.addWidget(self.verify_widget)
|
||||||
|
self.layout.addLayout(verify_layout)
|
||||||
|
|
||||||
|
repair_layout = QHBoxLayout()
|
||||||
|
repair_info = QLabel("Repair Game")
|
||||||
|
repair_layout.addWidget(repair_info)
|
||||||
|
self.repair_button = QPushButton("Repair")
|
||||||
|
self.repair_button.setFixedWidth(250)
|
||||||
|
repair_layout.addWidget(self.repair_button)
|
||||||
|
self.layout.addLayout(repair_layout)
|
||||||
|
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
|
@ -19,7 +19,6 @@ class GameList(QScrollArea):
|
||||||
super(GameList, self).__init__()
|
super(GameList, self).__init__()
|
||||||
self.core = core
|
self.core = core
|
||||||
self.widgets = []
|
self.widgets = []
|
||||||
|
|
||||||
self.setObjectName("list_widget")
|
self.setObjectName("list_widget")
|
||||||
self.setWidgetResizable(True)
|
self.setWidgetResizable(True)
|
||||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||||
|
@ -88,3 +87,6 @@ class GameList(QScrollArea):
|
||||||
self.setWidget(QWidget())
|
self.setWidget(QWidget())
|
||||||
self.init_ui(icon_view)
|
self.init_ui(icon_view)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def import_game(self):
|
||||||
|
pass
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
from PyQt5.QtCore import QEvent, pyqtSignal, QSettings, QSize
|
from PyQt5.QtCore import QEvent, pyqtSignal, QSettings, QSize, Qt
|
||||||
from PyQt5.QtGui import QPixmap
|
from PyQt5.QtGui import QPixmap
|
||||||
from PyQt5.QtWidgets import *
|
from PyQt5.QtWidgets import *
|
||||||
from legendary.core import LegendaryCore
|
from legendary.core import LegendaryCore
|
||||||
|
@ -22,6 +22,7 @@ class GameWidgetInstalled(QWidget):
|
||||||
def __init__(self, core: LegendaryCore, game: InstalledGame):
|
def __init__(self, core: LegendaryCore, game: InstalledGame):
|
||||||
super(GameWidgetInstalled, self).__init__()
|
super(GameWidgetInstalled, self).__init__()
|
||||||
self.setObjectName("game_widget_parent")
|
self.setObjectName("game_widget_parent")
|
||||||
|
|
||||||
self.layout = QVBoxLayout()
|
self.layout = QVBoxLayout()
|
||||||
self.core = core
|
self.core = core
|
||||||
self.game = game
|
self.game = game
|
||||||
|
@ -54,6 +55,7 @@ class GameWidgetInstalled(QWidget):
|
||||||
self.layout.addWidget(self.image)
|
self.layout.addWidget(self.image)
|
||||||
|
|
||||||
self.title_label = QLabel(f"<h4>{game.title}</h4>")
|
self.title_label = QLabel(f"<h4>{game.title}</h4>")
|
||||||
|
self.title_label.setAutoFillBackground(False)
|
||||||
self.title_label.setWordWrap(True)
|
self.title_label.setWordWrap(True)
|
||||||
self.title_label.setFixedWidth(175)
|
self.title_label.setFixedWidth(175)
|
||||||
minilayout = QHBoxLayout()
|
minilayout = QHBoxLayout()
|
||||||
|
@ -81,9 +83,14 @@ class GameWidgetInstalled(QWidget):
|
||||||
self.layout.addLayout(minilayout)
|
self.layout.addLayout(minilayout)
|
||||||
|
|
||||||
self.info_label = QLabel(self.info_text)
|
self.info_label = QLabel(self.info_text)
|
||||||
|
self.info_label.setAutoFillBackground(False)
|
||||||
self.info_label.setObjectName("info_label")
|
self.info_label.setObjectName("info_label")
|
||||||
self.layout.addWidget(self.info_label)
|
self.layout.addWidget(self.info_label)
|
||||||
|
|
||||||
|
#p = self.palette()
|
||||||
|
#p.setColor(self.backgroundRole(), Qt.red)
|
||||||
|
#self.setPalette(p)
|
||||||
|
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
self.setFixedWidth(self.sizeHint().width())
|
self.setFixedWidth(self.sizeHint().width())
|
||||||
|
|
||||||
|
@ -92,8 +99,13 @@ class GameWidgetInstalled(QWidget):
|
||||||
self.info_label.setText(self.tr("Please update Game"))
|
self.info_label.setText(self.tr("Please update Game"))
|
||||||
elif not self.running:
|
elif not self.running:
|
||||||
self.info_label.setText("Start Game")
|
self.info_label.setText("Start Game")
|
||||||
|
else:
|
||||||
|
self.info_label.setText(self.tr("Game running"))
|
||||||
|
|
||||||
def leaveEvent(self, a0: QEvent) -> None:
|
def leaveEvent(self, a0: QEvent) -> None:
|
||||||
|
if self.running:
|
||||||
|
self.info_label.setText(self.tr("Game running"))
|
||||||
|
else:
|
||||||
self.info_label.setText(self.info_text)
|
self.info_label.setText(self.info_text)
|
||||||
|
|
||||||
def mousePressEvent(self, a0) -> None:
|
def mousePressEvent(self, a0) -> None:
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QLineEdit, QLabel, QPushButton, QStyle, \
|
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QLineEdit, QPushButton, QStackedLayout
|
||||||
QStackedLayout
|
|
||||||
from qtawesome import icon
|
from qtawesome import icon
|
||||||
|
|
||||||
from Rare.Components.Tabs.Games.GameInfo.GameInfo import InfoTabs
|
from Rare.Components.Tabs.Games.GameInfo.GameInfo import InfoTabs
|
||||||
from Rare.Components.Tabs.Games.GameList import GameList
|
from Rare.Components.Tabs.Games.GameList import GameList
|
||||||
from Rare.utils.QtExtensions import Switch
|
from Rare.Components.Tabs.Games.ImportWidget import ImportWidget
|
||||||
|
from Rare.utils.QtExtensions import SelectViewWidget
|
||||||
|
|
||||||
|
|
||||||
class GameTab(QWidget):
|
class GameTab(QWidget):
|
||||||
|
@ -14,14 +14,23 @@ class GameTab(QWidget):
|
||||||
self.layout = QStackedLayout()
|
self.layout = QStackedLayout()
|
||||||
self.default_widget = Games(core)
|
self.default_widget = Games(core)
|
||||||
self.default_widget.game_list.show_game_info.connect(self.show_info)
|
self.default_widget.game_list.show_game_info.connect(self.show_info)
|
||||||
|
self.default_widget.head_bar.import_game.clicked.connect(lambda: self.layout.setCurrentIndex(2))
|
||||||
self.layout.addWidget(self.default_widget)
|
self.layout.addWidget(self.default_widget)
|
||||||
self.game_info = InfoTabs(core)
|
self.game_info = InfoTabs(core)
|
||||||
self.game_info.info.back_button.clicked.connect(lambda: self.layout.setCurrentIndex(0))
|
self.game_info.info.back_button.clicked.connect(lambda: self.layout.setCurrentIndex(0))
|
||||||
self.game_info.info.update_list.connect(
|
self.game_info.info.update_list.connect(self.update_list)
|
||||||
lambda: self.default_widget.game_list.update_list(not self.default_widget.head_bar.view.isChecked()))
|
|
||||||
self.layout.addWidget(self.game_info)
|
self.layout.addWidget(self.game_info)
|
||||||
|
|
||||||
|
self.import_widget = ImportWidget(core)
|
||||||
|
self.layout.addWidget(self.import_widget)
|
||||||
|
self.import_widget.back_button.clicked.connect(lambda: self.layout.setCurrentIndex(0))
|
||||||
|
self.import_widget.update_list.connect(self.update_list)
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
|
def update_list(self):
|
||||||
|
self.default_widget.game_list.update_list(self.default_widget.head_bar.view.isChecked())
|
||||||
|
self.layout.setCurrentIndex(0)
|
||||||
|
|
||||||
def show_info(self, app_name):
|
def show_info(self, app_name):
|
||||||
self.game_info.info.update_game(app_name)
|
self.game_info.info.update_game(app_name)
|
||||||
self.layout.setCurrentIndex(1)
|
self.layout.setCurrentIndex(1)
|
||||||
|
@ -43,12 +52,15 @@ class Games(QWidget):
|
||||||
self.head_bar.installed_only.stateChanged.connect(lambda:
|
self.head_bar.installed_only.stateChanged.connect(lambda:
|
||||||
self.game_list.installed_only(
|
self.game_list.installed_only(
|
||||||
self.head_bar.installed_only.isChecked()))
|
self.head_bar.installed_only.isChecked()))
|
||||||
self.head_bar.refresh_list.clicked.connect(lambda: self.game_list.update_list(not self.head_bar.view.isChecked()))
|
self.head_bar.refresh_list.clicked.connect(
|
||||||
|
lambda: self.game_list.update_list(not self.head_bar.view.isChecked()))
|
||||||
self.layout.addWidget(self.head_bar)
|
self.layout.addWidget(self.head_bar)
|
||||||
self.layout.addWidget(self.game_list)
|
self.layout.addWidget(self.game_list)
|
||||||
# self.layout.addStretch(1)
|
# self.layout.addStretch(1)
|
||||||
self.head_bar.view.toggled.connect(
|
self.head_bar.view.toggled.connect(
|
||||||
lambda: self.game_list.update_list(not self.head_bar.view.isChecked()))
|
lambda: self.game_list.update_list(not self.head_bar.view.isChecked()))
|
||||||
|
|
||||||
|
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,24 +72,25 @@ class GameListHeadBar(QWidget):
|
||||||
self.installed_only = QCheckBox(self.tr("Installed only"))
|
self.installed_only = QCheckBox(self.tr("Installed only"))
|
||||||
self.layout.addWidget(self.installed_only)
|
self.layout.addWidget(self.installed_only)
|
||||||
|
|
||||||
self.layout.addStretch()
|
self.layout.addStretch(1)
|
||||||
|
|
||||||
|
self.import_game = QPushButton(icon("mdi.import", color="white"), self.tr("Import Game"))
|
||||||
|
self.layout.addWidget(self.import_game)
|
||||||
|
|
||||||
|
self.layout.addStretch(1)
|
||||||
|
|
||||||
self.search_bar = QLineEdit()
|
self.search_bar = QLineEdit()
|
||||||
|
self.search_bar.setMinimumWidth(200)
|
||||||
self.search_bar.setPlaceholderText(self.tr("Search Game"))
|
self.search_bar.setPlaceholderText(self.tr("Search Game"))
|
||||||
self.layout.addWidget(self.search_bar)
|
self.layout.addWidget(self.search_bar)
|
||||||
|
|
||||||
self.layout.addStretch()
|
self.layout.addStretch(2)
|
||||||
self.list_view = QLabel(self.tr("List view"))
|
|
||||||
|
|
||||||
self.icon_view = QLabel(self.tr("Icon view"))
|
checked = QSettings().value("icon_view", True, bool)
|
||||||
|
|
||||||
self.view = Switch()
|
self.view = SelectViewWidget(checked)
|
||||||
checked = not QSettings().value("icon_view", True, bool)
|
|
||||||
self.view.setChecked(checked)
|
|
||||||
self.layout.addWidget(self.icon_view)
|
|
||||||
self.layout.addWidget(self.view)
|
self.layout.addWidget(self.view)
|
||||||
self.layout.addWidget(self.list_view)
|
self.layout.addStretch(1)
|
||||||
|
|
||||||
self.refresh_list = QPushButton()
|
self.refresh_list = QPushButton()
|
||||||
self.refresh_list.setIcon(icon("fa.refresh", color="white")) # Reload icon
|
self.refresh_list.setIcon(icon("fa.refresh", color="white")) # Reload icon
|
||||||
self.layout.addWidget(self.refresh_list)
|
self.layout.addWidget(self.refresh_list)
|
||||||
|
|
120
Rare/Components/Tabs/Games/ImportWidget.py
Normal file
120
Rare/Components/Tabs/Games/ImportWidget.py
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import string
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
from PyQt5.QtCore import pyqtSignal
|
||||||
|
from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QPushButton, QVBoxLayout, QFileDialog, QMessageBox
|
||||||
|
from qtawesome import icon
|
||||||
|
|
||||||
|
from Rare.utils import LegendaryApi
|
||||||
|
from Rare.utils.QtExtensions import PathEdit
|
||||||
|
|
||||||
|
logger = getLogger("Import")
|
||||||
|
|
||||||
|
|
||||||
|
class ImportWidget(QWidget):
|
||||||
|
update_list = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, core):
|
||||||
|
super(ImportWidget, self).__init__()
|
||||||
|
self.core = core
|
||||||
|
self.main_layout = QHBoxLayout()
|
||||||
|
self.back_button = QPushButton(icon("mdi.keyboard-backspace", color="white"), self.tr("Back"))
|
||||||
|
self.right_layout = QVBoxLayout()
|
||||||
|
self.right_layout.addWidget(self.back_button)
|
||||||
|
self.right_layout.addStretch(1)
|
||||||
|
self.main_layout.addLayout(self.right_layout)
|
||||||
|
self.back_button.setFixedWidth(75)
|
||||||
|
self.layout = QVBoxLayout()
|
||||||
|
|
||||||
|
self.title = QLabel("<h2>Import Game</h2")
|
||||||
|
self.layout.addWidget(self.title)
|
||||||
|
|
||||||
|
self.import_one_game = QLabel("<h3>Import existing game</h3>")
|
||||||
|
self.layout.addWidget(self.import_one_game)
|
||||||
|
|
||||||
|
self.import_game_info = QLabel(self.tr("Select path to game"))
|
||||||
|
self.layout.addWidget(self.import_game_info)
|
||||||
|
|
||||||
|
self.path_edit = PathEdit(os.path.expanduser("~"), QFileDialog.DirectoryOnly)
|
||||||
|
self.layout.addWidget(self.path_edit)
|
||||||
|
|
||||||
|
self.import_button = QPushButton("Import Game")
|
||||||
|
self.layout.addWidget(self.import_button)
|
||||||
|
self.import_button.clicked.connect(self.import_game)
|
||||||
|
|
||||||
|
self.layout.addStretch(1)
|
||||||
|
|
||||||
|
self.auto_import = QLabel("<h3>Auto import all existing games</h3>")
|
||||||
|
self.layout.addWidget(self.auto_import)
|
||||||
|
self.auto_import_button = QPushButton(self.tr("Import all games from Epic Games Launcher"))
|
||||||
|
self.auto_import_button.clicked.connect(self.import_games_prepare)
|
||||||
|
self.layout.addWidget(self.auto_import_button)
|
||||||
|
self.layout.addStretch(1)
|
||||||
|
|
||||||
|
self.main_layout.addLayout(self.layout)
|
||||||
|
|
||||||
|
self.setLayout(self.main_layout)
|
||||||
|
|
||||||
|
def import_game(self, path=None):
|
||||||
|
if not path:
|
||||||
|
path = self.path_edit.text()
|
||||||
|
if not path.endswith("/"):
|
||||||
|
path = path + "/"
|
||||||
|
|
||||||
|
for i in os.listdir(os.path.join(path, ".egstore")):
|
||||||
|
if i.endswith(".mancpn"):
|
||||||
|
file = path + ".egstore/" + i
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logger.warning("File was not found")
|
||||||
|
return
|
||||||
|
app_name = json.load(open(file, "r"))["AppName"]
|
||||||
|
if LegendaryApi.import_game(self.core, app_name=app_name, path=path):
|
||||||
|
self.update_list.emit()
|
||||||
|
else:
|
||||||
|
logger.warning("Failed to import" + app_name)
|
||||||
|
return
|
||||||
|
|
||||||
|
def auto_import_games(self, game_path):
|
||||||
|
imported = 0
|
||||||
|
if not os.path.exists(game_path):
|
||||||
|
return 0
|
||||||
|
if os.listdir(game_path) == 0:
|
||||||
|
logger.info(f"No Games found in {game_path}")
|
||||||
|
return 0
|
||||||
|
for path in os.listdir(game_path):
|
||||||
|
json_path = game_path + path + "/.egstore"
|
||||||
|
print(json_path)
|
||||||
|
if not os.path.isdir(json_path):
|
||||||
|
logger.info(f"Game at {game_path + path} doesn't exist")
|
||||||
|
continue
|
||||||
|
|
||||||
|
for file in os.listdir(json_path):
|
||||||
|
if file.endswith(".mancpn"):
|
||||||
|
app_name = json.load(open(os.path.join(json_path, file)))["AppName"]
|
||||||
|
if LegendaryApi.import_game(self.core, app_name, game_path + path):
|
||||||
|
imported += 1
|
||||||
|
return imported
|
||||||
|
|
||||||
|
def import_games_prepare(self):
|
||||||
|
# Automatically import from windows
|
||||||
|
imported = 0
|
||||||
|
if os.name == "nt":
|
||||||
|
available_drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
|
||||||
|
for drive in available_drives:
|
||||||
|
path = f"{drive}/Program Files/Epic Games/"
|
||||||
|
if os.path.exists(path):
|
||||||
|
imported += self.auto_import_games(path)
|
||||||
|
|
||||||
|
else:
|
||||||
|
possible_wineprefixes = [os.path.expanduser("~/.wine/"), os.path.expanduser("~/Games/epic-games-store/")]
|
||||||
|
for wine_prefix in possible_wineprefixes:
|
||||||
|
imported += self.auto_import_games(f"{wine_prefix}drive_c/Program Files/Epic Games/")
|
||||||
|
if imported > 0:
|
||||||
|
QMessageBox.information(self, "Imported Games", self.tr(f"Successfully imported {imported} Games"))
|
||||||
|
self.update_list.emit()
|
||||||
|
logger.info("Restarting app to import games")
|
||||||
|
else:
|
||||||
|
QMessageBox.information(self, "Imported Games", "No Games were found")
|
|
@ -30,7 +30,7 @@ class RareSettings(QWidget):
|
||||||
# select Image dir
|
# select Image dir
|
||||||
self.select_path = PathEdit(img_dir, type_of_file=QFileDialog.DirectoryOnly)
|
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))
|
self.select_path.text_edit.textChanged.connect(lambda t: self.save_path_button.setDisabled(False))
|
||||||
self.save_path_button = QPushButton("Save")
|
self.save_path_button = QPushButton(self.tr("Save"))
|
||||||
self.save_path_button.clicked.connect(self.save_path)
|
self.save_path_button.clicked.connect(self.save_path)
|
||||||
self.img_dir = SettingsWidget(self.tr("Image Directory"), self.select_path, self.save_path_button)
|
self.img_dir = SettingsWidget(self.tr("Image Directory"), self.select_path, self.save_path_button)
|
||||||
self.layout.addWidget(self.img_dir)
|
self.layout.addWidget(self.img_dir)
|
||||||
|
@ -46,7 +46,7 @@ class RareSettings(QWidget):
|
||||||
self.select_lang.setCurrentIndex(0)
|
self.select_lang.setCurrentIndex(0)
|
||||||
else:
|
else:
|
||||||
self.select_lang.setCurrentIndex(0)
|
self.select_lang.setCurrentIndex(0)
|
||||||
self.lang_widget = SettingsWidget("Language", self.select_lang)
|
self.lang_widget = SettingsWidget(self.tr("Language"), self.select_lang)
|
||||||
self.select_lang.currentIndexChanged.connect(self.update_lang)
|
self.select_lang.currentIndexChanged.connect(self.update_lang)
|
||||||
self.layout.addWidget(self.lang_widget)
|
self.layout.addWidget(self.lang_widget)
|
||||||
|
|
||||||
|
|
21
Rare/Main.py
21
Rare/Main.py
|
@ -23,6 +23,8 @@ def main():
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
app.setApplicationName("Rare")
|
app.setApplicationName("Rare")
|
||||||
app.setOrganizationName("Rare")
|
app.setOrganizationName("Rare")
|
||||||
|
# app.setQuitOnLastWindowClosed(False)
|
||||||
|
|
||||||
settings = QSettings()
|
settings = QSettings()
|
||||||
# Translator
|
# Translator
|
||||||
translator = QTranslator()
|
translator = QTranslator()
|
||||||
|
@ -35,7 +37,7 @@ def main():
|
||||||
app.installTranslator(translator)
|
app.installTranslator(translator)
|
||||||
# Style
|
# Style
|
||||||
app.setStyleSheet(open(style_path + "RareStyle.qss").read())
|
app.setStyleSheet(open(style_path + "RareStyle.qss").read())
|
||||||
app.setWindowIcon(QIcon(style_path+"Logo.png"))
|
app.setWindowIcon(QIcon(style_path + "Logo.png"))
|
||||||
launch_dialog = LaunchDialog(core)
|
launch_dialog = LaunchDialog(core)
|
||||||
launch_dialog.exec_()
|
launch_dialog.exec_()
|
||||||
mainwindow = MainWindow(core)
|
mainwindow = MainWindow(core)
|
||||||
|
@ -45,3 +47,20 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
"""
|
||||||
|
tray = QSystemTrayIcon()
|
||||||
|
tray.setIcon(icon("fa.gamepad", color="white"))
|
||||||
|
tray.setVisible(True)
|
||||||
|
menu = QMenu()
|
||||||
|
option1 = QAction("Geeks for Geeks")
|
||||||
|
option1.triggered.connect(lambda: app.exec_())
|
||||||
|
option2 = QAction("GFG")
|
||||||
|
menu.addAction(option1)
|
||||||
|
menu.addAction(option2)
|
||||||
|
# To quit the app
|
||||||
|
quit = QAction("Quit")
|
||||||
|
quit.triggered.connect(app.quit)
|
||||||
|
menu.addAction(quit)
|
||||||
|
# Adding options to the System Tray
|
||||||
|
tray.setContextMenu(menu)"""
|
||||||
|
|
Binary file not shown.
|
@ -22,91 +22,160 @@
|
||||||
<context>
|
<context>
|
||||||
<name>DownloadTab</name>
|
<name>DownloadTab</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="102"/>
|
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="121"/>
|
||||||
<source><b>WARNING</b>: The progress bar is not implemented. It is normal, if there is no progress. The progress is visible in console, because Legendary prints output to console. A pull request is active to get output</source>
|
<source><b>WARNING</b>: The progress bar is not implemented. It is normal, if there is no progress. The progress is visible in console, because Legendary prints output to console. A pull request is active to get output</source>
|
||||||
<translation><b>Warnung</b>: Die Anzeige ist noch nicht fertig. Es wird normal sein, wenn dort kein Fortschritt angezeigt wird. Dafür musst du in die Konsole schauen, da es in Legendary noch keine Möglichkeit gibt, die Ausgabe zu verarbeiten. Ein Pull Request ist eingereicht</translation>
|
<translation><b>Warnung</b>: Die Anzeige ist noch nicht fertig. Es wird normal sein, wenn dort kein Fortschritt angezeigt wird. Dafür musst du in die Konsole schauen, da es in Legendary noch keine Möglichkeit gibt, die Ausgabe zu verarbeiten. Ein Pull Request ist eingereicht</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="109"/>
|
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="128"/>
|
||||||
<source>No active Download</source>
|
<source>No active Download</source>
|
||||||
<translation>Keine aktiven Downloads</translation>
|
<translation>Keine aktiven Downloads</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="120"/>
|
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="139"/>
|
||||||
<source>No updates available</source>
|
<source>No updates available</source>
|
||||||
<translation>Keine verfügbaren Updates</translation>
|
<translation>Keine verfügbaren Updates</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="140"/>
|
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="203"/>
|
||||||
<source>Download size is 0. Game already exists</source>
|
<source>Download size is 0. Game already exists</source>
|
||||||
<translation>Downloadgröße ist 0. Spiel exitstiert bereits</translation>
|
<translation>Downloadgröße ist 0. Spiel exitstiert bereits</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="158"/>
|
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="221"/>
|
||||||
<source>Installation failed. See logs for more information</source>
|
<source>Installation failed. See logs for more information</source>
|
||||||
<translation>Installation fehlgeschlagen. Siehe Log für mehr Informationen</translation>
|
<translation>Installation fehlgeschlagen. Siehe Log für mehr Informationen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="171"/>
|
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="234"/>
|
||||||
<source>Installation finished</source>
|
<source>Installation finished</source>
|
||||||
<translation>Installation abgeschlossen</translation>
|
<translation>Installation abgeschlossen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="172"/>
|
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="235"/>
|
||||||
<source>Download of game </source>
|
<source>Download of game </source>
|
||||||
<translation>Download des Spiels </translation>
|
<translation>Download des Spiels </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="176"/>
|
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="239"/>
|
||||||
<source>Installing Game: No active download</source>
|
<source>Installing Game: No active download</source>
|
||||||
<translation>Zu installierendes Spiel: Kein aktiver Download</translation>
|
<translation>Zu installierendes Spiel: Kein aktiver Download</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>GameActions</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/GameInfo/GameInfo.py" line="151"/>
|
||||||
|
<source>Uninstall game</source>
|
||||||
|
<translation>Spiel deinstallieren</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/GameInfo/GameInfo.py" line="159"/>
|
||||||
|
<source>Verify Game</source>
|
||||||
|
<translation>Spieldateien überprüfen</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>GameInfo</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/GameInfo/GameInfo.py" line="83"/>
|
||||||
|
<source>Are you sure to uninstall </source>
|
||||||
|
<translation>Möchstes du das Spiel deinstallieren </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/GameInfo/GameInfo.py" line="92"/>
|
||||||
|
<source>Repair file does not exist or game does not need a repair. Please verify game first</source>
|
||||||
|
<translation>Es ist keine Reperatur nötig oder das Spiel wurde noch nicht verifiziert. Verifiziere das Spiel zu erst</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/GameInfo/GameInfo.py" line="109"/>
|
||||||
|
<source>Verification failed, {} file(s) corrupted, {} file(s) are missing. Do you want to repair them?</source>
|
||||||
|
<translation>Überprüfung fehlgeschlagen, {} Dateien sind falsch, {} Dateien fehlen. Willst du das Spiel reparieren?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/GameInfo/GameInfo.py" line="137"/>
|
||||||
|
<source>Developer: </source>
|
||||||
|
<translation>Entwickler: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/GameInfo/GameInfo.py" line="138"/>
|
||||||
|
<source>Install size: </source>
|
||||||
|
<translation>Installationsgröße: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/GameInfo/GameInfo.py" line="140"/>
|
||||||
|
<source>Install path: </source>
|
||||||
|
<translation>Installations pfad: </translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>GameListHeadBar</name>
|
<name>GameListHeadBar</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Games/GamesTab.py" line="52"/>
|
<location filename="../Components/Tabs/Games/GamesTab.py" line="60"/>
|
||||||
<source>Installed only</source>
|
<source>Installed only</source>
|
||||||
<translation>Nur installierte</translation>
|
<translation>Nur installierte</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Games/GamesTab.py" line="58"/>
|
<location filename="../Components/Tabs/Games/GamesTab.py" line="67"/>
|
||||||
<source>Search Game</source>
|
<source>Search Game</source>
|
||||||
<translation>Spiel suchen</translation>
|
<translation>Spiel suchen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Games/GamesTab.py" line="62"/>
|
<location filename="../Components/Tabs/Games/GamesTab.py" line="71"/>
|
||||||
<source>List view</source>
|
<source>List view</source>
|
||||||
<translation>Listenansicht</translation>
|
<translation>Listenansicht</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Games/GamesTab.py" line="64"/>
|
<location filename="../Components/Tabs/Games/GamesTab.py" line="73"/>
|
||||||
<source>Icon view</source>
|
<source>Icon view</source>
|
||||||
<translation>Iconansicht</translation>
|
<translation>Iconansicht</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>GameWidget</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/InstalledListWidget.py" line="59"/>
|
||||||
|
<source>Launch</source>
|
||||||
|
<translation>Starten</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/InstalledListWidget.py" line="66"/>
|
||||||
|
<source>Developer: </source>
|
||||||
|
<translation>Entwickler: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/InstalledListWidget.py" line="91"/>
|
||||||
|
<source>Game running</source>
|
||||||
|
<translation>Spiel läuft</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>GameWidgetInstalled</name>
|
<name>GameWidgetInstalled</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="35"/>
|
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="36"/>
|
||||||
<source>Update available</source>
|
<source>Update available</source>
|
||||||
<translation>Update verfügbar</translation>
|
<translation>Update verfügbar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="99"/>
|
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="115"/>
|
||||||
<source>Game running</source>
|
<source>Game running</source>
|
||||||
<translation>Spiel läuft</translation>
|
<translation>Spiel läuft</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="108"/>
|
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="108"/>
|
||||||
<source>Do you want to uninstall</source>
|
<source>Do you want to uninstall</source>
|
||||||
<translation>Möchtest du das Spiel deinstallieren: </translation>
|
<translation type="obsolete">Möchtest du das Spiel deinstallieren: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="92"/>
|
||||||
|
<source>Please update Game</source>
|
||||||
|
<translation>Bitte das Spiel updaten</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>GameWidgetUninstalled</name>
|
<name>GameWidgetUninstalled</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Games/GameWidgetUninstalled.py" line="60"/>
|
<location filename="../Components/Tabs/Games/GameWidgetUninstalled.py" line="61"/>
|
||||||
<source>Install Game</source>
|
<source>Install Game</source>
|
||||||
<translation>Spiel installieren</translation>
|
<translation>Spiel installieren</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -176,12 +245,12 @@
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="122"/>
|
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="122"/>
|
||||||
<source>Game info</source>
|
<source>Game info</source>
|
||||||
<translation>Spielinformation</translation>
|
<translation type="obsolete">Spielinformation</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="123"/>
|
<location filename="../Components/Tabs/Games/GameWidgetInstalled.py" line="123"/>
|
||||||
<source>Uninstall</source>
|
<source>Uninstall</source>
|
||||||
<translation>Deinstallieren</translation>
|
<translation type="obsolete">Deinstallieren</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -210,12 +279,12 @@
|
||||||
<context>
|
<context>
|
||||||
<name>PathEdit</name>
|
<name>PathEdit</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../utils/QtExtensions.py" line="131"/>
|
<location filename="../utils/QtExtensions.py" line="132"/>
|
||||||
<source>Select Path</source>
|
<source>Select Path</source>
|
||||||
<translation>Wähle Pfad</translation>
|
<translation>Wähle Pfad</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../utils/QtExtensions.py" line="144"/>
|
<location filename="../utils/QtExtensions.py" line="145"/>
|
||||||
<source>Choose Path</source>
|
<source>Choose Path</source>
|
||||||
<translation>Wähle Pfad</translation>
|
<translation>Wähle Pfad</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -245,6 +314,16 @@
|
||||||
<source>Restart Application to activate changes</source>
|
<source>Restart Application to activate changes</source>
|
||||||
<translation>Starte die App neu um die Änderungen zu aktivieren</translation>
|
<translation>Starte die App neu um die Änderungen zu aktivieren</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Settings/Rare.py" line="33"/>
|
||||||
|
<source>Save</source>
|
||||||
|
<translation>Speichern</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Settings/Rare.py" line="49"/>
|
||||||
|
<source>Language</source>
|
||||||
|
<translation>Sprache</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>SyncSaves</name>
|
<name>SyncSaves</name>
|
||||||
|
@ -385,15 +464,23 @@
|
||||||
<context>
|
<context>
|
||||||
<name>TabWidget</name>
|
<name>TabWidget</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/TabWidget.py" line="21"/>
|
<location filename="../Components/TabWidget.py" line="24"/>
|
||||||
<source>Games</source>
|
<source>Games</source>
|
||||||
<translation>Spiele</translation>
|
<translation>Spiele</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>UninstalledGameWidget</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../Components/Tabs/Games/GameWidgetListUninstalled.py" line="42"/>
|
||||||
|
<source>Install</source>
|
||||||
|
<translation>Installieren</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>UpdateWidget</name>
|
<name>UpdateWidget</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="196"/>
|
<location filename="../Components/Tabs/Downloads/DownloadTab.py" line="263"/>
|
||||||
<source>Update Game</source>
|
<source>Update Game</source>
|
||||||
<translation>Spiel updaten</translation>
|
<translation>Spiel updaten</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
from sys import stdout
|
||||||
|
|
||||||
from PyQt5.QtCore import QProcess, QProcessEnvironment
|
from PyQt5.QtCore import QProcess, QProcessEnvironment, QThread, pyqtSignal
|
||||||
|
from PyQt5.QtWidgets import QMessageBox, QWidget
|
||||||
|
from legendary.core import LegendaryCore
|
||||||
|
from legendary.models.game import VerifyResult
|
||||||
|
from legendary.utils.lfs import validate_files
|
||||||
|
|
||||||
logger = getLogger("Legendary Utils")
|
logger = getLogger("Legendary Utils")
|
||||||
|
|
||||||
|
@ -54,6 +60,99 @@ def uninstall(app_name: str, core):
|
||||||
logger.info(f'Removing "{igame.title}" from "{igame.install_path}"...')
|
logger.info(f'Removing "{igame.title}" from "{igame.install_path}"...')
|
||||||
core.uninstall_game(igame, delete_files=True, delete_root_directory=True)
|
core.uninstall_game(igame, delete_files=True, delete_root_directory=True)
|
||||||
logger.info('Game has been uninstalled.')
|
logger.info('Game has been uninstalled.')
|
||||||
|
shutil.rmtree(igame.install_path)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f'Removing game failed: {e!r}, please remove {igame.install_path} manually.')
|
logger.warning(f'Removing game failed: {e!r}, please remove {igame.install_path} manually.')
|
||||||
|
|
||||||
|
|
||||||
|
class VerifyThread(QThread):
|
||||||
|
status = pyqtSignal(tuple)
|
||||||
|
summary = pyqtSignal(tuple)
|
||||||
|
|
||||||
|
def __init__(self, core, app_name):
|
||||||
|
super(VerifyThread, self).__init__()
|
||||||
|
self.core, self.app_name = core, app_name
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if not self.core.is_installed(self.app_name):
|
||||||
|
logger.error(f'Game "{self.app_name}" is not installed')
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f'Loading installed manifest for "{self.app_name}"')
|
||||||
|
igame = self.core.get_installed_game(self.app_name)
|
||||||
|
manifest_data, _ = self.core.get_installed_manifest(self.app_name)
|
||||||
|
manifest = self.core.load_manifest(manifest_data)
|
||||||
|
|
||||||
|
files = sorted(manifest.file_manifest_list.elements,
|
||||||
|
key=lambda a: a.filename.lower())
|
||||||
|
|
||||||
|
# build list of hashes
|
||||||
|
file_list = [(f.filename, f.sha_hash.hex()) for f in files]
|
||||||
|
total = len(file_list)
|
||||||
|
num = 0
|
||||||
|
failed = []
|
||||||
|
missing = []
|
||||||
|
|
||||||
|
logger.info(f'Verifying "{igame.title}" version "{manifest.meta.build_version}"')
|
||||||
|
repair_file = []
|
||||||
|
for result, path, result_hash in validate_files(igame.install_path, file_list):
|
||||||
|
self.status.emit((num, total))
|
||||||
|
num += 1
|
||||||
|
|
||||||
|
if result == VerifyResult.HASH_MATCH:
|
||||||
|
repair_file.append(f'{result_hash}:{path}')
|
||||||
|
continue
|
||||||
|
elif result == VerifyResult.HASH_MISMATCH:
|
||||||
|
logger.error(f'File does not match hash: "{path}"')
|
||||||
|
repair_file.append(f'{result_hash}:{path}')
|
||||||
|
failed.append(path)
|
||||||
|
elif result == VerifyResult.FILE_MISSING:
|
||||||
|
logger.error(f'File is missing: "{path}"')
|
||||||
|
missing.append(path)
|
||||||
|
else:
|
||||||
|
logger.error(f'Other failure (see log), treating file as missing: "{path}"')
|
||||||
|
missing.append(path)
|
||||||
|
|
||||||
|
stdout.write(f'Verification progress: {num}/{total} ({num * 100 / total:.01f}%)\t\n')
|
||||||
|
|
||||||
|
# always write repair file, even if all match
|
||||||
|
if repair_file:
|
||||||
|
repair_filename = os.path.join(self.core.lgd.get_tmp_path(), f'{self.app_name}.repair')
|
||||||
|
with open(repair_filename, 'w') as f:
|
||||||
|
f.write('\n'.join(repair_file))
|
||||||
|
logger.debug(f'Written repair file to "{repair_filename}"')
|
||||||
|
|
||||||
|
if not missing and not failed:
|
||||||
|
logger.info('Verification finished successfully.')
|
||||||
|
self.summary.emit((0,0))
|
||||||
|
|
||||||
|
else:
|
||||||
|
logger.error(f'Verification failed, {len(failed)} file(s) corrupted, {len(missing)} file(s) are missing.')
|
||||||
|
self.summary.emit((len(failed), len(missing)))
|
||||||
|
|
||||||
|
|
||||||
|
def import_game(core: LegendaryCore, app_name: str, path: str):
|
||||||
|
logger.info("Import " + app_name)
|
||||||
|
game = core.get_game(app_name)
|
||||||
|
manifest, igame = core.import_game(game, path)
|
||||||
|
exe_path = os.path.join(path, manifest.meta.launch_exe.lstrip('/'))
|
||||||
|
total = len(manifest.file_manifest_list.elements)
|
||||||
|
found = sum(os.path.exists(os.path.join(path, f.filename))
|
||||||
|
for f in manifest.file_manifest_list.elements)
|
||||||
|
ratio = found / total
|
||||||
|
if not os.path.exists(exe_path):
|
||||||
|
logger.error(f"Game {game.app_title} failed to import")
|
||||||
|
return False
|
||||||
|
if ratio < 0.95:
|
||||||
|
logger.error(
|
||||||
|
"Game files are missing. It may be not the lates version ore it is corrupt")
|
||||||
|
return False
|
||||||
|
core.install_game(igame)
|
||||||
|
if igame.needs_verification:
|
||||||
|
logger.info(logger.info(
|
||||||
|
f'NOTE: The game installation will have to be verified before it can be updated '
|
||||||
|
f'with legendary. Run "legendary repair {app_name}" to do so.'))
|
||||||
|
|
||||||
|
logger.info("Successfully imported Game: " + game.app_title)
|
||||||
|
return True
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal, QPropertyAnimation, pyqtSlot, QPointF, QEasingCurve, \
|
from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal
|
||||||
QObject, pyqtProperty
|
from PyQt5.QtGui import QMovie
|
||||||
from PyQt5.QtGui import QMovie, QPainter, QPalette, QLinearGradient, QGradient
|
|
||||||
from PyQt5.QtWidgets import QLayout, QStyle, QSizePolicy, QLabel, QFileDialog, QHBoxLayout, QWidget, QLineEdit, \
|
from PyQt5.QtWidgets import QLayout, QStyle, QSizePolicy, QLabel, QFileDialog, QHBoxLayout, QWidget, QLineEdit, \
|
||||||
QPushButton, QStyleOptionTab, QStylePainter, QTabBar, QAbstractButton, QCheckBox
|
QPushButton, QStyleOptionTab, QStylePainter, QTabBar
|
||||||
|
from qtawesome import icon
|
||||||
|
|
||||||
from Rare import style_path
|
from Rare import style_path
|
||||||
|
|
||||||
|
@ -121,8 +121,9 @@ class ClickableLabel(QLabel):
|
||||||
|
|
||||||
|
|
||||||
class PathEdit(QWidget):
|
class PathEdit(QWidget):
|
||||||
def __init__(self, text: str = "", type_of_file: QFileDialog.FileType = QFileDialog.AnyFile, infotext: str = "",
|
def __init__(self, text: str = "",
|
||||||
filter: str = None):
|
type_of_file: QFileDialog.FileType = QFileDialog.AnyFile,
|
||||||
|
infotext: str = "", filter: str = None):
|
||||||
super(PathEdit, self).__init__()
|
super(PathEdit, self).__init__()
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
self.type_of_file = type_of_file
|
self.type_of_file = type_of_file
|
||||||
|
@ -182,6 +183,7 @@ class SideTabBar(QTabBar):
|
||||||
painter.drawControl(QStyle.CE_TabBarTabLabel, opt);
|
painter.drawControl(QStyle.CE_TabBarTabLabel, opt);
|
||||||
painter.restore()
|
painter.restore()
|
||||||
|
|
||||||
|
|
||||||
class WaitingSpinner(QLabel):
|
class WaitingSpinner(QLabel):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(WaitingSpinner, self).__init__()
|
super(WaitingSpinner, self).__init__()
|
||||||
|
@ -189,92 +191,47 @@ class WaitingSpinner(QLabel):
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
""")
|
""")
|
||||||
self.movie = QMovie(style_path+"Icons/loader.gif")
|
self.movie = QMovie(style_path + "Icons/loader.gif")
|
||||||
self.setMovie(self.movie)
|
self.setMovie(self.movie)
|
||||||
self.movie.start()
|
self.movie.start()
|
||||||
|
|
||||||
|
|
||||||
class SwitchPrivate(QObject):
|
class SelectViewWidget(QWidget):
|
||||||
def __init__(self, q, parent=None):
|
toggled = pyqtSignal()
|
||||||
QObject.__init__(self, parent=parent)
|
|
||||||
self.mPointer = q
|
|
||||||
self.mPosition = 0.0
|
|
||||||
self.mGradient = QLinearGradient()
|
|
||||||
self.mGradient.setSpread(QGradient.PadSpread)
|
|
||||||
|
|
||||||
self.animation = QPropertyAnimation(self)
|
def __init__(self, icon_view: bool):
|
||||||
self.animation.setTargetObject(self)
|
super(SelectViewWidget, self).__init__()
|
||||||
self.animation.setPropertyName(b'position')
|
self.icon_view = icon_view
|
||||||
self.animation.setStartValue(0.0)
|
self.setStyleSheet("""QPushButton{border: none}""")
|
||||||
self.animation.setEndValue(1.0)
|
self.icon_view_button = QPushButton()
|
||||||
self.animation.setDuration(200)
|
self.list_view = QPushButton()
|
||||||
self.animation.setEasingCurve(QEasingCurve.InOutExpo)
|
if icon_view:
|
||||||
|
self.icon_view_button.setIcon(icon("mdi.view-grid-outline", color="orange"))
|
||||||
|
self.list_view.setIcon(icon("fa5s.list", color="white"))
|
||||||
|
else:
|
||||||
|
self.icon_view_button.setIcon(icon("mdi.view-grid-outline", color="white"))
|
||||||
|
self.list_view.setIcon(icon("fa5s.list", color="orange"))
|
||||||
|
|
||||||
self.animation.finished.connect(self.mPointer.update)
|
self.icon_view_button.clicked.connect(self.icon)
|
||||||
|
self.list_view.clicked.connect(self.list)
|
||||||
|
|
||||||
@pyqtProperty(float)
|
self.layout = QHBoxLayout()
|
||||||
def position(self):
|
self.layout.addWidget(self.icon_view_button)
|
||||||
return self.mPosition
|
self.layout.addWidget(self.list_view)
|
||||||
|
|
||||||
@position.setter
|
self.setLayout(self.layout)
|
||||||
def position(self, value):
|
|
||||||
self.mPosition = value
|
|
||||||
self.mPointer.update()
|
|
||||||
|
|
||||||
def draw(self, painter):
|
def isChecked(self):
|
||||||
r = self.mPointer.rect()
|
return self.icon_view
|
||||||
margin = r.height()/10
|
|
||||||
shadow = self.mPointer.palette().color(QPalette.Dark)
|
|
||||||
light = self.mPointer.palette().color(QPalette.Light)
|
|
||||||
button = self.mPointer.palette().color(QPalette.Button)
|
|
||||||
painter.setPen(Qt.NoPen)
|
|
||||||
|
|
||||||
#self.mGradient.setColorAt(0, shadow.darker(130))
|
def icon(self):
|
||||||
#self.mGradient.setColorAt(1, light.darker(130))
|
self.icon_view_button.setIcon(icon("mdi.view-grid-outline", color="orange"))
|
||||||
#self.mGradient.setStart(0, r.height())
|
self.list_view.setIcon(icon("fa5s.list", color="white"))
|
||||||
#self.mGradient.setFinalStop(0, 0)
|
self.icon_view = False
|
||||||
painter.setBrush(self.mGradient)
|
self.toggled.emit()
|
||||||
painter.drawRoundedRect(r, r.height()/2, r.height()/2)
|
|
||||||
|
|
||||||
self.mGradient.setColorAt(0, shadow.darker(140))
|
def list(self):
|
||||||
self.mGradient.setColorAt(1, light.darker(160))
|
self.icon_view_button.setIcon(icon("mdi.view-grid-outline", color="white"))
|
||||||
self.mGradient.setStart(0, 0)
|
self.list_view.setIcon(icon("fa5s.list", color="orange"))
|
||||||
self.mGradient.setFinalStop(0, r.height())
|
self.icon_view = True
|
||||||
painter.setBrush(self.mGradient)
|
self.toggled.emit()
|
||||||
painter.drawRoundedRect(r.adjusted(margin, margin, -margin, -margin), r.height()/2, r.height()/2)
|
|
||||||
|
|
||||||
self.mGradient.setColorAt(0, button.darker(130))
|
|
||||||
self.mGradient.setColorAt(1, button)
|
|
||||||
|
|
||||||
painter.setBrush(self.mGradient)
|
|
||||||
|
|
||||||
x = r.height()/2.0 + self.mPosition*(r.width()-r.height())
|
|
||||||
painter.drawEllipse(QPointF(x, r.height()/2), r.height()/2-margin, r.height()/2-margin)
|
|
||||||
|
|
||||||
@pyqtSlot(bool, name='animate')
|
|
||||||
def animate(self, checked):
|
|
||||||
self.animation.setDirection(QPropertyAnimation.Forward if checked else QPropertyAnimation.Backward)
|
|
||||||
self.animation.start()
|
|
||||||
|
|
||||||
|
|
||||||
class Switch(QAbstractButton):
|
|
||||||
def __init__(self):
|
|
||||||
QAbstractButton.__init__(self)
|
|
||||||
self.dPtr = SwitchPrivate(self)
|
|
||||||
self.setCheckable(True)
|
|
||||||
self.clicked.connect(self.dPtr.animate)
|
|
||||||
|
|
||||||
def sizeHint(self):
|
|
||||||
return QSize(42, 21)
|
|
||||||
|
|
||||||
def paintEvent(self, event):
|
|
||||||
painter = QPainter(self)
|
|
||||||
painter.setRenderHint(QPainter.Antialiasing)
|
|
||||||
self.dPtr.draw(painter)
|
|
||||||
|
|
||||||
def setChecked(self, a0: bool) -> None:
|
|
||||||
super().setChecked(a0)
|
|
||||||
self.dPtr.animate(a0)
|
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
|
||||||
self.update()
|
|
||||||
|
|
Loading…
Reference in a new issue