diff --git a/winehelper_gui.py b/winehelper_gui.py index 354f304..cb8fefd 100644 --- a/winehelper_gui.py +++ b/winehelper_gui.py @@ -2,6 +2,7 @@ import os import subprocess import sys +import re import shlex import shutil import html @@ -96,6 +97,9 @@ class WineHelperGUI(QMainWindow): self.create_installed_tab() self.create_help_tab() + # Инициализируем состояние, которое будет использоваться для логов + self._reset_log_state() + # Обновляем список установленных приложений self.update_installed_apps() @@ -1339,10 +1343,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", @@ -1395,28 +1405,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'{text}
') 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=== Установка успешно завершена ===") # Создаем кастомный диалог, чтобы кнопка была на русском