@ -2,6 +2,7 @@
import os
import subprocess
import sys
import re
import shlex
import shutil
from PyQt5 . QtWidgets import ( QApplication , QMainWindow , QWidget , QVBoxLayout , QHBoxLayout , QPushButton , QLabel , QTabWidget ,
@ -89,6 +90,9 @@ class WineHelperGUI(QMainWindow):
self . create_installed_tab ( )
self . create_help_tab ( )
# Инициализируем состояние, которое будет использоваться для логов
self . _reset_log_state ( )
# Обновляем список установленных приложений
self . update_installed_apps ( )
@ -1335,10 +1339,16 @@ class WineHelperGUI(QMainWindow):
self . install_dialog . show ( )
def _reset_log_state ( self ) :
""" Сбрасывает состояние буфера и флага прогресса для лога установки. """
self . output_buffer = " "
self . last_line_was_progress = False
def _prepare_installation ( self ) :
""" Подготавливает и запускает процесс установки """
self . stacked_widget . setCurrentIndex ( 1 )
self . _reset_log_state ( ) # Сбрасываем состояние для обработки лога
winehelper_path = self . winehelper_path
script_path = os . path . join ( Var . DATA_PATH ,
" autoinstall " if self . current_script in self . autoinstall_scripts else " manualinstall " ,
@ -1391,28 +1401,93 @@ class WineHelperGUI(QMainWindow):
QMessageBox . critical ( self . install_dialog , " Ошибка " , f " Н е удалось запустить установку:\n { str ( e ) } " )
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
cursor = self . log_output . textCursor ( )
cursor . movePosition ( QTextCursor . End )
if is_error :
# Для ошибок всегда добавляем перенос строки для лучшей читаемости
cursor . insertHtml ( f ' <font color= " red " > { text } </font><br> ' )
else :
cursor . insertText ( f " { text } \n " )
# Вставляем текст. Добавляем перенос строки, если нужно.
formatted_text = f " { text } \n " if add_newline else text
cursor . insertText ( formatted_text )
self . log_output . ensureCursorVisible ( )
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 ) :
""" Обрабатывает вывод процесса """
output = self . install_process . readAllStandardOutput ( ) . data ( ) . decode ( ' utf-8 ' , errors = ' ignore ' ) . strip ( )
if output :
self . append_log ( output )
""" Обрабатывает вывод процесса, корректно отображая однострочный прогресс. """
new_data = self . install_process . readAllStandardOutput ( ) . data ( ) . decode ( ' utf-8 ' , errors = ' ignore ' )
self . output_buffer + = new_data
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 ) :
""" Обрабатывает завершение процесса """
# Обрабатываем остаток в буфере, если он есть
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 :
self . append_log ( " \n === Установка успешно завершена === " )
# Создаем кастомный диалог, чтобы кнопка была на русском