diff --git a/winehelper_gui.py b/winehelper_gui.py
index 4ad2f16..4affe8c 100644
--- a/winehelper_gui.py
+++ b/winehelper_gui.py
@@ -33,393 +33,6 @@ class Var:
GENERAL = os.environ.get("GENERAL")
WH_WINETRICKS = os.environ.get("WH_WINETRICKS")
-class DependencyManager:
- """Класс для управления проверкой и установкой системных зависимостей."""
-
- def __init__(self):
- """Инициализирует менеджер, определяя необходимые пути."""
- self.dependencies_script_path = self._get_dependencies_path()
- self.config_dir = os.path.join(os.path.expanduser("~"), ".config", "winehelper")
- self.hash_flag_file = os.path.join(self.config_dir, "dependencies_hash.txt")
- os.makedirs(self.config_dir, exist_ok=True)
- self.app_icon = QIcon(Var.WH_ICON_PATH) if Var.WH_ICON_PATH and os.path.exists(Var.WH_ICON_PATH) else QIcon()
-
- def _get_dependencies_path(self):
- """Определяет и возвращает путь к скрипту dependencies.sh."""
- if not Var.DATA_PATH:
- return None
-
- return os.path.join(Var.DATA_PATH, 'dependencies.sh')
-
- def _calculate_file_hash(self):
- """Вычисляет хэш SHA256 файла зависимостей."""
- if not self.dependencies_script_path or not os.path.exists(self.dependencies_script_path):
- return None
- hasher = hashlib.sha256()
- try:
- with open(self.dependencies_script_path, 'rb') as f:
- while chunk := f.read(4096):
- hasher.update(chunk)
- return hasher.hexdigest()
- except IOError:
- return None
-
- def _parse_dependencies_from_script(self):
- """
- Парсит скрипт dependencies.sh для извлечения списка базовых пакетов.
- Возвращает список пакетов или None в случае ошибки.
- """
- if not os.path.exists(self.dependencies_script_path):
- return None
-
- base_packages = []
- try:
- with open(self.dependencies_script_path, 'r', encoding='utf-8') as f:
- content = f.read()
-
- content = content.replace('\\\n', '')
- pattern = r'apt-get install\s+\{i586-,\}(\{.*?\}|[\w.-]+)'
- matches = re.findall(pattern, content)
-
- for match in matches:
- match = match.strip()
- if match.startswith('{') and match.endswith('}'):
- group_content = match[1:-1]
- packages = [pkg.strip() for pkg in group_content.split(',')]
- base_packages.extend(packages)
- else:
- base_packages.append(match)
-
- return sorted(list(set(pkg for pkg in base_packages if pkg))) or None
- except Exception:
- return None
-
- def _parse_repo_error_from_script(self):
- """
- Парсит скрипт dependencies.sh для извлечения сообщения об ошибке репозитория.
- Возвращает сообщение или None в случае ошибки.
- """
- if not os.path.exists(self.dependencies_script_path):
- return None
-
- try:
- with open(self.dependencies_script_path, 'r', encoding='utf-8') as f:
- content = f.read()
-
- content = content.replace('\\\n', ' ')
- match = re.search(r'apt-repo.*\|\|.*fatal\s+"([^"]+)"', content)
- if match:
- error_message = match.group(1).strip()
- return re.sub(r'\s+', ' ', error_message)
- return None
- except Exception:
- return None
-
- def _show_startup_messages(self):
- """
- Проверяет, является ли это первым запуском или обновлением,
- и показывает соответствующие сообщения.
- """
- current_hash = self._calculate_file_hash()
- stored_hash = None
- hash_file_exists = os.path.exists(self.hash_flag_file)
-
- if hash_file_exists:
- try:
- with open(self.hash_flag_file, 'r', encoding='utf-8') as f:
- stored_hash = f.read().strip()
- except IOError:
- pass
-
- if not hash_file_exists:
- msg_box = QMessageBox(QMessageBox.Information, "Первый запуск WineHelper",
- "Поскольку это первый запуск, программа проверит наличие необходимых системных зависимостей.")
- msg_box.setWindowIcon(self.app_icon)
- msg_box.exec_()
- elif current_hash != stored_hash:
- msg_box = QMessageBox(QMessageBox.Information, "Обновление зависимостей",
- "Обнаружены изменения в системных требованиях.\n\n"
- "Программа выполнит проверку, чтобы убедиться, что все компоненты на месте.")
- msg_box.setWindowIcon(self.app_icon)
- msg_box.exec_()
-
- def _save_dependency_hash(self):
- """Сохраняет хэш зависимостей в конфигурационный файл."""
- current_hash = self._calculate_file_hash()
- if not current_hash:
- return
- try:
- with open(self.hash_flag_file, 'w', encoding='utf-8') as f:
- f.write(current_hash)
- except IOError:
- print("Предупреждение: не удалось записать файл с хэшем зависимостей.")
-
- def _perform_check_and_install(self):
- """
- Выполняет основную логику проверки и установки зависимостей.
- Возвращает True в случае успеха, иначе False.
- """
- # Проверка наличия ключевых утилит
- if not shutil.which('apt-repo') or not shutil.which('rpm'):
- return True
-
- if not self.dependencies_script_path or not os.path.exists(self.dependencies_script_path):
- msg_box = QMessageBox(QMessageBox.Critical, "Критическая ошибка",
- f"Файл зависимостей не найден по пути:\n'{self.dependencies_script_path}'.\n\n"
- "Программа не может продолжить работу."
- )
- msg_box.setWindowIcon(self.app_icon)
- msg_box.exec_()
- return False
-
- # 1. Проверка наличия репозитория x86_64-i586
- try:
- result = subprocess.run(['apt-repo'], capture_output=True, text=True, check=False, encoding='utf-8')
- if result.returncode != 0 or 'x86_64-i586' not in result.stdout:
- error_message = self._parse_repo_error_from_script()
- if not error_message:
- msg_box = QMessageBox(QMessageBox.Critical, "Критическая ошибка",
- f"Репозиторий x86_64-i586 не подключен, но не удалось извлечь текст ошибки из файла:\n'{self.dependencies_script_path}'.\n\n"
- "Проверьте целостность скрипта. Работа программы будет прекращена."
- )
- msg_box.setWindowIcon(self.app_icon)
- msg_box.exec_()
- return False
-
- msg_box = QMessageBox(QMessageBox.Critical, "Ошибка репозитория",
- f"{error_message}\n\nРабота программы будет прекращена.")
- msg_box.setWindowIcon(self.app_icon)
- msg_box.exec_()
- return False
- except FileNotFoundError:
- return True
-
- # 2. Определение списка пакетов из dependencies.sh
- base_packages = self._parse_dependencies_from_script()
-
- # Если парсинг не удался или скрипт не найден, прерываем работу.
- if not base_packages:
- msg_box = QMessageBox(QMessageBox.Critical, "Критическая ошибка",
- f"Не удалось найти или проанализировать файл зависимостей:\n'{self.dependencies_script_path}'.\n\n"
- "Программа не может продолжить работу без списка необходимых пакетов."
- )
- msg_box.setWindowIcon(self.app_icon)
- msg_box.exec_()
- return False
-
- required_packages = [f"i586-{pkg}" for pkg in base_packages] + base_packages
-
- # 3. Проверка, какие пакеты отсутствуют
- try:
- # Запрашиваем список всех установленных пакетов один раз для эффективности
- result = subprocess.run(
- ['rpm', '-qa', '--queryformat', '%{NAME}\n'],
- capture_output=True, text=True, check=True, encoding='utf-8'
- )
- installed_packages_set = set(result.stdout.splitlines())
- required_packages_set = set(required_packages)
-
- # Находим разницу между требуемыми и установленными пакетами
- missing_packages = sorted(list(required_packages_set - installed_packages_set))
-
- except (subprocess.CalledProcessError, FileNotFoundError) as e:
- # В случае ошибки (например, rpm не найден), показываем критическое сообщение
- msg_box = QMessageBox(QMessageBox.Critical, "Критическая ошибка",
- f"Не удалось получить список установленных пакетов с помощью rpm.\n\nОшибка: {e}\n\n"
- "Программа не может проверить зависимости и будет закрыта."
- )
- msg_box.setWindowIcon(self.app_icon)
- msg_box.exec_()
- return False
-
- if not missing_packages:
- return True
-
- # 4. Запрос у пользователя на установку
- msg_box = QMessageBox()
- msg_box.setWindowIcon(self.app_icon)
- msg_box.setIcon(QMessageBox.Warning)
- msg_box.setWindowTitle("Отсутствуют зависимости")
- # Устанавливаем формат текста в RichText, чтобы QMessageBox корректно обрабатывал HTML-теги.
- msg_box.setTextFormat(Qt.RichText)
-
- # Формируем весь текст как единый HTML-блок для корректного отображения.
- full_html_text = (
- "Для корректной работы WineHelper требуются дополнительные системные компоненты.
"
- "Отсутствуют следующие пакеты:
"
- # Ограничиваем высоту блока с пакетами и добавляем прокрутку, если список длинный.
- f"""
Пожалуйста, дождитесь окончания процесса.
" - "Закрыть основное окно можно будет после завершения установки.
" - ) - label.setTextFormat(Qt.RichText) - label.setAlignment(Qt.AlignCenter) - layout.addWidget(label) - - info_dialog.exec_() - event.ignore() - else: - event.accept() - - process.readyRead.connect(handle_output) - process.finished.connect(handle_finish) - - log_output.append(f"Выполнение команды:\npkexec sh -c \"{install_cmd_str}\"\n") - log_output.append("Пожалуйста, введите пароль в появившемся окне аутентификации...") - log_output.append("-" * 40 + "\n") - - dialog.closeEvent = dialog_close_handler - process.start('pkexec', ['sh', '-c', install_cmd_str]) - dialog.exec_() - - return installation_successful - - def run(self): - """ - Основной публичный метод для запуска полной проверки зависимостей. - Возвращает True, если все зависимости удовлетворены, иначе False. - """ - self._show_startup_messages() - if self._perform_check_and_install(): - self._save_dependency_hash() - return True - return False class WinetricksManagerDialog(QDialog): """Диалог для управления компонентами Winetricks.""" @@ -5201,38 +4814,35 @@ def main(): # На всякий случай удаляем старый файл сокета, если он остался от сбоя. QLocalServer.removeServer(socket_name) - dependency_manager = DependencyManager() - if dependency_manager.run(): - window = WineHelperGUI() + window = WineHelperGUI() - # Создаем локальный сервер для приема "сигналов" от последующих запусков - server = QLocalServer(window) + # Создаем локальный сервер для приема "сигналов" от последующих запусков + server = QLocalServer(window) - # Функция для обработки входящих подключений - def handle_new_connection(): - client_socket = server.nextPendingConnection() - if client_socket: - # Ждем данные (не обязательно, но для надежности) - client_socket.waitForReadyRead(100) - client_socket.readAll() # Очищаем буфер - window.activate() - client_socket.close() + # Функция для обработки входящих подключений + def handle_new_connection(): + client_socket = server.nextPendingConnection() + if client_socket: + # Ждем данные (не обязательно, но для надежности) + client_socket.waitForReadyRead(100) + client_socket.readAll() # Очищаем буфер + window.activate() + client_socket.close() - server.newConnection.connect(handle_new_connection) + server.newConnection.connect(handle_new_connection) - # Начинаем слушать. Если не удалось, программа все равно будет работать, - # но без функции активации существующего окна. - if not server.listen(socket_name): - print(f"Предупреждение: не удалось запустить сервер {socket_name}: {server.errorString()}") + # Начинаем слушать. Если не удалось, программа все равно будет работать, + # но без функции активации существующего окна. + if not server.listen(socket_name): + print(f"Предупреждение: не удалось запустить сервер {socket_name}: {server.errorString()}") - # Сохраняем ссылку на сервер, чтобы он не был удален сборщиком мусора - window.server = server - window.show() - # Создаем иконку в системном трее после создания окна - # window.create_tray_icon() # Временно отключено - return app.exec_() + # Сохраняем ссылку на сервер, чтобы он не был удален сборщиком мусора + window.server = server + window.show() + # Создаем иконку в системном трее после создания окна + # window.create_tray_icon() # Временно отключено + return app.exec_() - return 1 if __name__ == "__main__": sys.exit(main())