From aadd579cdc849800792edd41f0814f647a370144 Mon Sep 17 00:00:00 2001 From: Sergey Palcheh Date: Mon, 25 Aug 2025 16:58:42 +0600 Subject: [PATCH] added the ability to install the application in the created prefix --- winehelper_gui.py | 172 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 144 insertions(+), 28 deletions(-) diff --git a/winehelper_gui.py b/winehelper_gui.py index 63dc9da..81056b5 100644 --- a/winehelper_gui.py +++ b/winehelper_gui.py @@ -1220,7 +1220,7 @@ class WineHelperGUI(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("WineHelper") - self.setMinimumSize(950, 500) + self.setMinimumSize(950, 550) if Var.WH_ICON_PATH and os.path.exists(Var.WH_ICON_PATH): self.setWindowIcon(QIcon(Var.WH_ICON_PATH)) @@ -1900,6 +1900,7 @@ class WineHelperGUI(QMainWindow): # --- Левая сторона: Кнопки --- self.prefix_winetricks_button = QPushButton("Менеджер компонентов") + self.prefix_winetricks_button.setMinimumHeight(32) self.prefix_winetricks_button.clicked.connect( lambda: self.open_winetricks_manager(prefix_name=self.current_managed_prefix_name)) self.prefix_winetricks_button.setToolTip( @@ -1907,6 +1908,7 @@ class WineHelperGUI(QMainWindow): management_layout.addWidget(self.prefix_winetricks_button, 0, 0) self.prefix_winecfg_button = QPushButton("Редактор настроек") + self.prefix_winecfg_button.setMinimumHeight(32) self.prefix_winecfg_button.clicked.connect( lambda: self._run_wine_util('winecfg', prefix_name=self.current_managed_prefix_name)) self.prefix_winecfg_button.setToolTip( @@ -1914,6 +1916,7 @@ class WineHelperGUI(QMainWindow): management_layout.addWidget(self.prefix_winecfg_button, 0, 1) self.prefix_regedit_button = QPushButton("Редактор реестра") + self.prefix_regedit_button.setMinimumHeight(32) self.prefix_regedit_button.clicked.connect( lambda: self._run_wine_util('regedit', prefix_name=self.current_managed_prefix_name)) self.prefix_regedit_button.setToolTip( @@ -1921,6 +1924,7 @@ class WineHelperGUI(QMainWindow): management_layout.addWidget(self.prefix_regedit_button, 1, 0) self.prefix_uninstaller_button = QPushButton("Удаление программ") + self.prefix_uninstaller_button.setMinimumHeight(32) self.prefix_uninstaller_button.clicked.connect( lambda: self._run_wine_util('uninstaller', prefix_name=self.current_managed_prefix_name)) self.prefix_uninstaller_button.setToolTip( @@ -1928,11 +1932,13 @@ class WineHelperGUI(QMainWindow): management_layout.addWidget(self.prefix_uninstaller_button, 1, 1) self.prefix_cmd_button = QPushButton("Командная строка") + self.prefix_cmd_button.setMinimumHeight(32) self.prefix_cmd_button.clicked.connect(lambda: self._run_wine_util('cmd', prefix_name=self.current_managed_prefix_name)) self.prefix_cmd_button.setToolTip("Запуск командной строки (cmd) в окружении выбранного префикса.") management_layout.addWidget(self.prefix_cmd_button, 2, 0) self.prefix_winefile_button = QPushButton("Файловый менеджер") + self.prefix_winefile_button.setMinimumHeight(32) self.prefix_winefile_button.clicked.connect( lambda: self._run_wine_util('winefile', prefix_name=self.current_managed_prefix_name)) self.prefix_winefile_button.setToolTip("Запуск файлового менеджера Wine (winefile) для просмотра файлов внутри префикса.") @@ -1948,6 +1954,34 @@ class WineHelperGUI(QMainWindow): management_layout.setColumnStretch(1, 1) management_layout.setColumnStretch(2, 2) + # --- Separator and Installer --- + separator = QFrame() + separator.setFrameShape(QFrame.HLine) + separator.setFrameShadow(QFrame.Sunken) + management_layout.addWidget(separator, 3, 0, 1, 3) + + install_group = QWidget() + install_layout = QVBoxLayout(install_group) + install_layout.setContentsMargins(0, 5, 0, 0) + install_layout.setSpacing(5) + + install_path_layout = QHBoxLayout() + self.prefix_install_path_edit = QLineEdit() + self.prefix_install_path_edit.setPlaceholderText("Путь к .exe или .msi файлу...") + install_path_layout.addWidget(self.prefix_install_path_edit) + + self.prefix_browse_button = QPushButton("Обзор...") + self.prefix_browse_button.clicked.connect(self.browse_for_prefix_installer) + install_path_layout.addWidget(self.prefix_browse_button) + install_layout.addLayout(install_path_layout) + + self.prefix_install_button = QPushButton("Установить приложение в префикс") + self.prefix_install_button.setEnabled(False) + self.prefix_install_button.clicked.connect(self.run_prefix_installer) + install_layout.addWidget(self.prefix_install_button) + + management_layout.addWidget(install_group, 4, 0, 1, 3) + container_layout.addWidget(self.prefix_management_groupbox) layout.addWidget(self.management_container_groupbox) layout.addStretch() @@ -1957,6 +1991,7 @@ class WineHelperGUI(QMainWindow): self.prefix_name_edit.textChanged.connect(self.update_create_prefix_button_state) self.prefix_name_edit.textChanged.connect(self.on_prefix_name_edited) self.wine_version_edit.textChanged.connect(self.update_create_prefix_button_state) + self.prefix_install_path_edit.textChanged.connect(self.update_prefix_install_button_state) def _load_state(self): """Загружает последнее состояние GUI из файла.""" @@ -2072,6 +2107,8 @@ class WineHelperGUI(QMainWindow): else: self.prefix_management_groupbox.setEnabled(False) self.prefix_info_display.clear() + self.prefix_install_path_edit.clear() + self.update_prefix_install_button_state() def update_prefix_info_display(self, prefix_name): """Обновляет информационный блок для созданного префикса.""" @@ -2088,6 +2125,70 @@ class WineHelperGUI(QMainWindow): Путь: {html.escape(info['path'])}

""" self.prefix_info_display.setHtml(html_content) + def browse_for_prefix_installer(self): + """Открывает диалог выбора файла для установки в созданный префикс.""" + file_path, _ = QFileDialog.getOpenFileName( + self, + "Выберите установочный файл", + os.path.expanduser("~"), + "Исполняемые файлы (*.exe *.msi);;Все файлы (*)" + ) + if file_path: + self.prefix_install_path_edit.setText(file_path) + + def update_prefix_install_button_state(self): + """Обновляет состояние кнопки установки в префикс.""" + path_ok = bool(self.prefix_install_path_edit.text().strip()) + prefix_selected = self.current_managed_prefix_name is not None + self.prefix_install_button.setEnabled(path_ok and prefix_selected) + + def run_prefix_installer(self): + """Запускает установку файла в выбранный префикс.""" + prefix_name = self.current_managed_prefix_name + installer_path = self.prefix_install_path_edit.text().strip() + + if not prefix_name: + QMessageBox.warning(self, "Ошибка", "Не выбран префикс для установки.") + return + if not installer_path or not os.path.isfile(installer_path): + QMessageBox.warning(self, "Ошибка", "Указан неверный путь к установочному файлу.") + return + + prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name) + wine_executable = self._get_wine_executable_for_prefix(prefix_name) + + 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) + + env = QProcessEnvironment.systemEnvironment() + env.insert("WINEPREFIX", prefix_path) + self.command_process.setProcessEnvironment(env) + + args = [installer_path] + self.command_log_output.append(f"Запуск установки: {shlex.quote(wine_executable)} {shlex.quote(installer_path)}") + self.command_process.start(wine_executable, args) + self.command_dialog.exec_() + def create_help_tab(self): """Создает вкладку 'Справка' с подвкладками""" help_tab = QWidget() @@ -2390,6 +2491,20 @@ class WineHelperGUI(QMainWindow): self.installed_scroll_layout.addWidget(frame, row, column) self.installed_buttons.append(btn) + def _handle_prefix_install_finished(self, exit_code, exit_status): + """Обрабатывает завершение установки в префикс.""" + if exit_code == 0: + self.command_log_output.append("\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.prefix_install_path_edit.clear() + self.update_installed_apps() + def _set_active_button(self, button_widget): """Устанавливает активную кнопку и обновляет стили ее обертки (QFrame).""" # Сброс стиля предыдущей активной кнопки @@ -2626,6 +2741,33 @@ class WineHelperGUI(QMainWindow): "Не удалось определить префикс. Выберите установленное приложение или создайте новый префикс.") return + def _get_wine_executable_for_prefix(self, prefix_name): + """Определяет и возвращает путь к исполняемому файлу wine для указанного префикса.""" + prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name) + last_conf_path = os.path.join(prefix_path, "last.conf") + wh_wine_use = None + + if os.path.exists(last_conf_path): + try: + with open(last_conf_path, 'r', encoding='utf-8') as f: + for line in f: + if 'WH_WINE_USE=' in line: + value = line.split('=', 1)[1].strip().strip('"\'') + if value: + wh_wine_use = value + break + except Exception as e: + print(f"Предупреждение: не удалось прочитать или обработать {last_conf_path}: {e}") + + if wh_wine_use and not wh_wine_use.startswith('system'): + local_wine_path = os.path.join(Var.USER_WORK_PATH, "dist", wh_wine_use, "bin", "wine") + if os.path.exists(local_wine_path): + return local_wine_path + QMessageBox.warning(self, "Предупреждение", + f"Локальная версия Wine '{wh_wine_use}' не найдена по пути:\n{local_wine_path}\n\n" + "Будет использована системная версия Wine.") + return 'wine' # По умолчанию системный wine + prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name) if not os.path.isdir(prefix_path): QMessageBox.critical(self, "Ошибка", f"Каталог префикса не найден:\n{prefix_path}") @@ -2665,33 +2807,7 @@ class WineHelperGUI(QMainWindow): QMessageBox.critical(self, "Ошибка", f"Каталог префикса не найден:\n{prefix_path}") return - # --- Определение используемой версии Wine --- - wine_executable = 'wine' # По умолчанию системный wine - last_conf_path = os.path.join(prefix_path, "last.conf") - wh_wine_use = None - - if os.path.exists(last_conf_path): - try: - with open(last_conf_path, 'r', encoding='utf-8') as f: - for line in f: - if 'WH_WINE_USE=' in line: - # Извлекаем значение из строки вида: export WH_WINE_USE="wine_ver" - value = line.split('=', 1)[1].strip().strip('"\'') - if value: - wh_wine_use = value - break - except Exception as e: - print(f"Предупреждение: не удалось прочитать или обработать {last_conf_path}: {e}") - - if wh_wine_use and not wh_wine_use.startswith('system'): - local_wine_path = os.path.join(Var.USER_WORK_PATH, "dist", wh_wine_use, "bin", "wine") - if os.path.exists(local_wine_path): - wine_executable = local_wine_path - else: - QMessageBox.warning(self, "Предупреждение", - f"Локальная версия Wine '{wh_wine_use}' не найдена по пути:\n{local_wine_path}\n\n" - "Будет использована системная версия Wine.") - # --- Конец определения версии Wine --- + wine_executable = self._get_wine_executable_for_prefix(prefix_name) env = os.environ.copy() env["WINEPREFIX"] = prefix_path