added control buttons for dxvk/vkd3d
This commit is contained in:
		
							
								
								
									
										86
									
								
								winehelper
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								winehelper
									
									
									
									
									
								
							| @@ -776,9 +776,10 @@ init_wined3d () { | ||||
| init_dxvk () { | ||||
|     check_variables USE_DXVK_VER "$1" | ||||
|  | ||||
|     get_dxvk () { | ||||
|         DXVK_URL="$1" | ||||
|         DXVK_PACKAGE="${WH_VULKAN_LIBDIR}/dxvk-${DXVK_VAR_VER}.tar.$(echo ${DXVK_URL#*.tar.})" | ||||
|     get_dxvk() { | ||||
|         local DXVK_URL="$1" | ||||
|         local DXVK_VAR_VER="$2" | ||||
|         local DXVK_PACKAGE="${WH_VULKAN_LIBDIR}/${DXVK_VAR_VER}.tar.$(echo "${DXVK_URL#*.tar.}")" | ||||
|         if try_download "$DXVK_URL" "$DXVK_PACKAGE" check256sum \ | ||||
|         && unpack "$DXVK_PACKAGE" "$WH_VULKAN_LIBDIR" | ||||
|         then | ||||
| @@ -789,8 +790,8 @@ init_dxvk () { | ||||
|     } | ||||
|  | ||||
|     for DXVK_VAR_VER in "$USE_DXVK_VER" $@ ; do | ||||
|         if [[ ! -d "${WH_VULKAN_LIBDIR}/dxvk-$DXVK_VAR_VER" ]] ; then | ||||
|             get_dxvk "$CLOUD_URL/dxvk-${DXVK_VAR_VER}.tar.xz" | ||||
|         if [[ ! -d "${WH_VULKAN_LIBDIR}/${DXVK_VAR_VER}" ]] ; then | ||||
|             get_dxvk "$CLOUD_URL/${DXVK_VAR_VER}.tar.xz" "$DXVK_VAR_VER" | ||||
|         fi | ||||
|     done | ||||
|  | ||||
| @@ -803,8 +804,8 @@ init_dxvk () { | ||||
|     fi | ||||
|  | ||||
|     for dxvkfiles in $DXVK_FILES ; do | ||||
|         try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/dxvk-$USE_DXVK_VER/x64/$dxvkfiles.dll" | ||||
|         if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/dxvk-$USE_DXVK_VER/x32/$dxvkfiles.dll" | ||||
|         try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/${USE_DXVK_VER}/x64/$dxvkfiles.dll" | ||||
|         if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/${USE_DXVK_VER}/x32/$dxvkfiles.dll" | ||||
|         then var_winedlloverride_update "$dxvkfiles=n" | ||||
|         fi | ||||
|     done | ||||
| @@ -813,9 +814,10 @@ init_dxvk () { | ||||
| init_vkd3d () { | ||||
|     check_variables USE_VKD3D_VER "$1" | ||||
|  | ||||
|     get_vkd3d () { | ||||
|         VKD3D_URL="$1" | ||||
|         VKD3D_PACKAGE="${WH_VULKAN_LIBDIR}/vkd3d-proton-${VKD3D_VAR_VER}.tar.$(echo ${VKD3D_URL#*.tar.})" | ||||
|     get_vkd3d() { | ||||
|         local VKD3D_URL="$1" | ||||
|         local VKD3D_VAR_VER="$2" | ||||
|         local VKD3D_PACKAGE="${WH_VULKAN_LIBDIR}/${VKD3D_VAR_VER}.tar.$(echo "${VKD3D_URL#*.tar.}")" | ||||
|         if try_download "$VKD3D_URL" "$VKD3D_PACKAGE" check256sum \ | ||||
|         && unpack "$VKD3D_PACKAGE" "$WH_VULKAN_LIBDIR" | ||||
|         then | ||||
| @@ -826,15 +828,15 @@ init_vkd3d () { | ||||
|     } | ||||
|  | ||||
|     for VKD3D_VAR_VER in "$USE_VKD3D_VER" $@ ; do | ||||
|         if [[ ! -d "${WH_VULKAN_LIBDIR}/vkd3d-proton-$VKD3D_VAR_VER" ]] ; then | ||||
|             get_vkd3d "$CLOUD_URL/vkd3d-proton-${VKD3D_VAR_VER}.tar.xz" | ||||
|         if [[ ! -d "${WH_VULKAN_LIBDIR}/${VKD3D_VAR_VER}" ]] ; then | ||||
|             get_vkd3d "$CLOUD_URL/${VKD3D_VAR_VER}.tar.xz" "$VKD3D_VAR_VER" | ||||
|         fi | ||||
|     done | ||||
|  | ||||
|     VKD3D_FILES="d3d12 d3d12core libvkd3d-shader-1 libvkd3d-1" # libvkd3d-proton-utils-3 | ||||
|     for vkd3dfiles in $VKD3D_FILES ; do | ||||
|         try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/vkd3d-proton-$USE_VKD3D_VER/x64/$vkd3dfiles.dll" | ||||
|         if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/vkd3d-proton-$USE_VKD3D_VER/x86/$vkd3dfiles.dll" | ||||
|         try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/${USE_VKD3D_VER}/x64/$vkd3dfiles.dll" | ||||
|         if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/${USE_VKD3D_VER}/x86/$vkd3dfiles.dll" | ||||
|         then var_winedlloverride_update "$vkd3dfiles=n" | ||||
|         fi | ||||
|     done | ||||
| @@ -2011,6 +2013,57 @@ restore_prefix() { | ||||
|     return 0 | ||||
| } | ||||
|  | ||||
| update_last_conf_var() { | ||||
|     local var_name="$1" | ||||
|     local new_value="$2" | ||||
|     local conf_file="$WINEPREFIX/last.conf" | ||||
|  | ||||
|     if [[ ! -f "$conf_file" ]]; then | ||||
|         print_warning "Файл last.conf не найден, не могу обновить переменную $var_name." | ||||
|         return 1 | ||||
|     fi | ||||
|  | ||||
|     if grep -q "export $var_name=" "$conf_file"; then | ||||
|         sed -i "s|^export $var_name=.*|export $var_name=\"$new_value\"|" "$conf_file" | ||||
|     else | ||||
|         echo "export $var_name=\"$new_value\"" >> "$conf_file" | ||||
|     fi | ||||
| } | ||||
|  | ||||
| run_install_dxvk() { | ||||
|     local version="$1" | ||||
|     check_prefix_var | ||||
|     init_database | ||||
|     init_wine_ver | ||||
|     init_wineprefix | ||||
|     if [[ "$version" == "none" ]] ; then | ||||
|         print_info "Удаление DXVK..." | ||||
|         init_wined3d | ||||
|         update_last_conf_var "DXVK_VER" "" | ||||
|     else | ||||
|         init_dxvk "$version" | ||||
|         update_last_conf_var "DXVK_VER" "$USE_DXVK_VER" | ||||
|     fi | ||||
|     wait_wineserver | ||||
| } | ||||
|  | ||||
| run_install_vkd3d() { | ||||
|     local version="$1" | ||||
|     check_prefix_var | ||||
|     init_database | ||||
|     init_wine_ver | ||||
|     init_wineprefix | ||||
|     if [[ "$version" == "none" ]] ; then | ||||
|         print_info "Удаление VKD3D..." | ||||
|         init_wined3d | ||||
|         update_last_conf_var "VKD3D_VER" "" | ||||
|     else | ||||
|         init_vkd3d "$version" | ||||
|         update_last_conf_var "VKD3D_VER" "$USE_VKD3D_VER" | ||||
|     fi | ||||
|     wait_wineserver | ||||
| } | ||||
|  | ||||
| wh_info () { | ||||
|     echo "Использование: $SCRIPT_NAME [команда] | ||||
|  | ||||
| @@ -2019,6 +2072,9 @@ wh_info () { | ||||
|     install [скрипт]                запустить скрипт установки программы | ||||
|     install [скрипт] --clear-pfx    не использовать готовый префикс для установки ПО | ||||
|  | ||||
|     install-dxvk [версия|none]      установить/удалить DXVK в выбранный префикс | ||||
|     install-vkd3d [версия|none]     установить/удалить VKD3D в выбранный префикс | ||||
|  | ||||
|     installed                       список установленных программ | ||||
|     run [программа]                 запуск программы (отладка) | ||||
|     remove-all                      удалить WineHelper и все связанные данные | ||||
| @@ -2066,6 +2122,8 @@ case "$arg1" in | ||||
|     winetricks) prepair_wine ; "$WH_WINETRICKS" -q "$@" ;; | ||||
|     desktop) create_desktop "$@" ; exit 0 ;; | ||||
|     install|-i) run_autoinstall "$@" ;; | ||||
|     install-dxvk) run_install_dxvk "$@" ;; | ||||
|     install-vkd3d) run_install_vkd3d "$@" ;; | ||||
|     installed) check_installed_programs "$1" ;; | ||||
|     run|-r) run_installed_programs "$1" ;; | ||||
|     backup-prefix) backup_prefix "$@" ;; | ||||
|   | ||||
| @@ -1078,7 +1078,7 @@ class WineVersionSelectionDialog(QDialog): | ||||
|                     if not line: | ||||
|                         continue | ||||
|  | ||||
|                     match = re.match(r'^#+\s+([A-Z_]+)\s+#*$', line) | ||||
|                     match = re.match(r'^#+\s*([^#]+?)\s*#*$', line) | ||||
|                     if match: | ||||
|                         group_name = match.group(1) | ||||
|                         allowed_groups = {"WINE", "WINE_LG", "PROTON_LG", "PROTON_STEAM"} | ||||
| @@ -1354,6 +1354,118 @@ class CreatePrefixDialog(QDialog): | ||||
|  | ||||
|         self.accept() | ||||
|  | ||||
| class ComponentVersionSelectionDialog(QDialog): | ||||
|     """Диалог для выбора версии компонента (DXVK, VKD3D).""" | ||||
|  | ||||
|     def __init__(self, component_group, title, parent=None, add_extra_options=True): | ||||
|         super().__init__(parent) | ||||
|         self.component_group = component_group | ||||
|         self.selected_version = None | ||||
|         self.versions_data = [] | ||||
|  | ||||
|         self.setWindowTitle(title) | ||||
|         self.setMinimumSize(600, 400) | ||||
|         self.setModal(True) | ||||
|  | ||||
|         main_layout = QVBoxLayout(self) | ||||
|  | ||||
|         self.search_edit = QLineEdit() | ||||
|         self.search_edit.setPlaceholderText("Поиск версии...") | ||||
|         self.search_edit.textChanged.connect(self.filter_versions) | ||||
|         main_layout.addWidget(self.search_edit) | ||||
|  | ||||
|         self.scroll_area = QScrollArea() | ||||
|         self.scroll_area.setWidgetResizable(True) | ||||
|         main_layout.addWidget(self.scroll_area) | ||||
|  | ||||
|         scroll_content = QWidget() | ||||
|         self.scroll_area.setWidget(scroll_content) | ||||
|  | ||||
|         self.grid_layout = QGridLayout(scroll_content) | ||||
|         self.grid_layout.setAlignment(Qt.AlignTop) | ||||
|  | ||||
|         self.buttons = [] | ||||
|         # Кнопка "Удалить" теперь находится вне сетки, поэтому начинаем с 0 строки. | ||||
|         self.load_versions(start_row=0) | ||||
|  | ||||
|         # --- Панель с кнопками действий внизу диалога --- | ||||
|         button_layout = QHBoxLayout() | ||||
|  | ||||
|         if add_extra_options: | ||||
|             uninstall_btn = QPushButton("Удалить из префикса") | ||||
|             uninstall_btn.setToolTip("Удаляет текущую установленную версию компонента из префикса.") | ||||
|             uninstall_btn.clicked.connect(partial(self.on_version_selected, "none")) | ||||
|             # Добавляем кнопку слева | ||||
|             button_layout.addWidget(uninstall_btn) | ||||
|  | ||||
|         button_layout.addStretch(1)  # Растягиваем пространство, чтобы разнести кнопки | ||||
|  | ||||
|         cancel_button = QPushButton("Отмена") | ||||
|         cancel_button.clicked.connect(self.reject) | ||||
|         button_layout.addWidget(cancel_button) | ||||
|  | ||||
|         main_layout.addLayout(button_layout) | ||||
|  | ||||
|     def load_versions(self, start_row): | ||||
|         """Загружает и отображает версии.""" | ||||
|         self._parse_sha256_list() | ||||
|         self.populate_ui(start_row) | ||||
|  | ||||
|     def _parse_sha256_list(self): | ||||
|         """Парсит sha256sum.list для получения списка версий.""" | ||||
|         sha256_path = os.path.join(Var.DATA_PATH, "sha256sum.list") | ||||
|         if not os.path.exists(sha256_path): | ||||
|             self.versions_data = [] | ||||
|             return | ||||
|  | ||||
|         current_group = None | ||||
|         try: | ||||
|             with open(sha256_path, 'r', encoding='utf-8') as f: | ||||
|                 for line in f: | ||||
|                     line = line.strip() | ||||
|                     if not line: | ||||
|                         continue | ||||
|                     match = re.match(r'^#+\s*([^#]+?)\s*#*$', line) | ||||
|                     if match: | ||||
|                         current_group = match.group(1).strip() | ||||
|                         continue | ||||
|                     if current_group == self.component_group and re.match(r'^[a-f0-9]{64}', line): | ||||
|                         parts = line.split(maxsplit=1) | ||||
|                         if len(parts) == 2: | ||||
|                             filename = parts[1] | ||||
|                             if filename.endswith('.tar.xz'): | ||||
|                                 version_name = filename[:-7] | ||||
|                                 self.versions_data.append(version_name) | ||||
|         except IOError: | ||||
|             self.versions_data = [] | ||||
|  | ||||
|     def populate_ui(self, start_row): | ||||
|         """Заполняет UI кнопками версий.""" | ||||
|         versions = sorted(self.versions_data, reverse=True) | ||||
|         num_columns = 3 | ||||
|         row, col = start_row, 0 | ||||
|         for version_name in versions: | ||||
|             btn = QPushButton(version_name) | ||||
|             btn.clicked.connect(partial(self.on_version_selected, version_name)) | ||||
|             self.grid_layout.addWidget(btn, row, col) | ||||
|             self.buttons.append(btn) | ||||
|             col += 1 | ||||
|             if col >= num_columns: | ||||
|                 col = 0 | ||||
|                 row += 1 | ||||
|  | ||||
|     def filter_versions(self): | ||||
|         """Фильтрует видимость кнопок версий на основе текста поиска.""" | ||||
|         search_text = self.search_edit.text().lower() | ||||
|         for btn_widget in self.buttons: | ||||
|             is_match = search_text in btn_widget.text().lower() | ||||
|             btn_widget.setVisible(is_match) | ||||
|  | ||||
|     def on_version_selected(self, version_name): | ||||
|         """Обрабатывает выбор версии.""" | ||||
|         self.selected_version = version_name | ||||
|         self.accept() | ||||
|  | ||||
| class WineHelperGUI(QMainWindow): | ||||
|     def __init__(self): | ||||
|         super().__init__() | ||||
| @@ -1980,11 +2092,29 @@ class WineHelperGUI(QMainWindow): | ||||
|         self.prefix_winefile_button.setToolTip("Запуск файлового менеджера Wine (winefile) для просмотра файлов внутри префикса.") | ||||
|         management_layout.addWidget(self.prefix_winefile_button, 2, 1) | ||||
|  | ||||
|         # Добавляем небольшой отступ | ||||
|         spacer_widget = QWidget() | ||||
|         spacer_widget.setFixedHeight(5) | ||||
|         management_layout.addWidget(spacer_widget, 3, 0, 1, 2) | ||||
|  | ||||
|         self.dxvk_manage_button = QPushButton("Управление DXVK") | ||||
|         self.dxvk_manage_button.setMinimumHeight(32) | ||||
|         self.dxvk_manage_button.clicked.connect(lambda: self.open_component_version_manager('dxvk')) | ||||
|         self.dxvk_manage_button.setToolTip("Установка или удаление определенной версии DXVK в префиксе.") | ||||
|         management_layout.addWidget(self.dxvk_manage_button, 4, 0) | ||||
|  | ||||
|         self.vkd3d_manage_button = QPushButton("Управление VKD3D") | ||||
|         self.vkd3d_manage_button.setMinimumHeight(32) | ||||
|         self.vkd3d_manage_button.clicked.connect(lambda: self.open_component_version_manager('vkd3d-proton')) | ||||
|         self.vkd3d_manage_button.setToolTip("Установка или удаление определенной версии vkd3d-proton в префиксе.") | ||||
|         management_layout.addWidget(self.vkd3d_manage_button, 4, 1) | ||||
|  | ||||
|         # --- Правая сторона: Информационный блок --- | ||||
|         self.prefix_info_display = QTextBrowser() | ||||
|         self.prefix_info_display.setReadOnly(True) | ||||
|         self.prefix_info_display.setFrameStyle(QFrame.StyledPanel) | ||||
|         management_layout.addWidget(self.prefix_info_display, 0, 2, 3, 1) | ||||
|         # Увеличиваем rowspan, чтобы учесть добавленный отступ | ||||
|         management_layout.addWidget(self.prefix_info_display, 0, 2, 5, 1) | ||||
|  | ||||
|         management_layout.setColumnStretch(0, 1) | ||||
|         management_layout.setColumnStretch(1, 1) | ||||
| @@ -1994,7 +2124,7 @@ class WineHelperGUI(QMainWindow): | ||||
|         separator = QFrame() | ||||
|         separator.setFrameShape(QFrame.HLine) | ||||
|         separator.setFrameShadow(QFrame.Sunken) | ||||
|         management_layout.addWidget(separator, 3, 0, 1, 3) | ||||
|         management_layout.addWidget(separator, 5, 0, 1, 3) | ||||
|  | ||||
|         install_group = QWidget() | ||||
|         install_layout = QVBoxLayout(install_group) | ||||
| @@ -2027,7 +2157,7 @@ class WineHelperGUI(QMainWindow): | ||||
|         action_buttons_layout.addWidget(self.create_launcher_button) | ||||
|         install_layout.addLayout(action_buttons_layout) | ||||
|  | ||||
|         management_layout.addWidget(install_group, 4, 0, 1, 3) | ||||
|         management_layout.addWidget(install_group, 6, 0, 1, 3) | ||||
|  | ||||
|         container_layout.addWidget(self.prefix_management_groupbox) | ||||
|         layout.addWidget(self.management_container_groupbox) | ||||
| @@ -2220,8 +2350,10 @@ class WineHelperGUI(QMainWindow): | ||||
|             "WINEARCH": ("Архитектура", lambda v: "64-bit" if v == "win64" else "32-bit"), | ||||
|             "WH_WINE_USE": ("Версия Wine", lambda v: "Системная" if v == "system" else v), | ||||
|             "BASE_PFX": ("Тип", lambda v: 'Чистый' if v == "none" else 'С рекомендуемыми библиотеками'), | ||||
|             "DXVK_VER": ("Версия DXVK", lambda v: v if v else "Не установлено"), | ||||
|             "VKD3D_VER": ("Версия VKD3D", lambda v: v if v else "Не установлено"), | ||||
|         } | ||||
|         display_order = ["WINEPREFIX", "WINEARCH", "WH_WINE_USE", "BASE_PFX"] | ||||
|         display_order = ["WINEPREFIX", "WINEARCH", "WH_WINE_USE", "BASE_PFX", "DXVK_VER", "VKD3D_VER"] | ||||
|  | ||||
|         html_content = f'<p style="line-height: 1.3; font-size: 9pt;">' | ||||
|         html_content += f"<b>Имя:</b> {html.escape(prefix_name)}<br>" | ||||
| @@ -2310,6 +2442,128 @@ class WineHelperGUI(QMainWindow): | ||||
|         self.command_process.start(wine_executable, args) | ||||
|         self.command_dialog.exec_() | ||||
|  | ||||
|     def _get_prefix_component_version(self, prefix_name, component_key): | ||||
|         """ | ||||
|         Читает last.conf префикса и возвращает версию указанного компонента. | ||||
|         :param prefix_name: Имя префикса. | ||||
|         :param component_key: Ключ компонента (например, 'DXVK_VER'). | ||||
|         :return: Строку с версией или None, если не найдено или значение пустое. | ||||
|         """ | ||||
|         if not prefix_name: | ||||
|             return None | ||||
|  | ||||
|         last_conf_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name, "last.conf") | ||||
|         if not os.path.exists(last_conf_path): | ||||
|             return None | ||||
|  | ||||
|         try: | ||||
|             with open(last_conf_path, 'r', encoding='utf-8') as f: | ||||
|                 for line in f: | ||||
|                     line = line.strip() | ||||
|                     # Ищем строку вида 'export KEY="value"' или 'export KEY=value' | ||||
|                     if line.startswith('export '): | ||||
|                         parts = line[7:].split('=', 1) | ||||
|                         if len(parts) == 2: | ||||
|                             key = parts[0].strip() | ||||
|                             if key == component_key: | ||||
|                                 value = parts[1].strip().strip('"\'') | ||||
|                                 # Возвращаем значение, только если оно не пустое. | ||||
|                                 return value if value else None | ||||
|         except IOError as e: | ||||
|             print(f"Ошибка чтения last.conf для {prefix_name}: {e}") | ||||
|             return None | ||||
|         return None | ||||
|  | ||||
|     def open_component_version_manager(self, component): | ||||
|         """Открывает диалог выбора версии для DXVK/VKD3D и запускает установку.""" | ||||
|         prefix_name = self.current_managed_prefix_name | ||||
|         if not prefix_name: | ||||
|             QMessageBox.warning(self, "Ошибка", "Сначала выберите префикс.") | ||||
|             return | ||||
|  | ||||
|         component_key = None | ||||
|         if component == 'dxvk': | ||||
|             group = 'DXVK' | ||||
|             title = f"Управление DXVK для префикса: {prefix_name}" | ||||
|             command = 'install-dxvk' | ||||
|             component_key = 'DXVK_VER' | ||||
|         elif component == 'vkd3d-proton': | ||||
|             group = 'VKD3D' | ||||
|             title = f"Управление vkd3d для префикса: {prefix_name}" | ||||
|             command = 'install-vkd3d' | ||||
|             component_key = 'VKD3D_VER' | ||||
|         else: | ||||
|             return | ||||
|  | ||||
|         dialog = ComponentVersionSelectionDialog(group, title, self) | ||||
|         if dialog.exec_() == QDialog.Accepted and dialog.selected_version: | ||||
|             version = dialog.selected_version | ||||
|  | ||||
|             if version == "none": | ||||
|                 # Удаление: сначала проверяем, есть ли что удалять. | ||||
|                 installed_version = self._get_prefix_component_version(prefix_name, component_key) | ||||
|                 if not installed_version: | ||||
|                     QMessageBox.information(self, "Информация", "Установленных версий нет.") | ||||
|                     return  # Прерываем выполнение, т.к. удалять нечего | ||||
|                 # Для удаления лицензия не нужна, запускаем сразу. | ||||
|                 self.run_component_install_command(prefix_name, command, version) | ||||
|             else: | ||||
|                 # Установка: сначала показываем лицензионное соглашение. | ||||
|                 if not self._show_license_agreement_dialog(): | ||||
|                     return  # Пользователь отклонил лицензию | ||||
|  | ||||
|                 # Если лицензия принята, запускаем установку. | ||||
|                 self.run_component_install_command(prefix_name, command, version) | ||||
|  | ||||
|     def run_component_install_command(self, prefix_name, command, version): | ||||
|         """Выполняет команду установки компонента (DXVK/VKD3D) через winehelper.""" | ||||
|         prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name) | ||||
|  | ||||
|         self.command_dialog = QDialog(self) | ||||
|         self.command_dialog.setWindowTitle(f"Выполнение: {command} {version}") | ||||
|         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( | ||||
|             lambda exit_code, exit_status: self._handle_component_install_finished( | ||||
|                 prefix_name, exit_code, exit_status | ||||
|             ) | ||||
|         ) | ||||
|  | ||||
|         env = QProcessEnvironment.systemEnvironment() | ||||
|         env.insert("WINEPREFIX", prefix_path) | ||||
|         self.command_process.setProcessEnvironment(env) | ||||
|  | ||||
|         args = [command, version] | ||||
|         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_() | ||||
|  | ||||
|     def _handle_component_install_finished(self, prefix_name, exit_code, exit_status): | ||||
|         """Обрабатывает завершение установки компонента и обновляет информацию о префиксе.""" | ||||
|         # Вызываем общий обработчик для обновления лога и кнопки закрытия | ||||
|         self._handle_command_finished(exit_code, exit_status) | ||||
|  | ||||
|         # В случае успеха обновляем панель информации о префиксе | ||||
|         if exit_code == 0: | ||||
|             self.update_prefix_info_display(prefix_name) | ||||
|  | ||||
|     def create_launcher_for_prefix(self): | ||||
|         """ | ||||
|         Открывает диалог для создания ярлыка для приложения внутри выбранного префикса. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user