feat(settings): added advanced

Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
2025-10-30 16:27:45 +05:00
parent dec24429f5
commit 0231073b19

View File

@@ -5,9 +5,8 @@ from typing import cast, TYPE_CHECKING
from PySide6.QtGui import QPixmap, QIcon, QTextCursor, QColor from PySide6.QtGui import QPixmap, QIcon, QTextCursor, QColor
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QDialog, QFormLayout, QHBoxLayout, QLabel, QVBoxLayout, QListWidget, QScrollArea, QWidget, QListWidgetItem, QSizePolicy, QApplication, QProgressBar, QScroller, QDialog, QFormLayout, QHBoxLayout, QLabel, QVBoxLayout, QListWidget, QScrollArea, QWidget, QListWidgetItem, QSizePolicy, QApplication, QProgressBar, QScroller,
QTabWidget, QTableWidget, QHeaderView, QMessageBox, QTableWidgetItem, QTextEdit, QAbstractItemView, QStackedWidget QTabWidget, QTableWidget, QHeaderView, QMessageBox, QTableWidgetItem, QTextEdit, QAbstractItemView, QStackedWidget, QComboBox
) )
from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer, QThreadPool, QRunnable, Slot, QProcess, QProcessEnvironment from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer, QThreadPool, QRunnable, Slot, QProcess, QProcessEnvironment
from icoextract import IconExtractor, IconExtractorError from icoextract import IconExtractor, IconExtractorError
from PIL import Image from PIL import Image
@@ -1674,27 +1673,32 @@ class WinetricksDialog(QDialog):
if self.input_manager: if self.input_manager:
self.input_manager.disable_winetricks_mode() self.input_manager.disable_winetricks_mode()
super().reject() super().reject()
class ExeSettingsDialog(QDialog): class ExeSettingsDialog(QDialog):
def __init__(self, parent=None, theme=None, exe_path=None): def __init__(self, parent=None, theme=None, exe_path=None):
super().__init__(parent) super().__init__(parent)
self.theme = theme if theme else theme_manager.apply_theme(read_theme_from_config()) self.theme = theme if theme else theme_manager.apply_theme(read_theme_from_config())
self.exe_path = exe_path self.exe_path = exe_path
if not self.exe_path: if not self.exe_path:
logger.error("Exe path not provided")
return return
self.portproton_path = get_portproton_location() self.portproton_path = get_portproton_location()
if self.portproton_path is None: if self.portproton_path is None:
logger.error("PortProton location not found") logger.error("PortProton location not found")
return return
base_path = os.path.join(self.portproton_path, "data") base_path = os.path.join(self.portproton_path, "data")
self.start_sh = os.path.join(base_path, "scripts", "start.sh") self.start_sh = [os.path.join(base_path, "scripts", "start.sh")]
self.ppdb_path = self.exe_path + ".ppdb" if not self.exe_path.endswith('.ppdb') else self.exe_path
self.current_settings = {} self.current_settings = {}
self.value_widgets = {} self.value_widgets = {}
self.original_values = {} self.original_values = {}
self.advanced_widgets = {}
self.original_display_values = {}
self.available_keys = set() self.available_keys = set()
self.blocked_keys = set() self.blocked_keys = set()
self.numa_nodes = {}
self.is_amd = False
self.locale_options = []
self.logical_core_options = []
self.amd_vulkan_drivers = []
self.branch_name = _("Unknown") self.branch_name = _("Unknown")
self.setWindowTitle(_("Exe Settings")) self.setWindowTitle(_("Exe Settings"))
@@ -1717,25 +1721,16 @@ class ExeSettingsDialog(QDialog):
self.current_theme_name = read_theme_from_config() self.current_theme_name = read_theme_from_config()
# Create hints widget using common function
self.hints_widget, self.hints_labels = create_dialog_hints_widget(
self.theme, self.main_window, self.input_manager, context='winetricks'
)
self.main_layout.addWidget(self.hints_widget)
# Connect signals
if self.input_manager:
self.input_manager.button_event.connect(
lambda *args: update_dialog_hints(self.hints_labels, self.main_window, self.input_manager, theme_manager, self.current_theme_name)
)
self.input_manager.dpad_moved.connect(
lambda *args: update_dialog_hints(self.hints_labels, self.main_window, self.input_manager, theme_manager, self.current_theme_name)
)
update_dialog_hints(self.hints_labels, self.main_window, self.input_manager, theme_manager, self.current_theme_name)
# Load current settings (includes list-db) # Load current settings (includes list-db)
self.load_current_settings() self.load_current_settings()
def _get_process_args(self, subcommand_args):
"""Get the full arguments for QProcess.start, handling flatpak separator."""
if self.start_sh[0] == "flatpak":
return self.start_sh[1:] + ["--"] + subcommand_args
else:
return self.start_sh + subcommand_args
def init_toggle_settings(self): def init_toggle_settings(self):
"""Initialize predefined toggle settings with descriptions.""" """Initialize predefined toggle settings with descriptions."""
self.toggle_settings = { self.toggle_settings = {
@@ -1786,10 +1781,15 @@ class ExeSettingsDialog(QDialog):
self.main_layout.setContentsMargins(10, 10, 10, 10) self.main_layout.setContentsMargins(10, 10, 10, 10)
self.main_layout.setSpacing(10) self.main_layout.setSpacing(10)
# Метка с текущей веткой (STABLE / DEVEL) # Tab widget
self.branch_label = QLabel(_("Detected branch: Unknown")) self.tab_widget = QTabWidget()
self.branch_label.setStyleSheet("font-weight: bold;") self.main_tab = QWidget()
self.main_layout.addWidget(self.branch_label) self.main_tab_layout = QVBoxLayout(self.main_tab)
self.advanced_tab = QWidget()
self.advanced_tab_layout = QVBoxLayout(self.advanced_tab)
self.tab_widget.addTab(self.main_tab, _("Main"))
self.tab_widget.addTab(self.advanced_tab, _("Advanced"))
# Таблица настроек # Таблица настроек
self.settings_table = QTableWidget() self.settings_table = QTableWidget()
@@ -1806,12 +1806,31 @@ class ExeSettingsDialog(QDialog):
self.settings_table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) self.settings_table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
self.settings_table.setTextElideMode(Qt.TextElideMode.ElideNone) self.settings_table.setTextElideMode(Qt.TextElideMode.ElideNone)
self.settings_table.setStyleSheet(self.theme.WINETRICKS_TABBLE_STYLE) self.settings_table.setStyleSheet(self.theme.WINETRICKS_TABBLE_STYLE)
self.main_layout.addWidget(self.settings_table) self.main_tab_layout.addWidget(self.settings_table)
# Таблица Advanced
self.advanced_table = QTableWidget()
self.advanced_table.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
self.advanced_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.advanced_table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.advanced_table.setColumnCount(3)
self.advanced_table.setHorizontalHeaderLabels([_("Setting"), _("Value"), _("Description")])
self.advanced_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
self.advanced_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)
self.advanced_table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch)
self.advanced_table.horizontalHeader().resizeSection(1, 200)
self.advanced_table.setWordWrap(True)
self.advanced_table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
self.advanced_table.setTextElideMode(Qt.TextElideMode.ElideNone)
self.advanced_table.setStyleSheet(self.theme.WINETRICKS_TABBLE_STYLE)
self.advanced_tab_layout.addWidget(self.advanced_table)
self.main_layout.addWidget(self.tab_widget)
# Кнопки # Кнопки
button_layout = QHBoxLayout() button_layout = QHBoxLayout()
self.apply_button = AutoSizeButton(_("Apply"), icon=theme_manager.get_icon("apply")) self.apply_button = AutoSizeButton(_("Apply"), icon=ThemeManager().get_icon("apply"))
self.cancel_button = AutoSizeButton(_("Cancel"), icon=theme_manager.get_icon("cancel")) self.cancel_button = AutoSizeButton(_("Cancel"), icon=ThemeManager().get_icon("cancel"))
self.apply_button.setStyleSheet(self.theme.ACTION_BUTTON_STYLE) self.apply_button.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
self.cancel_button.setStyleSheet(self.theme.ACTION_BUTTON_STYLE) self.cancel_button.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
button_layout.addWidget(self.apply_button) button_layout.addWidget(self.apply_button)
@@ -1821,69 +1840,107 @@ class ExeSettingsDialog(QDialog):
self.apply_button.clicked.connect(self.apply_changes) self.apply_button.clicked.connect(self.apply_changes)
self.cancel_button.clicked.connect(self.reject) self.cancel_button.clicked.connect(self.reject)
def load_current_settings(self): def load_current_settings(self):
"""Load available toggles first, then current settings.""" """Load available toggles first, then current settings."""
process = QProcess(self) process = QProcess(self)
process.finished.connect(self.on_list_db_finished) process.finished.connect(self.on_list_db_finished)
process.start(self.start_sh, ["cli", "--list-db"]) process.start(self.start_sh[0], ["cli", "--list-db"])
def on_list_db_finished(self, exit_code, exit_status): def on_list_db_finished(self, exit_code, exit_status):
"""Handle --list-db output and extract available keys.""" """Handle --list-db output and extract available keys and system info."""
process = cast(QProcess, self.sender()) process = cast(QProcess, self.sender())
self.available_keys = set() self.available_keys = set()
self.blocked_keys = set() self.blocked_keys = set()
if exit_code == 0 and exit_status == QProcess.ExitStatus.NormalExit: if exit_code == 0 and exit_status == QProcess.ExitStatus.NormalExit:
output = bytes(process.readAllStandardOutput().data()).decode('utf-8', 'ignore') output = bytes(process.readAllStandardOutput().data()).decode('utf-8', 'ignore')
for line in output.splitlines(): lines = output.splitlines()
if "Branch in used:" in line: self.numa_nodes = {}
self.branch_name = line.split(":", 1)[1].strip() self.is_amd = False
self.branch_label.setText(_("Detected branch: ") + self.branch_name) self.logical_core_options = []
self.locale_options = []
self.amd_vulkan_drivers = []
for line in lines:
line_stripped = line.strip()
if not line_stripped:
continue continue
stripped_line = line.strip() if re.match(r'^[A-Z_0-9]+=[^=]+$', line_stripped) and not line_stripped.startswith('PW_'):
if stripped_line.startswith("PW_"): # System info
parts = stripped_line.split(maxsplit=1) k, v = line_stripped.split('=', 1)
if k.startswith('NUMA_NODE_'):
node_id = k[10:]
self.numa_nodes[node_id] = v
elif k == 'IS_AMD':
self.is_amd = v.lower() == 'true'
elif k == 'LOGICAL_CORE_OPTIONS':
self.logical_core_options = v.split('!') if v else []
elif k == 'LOCALE_LIST':
self.locale_options = v.split('!') if v else []
elif k == 'AMD_VULKAN_DRIVER_LIST':
self.amd_vulkan_drivers = v.split('!') if v else []
continue
if line_stripped.startswith('PW_'):
parts = line_stripped.split(maxsplit=1)
key = parts[0] key = parts[0]
self.available_keys.add(key) self.available_keys.add(key)
if len(parts) > 1 and parts[1] == "blocked": if len(parts) > 1 and 'blocked' in parts[1]:
self.blocked_keys.add(key) self.blocked_keys.add(key)
# Показываем только пересечение # Показываем только пересечение
self.available_keys &= set(self.toggle_settings.keys()) self.available_keys &= set(self.toggle_settings.keys())
logger.debug(f"Filtered available keys (intersection): {self.available_keys}")
else:
logger.warning("Failed to get --list-db output; showing all toggles")
self.available_keys = set(self.toggle_settings.keys())
# Загружаем текущие настройки # Загружаем текущие настройки
process = QProcess(self) process = QProcess(self)
process.finished.connect(self.on_show_ppdb_finished) process.finished.connect(self.on_show_ppdb_finished)
process.start(self.start_sh, ["cli", "--show-ppdb", self.ppdb_path]) process.start(self.start_sh[0], ["cli", "--show-ppdb", f"{self.exe_path}.ppdb"])
def on_show_ppdb_finished(self, exit_code, exit_status): def on_show_ppdb_finished(self, exit_code, exit_status):
"""Handle --show-ppdb output.""" """Handle --show-ppdb output."""
process = cast(QProcess, self.sender()) process = cast(QProcess, self.sender())
if exit_code != 0 or exit_status != QProcess.ExitStatus.NormalExit: if exit_code != 0 or exit_status != QProcess.ExitStatus.NormalExit:
logger.warning("Failed to load settings, using defaults") # Fallback to defaults if load fails
for key in self.toggle_settings:
self.current_settings[key] = '0'
for adv_key in ['PW_WINDOWS_VER', 'WINEDLLOVERRIDES', 'LAUNCH_PARAMETERS',
'PW_WINE_CPU_TOPOLOGY', 'PW_MESA_GL_VERSION_OVERRIDE',
'PW_VKD3D_FEATURE_LEVEL', 'PW_LOCALE_SELECT',
'PW_MESA_VK_WSI_PRESENT_MODE', 'PW_AMD_VULKAN_USE',
'PW_CPU_NUMA_NODE_INDEX']:
self.current_settings[adv_key] = 'disabled' if 'TOPOLOGY' in adv_key or 'SELECT' in adv_key or 'MODE' in adv_key or 'LEVEL' in adv_key or 'GL_VERSION' in adv_key or 'NUMA' in adv_key else ''
else: else:
output = bytes(process.readAllStandardOutput().data()).decode('utf-8', 'ignore').strip() output = bytes(process.readAllStandardOutput().data()).decode('utf-8', 'ignore').strip()
self.current_settings = {} self.current_settings = {}
for line in output.split('\n'): for line in output.split('\n'):
if '=' in line and line.strip().startswith('PW_'): line_stripped = line.strip()
key, val = line.split('=', 1) if '=' in line_stripped:
self.current_settings[key.strip()] = val.strip() # Parse all KEY=VALUE lines, not just specific prefixes, to catch more
logger.debug(f"Loaded current settings: {self.current_settings}") try:
key, val = line_stripped.split('=', 1)
if key in self.toggle_settings or key in ['PW_WINDOWS_VER', 'WINEDLLOVERRIDES', 'LAUNCH_PARAMETERS',
'PW_WINE_CPU_TOPOLOGY', 'PW_MESA_GL_VERSION_OVERRIDE',
'PW_VKD3D_FEATURE_LEVEL', 'PW_LOCALE_SELECT',
'PW_MESA_VK_WSI_PRESENT_MODE', 'PW_AMD_VULKAN_USE',
'PW_CPU_NUMA_NODE_INDEX', 'PW_TASKSET_SLR']:
self.current_settings[key] = val
except ValueError:
continue
# Force blocked settings to '0' # Force blocked settings to '0'
for key in self.blocked_keys: for key in self.blocked_keys:
self.current_settings[key] = '0' self.current_settings[key] = '0'
self.original_values = self.current_settings.copy()
for key in set(self.toggle_settings.keys()):
self.original_values.setdefault(key, '0')
self.populate_table() self.populate_table()
self.populate_advanced()
def populate_table(self): def populate_table(self):
"""Populate the table with settings that are available in both lists.""" """Populate the table with settings that are available in both lists."""
self.settings_table.setRowCount(0) self.settings_table.setRowCount(0)
self.value_widgets.clear() self.value_widgets.clear()
self.original_values.clear()
self.settings_table.verticalHeader().setVisible(False) self.settings_table.verticalHeader().setVisible(False)
visible_keys = sorted(self.available_keys) if self.available_keys else sorted(self.toggle_settings.keys()) visible_keys = sorted(self.available_keys) if self.available_keys else sorted(self.toggle_settings.keys())
@@ -1922,16 +1979,187 @@ class ExeSettingsDialog(QDialog):
self.settings_table.setItem(row, 0, name_item) self.settings_table.setItem(row, 0, name_item)
self.value_widgets[(row, 1)] = checkbox self.value_widgets[(row, 1)] = checkbox
self.original_values[toggle] = current_val
self.settings_table.resizeRowsToContents() self.settings_table.resizeRowsToContents()
if self.settings_table.rowCount() > 0: if self.settings_table.rowCount() > 0:
self.settings_table.setCurrentCell(0, 0) self.settings_table.setCurrentCell(0, 0)
self.settings_table.setFocus(Qt.FocusReason.OtherFocusReason) self.settings_table.setFocus(Qt.FocusReason.OtherFocusReason)
def populate_advanced(self):
"""Populate the advanced tab with table format."""
self.advanced_table.setRowCount(0)
self.advanced_widgets.clear()
self.original_display_values = {}
self.advanced_table.verticalHeader().setVisible(False)
current = self.current_settings
disabled_text = _('disabled')
# Define advanced settings configuration
advanced_settings = []
# 1. Windows version
advanced_settings.append({
'key': 'PW_WINDOWS_VER',
'name': _("Windows version"),
'description': _("Changing the WINDOWS emulation version may be required to run older games. WINDOWS versions below 10 do not support new games with DirectX 12"),
'type': 'combo',
'options': ['11', '10', '7', 'XP'],
'default': '10'
})
# 2. Forced to use/disable libraries
advanced_settings.append({
'key': 'WINEDLLOVERRIDES',
'name': _("DLL Overrides"),
'description': _("Forced to use/disable the library only for the given application.\n\nA brief instruction:\n* libraries are written WITHOUT the .dll file extension\n* libraries are separated by semicolons - ;\n* library=n - use the WINDOWS (third-party) library\n* library=b - use WINE (built-in) library\n* library=n,b - use WINDOWS library and then WINE\n* library=b,n - use WINE library and then WINDOWS\n* library= - disable the use of this library\n\nExample: libglesv2=;d3dx9_36,d3dx9_42=n,b;mfc120=b,n"),
'type': 'text',
'default': ''
})
# 3. Launch arguments
advanced_settings.append({
'key': 'LAUNCH_PARAMETERS',
'name': _("Launch Arguments"),
'description': _("Adding an argument after the .exe file, just like you would add an argument in a shortcut on a WINDOWS system.\n\nExample: -dx11 -skipintro 1"),
'type': 'text',
'default': ''
})
# 4. CPU cores limit
advanced_settings.append({
'key': 'PW_WINE_CPU_TOPOLOGY',
'name': _("CPU Cores Limit"),
'description': _("Limiting the number of CPU cores is useful for Unity games (It is recommended to set the value equal to 8)"),
'type': 'combo',
'options': [disabled_text] + self.logical_core_options,
'default': disabled_text
})
# 5. OpenGL version
advanced_settings.append({
'key': 'PW_MESA_GL_VERSION_OVERRIDE',
'name': _("OpenGL Version"),
'description': _("You can select the required OpenGL version, some games require a forced Compatibility Profile (COMP)."),
'type': 'combo',
'options': [disabled_text, '4.6COMPAT', '4.5COMPAT', '4.3COMPAT', '4.1COMPAT', '3.3COMPAT', '3.2COMPAT'],
'default': disabled_text
})
# 6. VKD3D feature level
advanced_settings.append({
'key': 'PW_VKD3D_FEATURE_LEVEL',
'name': _("VKD3D Feature Level"),
'description': _("You can set a forced feature level VKD3D for games on DirectX12"),
'type': 'combo',
'options': [disabled_text, '12_2', '12_1', '12_0', '11_1', '11_0'],
'default': disabled_text
})
# 7. Locale
advanced_settings.append({
'key': 'PW_LOCALE_SELECT',
'name': _("Locale"),
'description': _("Force certain locale for an app. Fixes encoding issues in legacy software"),
'type': 'combo',
'options': [disabled_text] + self.locale_options,
'default': disabled_text
})
# 8. Present mode
advanced_settings.append({
'key': 'PW_MESA_VK_WSI_PRESENT_MODE',
'name': _("Window Mode"),
'description': _("Window mode (for Vulkan and OpenGL):\nfifo - First in, first out. Limits the frame rate + no tearing. (VSync)\nimmediate - Unlimited frame rate + tearing.\nmailbox - Triple buffering. Unlimited frame rate + no tearing.\nrelaxed - Same as fifo but allows tearing when below the monitors refresh rate."),
'type': 'combo',
'options': [disabled_text, 'fifo', 'immediate', 'mailbox', 'relaxed'],
'default': disabled_text
})
# 9. AMD Vulkan (always show, block if not applicable)
amd_options = [disabled_text] + self.amd_vulkan_drivers if self.is_amd and self.amd_vulkan_drivers else [disabled_text]
advanced_settings.append({
'key': 'PW_AMD_VULKAN_USE',
'name': _("AMD Vulkan Driver"),
'description': _("Select needed AMD vulkan implementation. Choosing which implementation of vulkan will be used to run the game"),
'type': 'combo',
'options': amd_options,
'default': disabled_text
})
# 10. NUMA node (always show if numa_nodes exist, block if <=1)
numa_ids = sorted(self.numa_nodes.keys())
numa_options = [disabled_text] + numa_ids if len(numa_ids) > 1 else [disabled_text]
advanced_settings.append({
'key': 'PW_CPU_NUMA_NODE_INDEX',
'name': _("NUMA Node"),
'description': _("NUMA node for CPU affinity. In multi-core systems, CPUs are split into NUMA nodes, each with its own local memory and cores. Binding a game to a single node reduces memory-access latency and limits costly core-to-core switches."),
'type': 'combo',
'options': numa_options,
'default': disabled_text
})
# Populate table
for setting in advanced_settings:
row = self.advanced_table.rowCount()
self.advanced_table.insertRow(row)
# Name column
name_item = QTableWidgetItem(setting['name'])
name_item.setFlags(Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled)
self.advanced_table.setItem(row, 0, name_item)
# Value column (widget)
if setting['type'] == 'combo':
combo = QComboBox()
combo.addItems(setting['options'])
# Get current value
current_raw = current.get(setting['key'], setting['default'])
if setting['key'] == 'PW_WINE_CPU_TOPOLOGY':
current_val = disabled_text if current_raw == 'disabled' else (current_raw.split(':')[0] if isinstance(current_raw, str) and ':' in current_raw else current_raw)
elif setting['key'] == 'PW_AMD_VULKAN_USE':
current_val = disabled_text if not current_raw or current_raw == '' else current_raw
else:
current_val = disabled_text if current_raw == 'disabled' else current_raw
if current_val not in setting['options']:
combo.addItem(current_val)
combo.setCurrentText(current_val)
# Block if only disabled option
if len(setting['options']) == 1:
combo.setEnabled(False)
self.advanced_table.setCellWidget(row, 1, combo)
self.advanced_widgets[setting['key']] = combo
self.original_display_values[setting['key']] = current_val
elif setting['type'] == 'text':
text_edit = QTextEdit()
current_val = current.get(setting['key'], setting['default'])
text_edit.setPlainText(current_val)
self.advanced_table.setCellWidget(row, 1, text_edit)
self.advanced_widgets[setting['key']] = text_edit
self.original_display_values[setting['key']] = current_val
# Description column
desc_item = QTableWidgetItem(setting['description'])
desc_item.setFlags(Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled)
desc_item.setToolTip(setting['description'])
desc_item.setTextAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
self.advanced_table.setItem(row, 2, desc_item)
self.advanced_table.resizeRowsToContents()
if self.advanced_table.rowCount() > 0:
self.advanced_table.setCurrentCell(0, 0)
def apply_changes(self): def apply_changes(self):
"""Apply changes by collecting diffs and running --edit-db.""" """Apply changes by collecting diffs from both main and advanced tabs."""
changes = [] changes = []
# --- 1. Обычные (toggle) настройки ---
for key, orig_val in self.original_values.items(): for key, orig_val in self.original_values.items():
if key in self.blocked_keys: if key in self.blocked_keys:
continue # Skip blocked keys continue # Skip blocked keys
@@ -1952,14 +2180,32 @@ class ExeSettingsDialog(QDialog):
if new_val != orig_val: if new_val != orig_val:
changes.append(f"{key}={new_val}") changes.append(f"{key}={new_val}")
# --- 2. Advanced настройки ---
for key, widget in self.advanced_widgets.items():
orig_val = self.original_display_values.get(key, '')
if isinstance(widget, QComboBox):
new_val = widget.currentText()
# приведение disabled к 'disabled'
if new_val.lower() == _('disabled').lower():
new_val = 'disabled'
elif isinstance(widget, QTextEdit):
new_val = widget.toPlainText().strip()
else:
continue
if new_val != orig_val:
changes.append(f"{key}={new_val}")
# --- 3. Проверка на изменения ---
if not changes: if not changes:
QMessageBox.information(self, _("Info"), _("No changes to apply.")) QMessageBox.information(self, _("Info"), _("No changes to apply."))
return return
# --- 4. Запуск процесса сохранения ---
process = QProcess(self) process = QProcess(self)
process.finished.connect(self.on_edit_db_finished) process.finished.connect(self.on_edit_db_finished)
args = ["cli", "--edit-db", self.exe_path] + changes args = ["cli", "--edit-db", self.exe_path] + changes
process.start(self.start_sh, args) process.start(self.start_sh[0], args)
self.apply_button.setEnabled(False) self.apply_button.setEnabled(False)
def on_edit_db_finished(self, exit_code, exit_status): def on_edit_db_finished(self, exit_code, exit_status):