added the ability to install the application in the created prefix

This commit is contained in:
Sergey Palcheh
2025-08-25 16:58:42 +06:00
parent 0608a3f250
commit aadd579cdc

View File

@@ -1220,7 +1220,7 @@ class WineHelperGUI(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setWindowTitle("WineHelper") self.setWindowTitle("WineHelper")
self.setMinimumSize(950, 500) self.setMinimumSize(950, 550)
if Var.WH_ICON_PATH and os.path.exists(Var.WH_ICON_PATH): if Var.WH_ICON_PATH and os.path.exists(Var.WH_ICON_PATH):
self.setWindowIcon(QIcon(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 = QPushButton("Менеджер компонентов")
self.prefix_winetricks_button.setMinimumHeight(32)
self.prefix_winetricks_button.clicked.connect( self.prefix_winetricks_button.clicked.connect(
lambda: self.open_winetricks_manager(prefix_name=self.current_managed_prefix_name)) lambda: self.open_winetricks_manager(prefix_name=self.current_managed_prefix_name))
self.prefix_winetricks_button.setToolTip( self.prefix_winetricks_button.setToolTip(
@@ -1907,6 +1908,7 @@ class WineHelperGUI(QMainWindow):
management_layout.addWidget(self.prefix_winetricks_button, 0, 0) management_layout.addWidget(self.prefix_winetricks_button, 0, 0)
self.prefix_winecfg_button = QPushButton("Редактор настроек") self.prefix_winecfg_button = QPushButton("Редактор настроек")
self.prefix_winecfg_button.setMinimumHeight(32)
self.prefix_winecfg_button.clicked.connect( self.prefix_winecfg_button.clicked.connect(
lambda: self._run_wine_util('winecfg', prefix_name=self.current_managed_prefix_name)) lambda: self._run_wine_util('winecfg', prefix_name=self.current_managed_prefix_name))
self.prefix_winecfg_button.setToolTip( self.prefix_winecfg_button.setToolTip(
@@ -1914,6 +1916,7 @@ class WineHelperGUI(QMainWindow):
management_layout.addWidget(self.prefix_winecfg_button, 0, 1) management_layout.addWidget(self.prefix_winecfg_button, 0, 1)
self.prefix_regedit_button = QPushButton("Редактор реестра") self.prefix_regedit_button = QPushButton("Редактор реестра")
self.prefix_regedit_button.setMinimumHeight(32)
self.prefix_regedit_button.clicked.connect( self.prefix_regedit_button.clicked.connect(
lambda: self._run_wine_util('regedit', prefix_name=self.current_managed_prefix_name)) lambda: self._run_wine_util('regedit', prefix_name=self.current_managed_prefix_name))
self.prefix_regedit_button.setToolTip( self.prefix_regedit_button.setToolTip(
@@ -1921,6 +1924,7 @@ class WineHelperGUI(QMainWindow):
management_layout.addWidget(self.prefix_regedit_button, 1, 0) management_layout.addWidget(self.prefix_regedit_button, 1, 0)
self.prefix_uninstaller_button = QPushButton("Удаление программ") self.prefix_uninstaller_button = QPushButton("Удаление программ")
self.prefix_uninstaller_button.setMinimumHeight(32)
self.prefix_uninstaller_button.clicked.connect( self.prefix_uninstaller_button.clicked.connect(
lambda: self._run_wine_util('uninstaller', prefix_name=self.current_managed_prefix_name)) lambda: self._run_wine_util('uninstaller', prefix_name=self.current_managed_prefix_name))
self.prefix_uninstaller_button.setToolTip( self.prefix_uninstaller_button.setToolTip(
@@ -1928,11 +1932,13 @@ class WineHelperGUI(QMainWindow):
management_layout.addWidget(self.prefix_uninstaller_button, 1, 1) management_layout.addWidget(self.prefix_uninstaller_button, 1, 1)
self.prefix_cmd_button = QPushButton("Командная строка") 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.clicked.connect(lambda: self._run_wine_util('cmd', prefix_name=self.current_managed_prefix_name))
self.prefix_cmd_button.setToolTip("Запуск командной строки (cmd) в окружении выбранного префикса.") self.prefix_cmd_button.setToolTip("Запуск командной строки (cmd) в окружении выбранного префикса.")
management_layout.addWidget(self.prefix_cmd_button, 2, 0) management_layout.addWidget(self.prefix_cmd_button, 2, 0)
self.prefix_winefile_button = QPushButton("Файловый менеджер") self.prefix_winefile_button = QPushButton("Файловый менеджер")
self.prefix_winefile_button.setMinimumHeight(32)
self.prefix_winefile_button.clicked.connect( self.prefix_winefile_button.clicked.connect(
lambda: self._run_wine_util('winefile', prefix_name=self.current_managed_prefix_name)) lambda: self._run_wine_util('winefile', prefix_name=self.current_managed_prefix_name))
self.prefix_winefile_button.setToolTip("Запуск файлового менеджера Wine (winefile) для просмотра файлов внутри префикса.") self.prefix_winefile_button.setToolTip("Запуск файлового менеджера Wine (winefile) для просмотра файлов внутри префикса.")
@@ -1948,6 +1954,34 @@ class WineHelperGUI(QMainWindow):
management_layout.setColumnStretch(1, 1) management_layout.setColumnStretch(1, 1)
management_layout.setColumnStretch(2, 2) 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) container_layout.addWidget(self.prefix_management_groupbox)
layout.addWidget(self.management_container_groupbox) layout.addWidget(self.management_container_groupbox)
layout.addStretch() 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.update_create_prefix_button_state)
self.prefix_name_edit.textChanged.connect(self.on_prefix_name_edited) self.prefix_name_edit.textChanged.connect(self.on_prefix_name_edited)
self.wine_version_edit.textChanged.connect(self.update_create_prefix_button_state) 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): def _load_state(self):
"""Загружает последнее состояние GUI из файла.""" """Загружает последнее состояние GUI из файла."""
@@ -2072,6 +2107,8 @@ class WineHelperGUI(QMainWindow):
else: else:
self.prefix_management_groupbox.setEnabled(False) self.prefix_management_groupbox.setEnabled(False)
self.prefix_info_display.clear() 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): def update_prefix_info_display(self, prefix_name):
"""Обновляет информационный блок для созданного префикса.""" """Обновляет информационный блок для созданного префикса."""
@@ -2088,6 +2125,70 @@ class WineHelperGUI(QMainWindow):
<b>Путь:</b> {html.escape(info['path'])}</p>""" <b>Путь:</b> {html.escape(info['path'])}</p>"""
self.prefix_info_display.setHtml(html_content) 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): def create_help_tab(self):
"""Создает вкладку 'Справка' с подвкладками""" """Создает вкладку 'Справка' с подвкладками"""
help_tab = QWidget() help_tab = QWidget()
@@ -2390,6 +2491,20 @@ class WineHelperGUI(QMainWindow):
self.installed_scroll_layout.addWidget(frame, row, column) self.installed_scroll_layout.addWidget(frame, row, column)
self.installed_buttons.append(btn) 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): def _set_active_button(self, button_widget):
"""Устанавливает активную кнопку и обновляет стили ее обертки (QFrame).""" """Устанавливает активную кнопку и обновляет стили ее обертки (QFrame)."""
# Сброс стиля предыдущей активной кнопки # Сброс стиля предыдущей активной кнопки
@@ -2626,6 +2741,33 @@ class WineHelperGUI(QMainWindow):
"Не удалось определить префикс. Выберите установленное приложение или создайте новый префикс.") "Не удалось определить префикс. Выберите установленное приложение или создайте новый префикс.")
return 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) prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
if not os.path.isdir(prefix_path): if not os.path.isdir(prefix_path):
QMessageBox.critical(self, "Ошибка", f"Каталог префикса не найден:\n{prefix_path}") QMessageBox.critical(self, "Ошибка", f"Каталог префикса не найден:\n{prefix_path}")
@@ -2665,33 +2807,7 @@ class WineHelperGUI(QMainWindow):
QMessageBox.critical(self, "Ошибка", f"Каталог префикса не найден:\n{prefix_path}") QMessageBox.critical(self, "Ошибка", f"Каталог префикса не найден:\n{prefix_path}")
return return
# --- Определение используемой версии Wine --- wine_executable = self._get_wine_executable_for_prefix(prefix_name)
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 ---
env = os.environ.copy() env = os.environ.copy()
env["WINEPREFIX"] = prefix_path env["WINEPREFIX"] = prefix_path