Compare commits

..

1 Commits

Author SHA1 Message Date
9611cc52fc the gui loading process has been fixed 2025-08-05 15:05:28 +06:00
3 changed files with 122 additions and 55 deletions

View File

@ -1,14 +0,0 @@
Лицензионные соглашения использования сторонних компонентов:
Некоторые компоненты, установленные в префикс и необходимые для запуска приложений,
могут быть защищены авторским правом или лицензионными соглашениями. Вы обязаны
самостоятельно убедиться в законности использования этих компонентов в вашей
юрисдикции.
Мы не несём ответственности за нарушение лицензионных соглашений, связанное с
использованием подготовленного префикса, а так же за программное обеспечение,
поставляемое из сторонних источников.
Подтверждая продолжение установки, вы соглашаетесь, что ознакомились с данным
отказом от ответственности и принимаете все риски, связанные с использованием
программного обеспечения.

View File

@ -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 AGREEMENT export SCRIPT_NAME USER_WORK_PATH RUN_SCRIPT DATA_PATH CHANGELOG_FILE WH_ICON_PATH LICENSE_FILE
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,7 +18,6 @@ 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"
@ -27,7 +26,6 @@ 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" \
@ -369,14 +367,20 @@ 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"

View File

@ -2,9 +2,9 @@
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)
@ -21,7 +21,6 @@ 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):
@ -91,6 +90,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()
@ -606,6 +608,7 @@ 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)
@ -1256,32 +1259,38 @@ class WineHelperGUI(QMainWindow):
license_page = QWidget() license_page = QWidget()
license_layout = QVBoxLayout(license_page) license_layout = QVBoxLayout(license_page)
license_found = False license_text = QTextEdit()
license_text.setReadOnly(True)
license_text = QTextBrowser() # Получаем текст лицензии из скрипта winehelper
script_path = os.path.join(Var.DATA_PATH, "winehelper")
# Получаем текст лицензионного соглашения из файла license_content = ""
try: try:
license_file_path = Var.LICENSE_AGREEMENT_FILE with open(script_path, 'r', encoding='utf-8') as f:
if not license_file_path or not os.path.exists(license_file_path): capturing = False
raise FileNotFoundError for line in f:
if 'print_warning "Лицензионные соглашения использования сторонних компонентов:' in line:
capturing = True
continue
with open(license_file_path, 'r', encoding='utf-8') as f: if capturing:
license_content = f.read() if 'Подтверждая продолжение установки' in line:
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"""
<pre style="font-family: sans-serif; font-size: 10pt; white-space: pre-wrap; word-wrap: break-word;">{escaped_license_content}</pre> <h3>Лицензионные соглашения использования сторонних компонентов:</h3>
<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(f""" license_text.setHtml("""
<h3>Лицензионные соглашения</h3> <h3>Лицензионные соглашения</h3>
<p>Произошла ошибка при чтении файла лицензии:<br>{str(e)}</p> <p>Не удалось загрузить текст лицензионного соглашения.</p>
""") """)
license_layout.addWidget(license_text) license_layout.addWidget(license_text)
@ -1328,15 +1337,18 @@ 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,
"autoinstall" if self.current_script in self.autoinstall_scripts else "manualinstall", "autoinstall" if self.current_script in self.autoinstall_scripts else "manualinstall",
@ -1383,34 +1395,99 @@ 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=== Установка успешно завершена ===")
# Создаем кастомный диалог, чтобы кнопка была на русском # Создаем кастомный диалог, чтобы кнопка была на русском