Compare commits
	
		
			15 Commits
		
	
	
		
			0.5.2
			...
			e766b4dba2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e766b4dba2 | ||
|  | bad7e5780a | ||
|  | 7fbe9ba31b | ||
|  | 377b9e9059 | ||
|  | a815fa1c20 | ||
|  | 63f301d2de | ||
|  | 213035d868 | ||
|  | 65bd514a1a | ||
|  | 8912134827 | ||
|  | cb3fdc62dc | ||
|  | 9611cc52fc | ||
|  | 5cdc4a8f4f | ||
|  | 32fa7f6278 | ||
|  | b44fcdb63e | ||
|  | 3e2ed1cff5 | 
| @@ -1,5 +1,9 @@ | |||||||
| История изменений: | История изменений: | ||||||
|  |  | ||||||
|  | 0.5.3: | ||||||
|  | * исправлена установка grdcontrol для t-flex-* | ||||||
|  | * обновлен графический режим Qt5 | ||||||
|  |  | ||||||
| 0.5.2: | 0.5.2: | ||||||
| * исправлен запуск winehelper.desktop для winehelper-qt | * исправлен запуск winehelper.desktop для winehelper-qt | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								LICENSE_AGREEMENT
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								LICENSE_AGREEMENT
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | Лицензионные соглашения использования сторонних компонентов: | ||||||
|  |  | ||||||
|  | Некоторые компоненты, установленные в префикс и необходимые для запуска приложений, | ||||||
|  | могут быть защищены авторским правом или лицензионными соглашениями. Вы обязаны | ||||||
|  | самостоятельно убедиться в законности использования этих компонентов в вашей | ||||||
|  | юрисдикции. | ||||||
|  |  | ||||||
|  | Мы не несём ответственности за нарушение лицензионных соглашений, связанное с | ||||||
|  | использованием подготовленного префикса, а так же за программное обеспечение, | ||||||
|  | поставляемое из сторонних источников. | ||||||
|  |  | ||||||
|  | Подтверждая продолжение установки, вы соглашаетесь, что ознакомились с данным | ||||||
|  | отказом от ответственности и принимаете все риски, связанные с использованием | ||||||
|  | программного обеспечения. | ||||||
| @@ -28,8 +28,8 @@ prepair_wine | |||||||
|  |  | ||||||
| if [[ -d "$WINEPREFIX" ]] \ | if [[ -d "$WINEPREFIX" ]] \ | ||||||
| && grep -q "t-flex-cad" "$WINEPREFIX/winetricks.log" \ | && grep -q "t-flex-cad" "$WINEPREFIX/winetricks.log" \ | ||||||
| && systemctl list-units --type service --state running | grep aksusbd \ | && systemctl list-units --type service --state running | grep -q aksusbd \ | ||||||
| && systemctl list-units --type service --state running | grep hasplmd \ | && systemctl list-units --type service --state running | grep -q hasplmd \ | ||||||
| && rpm -q grdcontrol | grep -q "$GRDCONTROL_VER" | && rpm -q grdcontrol | grep -q "$GRDCONTROL_VER" | ||||||
| then | then | ||||||
|     print_info "Префикс $PREFIX_NAME готов к установке ПО." |     print_info "Префикс $PREFIX_NAME готов к установке ПО." | ||||||
| @@ -50,7 +50,7 @@ else | |||||||
|  |  | ||||||
|         if rpm -q grdcontrol | grep -q "$GRDCONTROL_VER" |         if rpm -q grdcontrol | grep -q "$GRDCONTROL_VER" | ||||||
|         then print_info "grdcontrol-$GRDCONTROL_VER уже установлен в системе." |         then print_info "grdcontrol-$GRDCONTROL_VER уже установлен в системе." | ||||||
|         else su_run rpm -iv "$AUTOINSTALL_DIR_LIN/Guardant_Linux/grdcontrol.x86_64.rpm" |         else su_run "rpm -iv \"$AUTOINSTALL_DIR_LIN/Guardant_Linux/grdcontrol.x86_64.rpm\"" | ||||||
|         fi |         fi | ||||||
|  |  | ||||||
|         if [[ "$BASE_PFX" == "none" ]] ; then |         if [[ "$BASE_PFX" == "none" ]] ; then | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								winehelper
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								winehelper
									
									
									
									
									
								
							| @@ -7,7 +7,7 @@ if [[ $(id -u) -eq 0 ]] ; then | |||||||
| fi | fi | ||||||
|  |  | ||||||
| ##### DEFAULT PATH ##### | ##### DEFAULT PATH ##### | ||||||
| export SCRIPT_NAME USER_WORK_PATH RUN_SCRIPT DATA_PATH CHANGELOG_FILE WH_ICON_PATH LICENSE_FILE | export SCRIPT_NAME USER_WORK_PATH RUN_SCRIPT DATA_PATH CHANGELOG_FILE WH_ICON_PATH LICENSE_FILE AGREEMENT | ||||||
|  |  | ||||||
| SCRIPT_NAME="$(basename "$0")" | SCRIPT_NAME="$(basename "$0")" | ||||||
| if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then | if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then | ||||||
| @@ -18,6 +18,7 @@ if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then | |||||||
|     CHANGELOG_FILE="$(realpath "/usr/share/doc/winehelper"-*/CHANGELOG)" |     CHANGELOG_FILE="$(realpath "/usr/share/doc/winehelper"-*/CHANGELOG)" | ||||||
|     WH_ICON_PATH="$DATA_PATH/image/gui/winehelper.svg" |     WH_ICON_PATH="$DATA_PATH/image/gui/winehelper.svg" | ||||||
|     LICENSE_FILE="$(realpath "/usr/share/doc/winehelper"-*/LICENSE)" |     LICENSE_FILE="$(realpath "/usr/share/doc/winehelper"-*/LICENSE)" | ||||||
|  |     AGREEMENT="$(realpath "/usr/share/doc/winehelper"-*/LICENSE_AGREEMENT)" | ||||||
| else | else | ||||||
|     # переменные для тестового запуска WineHelper из репозитория |     # переменные для тестового запуска WineHelper из репозитория | ||||||
|     USER_WORK_PATH="$HOME/test-$SCRIPT_NAME" |     USER_WORK_PATH="$HOME/test-$SCRIPT_NAME" | ||||||
| @@ -26,6 +27,7 @@ else | |||||||
|     CHANGELOG_FILE="$DATA_PATH/CHANGELOG" |     CHANGELOG_FILE="$DATA_PATH/CHANGELOG" | ||||||
|     WH_ICON_PATH="$DATA_PATH/image/gui/winehelper-devel.svg" |     WH_ICON_PATH="$DATA_PATH/image/gui/winehelper-devel.svg" | ||||||
|     LICENSE_FILE="$DATA_PATH/LICENSE" |     LICENSE_FILE="$DATA_PATH/LICENSE" | ||||||
|  |     AGREEMENT="$DATA_PATH/LICENSE_AGREEMENT" | ||||||
|  |  | ||||||
|     # минимальная проверка синтаксиса скриптов |     # минимальная проверка синтаксиса скриптов | ||||||
|     for self_check_script in "$RUN_SCRIPT" \ |     for self_check_script in "$RUN_SCRIPT" \ | ||||||
| @@ -367,20 +369,14 @@ print_license_agreement () { | |||||||
|     then return 0 |     then return 0 | ||||||
|     fi |     fi | ||||||
|  |  | ||||||
|  |     if [[ -f "$AGREEMENT" ]]; then | ||||||
|  |         echo | ||||||
|  |         print_warning "$(cat "$AGREEMENT")" | ||||||
|  |     else | ||||||
|  |         fatal "Файл лицензионного соглашения не найден: $AGREEMENT" | ||||||
|  |     fi | ||||||
|  |  | ||||||
|     echo |     echo | ||||||
|     print_warning "Лицензионные соглашения использования сторонних компонентов: |  | ||||||
|  |  | ||||||
| Некоторые компоненты, установленные в префикс и необходимые для запуска приложений, могут |  | ||||||
| быть защищены авторским правом или лицензионными соглашениями. Вы обязаны самостоятельно |  | ||||||
| убедиться в законности использования этих компонентов в вашей юрисдикции. |  | ||||||
|  |  | ||||||
| Мы не несём ответственности за нарушение лицензионных соглашений, связанное с использованием |  | ||||||
| подготовленного префикса, а так же за программное обеспечение поставляемого из сторонних источников. |  | ||||||
|  |  | ||||||
| Подтверждая продолжение установки, вы соглашаетесь что ознакомились с данным отказом от |  | ||||||
| ответственности и принимаете все риски, связанные с использованием программного обеспечения. |  | ||||||
| " |  | ||||||
|  |  | ||||||
|     if print_confirmation "Подтвердите продолжение установки" ; then |     if print_confirmation "Подтвердите продолжение установки" ; then | ||||||
|         touch "$license_agreement_file" |         touch "$license_agreement_file" | ||||||
|         chmod 600 "$license_agreement_file" |         chmod 600 "$license_agreement_file" | ||||||
|   | |||||||
| @@ -2,8 +2,10 @@ | |||||||
| import os | import os | ||||||
| import subprocess | import subprocess | ||||||
| import sys | import sys | ||||||
|  | import re | ||||||
| import shlex | import shlex | ||||||
| import shutil | import shutil | ||||||
|  | import html | ||||||
| from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QTabWidget, | from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QTabWidget, | ||||||
|                              QTextEdit, QFileDialog, QMessageBox, QLineEdit, QCheckBox, QStackedWidget, QScrollArea, |                              QTextEdit, QFileDialog, QMessageBox, QLineEdit, QCheckBox, QStackedWidget, QScrollArea, | ||||||
|                              QGridLayout, QFrame, QDialog, QTextBrowser) |                              QGridLayout, QFrame, QDialog, QTextBrowser) | ||||||
| @@ -20,6 +22,7 @@ class Var: | |||||||
|     CHANGELOG_FILE = os.environ.get("CHANGELOG_FILE") |     CHANGELOG_FILE = os.environ.get("CHANGELOG_FILE") | ||||||
|     WH_ICON_PATH = os.environ.get("WH_ICON_PATH") |     WH_ICON_PATH = os.environ.get("WH_ICON_PATH") | ||||||
|     LICENSE_FILE = os.environ.get("LICENSE_FILE") |     LICENSE_FILE = os.environ.get("LICENSE_FILE") | ||||||
|  |     LICENSE_AGREEMENT_FILE = os.environ.get("AGREEMENT") | ||||||
|  |  | ||||||
| class WineHelperGUI(QMainWindow): | class WineHelperGUI(QMainWindow): | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
| @@ -56,14 +59,15 @@ class WineHelperGUI(QMainWindow): | |||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         # Стили для оберток кнопок (для рамки выделения) |         # Стили для оберток кнопок (для рамки выделения) | ||||||
|         self.FRAME_STYLE_DEFAULT = "QFrame { border: 2px solid transparent; border-radius: 2px; padding: 0px; }" |         self.FRAME_STYLE_DEFAULT = "QFrame { border: 2px solid transparent; border-radius: 8px; padding: 0px; }" | ||||||
|         self.FRAME_STYLE_SELECTED = "QFrame { border: 2px solid #0078d7; border-radius: 2px; padding: 0px; }" |         self.FRAME_STYLE_SELECTED = "QFrame { border: 2px solid #0078d7; border-radius: 8px; padding: 0px; }" | ||||||
|  |  | ||||||
|         # Основные переменные |         # Основные переменные | ||||||
|         self.winehelper_path = Var.RUN_SCRIPT |         self.winehelper_path = Var.RUN_SCRIPT | ||||||
|         self.process = None |         self.process = None | ||||||
|         self.current_script = None |         self.current_script = None | ||||||
|         self.install_process = None |         self.install_process = None | ||||||
|  |         self.current_display_name = None | ||||||
|         self.install_dialog = None |         self.install_dialog = None | ||||||
|         self.current_active_button = None |         self.current_active_button = None | ||||||
|         self.installed_buttons = [] |         self.installed_buttons = [] | ||||||
| @@ -78,10 +82,15 @@ class WineHelperGUI(QMainWindow): | |||||||
|  |  | ||||||
|         # Создаем табы |         # Создаем табы | ||||||
|         self.tabs = QTabWidget() |         self.tabs = QTabWidget() | ||||||
|         self.main_layout.addWidget(self.tabs, stretch=2) |         self.main_layout.addWidget(self.tabs, stretch=1) | ||||||
|  |  | ||||||
|         # Создаем панель информации о скрипте |         # Создаем панель информации о скрипте | ||||||
|         self.create_info_panel() |         self.create_info_panel() | ||||||
|  |         self.main_layout.addWidget(self.info_panel, stretch=1) | ||||||
|  |  | ||||||
|  |         # Фиксируем минимальные размеры | ||||||
|  |         self.tabs.setMinimumWidth(520) | ||||||
|  |         self.info_panel.setMinimumWidth(415) | ||||||
|  |  | ||||||
|         # Вкладки |         # Вкладки | ||||||
|         self.create_auto_install_tab() |         self.create_auto_install_tab() | ||||||
| @@ -89,6 +98,9 @@ class WineHelperGUI(QMainWindow): | |||||||
|         self.create_installed_tab() |         self.create_installed_tab() | ||||||
|         self.create_help_tab() |         self.create_help_tab() | ||||||
|  |  | ||||||
|  |         # Инициализируем состояние, которое будет использоваться для логов | ||||||
|  |         self._reset_log_state() | ||||||
|  |  | ||||||
|         # Обновляем список установленных приложений |         # Обновляем список установленных приложений | ||||||
|         self.update_installed_apps() |         self.update_installed_apps() | ||||||
|  |  | ||||||
| @@ -162,7 +174,7 @@ class WineHelperGUI(QMainWindow): | |||||||
|  |  | ||||||
|         # Заголовок |         # Заголовок | ||||||
|         self.script_title = QLabel("Выберите программу") |         self.script_title = QLabel("Выберите программу") | ||||||
|         self.script_title.setFont(QFont('Arial', 14, QFont.Bold))  # Шрифт и размер шрифта в заголовке инф. панели |         self.script_title.setFont(QFont('Arial', 12, QFont.Bold))  # Шрифт и размер шрифта в заголовке инф. панели | ||||||
|         self.script_title.setAlignment(Qt.AlignCenter) |         self.script_title.setAlignment(Qt.AlignCenter) | ||||||
|         self.info_panel_layout.addWidget(self.script_title) |         self.info_panel_layout.addWidget(self.script_title) | ||||||
|  |  | ||||||
| @@ -198,7 +210,7 @@ class WineHelperGUI(QMainWindow): | |||||||
|         install_action_layout = QVBoxLayout() |         install_action_layout = QVBoxLayout() | ||||||
|         install_action_layout.setContentsMargins(0, 0, 0, 0) |         install_action_layout.setContentsMargins(0, 0, 0, 0) | ||||||
|         self.install_button = QPushButton("Установить") |         self.install_button = QPushButton("Установить") | ||||||
|         self.install_button.setFont(QFont('Arial', 13, QFont.Bold)) |         self.install_button.setFont(QFont('Arial', 12, QFont.Bold))  # Шрифт и размер шрифта в кнопке Установить | ||||||
|         self.install_button.setStyleSheet("background-color: #4CAF50; color: white;") |         self.install_button.setStyleSheet("background-color: #4CAF50; color: white;") | ||||||
|         self.install_button.clicked.connect(self.install_current_script) |         self.install_button.clicked.connect(self.install_current_script) | ||||||
|         install_action_layout.addWidget(self.install_button) |         install_action_layout.addWidget(self.install_button) | ||||||
| @@ -249,8 +261,6 @@ class WineHelperGUI(QMainWindow): | |||||||
|         self.installed_action_widget.setVisible(False) |         self.installed_action_widget.setVisible(False) | ||||||
|         self.installed_global_action_widget.setVisible(False) |         self.installed_global_action_widget.setVisible(False) | ||||||
|  |  | ||||||
|         self.main_layout.addWidget(self.info_panel, stretch=1) |  | ||||||
|  |  | ||||||
|     def browse_install_file(self): |     def browse_install_file(self): | ||||||
|         """Открывает диалог выбора файла для ручной установки""" |         """Открывает диалог выбора файла для ручной установки""" | ||||||
|         file_path, _ = QFileDialog.getOpenFileName( |         file_path, _ = QFileDialog.getOpenFileName( | ||||||
| @@ -604,7 +614,6 @@ class WineHelperGUI(QMainWindow): | |||||||
|         # Подвкладка "Лицензия" |         # Подвкладка "Лицензия" | ||||||
|         license_tab = QWidget() |         license_tab = QWidget() | ||||||
|         license_layout = QVBoxLayout(license_tab) |         license_layout = QVBoxLayout(license_tab) | ||||||
|         import html |  | ||||||
|         license_text = QTextBrowser() |         license_text = QTextBrowser() | ||||||
|         license_text.setOpenExternalLinks(True) |         license_text.setOpenExternalLinks(True) | ||||||
|  |  | ||||||
| @@ -685,9 +694,12 @@ class WineHelperGUI(QMainWindow): | |||||||
|         if self.current_active_button in self.installed_buttons: |         if self.current_active_button in self.installed_buttons: | ||||||
|             self.current_active_button = None |             self.current_active_button = None | ||||||
|  |  | ||||||
|         # Очистить существующие кнопки |         # Полностью очищаем layout перед обновлением, удаляя старые виджеты (рамки с кнопками) | ||||||
|         for btn in self.installed_buttons: |         while self.installed_scroll_layout.count(): | ||||||
|             btn.deleteLater() |             item = self.installed_scroll_layout.takeAt(0) | ||||||
|  |             widget = item.widget() | ||||||
|  |             if widget: | ||||||
|  |                 widget.deleteLater() | ||||||
|         self.installed_buttons.clear() |         self.installed_buttons.clear() | ||||||
|  |  | ||||||
|         if not os.path.exists(Var.USER_WORK_PATH): |         if not os.path.exists(Var.USER_WORK_PATH): | ||||||
| @@ -833,6 +845,11 @@ class WineHelperGUI(QMainWindow): | |||||||
|                 print(f"Error getting prefix name from {desktop_file}: {e}") |                 print(f"Error getting prefix name from {desktop_file}: {e}") | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
|  |     def _get_current_app_title(self): | ||||||
|  |         """Возвращает отображаемое имя для текущей выбранной программы.""" | ||||||
|  |         # Если display_name не установлено (например, при ошибке), используем имя скрипта | ||||||
|  |         return self.current_display_name or self.current_script | ||||||
|  |  | ||||||
|     def backup_prefix_for_app(self): |     def backup_prefix_for_app(self): | ||||||
|         """Создает резервную копию префикса для выбранного приложения.""" |         """Создает резервную копию префикса для выбранного приложения.""" | ||||||
|         prefix_name = self._get_prefix_name_for_selected_app() |         prefix_name = self._get_prefix_name_for_selected_app() | ||||||
| @@ -1206,6 +1223,7 @@ class WineHelperGUI(QMainWindow): | |||||||
|         prog_name = self.extract_prog_name_from_script(script_path) |         prog_name = self.extract_prog_name_from_script(script_path) | ||||||
|         prog_url = self.extract_prog_url_from_script(script_path) |         prog_url = self.extract_prog_url_from_script(script_path) | ||||||
|         display_name = prog_name if prog_name else script_name |         display_name = prog_name if prog_name else script_name | ||||||
|  |         self.current_display_name = display_name | ||||||
|  |  | ||||||
|         if icon_names: |         if icon_names: | ||||||
|             # Для заголовка используем первую иконку из списка |             # Для заголовка используем первую иконку из списка | ||||||
| @@ -1228,7 +1246,7 @@ class WineHelperGUI(QMainWindow): | |||||||
|         self.install_action_widget.setVisible(True) |         self.install_action_widget.setVisible(True) | ||||||
|         self.installed_action_widget.setVisible(False) |         self.installed_action_widget.setVisible(False) | ||||||
|         self.installed_global_action_widget.setVisible(False) |         self.installed_global_action_widget.setVisible(False) | ||||||
|         self.install_button.setText(f"Установить {display_name}") |         self.install_button.setText(f"Установить «{display_name}»") | ||||||
|  |  | ||||||
|     def install_current_script(self): |     def install_current_script(self): | ||||||
|         """Устанавливает текущий выбранный скрипт""" |         """Устанавливает текущий выбранный скрипт""" | ||||||
| @@ -1242,7 +1260,8 @@ class WineHelperGUI(QMainWindow): | |||||||
|  |  | ||||||
|         # Создаем диалоговое окно установки |         # Создаем диалоговое окно установки | ||||||
|         self.install_dialog = QDialog(self) |         self.install_dialog = QDialog(self) | ||||||
|         self.install_dialog.setWindowTitle(f"Установка {self.current_script}") |         title_name = self._get_current_app_title() | ||||||
|  |         self.install_dialog.setWindowTitle(f"Установка «{title_name}»") | ||||||
|         self.install_dialog.setMinimumSize(750, 400) |         self.install_dialog.setMinimumSize(750, 400) | ||||||
|         self.install_dialog.setWindowModality(Qt.WindowModal) |         self.install_dialog.setWindowModality(Qt.WindowModal) | ||||||
|  |  | ||||||
| @@ -1255,38 +1274,32 @@ class WineHelperGUI(QMainWindow): | |||||||
|         license_page = QWidget() |         license_page = QWidget() | ||||||
|         license_layout = QVBoxLayout(license_page) |         license_layout = QVBoxLayout(license_page) | ||||||
|  |  | ||||||
|         license_text = QTextEdit() |         license_found = False | ||||||
|         license_text.setReadOnly(True) |  | ||||||
|  |  | ||||||
|         # Получаем текст лицензии из скрипта winehelper |         license_text = QTextBrowser() | ||||||
|         script_path = os.path.join(Var.DATA_PATH, "winehelper") |  | ||||||
|         license_content = "" |         # Получаем текст лицензионного соглашения из файла | ||||||
|         try: |         try: | ||||||
|             with open(script_path, 'r', encoding='utf-8') as f: |             license_file_path = Var.LICENSE_AGREEMENT_FILE | ||||||
|                 capturing = False |             if not license_file_path or not os.path.exists(license_file_path): | ||||||
|                 for line in f: |                 raise FileNotFoundError | ||||||
|                     if 'print_warning "Лицензионные соглашения использования сторонних компонентов:' in line: |  | ||||||
|                         capturing = True |  | ||||||
|                         continue |  | ||||||
|  |  | ||||||
|                     if capturing: |             with open(license_file_path, 'r', encoding='utf-8') as f: | ||||||
|                         if 'Подтверждая продолжение установки' in line: |                 license_content = f.read() | ||||||
|                             break |  | ||||||
|                         # Очищаем строку от лишних символов |             escaped_license_content = html.escape(license_content) | ||||||
|                         clean_line = line.strip() |  | ||||||
|                         clean_line = clean_line.replace('print_warning "', '').replace('\\n', '\n') |  | ||||||
|                         clean_line = clean_line.rstrip('"') |  | ||||||
|                         license_content += clean_line + '\n' |  | ||||||
|  |  | ||||||
|             license_text.setHtml(f""" |             license_text.setHtml(f""" | ||||||
|                 <h3>Лицензионные соглашения использования сторонних компонентов:</h3> |                 <pre style="font-family: sans-serif; font-size: 10pt; white-space: pre-wrap; word-wrap: break-word;">{escaped_license_content}</pre> | ||||||
|                 <p>{license_content}</p> |  | ||||||
|             """) |             """) | ||||||
|  |             license_found = True | ||||||
|  |         except (FileNotFoundError, TypeError): | ||||||
|  |             license_text.setHtml(f'<h3>Лицензионные соглашения</h3><p>Не удалось загрузить файл лицензионного соглашения по пути:<br>{Var.LICENSE_AGREEMENT_FILE}</p>') | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print(f"Ошибка чтения файла для извлечения лицензии: {str(e)}") |             print(f"Ошибка чтения файла лицензии: {str(e)}") | ||||||
|             license_text.setHtml(""" |             license_text.setHtml(f""" | ||||||
|                 <h3>Лицензионные соглашения</h3> |                 <h3>Лицензионные соглашения</h3> | ||||||
|                 <p>Не удалось загрузить текст лицензионного соглашения.</p> |                 <p>Произошла ошибка при чтении файла лицензии:<br>{str(e)}</p> | ||||||
|             """) |             """) | ||||||
|  |  | ||||||
|         license_layout.addWidget(license_text) |         license_layout.addWidget(license_text) | ||||||
| @@ -1333,11 +1346,20 @@ class WineHelperGUI(QMainWindow): | |||||||
|             lambda state: self.btn_continue.setEnabled(state == Qt.Checked) |             lambda state: self.btn_continue.setEnabled(state == Qt.Checked) | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |         if not license_found: | ||||||
|  |             self.license_checkbox.setEnabled(False) | ||||||
|  |  | ||||||
|         self.install_dialog.show() |         self.install_dialog.show() | ||||||
|  |  | ||||||
|  |     def _reset_log_state(self): | ||||||
|  |         """Сбрасывает состояние буфера и флага прогресса для лога установки.""" | ||||||
|  |         self.output_buffer = "" | ||||||
|  |         self.last_line_was_progress = False | ||||||
|  |  | ||||||
|     def _prepare_installation(self): |     def _prepare_installation(self): | ||||||
|         """Подготавливает и запускает процесс установки""" |         """Подготавливает и запускает процесс установки""" | ||||||
|         self.stacked_widget.setCurrentIndex(1) |         self.stacked_widget.setCurrentIndex(1) | ||||||
|  |         self._reset_log_state()  # Сбрасываем состояние для обработки лога | ||||||
|  |  | ||||||
|         winehelper_path = self.winehelper_path |         winehelper_path = self.winehelper_path | ||||||
|         script_path = os.path.join(Var.DATA_PATH, |         script_path = os.path.join(Var.DATA_PATH, | ||||||
| @@ -1377,7 +1399,8 @@ class WineHelperGUI(QMainWindow): | |||||||
|         if install_file: |         if install_file: | ||||||
|             args.append(install_file) |             args.append(install_file) | ||||||
|  |  | ||||||
|         self.append_log(f"=== Начало установки {self.current_script} ===") |         title_name = self._get_current_app_title() | ||||||
|  |         self.append_log(f"=== Начало установки «{title_name}» ===") | ||||||
|         self.append_log(f"Исполняемый файл: {winehelper_path}") |         self.append_log(f"Исполняемый файл: {winehelper_path}") | ||||||
|         self.append_log(f"Аргументы: {' '.join(shlex.quote(a) for a in args)}") |         self.append_log(f"Аргументы: {' '.join(shlex.quote(a) for a in args)}") | ||||||
|  |  | ||||||
| @@ -1385,40 +1408,106 @@ class WineHelperGUI(QMainWindow): | |||||||
|             self.install_process.start(winehelper_path, args) |             self.install_process.start(winehelper_path, args) | ||||||
|             if not self.install_process.waitForStarted(3000): |             if not self.install_process.waitForStarted(3000): | ||||||
|                 raise RuntimeError("Не удалось запустить процесс установки") |                 raise RuntimeError("Не удалось запустить процесс установки") | ||||||
|             self.append_log("Процесс установки успешно запущен...") |             self.append_log("Процесс установки запущен...") | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             self.append_log(f"\n=== ОШИБКА: {str(e)} ===", is_error=True) |             self.append_log(f"\n=== ОШИБКА: {str(e)} ===", is_error=True) | ||||||
|             QMessageBox.critical(self.install_dialog, "Ошибка", f"Не удалось запустить установку:\n{str(e)}") |             QMessageBox.critical(self.install_dialog, "Ошибка", f"Не удалось запустить установку:\n{str(e)}") | ||||||
|             self.cleanup_process() |             self.cleanup_process() | ||||||
|  |  | ||||||
|     def append_log(self, text, is_error=False): |     def append_log(self, text, is_error=False, add_newline=True): | ||||||
|         """Добавляет сообщение в лог""" |         """Добавляет сообщение в лог""" | ||||||
|         if not hasattr(self, 'log_output'): return |         if not hasattr(self, 'log_output'): return | ||||||
|         cursor = self.log_output.textCursor() |         cursor = self.log_output.textCursor() | ||||||
|         cursor.movePosition(QTextCursor.End) |         cursor.movePosition(QTextCursor.End) | ||||||
|  |  | ||||||
|         if is_error: |         if is_error: | ||||||
|  |             # Для ошибок всегда добавляем перенос строки для лучшей читаемости | ||||||
|             cursor.insertHtml(f'<font color="red">{text}</font><br>') |             cursor.insertHtml(f'<font color="red">{text}</font><br>') | ||||||
|         else: |         else: | ||||||
|             cursor.insertText(f"{text}\n") |             # Вставляем текст. Добавляем перенос строки, если нужно. | ||||||
|  |             formatted_text = f"{text}\n" if add_newline else text | ||||||
|  |             cursor.insertText(formatted_text) | ||||||
|  |  | ||||||
|         self.log_output.ensureCursorVisible() |         self.log_output.ensureCursorVisible() | ||||||
|         QApplication.processEvents() |         QApplication.processEvents() | ||||||
|  |  | ||||||
|  |     def _process_log_line(self, line_with_delimiter): | ||||||
|  |         """Обрабатывает одну строку лога, управляя заменой строк прогресса.""" | ||||||
|  |         is_progress_line = '\r' in line_with_delimiter | ||||||
|  |  | ||||||
|  |         # Фильтруем "мусорные" строки прогресса (например, '-=O=-' от wget), | ||||||
|  |         # обрабатывая только те, что содержат знак процента. | ||||||
|  |         if is_progress_line: | ||||||
|  |             if not re.search(r'\d\s*%', line_with_delimiter): | ||||||
|  |                 return  # Игнорируем строку прогресса без процентов | ||||||
|  |  | ||||||
|  |         clean_line = line_with_delimiter.replace('\r', '').replace('\n', '').strip() | ||||||
|  |  | ||||||
|  |         if not clean_line: | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         cursor = self.log_output.textCursor() | ||||||
|  |  | ||||||
|  |         # Если новая строка - это прогресс, и предыдущая тоже была прогрессом, | ||||||
|  |         # то мы удаляем старую, чтобы заменить ее новой. | ||||||
|  |         if is_progress_line and self.last_line_was_progress: | ||||||
|  |             cursor.movePosition(QTextCursor.End) | ||||||
|  |             cursor.select(QTextCursor.LineUnderCursor) | ||||||
|  |             cursor.removeSelectedText() | ||||||
|  |         elif not is_progress_line and self.last_line_was_progress: | ||||||
|  |             # Это переход от строки прогресса к финальной строке. | ||||||
|  |             # Вместо добавления переноса, мы заменяем предыдущую строку новой. | ||||||
|  |             cursor.movePosition(QTextCursor.End) | ||||||
|  |             cursor.select(QTextCursor.LineUnderCursor) | ||||||
|  |             cursor.removeSelectedText() | ||||||
|  |  | ||||||
|  |         # Добавляем новую очищенную строку. | ||||||
|  |         # Для прогресса - без переноса строки, для обычных строк - с переносом. | ||||||
|  |         self.append_log(clean_line, add_newline=not is_progress_line) | ||||||
|  |  | ||||||
|  |         self.last_line_was_progress = is_progress_line | ||||||
|  |  | ||||||
|     def handle_process_output(self): |     def handle_process_output(self): | ||||||
|         """Обрабатывает вывод процесса""" |         """Обрабатывает вывод процесса, корректно отображая однострочный прогресс.""" | ||||||
|         output = self.install_process.readAllStandardOutput().data().decode('utf-8', errors='ignore').strip() |         new_data = self.install_process.readAllStandardOutput().data().decode('utf-8', errors='ignore') | ||||||
|         if output: |         self.output_buffer += new_data | ||||||
|             self.append_log(output) |  | ||||||
|  |         while True: | ||||||
|  |             # Ищем ближайший разделитель (\n или \r) | ||||||
|  |             idx_n = self.output_buffer.find('\n') | ||||||
|  |             idx_r = self.output_buffer.find('\r') | ||||||
|  |  | ||||||
|  |             if idx_n == -1 and idx_r == -1: | ||||||
|  |                 break  # Нет полных строк для обработки | ||||||
|  |  | ||||||
|  |             split_idx = min(idx for idx in [idx_n, idx_r] if idx != -1) | ||||||
|  |  | ||||||
|  |             # Получаем строку, включая разделитель | ||||||
|  |             line = self.output_buffer[:split_idx + 1] | ||||||
|  |             self.output_buffer = self.output_buffer[split_idx + 1:] | ||||||
|  |  | ||||||
|  |             self._process_log_line(line) | ||||||
|  |  | ||||||
|     def handle_process_finished(self, exit_code, exit_status): |     def handle_process_finished(self, exit_code, exit_status): | ||||||
|         """Обрабатывает завершение процесса""" |         """Обрабатывает завершение процесса""" | ||||||
|  |         # Обрабатываем остаток в буфере, если он есть | ||||||
|  |         if self.output_buffer: | ||||||
|  |             self._process_log_line(self.output_buffer) | ||||||
|  |  | ||||||
|  |         # Если последней строкой был прогресс, "завершаем" его переносом строки. | ||||||
|  |         if self.last_line_was_progress: | ||||||
|  |             cursor = self.log_output.textCursor() | ||||||
|  |             cursor.movePosition(QTextCursor.End) | ||||||
|  |             cursor.insertText("\n") | ||||||
|  |  | ||||||
|  |         self._reset_log_state() | ||||||
|         if exit_code == 0 and exit_status == QProcess.NormalExit: |         if exit_code == 0 and exit_status == QProcess.NormalExit: | ||||||
|             self.append_log("\n=== Установка успешно завершена ===") |             self.append_log("\n=== Установка успешно завершена ===") | ||||||
|             # Создаем кастомный диалог, чтобы кнопка была на русском |             # Создаем кастомный диалог, чтобы кнопка была на русском | ||||||
|             success_box = QMessageBox(self.install_dialog) |             success_box = QMessageBox(self.install_dialog) | ||||||
|             success_box.setWindowTitle("Успех") |             success_box.setWindowTitle("Успех") | ||||||
|             success_box.setText(f"Программа {self.current_script} установлена успешно!") |             title_name = self._get_current_app_title() | ||||||
|  |             success_box.setText(f"Программа «{title_name}» установлена успешно!") | ||||||
|             success_box.setIcon(QMessageBox.Information) |             success_box.setIcon(QMessageBox.Information) | ||||||
|             success_box.addButton("Готово", QMessageBox.AcceptRole) |             success_box.addButton("Готово", QMessageBox.AcceptRole) | ||||||
|             success_box.exec_() |             success_box.exec_() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user