forked from Boria138/PortProtonQt
feat(wine settings): make pfx_backup work
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
@@ -597,6 +597,16 @@ class FileExplorer(QDialog):
|
|||||||
self.thumbnail_cache.clear() # Clear cache when changing directories
|
self.thumbnail_cache.clear() # Clear cache when changing directories
|
||||||
self.pending_thumbnails.clear() # Clear pending thumbnails
|
self.pending_thumbnails.clear() # Clear pending thumbnails
|
||||||
try:
|
try:
|
||||||
|
if self.directory_only:
|
||||||
|
item = QListWidgetItem("./")
|
||||||
|
folder_icon = theme_manager.get_icon("folder")
|
||||||
|
# Ensure the icon is a QIcon
|
||||||
|
if isinstance(folder_icon, str) and os.path.isfile(folder_icon):
|
||||||
|
folder_icon = QIcon(folder_icon)
|
||||||
|
elif not isinstance(folder_icon, QIcon):
|
||||||
|
folder_icon = QIcon() # Fallback to empty icon
|
||||||
|
item.setIcon(folder_icon)
|
||||||
|
self.file_list.addItem(item)
|
||||||
if self.current_path != "/":
|
if self.current_path != "/":
|
||||||
item = QListWidgetItem("../")
|
item = QListWidgetItem("../")
|
||||||
folder_icon = theme_manager.get_icon("folder")
|
folder_icon = theme_manager.get_icon("folder")
|
||||||
|
@@ -39,7 +39,7 @@ from portprotonqt.game_library_manager import GameLibraryManager
|
|||||||
|
|
||||||
from PySide6.QtWidgets import (QLineEdit, QMainWindow, QStatusBar, QWidget, QVBoxLayout, QLabel, QHBoxLayout, QStackedWidget, QComboBox,
|
from PySide6.QtWidgets import (QLineEdit, QMainWindow, QStatusBar, QWidget, QVBoxLayout, QLabel, QHBoxLayout, QStackedWidget, QComboBox,
|
||||||
QDialog, QFormLayout, QFrame, QGraphicsDropShadowEffect, QMessageBox, QApplication, QPushButton, QProgressBar, QCheckBox, QSizePolicy, QGridLayout)
|
QDialog, QFormLayout, QFrame, QGraphicsDropShadowEffect, QMessageBox, QApplication, QPushButton, QProgressBar, QCheckBox, QSizePolicy, QGridLayout)
|
||||||
from PySide6.QtCore import Qt, QAbstractAnimation, QUrl, Signal, QTimer, Slot
|
from PySide6.QtCore import Qt, QAbstractAnimation, QUrl, Signal, QTimer, Slot, QProcess
|
||||||
from PySide6.QtGui import QIcon, QPixmap, QColor, QDesktopServices
|
from PySide6.QtGui import QIcon, QPixmap, QColor, QDesktopServices
|
||||||
from typing import cast
|
from typing import cast
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
@@ -1006,7 +1006,6 @@ class MainWindow(QMainWindow):
|
|||||||
self.wineTitle.setObjectName("tabTitle")
|
self.wineTitle.setObjectName("tabTitle")
|
||||||
layout.addWidget(self.wineTitle)
|
layout.addWidget(self.wineTitle)
|
||||||
|
|
||||||
# Путь к дистрибутивам Wine/Proton
|
|
||||||
if self.portproton_location is None:
|
if self.portproton_location is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1016,13 +1015,11 @@ class MainWindow(QMainWindow):
|
|||||||
if not os.path.exists(dist_path):
|
if not os.path.exists(dist_path):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Форма с настройками
|
|
||||||
formLayout = QFormLayout()
|
formLayout = QFormLayout()
|
||||||
formLayout.setContentsMargins(0, 10, 0, 0)
|
formLayout.setContentsMargins(0, 10, 0, 0)
|
||||||
formLayout.setSpacing(10)
|
formLayout.setSpacing(10)
|
||||||
formLayout.setLabelAlignment(Qt.AlignmentFlag.AlignLeft)
|
formLayout.setLabelAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||||
|
|
||||||
# Выбор версии Wine/Proton
|
|
||||||
self.wine_versions = [d for d in os.listdir(dist_path) if os.path.isdir(os.path.join(dist_path, d))]
|
self.wine_versions = [d for d in os.listdir(dist_path) if os.path.isdir(os.path.join(dist_path, d))]
|
||||||
self.wineCombo = QComboBox()
|
self.wineCombo = QComboBox()
|
||||||
self.wineCombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
self.wineCombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
@@ -1033,10 +1030,9 @@ class MainWindow(QMainWindow):
|
|||||||
self.wineTitleLabel.setStyleSheet(self.theme.PARAMS_TITLE_STYLE)
|
self.wineTitleLabel.setStyleSheet(self.theme.PARAMS_TITLE_STYLE)
|
||||||
self.wineTitleLabel.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
self.wineTitleLabel.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
||||||
if self.wine_versions:
|
if self.wine_versions:
|
||||||
self.wineCombo.setCurrentIndex(0) # Выбрать первую по умолчанию
|
self.wineCombo.setCurrentIndex(0)
|
||||||
formLayout.addRow(self.wineTitleLabel, self.wineCombo)
|
formLayout.addRow(self.wineTitleLabel, self.wineCombo)
|
||||||
|
|
||||||
# Выбор префикса
|
|
||||||
self.prefixes = [d for d in os.listdir(prefixes_path) if os.path.isdir(os.path.join(prefixes_path, d))] if os.path.exists(prefixes_path) else []
|
self.prefixes = [d for d in os.listdir(prefixes_path) if os.path.isdir(os.path.join(prefixes_path, d))] if os.path.exists(prefixes_path) else []
|
||||||
self.prefixCombo = QComboBox()
|
self.prefixCombo = QComboBox()
|
||||||
self.prefixCombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
self.prefixCombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
@@ -1047,15 +1043,14 @@ class MainWindow(QMainWindow):
|
|||||||
self.prefixTitleLabel.setStyleSheet(self.theme.PARAMS_TITLE_STYLE)
|
self.prefixTitleLabel.setStyleSheet(self.theme.PARAMS_TITLE_STYLE)
|
||||||
self.prefixTitleLabel.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
self.prefixTitleLabel.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
||||||
if self.prefixes:
|
if self.prefixes:
|
||||||
self.prefixCombo.setCurrentIndex(0) # Выбрать первый по умолчанию
|
self.prefixCombo.setCurrentIndex(0)
|
||||||
formLayout.addRow(self.prefixTitleLabel, self.prefixCombo)
|
formLayout.addRow(self.prefixTitleLabel, self.prefixCombo)
|
||||||
|
|
||||||
layout.addLayout(formLayout)
|
layout.addLayout(formLayout)
|
||||||
|
|
||||||
# Кнопки для стандартных инструментов Wine в сетке 2x3
|
# --- Wine Tools ---
|
||||||
tools_grid = QGridLayout()
|
tools_grid = QGridLayout()
|
||||||
tools_grid.setSpacing(10)
|
tools_grid.setSpacing(6)
|
||||||
tools_grid.setContentsMargins(0, 0, 0, 0)
|
|
||||||
|
|
||||||
tools = [
|
tools = [
|
||||||
("winecfg", _("Wine Configuration")),
|
("winecfg", _("Wine Configuration")),
|
||||||
@@ -1064,61 +1059,115 @@ class MainWindow(QMainWindow):
|
|||||||
("taskmgr", _("Task Manager")),
|
("taskmgr", _("Task Manager")),
|
||||||
("explorer", _("File Explorer")),
|
("explorer", _("File Explorer")),
|
||||||
("cmd", _("Command Prompt")),
|
("cmd", _("Command Prompt")),
|
||||||
|
("uninstaller", _("Uninstaller")),
|
||||||
]
|
]
|
||||||
|
|
||||||
for i, (_tool_cmd, tool_name) in enumerate(tools):
|
for i, (_tool_cmd, tool_name) in enumerate(tools):
|
||||||
row = i // 3
|
row = i // 3
|
||||||
col = i % 3
|
col = i % 3
|
||||||
btn = AutoSizeButton(tool_name, update_size=False) # Отключаем авторазмер для избежания проблем
|
btn = AutoSizeButton(tool_name, update_size=False)
|
||||||
btn.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
|
btn.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
|
||||||
btn.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
btn.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||||
tools_grid.addWidget(btn, row, col)
|
tools_grid.addWidget(btn, row, col)
|
||||||
|
|
||||||
# Растягиваем столбцы равномерно
|
|
||||||
for col in range(3):
|
for col in range(3):
|
||||||
tools_grid.setColumnStretch(col, 1)
|
tools_grid.setColumnStretch(col, 1)
|
||||||
|
|
||||||
layout.addLayout(tools_grid)
|
layout.addLayout(tools_grid)
|
||||||
|
|
||||||
# Дополнительные инструменты в сетке 1x4 или 2x2 если нужно
|
# --- Additional Tools ---
|
||||||
additional_grid = QGridLayout()
|
additional_grid = QGridLayout()
|
||||||
additional_grid.setSpacing(10)
|
additional_grid.setSpacing(6)
|
||||||
additional_grid.setContentsMargins(0, 0, 0, 0)
|
|
||||||
|
|
||||||
# Wine Uninstaller
|
additional_buttons = [
|
||||||
uninstaller_btn = AutoSizeButton(_("Uninstaller"), update_size=False)
|
(_("Winetricks"), None),
|
||||||
uninstaller_btn.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
|
(_("Create Prefix Backup"), self.create_prefix_backup),
|
||||||
uninstaller_btn.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
(_("Load Prefix Backup"), self.load_prefix_backup),
|
||||||
additional_grid.addWidget(uninstaller_btn, 0, 0)
|
(_("Delete Proton"), None),
|
||||||
|
(_("Delete Prefix"), None),
|
||||||
|
(_("Clean Prefix"), None),
|
||||||
|
]
|
||||||
|
|
||||||
# Winetricks
|
for i, (text, callback) in enumerate(additional_buttons):
|
||||||
winetricks_btn = AutoSizeButton(_("Winetricks"), update_size=False)
|
row = i // 3
|
||||||
winetricks_btn.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
|
col = i % 3
|
||||||
winetricks_btn.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
btn = AutoSizeButton(text, update_size=False)
|
||||||
additional_grid.addWidget(winetricks_btn, 0, 1)
|
btn.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
|
||||||
|
btn.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||||
|
if callback:
|
||||||
|
btn.clicked.connect(callback)
|
||||||
|
additional_grid.addWidget(btn, row, col)
|
||||||
|
|
||||||
# Create Backup
|
for col in range(3):
|
||||||
create_backup_btn = AutoSizeButton(_("Create Prefix Backup"), update_size=False)
|
|
||||||
create_backup_btn.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
|
|
||||||
create_backup_btn.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
|
||||||
additional_grid.addWidget(create_backup_btn, 0, 2)
|
|
||||||
|
|
||||||
# Load Backup
|
|
||||||
load_backup_btn = AutoSizeButton(_("Load Prefix Backup"), update_size=False)
|
|
||||||
load_backup_btn.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
|
|
||||||
load_backup_btn.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
|
||||||
additional_grid.addWidget(load_backup_btn, 0, 3)
|
|
||||||
|
|
||||||
# Растягиваем столбцы равномерно
|
|
||||||
for col in range(4):
|
|
||||||
additional_grid.setColumnStretch(col, 1)
|
additional_grid.setColumnStretch(col, 1)
|
||||||
|
|
||||||
layout.addLayout(additional_grid)
|
layout.addLayout(additional_grid)
|
||||||
|
tools_grid.setContentsMargins(10, 4, 10, 0)
|
||||||
|
additional_grid.setContentsMargins(10, 6, 10, 0)
|
||||||
layout.addStretch(1)
|
layout.addStretch(1)
|
||||||
|
|
||||||
self.stackedWidget.addWidget(self.wineWidget)
|
self.stackedWidget.addWidget(self.wineWidget)
|
||||||
|
|
||||||
|
def create_prefix_backup(self):
|
||||||
|
selected_prefix = self.prefixCombo.currentText()
|
||||||
|
if not selected_prefix:
|
||||||
|
QMessageBox.warning(self, _("Error"), _("Select a prefix first."))
|
||||||
|
return
|
||||||
|
file_explorer = FileExplorer(self, directory_only=True)
|
||||||
|
file_explorer.file_signal.file_selected.connect(lambda path: self._perform_backup(path, selected_prefix))
|
||||||
|
file_explorer.exec()
|
||||||
|
|
||||||
|
def _perform_backup(self, backup_dir, prefix_name):
|
||||||
|
os.makedirs(backup_dir, exist_ok=True)
|
||||||
|
if not self.portproton_location:
|
||||||
|
QMessageBox.warning(self, _("Error"), _("PortProton location not found."))
|
||||||
|
return
|
||||||
|
start_sh = os.path.join(self.portproton_location, "data", "scripts", "start.sh")
|
||||||
|
if not os.path.exists(start_sh):
|
||||||
|
QMessageBox.warning(self, _("Error"), _("start.sh not found."))
|
||||||
|
return
|
||||||
|
self.backup_process = QProcess(self)
|
||||||
|
self.backup_process.finished.connect(lambda exitCode, exitStatus: self._on_backup_finished(exitCode))
|
||||||
|
cmd = [start_sh, "--backup-prefix", prefix_name, backup_dir]
|
||||||
|
self.backup_process.start(cmd[0], cmd[1:])
|
||||||
|
if not self.backup_process.waitForStarted():
|
||||||
|
QMessageBox.warning(self, _("Error"), _("Failed to start backup process."))
|
||||||
|
|
||||||
|
def load_prefix_backup(self):
|
||||||
|
file_explorer = FileExplorer(self, file_filter='.ppack')
|
||||||
|
file_explorer.file_signal.file_selected.connect(self._perform_restore)
|
||||||
|
file_explorer.exec()
|
||||||
|
|
||||||
|
def _perform_restore(self, file_path):
|
||||||
|
if not file_path or not os.path.exists(file_path):
|
||||||
|
QMessageBox.warning(self, _("Error"), _("Valid .ppack file path required."))
|
||||||
|
return
|
||||||
|
if not self.portproton_location:
|
||||||
|
QMessageBox.warning(self, _("Error"), _("PortProton location not found."))
|
||||||
|
return
|
||||||
|
start_sh = os.path.join(self.portproton_location, "data", "scripts", "start.sh")
|
||||||
|
if not os.path.exists(start_sh):
|
||||||
|
QMessageBox.warning(self, _("Error"), _("start.sh not found."))
|
||||||
|
return
|
||||||
|
self.restore_process = QProcess(self)
|
||||||
|
self.restore_process.finished.connect(lambda exitCode, exitStatus: self._on_restore_finished(exitCode))
|
||||||
|
cmd = [start_sh, "--restore-prefix", file_path]
|
||||||
|
self.restore_process.start(cmd[0], cmd[1:])
|
||||||
|
if not self.restore_process.waitForStarted():
|
||||||
|
QMessageBox.warning(self, _("Error"), _("Failed to start restore process."))
|
||||||
|
|
||||||
|
def _on_backup_finished(self, exitCode):
|
||||||
|
if exitCode == 0:
|
||||||
|
QMessageBox.information(self, _("Success"), _("Prefix backup completed."))
|
||||||
|
else:
|
||||||
|
QMessageBox.warning(self, _("Error"), _("Prefix backup failed."))
|
||||||
|
|
||||||
|
def _on_restore_finished(self, exitCode):
|
||||||
|
if exitCode == 0:
|
||||||
|
QMessageBox.information(self, _("Success"), _("Prefix restore completed."))
|
||||||
|
else:
|
||||||
|
QMessageBox.warning(self, _("Error"), _("Prefix restore failed."))
|
||||||
|
|
||||||
def createPortProtonTab(self):
|
def createPortProtonTab(self):
|
||||||
"""Вкладка 'PortProton Settings'."""
|
"""Вкладка 'PortProton Settings'."""
|
||||||
self.portProtonWidget = QWidget()
|
self.portProtonWidget = QWidget()
|
||||||
|
Reference in New Issue
Block a user