Compare commits

...

1 Commits

Author SHA1 Message Date
Sergey Palcheh
10ca3b3c98 refactor(gui): unification of team dialogues via _create_command_dialog()
Refactored 5 methods:
- run_prefix_installer;
- run_update_associations_command;
- create_launcher_for_prefix;
- backup_prefix_for_app;
- restore_prefix.
2026-01-21 19:41:00 +06:00

View File

@@ -2743,36 +2743,18 @@ class WineHelperGUI(QMainWindow):
QMessageBox.warning(self, "Ошибка", "Указан неверный путь к установочному файлу.")
return
self.command_dialog = QDialog(self)
self.command_dialog.setWindowTitle(f"Установка в префикс: {prefix_name}")
self.command_dialog.setMinimumSize(750, 400)
self.command_dialog.setModal(True)
self.command_dialog.setWindowFlags(self.command_dialog.windowFlags() & ~Qt.WindowCloseButtonHint)
layout = QVBoxLayout()
self.command_log_output = QTextEdit()
self.command_log_output.setReadOnly(True)
self.command_log_output.setFont(QFont('DejaVu Sans Mono', 10))
layout.addWidget(self.command_log_output)
self.command_close_button = QPushButton("Закрыть")
self.command_close_button.setEnabled(False)
self.command_close_button.clicked.connect(self.command_dialog.close)
layout.addWidget(self.command_close_button)
self.command_dialog.setLayout(layout)
self.command_process = QProcess(self.command_dialog)
self.command_process.setProcessChannelMode(QProcess.MergedChannels)
self.command_process.readyReadStandardOutput.connect(self._handle_command_output)
self.command_process.finished.connect(self._handle_prefix_install_finished)
# Окружение полностью настраивается скриптом winehelper
self.command_process.setProcessEnvironment(QProcessEnvironment.systemEnvironment())
args = ["install-to-prefix", prefix_name, installer_path]
self.command_log_output.append(f"Выполнение: {shlex.quote(self.winehelper_path)} {' '.join(shlex.quote(a) for a in args)}")
self.command_process.start(self.winehelper_path, args)
self.command_dialog.exec_()
dialog, process, log_output, close_button = self._create_command_dialog(
f"Установка в префикс: {prefix_name}",
self.winehelper_path,
["install-to-prefix", prefix_name, installer_path],
finished_callback=self._handle_prefix_install_finished
)
# Сохраняем ссылки для обратной совместимости с существующим кодом
self.command_dialog = dialog
self.command_process = process
self.command_log_output = log_output
self.command_close_button = close_button
dialog.exec_()
def _get_prefix_component_version(self, prefix_name, component_key):
"""
@@ -3035,41 +3017,22 @@ class WineHelperGUI(QMainWindow):
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
self.command_dialog = QDialog(self)
self.command_dialog.setWindowTitle("Обновление ассоциаций файлов")
self.command_dialog.setMinimumSize(750, 400)
self.command_dialog.setModal(True)
self.command_dialog.setWindowFlags(self.command_dialog.windowFlags() & ~Qt.WindowCloseButtonHint)
def on_finished(exit_code, exit_status):
self._handle_component_install_finished(prefix_name, exit_code, exit_status)
layout = QVBoxLayout()
self.command_log_output = QTextEdit()
self.command_log_output.setReadOnly(True)
self.command_log_output.setFont(QFont('DejaVu Sans Mono', 10))
layout.addWidget(self.command_log_output)
self.command_close_button = QPushButton("Закрыть")
self.command_close_button.setEnabled(False)
self.command_close_button.clicked.connect(self.command_dialog.close)
layout.addWidget(self.command_close_button)
self.command_dialog.setLayout(layout)
self.command_process = QProcess(self.command_dialog)
self.command_process.setProcessChannelMode(QProcess.MergedChannels)
self.command_process.readyReadStandardOutput.connect(self._handle_command_output)
self.command_process.finished.connect(
lambda exit_code, exit_status: self._handle_component_install_finished(
prefix_name, exit_code, exit_status))
env = QProcessEnvironment.systemEnvironment()
env.insert("WINEPREFIX", prefix_path)
# Переменная WH_XDG_OPEN теперь читается из измененного last.conf
self.command_process.setProcessEnvironment(env)
# Вызываем init-prefix, который теперь прочитает правильное значение из last.conf
args = ["init-prefix"]
self.command_log_output.append(f"Выполнение: {shlex.quote(self.winehelper_path)} {' '.join(shlex.quote(a) for a in args)}")
self.command_process.start(self.winehelper_path, args)
self.command_dialog.exec_()
dialog, process, log_output, close_button = self._create_command_dialog(
"Обновление ассоциаций файлов",
self.winehelper_path,
["init-prefix"],
env_vars={"WINEPREFIX": prefix_path},
finished_callback=on_finished
)
# Сохраняем ссылки для обратной совместимости с существующим кодом
self.command_dialog = dialog
self.command_process = process
self.command_log_output = log_output
self.command_close_button = close_button
dialog.exec_()
def create_launcher_for_prefix(self):
"""
@@ -3111,38 +3074,19 @@ class WineHelperGUI(QMainWindow):
return # Пользователь отменил или ввел пустое имя
# 3. Вызываем winehelper.sh create-desktop
self.command_dialog = QDialog(self)
self.command_dialog.setWindowTitle(f"Создание ярлыка для: {app_name}")
self.command_dialog.setMinimumSize(750, 400)
self.command_dialog.setModal(True)
self.command_dialog.setWindowFlags(self.command_dialog.windowFlags() & ~Qt.WindowCloseButtonHint)
layout = QVBoxLayout()
self.command_log_output = QTextEdit()
self.command_log_output.setReadOnly(True)
self.command_log_output.setFont(QFont('DejaVu Sans Mono', 10))
layout.addWidget(self.command_log_output)
self.command_close_button = QPushButton("Закрыть")
self.command_close_button.setEnabled(False)
self.command_close_button.clicked.connect(self.command_dialog.close)
layout.addWidget(self.command_close_button)
self.command_dialog.setLayout(layout)
self.command_process = QProcess(self.command_dialog)
self.command_process.setProcessChannelMode(QProcess.MergedChannels)
self.command_process.readyReadStandardOutput.connect(self._handle_command_output)
self.command_process.finished.connect(self._handle_launcher_creation_finished)
env = QProcessEnvironment.systemEnvironment()
env.insert("WINEPREFIX", prefix_path)
self.command_process.setProcessEnvironment(env)
args = ["desktop", app_name, exe_path, "auto"]
self.command_log_output.append(f"Выполнение: {shlex.quote(self.winehelper_path)} {' '.join(shlex.quote(a) for a in args)}")
self.command_process.start(self.winehelper_path, args)
self.command_dialog.exec_()
dialog, process, log_output, close_button = self._create_command_dialog(
f"Создание ярлыка для: {app_name}",
self.winehelper_path,
["desktop", app_name, exe_path, "auto"],
env_vars={"WINEPREFIX": prefix_path},
finished_callback=self._handle_launcher_creation_finished
)
# Сохраняем ссылки для обратной совместимости с существующим кодом
self.command_dialog = dialog
self.command_process = process
self.command_log_output = log_output
self.command_close_button = close_button
dialog.exec_()
def create_help_tab(self):
"""Создает вкладку 'Справка' с подвкладками"""
@@ -3439,16 +3383,26 @@ class WineHelperGUI(QMainWindow):
def _handle_prefix_install_finished(self, exit_code, exit_status):
"""Обрабатывает завершение установки в префикс."""
if exit_code == 0:
self.command_log_output.append("\n=== Установка успешно завершена ===")
self.create_launcher_button.setEnabled(True) # Активируем кнопку создания ярлыка
else:
self.command_log_output.append(f"\n=== Ошибка выполнения (код: {exit_code}) ===")
# Используем старый API для обратной совместимости
log_output = getattr(self, 'command_log_output', None)
process = getattr(self, 'command_process', None)
close_button = getattr(self, 'command_close_button', None)
if log_output:
if exit_code == 0:
log_output.append("\n=== Установка успешно завершена ===")
self.create_launcher_button.setEnabled(True) # Активируем кнопку создания ярлыка
else:
log_output.append(f"\n=== Ошибка выполнения (код: {exit_code}) ===")
if process:
process.deleteLater()
if hasattr(self, 'command_process') and self.command_process == process:
self.command_process = None
if close_button:
close_button.setEnabled(True)
if self.command_process:
self.command_process.deleteLater()
self.command_process = None
self.command_close_button.setEnabled(True)
self.update_installed_apps()
def _set_active_button(self, button_widget):
@@ -3609,37 +3563,29 @@ class WineHelperGUI(QMainWindow):
if msg_box.clickedButton() != yes_button:
return # Отмена, если нажато "Нет" или крестик
# Используем модальный диалог для отображения процесса резервного копирования (бэкап)
self.command_dialog = QDialog(self)
self.command_dialog.setWindowTitle(f"Резервное копирование: {prefix_name}")
self.command_dialog.setMinimumSize(750, 400)
self.command_dialog.setModal(True)
self.command_dialog.setWindowFlags(self.command_dialog.windowFlags() & ~Qt.WindowCloseButtonHint)
layout = QVBoxLayout()
self.command_log_output = QTextEdit()
self.command_log_output.setReadOnly(True)
self.command_log_output.setFont(QFont('DejaVu Sans Mono', 10))
layout.addWidget(self.command_log_output)
dialog, process, log, close_btn = self._create_command_dialog(
f"Резервное копирование: {prefix_name}",
self.winehelper_path,
["backup-prefix", prefix_name]
)
# Сохраняем ссылки для обратной совместимости с существующим кодом
self.command_dialog = dialog
self.command_process = process
self.command_log_output = log
self.command_close_button = close_btn
self.command_close_button = QPushButton("Закрыть")
self.command_close_button.setEnabled(False)
self.command_close_button.clicked.connect(self.command_dialog.close)
layout.addWidget(self.command_close_button)
self.command_dialog.setLayout(layout)
# Устанавливаем родителя, чтобы избежать утечек памяти
self.command_process = QProcess(self.command_dialog)
self.command_process.setProcessChannelMode(QProcess.MergedChannels)
self.command_process.readyReadStandardOutput.connect(self._handle_command_output)
self.command_process.finished.connect(self._handle_command_finished)
self.command_process.finished.connect(self.update_open_log_dir_button_visibility)
# Создаем callback после сохранения ссылок
def on_finished(exit_code, exit_status):
self._handle_command_finished(exit_code, exit_status, close_btn, process, log)
if exit_code == 0:
self.update_open_log_dir_button_visibility()
winehelper_path = self.winehelper_path
args = ["backup-prefix", prefix_name]
# Заменяем стандартный обработчик на наш
process.finished.disconnect()
process.finished.connect(on_finished)
self.command_log_output.append(f"Выполнение: {shlex.quote(winehelper_path)} {' '.join(shlex.quote(a) for a in args)}")
self.command_process.start(winehelper_path, args)
self.command_dialog.exec_()
dialog.exec_()
def restore_prefix(self):
"""Восстанавливает префикс из резервной копии."""
@@ -3653,36 +3599,21 @@ class WineHelperGUI(QMainWindow):
if not backup_path:
return
# Используем модальный диалог для отображения процесса восстановления из бэкапа
self.command_dialog = QDialog(self)
self.command_dialog.setWindowTitle(f"Восстановление из: {os.path.basename(backup_path)}")
self.command_dialog.setMinimumSize(600, 400)
self.command_dialog.setModal(True)
self.command_dialog.setWindowFlags(self.command_dialog.windowFlags() & ~Qt.WindowCloseButtonHint)
def on_finished(exit_code, exit_status):
self._handle_restore_finished(exit_code, exit_status)
layout = QVBoxLayout()
self.command_log_output = QTextEdit()
self.command_log_output.setReadOnly(True)
self.command_log_output.setFont(QFont('DejaVu Sans Mono', 10))
layout.addWidget(self.command_log_output)
self.command_close_button = QPushButton("Закрыть")
self.command_close_button.setEnabled(False)
self.command_close_button.clicked.connect(self.command_dialog.close)
layout.addWidget(self.command_close_button)
self.command_dialog.setLayout(layout)
# Устанавливаем родителя, чтобы избежать утечек памяти
self.command_process = QProcess(self.command_dialog)
self.command_process.setProcessChannelMode(QProcess.MergedChannels)
self.command_process.readyReadStandardOutput.connect(self._handle_command_output)
self.command_process.finished.connect(self._handle_restore_finished)
winehelper_path = self.winehelper_path
args = ["restore-prefix", backup_path]
self.command_log_output.append(f"Выполнение: {shlex.quote(winehelper_path)} {' '.join(shlex.quote(a) for a in args)}")
self.command_process.start(winehelper_path, args)
self.command_dialog.exec_()
dialog, process, log_output, close_button = self._create_command_dialog(
f"Восстановление из: {os.path.basename(backup_path)}",
self.winehelper_path,
["restore-prefix", backup_path],
finished_callback=on_finished
)
# Сохраняем ссылки для обратной совместимости с существующим кодом
self.command_dialog = dialog
self.command_process = process
self.command_log_output = log_output
self.command_close_button = close_button
dialog.exec_()
def run_installed_app_with_debug(self):
"""Запускает выбранное установленное приложение с созданием лога"""
@@ -4718,6 +4649,74 @@ class WineHelperGUI(QMainWindow):
self.installation_cancelled = True
self.install_process.terminate()
def _create_command_dialog(self, title, command, args, env_vars=None, finished_callback=None,
min_width=750, min_height=400):
"""
Создает стандартный модальный диалог для выполнения команд winehelper.
Args:
title: Заголовок диалога
command: Путь к исполняемому файлу
args: Список аргументов команды
env_vars: Словарь дополнительных переменных окружения (опционально)
finished_callback: Функция обратного вызова при завершении (exit_code, exit_status)
min_width: Минимальная ширина диалога
min_height: Минимальная высота диалога
Returns:
Кортеж (dialog, process, log_output, close_button)
"""
dialog = QDialog(self)
dialog.setWindowTitle(title)
dialog.setMinimumSize(min_width, min_height)
dialog.setModal(True)
dialog.setWindowFlags(dialog.windowFlags() & ~Qt.WindowCloseButtonHint)
layout = QVBoxLayout()
log_output = QTextEdit()
log_output.setReadOnly(True)
log_output.setFont(QFont('DejaVu Sans Mono', 10))
layout.addWidget(log_output)
close_button = QPushButton("Закрыть")
close_button.setEnabled(False)
close_button.clicked.connect(dialog.close)
layout.addWidget(close_button)
dialog.setLayout(layout)
process = QProcess(dialog)
process.setProcessChannelMode(QProcess.MergedChannels)
process.readyReadStandardOutput.connect(
lambda: self._append_command_output(process, log_output)
)
if finished_callback:
process.finished.connect(finished_callback)
else:
process.finished.connect(
lambda exit_code, exit_status: self._handle_command_finished(
exit_code, exit_status, close_button, process, log_output)
)
env = QProcessEnvironment.systemEnvironment()
if env_vars:
for key, value in env_vars.items():
env.insert(key, value)
process.setProcessEnvironment(env)
log_output.append(f"Выполнение: {shlex.quote(command)} {' '.join(shlex.quote(a) for a in args)}")
process.start(command, args)
return dialog, process, log_output, close_button
def _append_command_output(self, process, log_output):
"""Добавляет вывод процесса в лог."""
output_bytes = process.readAll()
output = output_bytes.data().decode('utf-8', errors='ignore').strip()
if output:
log_output.append(output)
QApplication.processEvents()
def _handle_command_output(self):
"""Обрабатывает вывод для общих команд в модальном диалоге."""
if hasattr(self, 'command_process') and self.command_process:
@@ -4735,20 +4734,34 @@ class WineHelperGUI(QMainWindow):
self.command_process.readyReadStandardOutput.connect(self._handle_command_output)
self.command_process.finished.connect(self._handle_command_finished)
self.command_process.start(self.winehelper_path, [command] + (args or []))
def _handle_command_finished(self, exit_code, exit_status):
def _handle_command_finished(self, exit_code, exit_status, close_button=None, process=None, log_output=None):
"""Обрабатывает завершение для диалога команды"""
if exit_code == 0:
self.command_log_output.append(f"\n=== Команда успешно завершена ===")
else:
self.command_log_output.append(f"\n=== Ошибка выполнения (код: {exit_code}) ===")
if self.command_process:
self.command_process.deleteLater()
self.command_process = None
self.command_close_button.setEnabled(True)
self.command_log_output.ensureCursorVisible()
# Поддержка старого API для обратной совместимости
if close_button is None:
close_button = getattr(self, 'command_close_button', None)
if process is None:
process = getattr(self, 'command_process', None)
if log_output is None:
log_output = getattr(self, 'command_log_output', None)
if log_output:
if exit_code == 0:
log_output.append(f"\n=== Команда успешно завершена ===")
else:
log_output.append(f"\n=== Ошибка выполнения (код: {exit_code}) ===")
log_output.ensureCursorVisible()
if process:
process.deleteLater()
if hasattr(self, 'command_process') and self.command_process == process:
self.command_process = None
if close_button:
close_button.setEnabled(True)
def _handle_launcher_creation_finished(self, exit_code, exit_status):
"""Обрабатывает завершение создания ярлыка."""
# Используем старый API для обратной совместимости
self._handle_command_finished(exit_code, exit_status)
if exit_code == 0:
self.update_installed_apps()
@@ -4760,17 +4773,27 @@ class WineHelperGUI(QMainWindow):
def _handle_restore_finished(self, exit_code, exit_status):
"""Обрабатывает завершение для диалога команды восстановления."""
if exit_code == 0:
self.command_log_output.append(f"\n=== Восстановление успешно завершено ===")
self.update_installed_apps()
self._load_created_prefixes()
self.filter_installed_buttons()
else:
self.command_log_output.append(f"\n=== Ошибка выполнения (код: {exit_code}) ===")
if self.command_process:
self.command_process.deleteLater()
self.command_process = None
self.command_close_button.setEnabled(True)
# Используем старый API для обратной совместимости
log_output = getattr(self, 'command_log_output', None)
if log_output:
if exit_code == 0:
log_output.append(f"\n=== Восстановление успешно завершено ===")
self.update_installed_apps()
self._load_created_prefixes()
self.filter_installed_buttons()
else:
log_output.append(f"\n=== Ошибка выполнения (код: {exit_code}) ===")
close_button = getattr(self, 'command_close_button', None)
process = getattr(self, 'command_process', None)
if process:
process.deleteLater()
if hasattr(self, 'command_process') and self.command_process == process:
self.command_process = None
if close_button:
close_button.setEnabled(True)
def cleanup_process(self):
"""Очищает ресурсы процесса, принудительно завершая его, если он активен."""