Compare commits

..

1 Commits

Author SHA1 Message Date
cb3fdc62dc added quotes in the install button 2025-08-05 17:09:13 +06:00

View File

@ -2,7 +2,6 @@
import os import os
import subprocess import subprocess
import sys import sys
import re
import shlex import shlex
import shutil import shutil
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QTabWidget, from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QTabWidget,
@ -65,6 +64,7 @@ class WineHelperGUI(QMainWindow):
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 = []
@ -90,9 +90,6 @@ 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()
@ -166,7 +163,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)
@ -202,7 +199,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)
@ -837,6 +834,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()
@ -1210,6 +1212,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:
# Для заголовка используем первую иконку из списка # Для заголовка используем первую иконку из списка
@ -1232,7 +1235,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):
"""Устанавливает текущий выбранный скрипт""" """Устанавливает текущий выбранный скрипт"""
@ -1246,7 +1249,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)
@ -1339,16 +1343,10 @@ class WineHelperGUI(QMainWindow):
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,
"autoinstall" if self.current_script in self.autoinstall_scripts else "manualinstall", "autoinstall" if self.current_script in self.autoinstall_scripts else "manualinstall",
@ -1387,7 +1385,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)}")
@ -1401,99 +1400,35 @@ class WineHelperGUI(QMainWindow):
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, add_newline=True): def append_log(self, text, is_error=False):
"""Добавляет сообщение в лог""" """Добавляет сообщение в лог"""
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):
"""Обрабатывает вывод процесса, корректно отображая однострочный прогресс.""" """Обрабатывает вывод процесса"""
new_data = self.install_process.readAllStandardOutput().data().decode('utf-8', errors='ignore') output = self.install_process.readAllStandardOutput().data().decode('utf-8', errors='ignore').strip()
self.output_buffer += new_data if output:
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_()