Compare commits

..

13 Commits

Author SHA1 Message Date
Mikhail Tergoev
0fc93f4d3c updated changelog to 0.8.0 2025-10-24 14:35:22 +03:00
Mikhail Tergoev
5f27bfa97a updated scadoffice script 2025-10-24 14:34:27 +03:00
Mikhail Tergoev
151d0ffc48 Merge branch 'minergenon-devel' 2025-10-23 11:11:50 +03:00
Mikhail Tergoev
ad91466475 updated print message functions 2025-10-23 11:11:18 +03:00
Sergey Palcheh
5e4d94bb57 temporarily disable the display of the tray icon 2025-10-23 14:02:50 +06:00
Sergey Palcheh
5b572ff540 added a change in the color of the tray icon depending on the theme 2025-10-23 14:00:20 +06:00
Sergey Palcheh
c68bcc9abf added a method to change the color of the tray icon 2025-10-23 12:38:10 +06:00
Sergey Palcheh
1ad2c6cfa8 The Manual sub-tab has been renamed to General 2025-10-23 11:31:43 +06:00
Sergey Palcheh
16a686dc37 added a new file with general information 2025-10-23 11:30:37 +06:00
Mikhail Tergoev
c9d5619ab9 used simply wine_run for .reg files 2025-10-22 15:16:22 +03:00
Mikhail Tergoev
74311e9c04 fixed: remove OUT_PAGE_TMP after use install scripts 2025-10-22 15:14:57 +03:00
Sergey Palcheh
eb9bef83e2 improved display of test versions 2025-10-22 11:13:55 +06:00
Sergey Palcheh
c7eddb8b53 tray icon changed to winehelper-symbolic.svg 2025-10-22 09:25:19 +06:00
5 changed files with 196 additions and 113 deletions

View File

@@ -1,5 +1,10 @@
История изменений: История изменений:
0.8.0
* добавлено автоопределение .reg и .dll файлов для их добавленния в реестр префикса
* во вкладку "Справка" добавлены ссылки на старницы с сертификатами
* другие небольшие улучшения и оптимизации скриптов и графического интерфейса
0.7.0 0.7.0
* обновлен графический режим Qt5 * обновлен графический режим Qt5
- добавлена кнопка открытия каталога с резервными копиями и логами - добавлена кнопка открытия каталога с резервными копиями и логами
@@ -8,7 +13,7 @@
- добавлено отображения процесса установки сторонних компонентов с помощью winetricks - добавлено отображения процесса установки сторонних компонентов с помощью winetricks
- добавлена возможность отображения и установки тестовых скриптов (выключено по умолчанию) - добавлена возможность отображения и установки тестовых скриптов (выключено по умолчанию)
* добавлены скрипты установки для t-flex версии 18 * добавлены скрипты установки для t-flex версии 18
* добавлен список тестовых скриптов установки ПО * в CLI добавлен список тестовых скриптов установки ПО
* добавлена возможность ассоциации файлов для передачи в приложения запускаемых в WineHelper * добавлена возможность ассоциации файлов для передачи в приложения запускаемых в WineHelper
0.6.0 0.6.0

7
GENERAL Normal file
View File

@@ -0,0 +1,7 @@
# Руководство пользователя
Подробное и актуальное руководство по использованию WineHelper смотрите на сайте: https://www.altlinux.org/Winehelper
# Совместимость ПО и сертификаты
С полным списком совместимого ПО и сертификатами можно ознакомиться по следующим ссылкам:
Для 10 платформы: https://www.basealt.ru/fileadmin/user_upload/compatibility/P10-view2.html
Для 11 платформы: https://www.basealt.ru/fileadmin/user_upload/compatibility/P11-view2.html

View File

@@ -2,15 +2,16 @@
# info_ru: SCAD Office — это программный комплекс для расчёта строительных конструкций. # info_ru: SCAD Office — это программный комплекс для расчёта строительных конструкций.
######################################################################## ########################################################################
export PROG_URL="https://scadoffice.ru" export PROG_URL="https://scadoffice.ru"
export WH_WINE_USE="wine_x_tkg_10-0_amd64" export WH_WINE_USE="wine-8.8-staging-amd64"
export WINEPREFIX="scadoffice" export WINEPREFIX="scadoffice"
export PROG_NAME="SCAD Office" export PROG_NAME="SCAD Office"
export PROG_ICON="scadoffice" export PROG_ICON="scadoffice"
export BASE_PFX="scadaoffice_pfx_x64_v05" export BASE_PFX="none"
export WH_WINDOWS_VER="10" export WH_WINDOWS_VER="10"
export WINEARCH="win64" export WINEARCH="win64"
export INSTALL_DLL="dotnet20 dotnet48 gdiplus vcrun6sp6 vcrun2005 vcrun2019 d3dx11_42 d3dx11_43 d3dx9 d3dcompiler_42 d3dcompiler_43 d3dcompiler_46 d3dcompiler_47 richtx32 riched30 riched20 msxml6" export INSTALL_DLL="dotnet48 gdiplus vcrun6sp6 vcrun2005 vcrun2019 d3dx11_42 d3dx11_43 d3dx9 d3dcompiler_42 d3dcompiler_43 d3dcompiler_46 d3dcompiler_47 richtx32 riched30 riched20 msxml6" #dotnet20
export WH_XDG_OPEN="rtf" export WH_XDG_OPEN="rtf"
AUTOINSTALL_EXE="${WH_TMP_DIR}/SCADOffice_installer.exe" AUTOINSTALL_EXE="${WH_TMP_DIR}/SCADOffice_installer.exe"
SCADOFFICE_ADDONS_URL="https://cloud.linux-gaming.ru/portproton/scadoffice_addons_v02.tar.xz" SCADOFFICE_ADDONS_URL="https://cloud.linux-gaming.ru/portproton/scadoffice_addons_v02.tar.xz"
@@ -32,40 +33,37 @@ if [[ -n $2 ]] ; then
fi fi
fi fi
print_info "Установка дополнительных компонентов..."
ADDONS_PACK="${WH_TMP_DIR}/$(basename "$SCADOFFICE_ADDONS_URL")"
ADDONS_PATH="${WH_TMP_DIR}/scadoffice_addons"
ADDONS_PATH_REG="${ADDONS_PATH}/REG"
ADDONS_PATH_MDAC="${ADDONS_PATH}/mdac64"
ADDONS_PATH_OPENSSH="${ADDONS_PATH}/OpenSSH"
if try_download "$SCADOFFICE_ADDONS_URL" "${ADDONS_PACK}" ; then
create_new_dir "${ADDONS_PATH}"
unpack "${ADDONS_PACK}" "${ADDONS_PATH}"
wine_run regedit "${ADDONS_PATH_REG}"/*.reg
# Установка ODBC
rm -fR "$DRIVE_C/Program Files (x86)/Common Files/System"
cp -r "${ADDONS_PATH_MDAC}/System" "$DRIVE_C/Program Files (x86)/Common Files/System"
cp -r "${ADDONS_PATH_MDAC}"/*.* "$DRIVE_C/windows/system32/"
wine_run regedit "${ADDONS_PATH_MDAC}"/*.reg
# Установка SSH
cp -r "${ADDONS_PATH_OPENSSH}" "$DRIVE_C/windows/system32/"
try_remove_dir "$ADDONS_PATH"
try_remove_file "$ADDONS_PACK"
fi
if try_download "https://scadhelp.ru/files/10/download" "${AUTOINSTALL_EXE}" ; then if try_download "https://scadhelp.ru/files/10/download" "${AUTOINSTALL_EXE}" ; then
create_new_dir "$DRIVE_C/SDATA" create_new_dir "$DRIVE_C/SDATA"
create_new_dir "$DRIVE_C/SWORK" create_new_dir "$DRIVE_C/SWORK"
# временно запрещаем запуск hasplms.exe, hasplmv.exe для успешного завершения установки
tmp_winedlloverride_update "hasplms.exe,hasplmv.exe=d"
wine_run_install "$AUTOINSTALL_EXE" wine_run_install "$AUTOINSTALL_EXE"
try_remove_file "$AUTOINSTALL_EXE" # try_remove_file "$AUTOINSTALL_EXE"
fi
print_info "Установка дополнительных компонентов..."
ADDONS_PACK="${WH_TMP_DIR}/$(basename "$SCADOFFICE_ADDONS_URL")"
if try_download "$SCADOFFICE_ADDONS_URL" "$ADDONS_PACK" ; then
ADDONS_PATH="${WH_TMP_DIR}/scadoffice_addons"
create_new_dir "$ADDONS_PATH"
unpack "$ADDONS_PACK" "$ADDONS_PATH"
# try_remove_file "$ADDONS_PACK"
# Установка ODBC
try_remove_dir "${DRIVE_C}/Program Files/Common Files/System"
cp -r "${ADDONS_PATH}/mdac64/System" "${DRIVE_C}/Program Files/Common Files/" || fatal
cp -r "${ADDONS_PATH}/mdac64"/*.* "${DRIVE_C}/windows/system32/" || fatal
# Установка SSH
cp -r "${ADDONS_PATH}/OpenSSH" "${DRIVE_C}/windows/system32/" || fatal
# Применение .reg файлов для изменения реестра
wine_run "${ADDONS_PATH}/mdac64"/*.reg
wine_run "${ADDONS_PATH}/REG"/*.reg
try_remove_dir "$ADDONS_PATH"
fi
# Определение всех программ, значков и исполняемых файлов # Определение всех программ, значков и исполняемых файлов
declare -a PROG_NAME_All=("Арбат" "Вест" "Глобальные настройки" "Декор" "Дискретная арматура" "Запрос" "Камин" "КоКон" "Комета" "Конструктор сечений" "Консул" "Конфигуратор лицензий" "Кристалл" "Кросс" "Куст" "Магнум" "Монолит" "Откос" "Пастернак" "Преобразование ед. измерений" "Cортамент металлопроката" "Расчет по формуле" "Редактор акселерограмм" "Редактор динамичности" "Редактор материалов" "Тонус" "Эквивалентное сечение" "SCAD++") declare -a PROG_NAME_All=("Арбат" "Вест" "Глобальные настройки" "Декор" "Дискретная арматура" "Запрос" "Камин" "КоКон" "Комета" "Конструктор сечений" "Консул" "Конфигуратор лицензий" "Кристалл" "Кросс" "Куст" "Магнум" "Монолит" "Откос" "Пастернак" "Преобразование ед. измерений" "Cортамент металлопроката" "Расчет по формуле" "Редактор акселерограмм" "Редактор динамичности" "Редактор материалов" "Тонус" "Эквивалентное сечение" "SCAD++")
@@ -78,4 +76,3 @@ if try_download "https://scadhelp.ru/files/10/download" "${AUTOINSTALL_EXE}" ; t
for i in "${!PROG_NAME_All[@]}"; do for i in "${!PROG_NAME_All[@]}"; do
create_desktop "${PROG_NAME_All[i]}" "${WIN_FILE_EXEC[i]}" "${PROG_ICON_ALL[i]}" "nocopy" create_desktop "${PROG_NAME_All[i]}" "${WIN_FILE_EXEC[i]}" "${PROG_ICON_ALL[i]}" "nocopy"
done done
fi

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 THIRD_PARTY_FILE export SCRIPT_NAME USER_WORK_PATH RUN_SCRIPT DATA_PATH CHANGELOG_FILE WH_ICON_PATH LICENSE_FILE AGREEMENT THIRD_PARTY_FILE WH_ICON_TRAY GENERAL
SCRIPT_NAME="$(basename "$0")" SCRIPT_NAME="$(basename "$0")"
if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then
@@ -22,6 +22,7 @@ if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then
LICENSE_FILE="/usr/share/doc/winehelper-$WH_VERSION/LICENSE" LICENSE_FILE="/usr/share/doc/winehelper-$WH_VERSION/LICENSE"
AGREEMENT="/usr/share/doc/winehelper-$WH_VERSION/LICENSE_AGREEMENT" AGREEMENT="/usr/share/doc/winehelper-$WH_VERSION/LICENSE_AGREEMENT"
THIRD_PARTY_FILE="/usr/share/doc/winehelper-$WH_VERSION/THIRD-PARTY" THIRD_PARTY_FILE="/usr/share/doc/winehelper-$WH_VERSION/THIRD-PARTY"
GENERAL="/usr/share/doc/winehelper-$WH_VERSION/GENERAL"
else else
# переменные для тестового запуска WineHelper из репозитория # переменные для тестового запуска WineHelper из репозитория
USER_WORK_PATH="$HOME/test-$SCRIPT_NAME" USER_WORK_PATH="$HOME/test-$SCRIPT_NAME"
@@ -33,6 +34,7 @@ else
LICENSE_FILE="$DATA_PATH/LICENSE" LICENSE_FILE="$DATA_PATH/LICENSE"
AGREEMENT="$DATA_PATH/LICENSE_AGREEMENT" AGREEMENT="$DATA_PATH/LICENSE_AGREEMENT"
THIRD_PARTY_FILE="$DATA_PATH/THIRD-PARTY" THIRD_PARTY_FILE="$DATA_PATH/THIRD-PARTY"
GENERAL="$DATA_PATH/GENERAL"
WH_DEVEL="1" WH_DEVEL="1"
# минимальная проверка синтаксиса скриптов # минимальная проверка синтаксиса скриптов
@@ -52,21 +54,21 @@ fi
##### MESSAGES FUNCTIONS ##### ##### MESSAGES FUNCTIONS #####
if [[ $WH_USE_GUI != "1" ]] ; then if [[ $WH_USE_GUI != "1" ]] ; then
print_error () { printf "\E[31m%s Ошибка: $@ %s\e[0m\n" ;} print_error () { printf "\E[31m%s Ошибка: $* %s\e[0m\n" ;}
print_warning () { printf "\E[33m%s Предупреждение: $@ %s\e[0m\n" ;} print_warning () { printf "\E[33m%s Предупреждение: $* %s\e[0m\n" ;}
print_info () { printf "\E[36m%s Информация: \"$@\" %s\e[0m\n" ;} print_info () { printf "\E[36m%s Информация: \"$*\" %s\e[0m\n" ;}
print_ok () { printf "\E[35m%s Успех: $@ %s\e[0m\n" ;} print_ok () { printf "\E[35m%s Успех: $* %s\e[0m\n" ;}
else else
print_error () { echo -e "Ошибка: $@" ;} print_error () { echo -e "Ошибка: $*" ;}
print_warning () { echo -e "Предупреждение: $@" ;} print_warning () { echo -e "Предупреждение: $*" ;}
print_info () { echo -e "Информация: \"$@\"" ;} print_info () { echo -e "Информация: \"$*\"" ;}
print_ok () { echo -e "Успех: $@" ;} print_ok () { echo -e "Успех: $*" ;}
fi fi
print_var () { for vp in $@ ; do echo "${vp}=${!vp}" ; done ;} print_var () { for vp in $@ ; do echo "${vp}=${!vp}" ; done ;}
fatal () { fatal () {
print_error "$@" print_error "$@ Аварийное завершение работы WineHelper!"
[[ -n "$WINESERVER" ]] && "$WINESERVER" -w [[ -n "$WINESERVER" ]] && "$WINESERVER" -w
exit 1 exit 1
} }
@@ -326,6 +328,7 @@ unpack () {
try_get_page () { try_get_page () {
local url_page="$1" local url_page="$1"
export OUT_PAGE_TMP="${WH_TMP_DIR}/url_page.tmp" export OUT_PAGE_TMP="${WH_TMP_DIR}/url_page.tmp"
try_remove_file "$OUT_PAGE_TMP"
print_info "Чтение страницы: $url_page" print_info "Чтение страницы: $url_page"
if ! curl -o "$OUT_PAGE_TMP" -A "Mozilla/5.0 (compatible; Konqueror/2.1.1; X11)" "$url_page" \ if ! curl -o "$OUT_PAGE_TMP" -A "Mozilla/5.0 (compatible; Konqueror/2.1.1; X11)" "$url_page" \
|| grep -q "Forbidden" "$OUT_PAGE_TMP" || grep -q "Forbidden" "$OUT_PAGE_TMP"
@@ -342,7 +345,6 @@ read_page () {
&& [[ -f "$OUT_PAGE_TMP" ]] && [[ -f "$OUT_PAGE_TMP" ]]
then then
cat "$OUT_PAGE_TMP" cat "$OUT_PAGE_TMP"
try_remove_file "$OUT_PAGE_TMP"
unset OUT_PAGE_TMP unset OUT_PAGE_TMP
else else
echo "Используй try_get_page перед read_page" echo "Используй try_get_page перед read_page"
@@ -1539,6 +1541,7 @@ run_autoinstall () {
else print_license_agreement else print_license_agreement
fi fi
source "$INSTALL_SCRIPT" "$@" source "$INSTALL_SCRIPT" "$@"
[[ -n $OUT_PAGE_TMP ]] && try_remove_file "$OUT_PAGE_TMP"
print_info "Завершена установка $INSTALL_SCRIPT_NAME" print_info "Завершена установка $INSTALL_SCRIPT_NAME"
else else
fatal "Скрипт автоматической установки для $INSTALL_SCRIPT_NAME не найден!" fatal "Скрипт автоматической установки для $INSTALL_SCRIPT_NAME не найден!"

View File

@@ -13,8 +13,8 @@ from functools import partial
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QTabWidget, QTabBar, from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QTabWidget, QTabBar,
QTextEdit, QFileDialog, QMessageBox, QLineEdit, QCheckBox, QStackedWidget, QScrollArea, QFormLayout, QGroupBox, QRadioButton, QComboBox, QTextEdit, QFileDialog, QMessageBox, QLineEdit, QCheckBox, QStackedWidget, QScrollArea, QFormLayout, QGroupBox, QRadioButton, QComboBox,
QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser, QInputDialog, QDialogButtonBox, QSystemTrayIcon, QMenu) QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser, QInputDialog, QDialogButtonBox, QSystemTrayIcon, QMenu)
from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment, QPropertyAnimation, QEasingCurve, pyqtSignal from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment, QPropertyAnimation, QEasingCurve, pyqtSignal, QRect
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QCursor, QTextCharFormat from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QCursor, QTextCharFormat, QColor, QPalette
from PyQt5.QtNetwork import QLocalServer, QLocalSocket from PyQt5.QtNetwork import QLocalServer, QLocalSocket
@@ -26,9 +26,11 @@ class Var:
DATA_PATH = os.environ.get("DATA_PATH") DATA_PATH = os.environ.get("DATA_PATH")
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")
WH_ICON_TRAY = os.environ.get("WH_ICON_TRAY")
LICENSE_FILE = os.environ.get("LICENSE_FILE") LICENSE_FILE = os.environ.get("LICENSE_FILE")
LICENSE_AGREEMENT_FILE = os.environ.get("AGREEMENT") LICENSE_AGREEMENT_FILE = os.environ.get("AGREEMENT")
THIRD_PARTY_FILE = os.environ.get("THIRD_PARTY_FILE") THIRD_PARTY_FILE = os.environ.get("THIRD_PARTY_FILE")
GENERAL = os.environ.get("GENERAL")
class DependencyManager: class DependencyManager:
"""Класс для управления проверкой и установкой системных зависимостей.""" """Класс для управления проверкой и установкой системных зависимостей."""
@@ -1737,6 +1739,18 @@ class WineHelperGUI(QMainWindow):
self.raise_() self.raise_()
self.activateWindow() self.activateWindow()
def create_colorized_icon(self, icon_path, color):
"""Загружает иконку (SVG) и применяет к ней указанный цвет."""
target_pixmap = QPixmap(icon_path)
if target_pixmap.isNull():
return QIcon()
painter = QPainter(target_pixmap)
painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
painter.fillRect(target_pixmap.rect(), QColor(color))
painter.end()
return QIcon(target_pixmap)
def create_tray_icon(self): def create_tray_icon(self):
"""Создает и настраивает иконку в системном трее.""" """Создает и настраивает иконку в системном трее."""
if not QSystemTrayIcon.isSystemTrayAvailable(): if not QSystemTrayIcon.isSystemTrayAvailable():
@@ -1745,11 +1759,23 @@ class WineHelperGUI(QMainWindow):
self.tray_icon = QSystemTrayIcon(self) self.tray_icon = QSystemTrayIcon(self)
icon_path = Var.WH_ICON_PATH # --- Определение цвета иконки в зависимости от темы ---
if icon_path and os.path.exists(icon_path): if Var.WH_ICON_TRAY and os.path.exists(Var.WH_ICON_TRAY):
pixmap = QPixmap(icon_path) # Получаем цвет текста окна из палитры приложения.
if not pixmap.isNull(): # Это хороший индикатор для определения контрастного цвета.
self.tray_icon.setIcon(QIcon(pixmap)) window_text_color = self.palette().color(QPalette.WindowText)
# Если цвет текста светлый (высокая яркость), значит, тема темная.
# И наоборот: если цвет текста темный, тема светлая.
# Яркость > 127 обычно указывает на светлый цвет.
is_dark_theme = window_text_color.lightness() > 127
if is_dark_theme:
# Для темных тем используем белую иконку
self.tray_icon.setIcon(self.create_colorized_icon(Var.WH_ICON_TRAY, Qt.white))
else:
# Для светлых тем используем черную иконку
self.tray_icon.setIcon(self.create_colorized_icon(Var.WH_ICON_TRAY, Qt.black))
# Создаем и сохраняем меню как атрибут класса, чтобы оно не удалялось # Создаем и сохраняем меню как атрибут класса, чтобы оно не удалялось
self.tray_menu = QMenu(self) self.tray_menu = QMenu(self)
@@ -1792,10 +1818,7 @@ class WineHelperGUI(QMainWindow):
title = "Автоматическая установка" title = "Автоматическая установка"
html_content = ("<h3>Автоматическая установка</h3>" html_content = ("<h3>Автоматическая установка</h3>"
"<p>Скрипты из этого списка скачают, установят и настроят приложение за вас. Просто выберите программу и нажмите «Установить».</p>" "<p>Скрипты из этого списка скачают, установят и настроят приложение за вас. Просто выберите программу и нажмите «Установить».</p>"
"<p>Для доступа к экспериментальным скриптам установки отметьте опцию <b>«Показать тестовые версии»</b> внизу списка.</p>" "<p>Для доступа к экспериментальным скриптам установки отметьте опцию <b>«Показать тестовые версии»</b> внизу списка.</p>")
"<br><h3>Совместимость с дистрибутивами Альт</h3>"
"<p>С полным списком совместимого ПО и сертификатами (не только для WineHelper) можно ознакомиться по следующим ссылкам:<br>"
"<a href='https://www.basealt.ru/fileadmin/user_upload/compatibility/P10-view2.html'>Для 10 платформы</a> | <a href='https://www.basealt.ru/fileadmin/user_upload/compatibility/P11-view2.html'>Для 11 платформы</a></p>")
show_global = False show_global = False
elif tab_name == "Ручная установка": elif tab_name == "Ручная установка":
title = "Ручная установка" title = "Ручная установка"
@@ -2201,6 +2224,7 @@ class WineHelperGUI(QMainWindow):
self.install_tabs_data['auto'] = { self.install_tabs_data['auto'] = {
'buttons': buttons, 'layout': layout, 'search_edit': search_edit, 'scroll_area': scroll_area 'buttons': buttons, 'layout': layout, 'search_edit': search_edit, 'scroll_area': scroll_area
} }
self.install_tabs_data['auto']['test_buttons'] = []
# Добавляем чекбокс для тестовых версий # Добавляем чекбокс для тестовых версий
test_checkbox = QCheckBox("Показать тестовые версии") test_checkbox = QCheckBox("Показать тестовые версии")
@@ -2236,12 +2260,39 @@ class WineHelperGUI(QMainWindow):
if not data: if not data:
return return
script_folders = ["autoinstall"] is_checked = data['test_checkbox'].isChecked()
if data['test_checkbox'].isChecked(): test_buttons = data.get('test_buttons', [])
script_folders.append("testinstall")
# Перед удалением кнопок останавливаем все связанные с ними таймеры анимации # Если нужно показать тестовые версии и они еще не добавлены
for btn in data['buttons']: if is_checked and not test_buttons:
test_script_folder = "testinstall"
script_path = os.path.join(Var.DATA_PATH, test_script_folder)
if os.path.isdir(script_path):
try:
folder_scripts = sorted(os.listdir(script_path))
# Запоминаем, какие кнопки являются тестовыми
new_test_buttons = []
self._populate_install_grid(data['layout'], folder_scripts, test_script_folder, new_test_buttons)
data['test_buttons'] = new_test_buttons
data['buttons'].extend(new_test_buttons)
self.autoinstall_scripts.extend(folder_scripts)
# Применяем фильтр и прокручиваем к первому новому элементу
self.filter_buttons('auto')
if new_test_buttons:
first_new_button = new_test_buttons[0]
frame = first_new_button.parent()
if isinstance(frame, QFrame):
# Даем время на отрисовку перед прокруткой
QTimer.singleShot(100, lambda: data['scroll_area'].ensureWidgetVisible(frame, 50, 50))
except OSError as e:
print(f"Не удалось прочитать директорию {test_script_folder}: {e}")
# Если нужно скрыть тестовые версии и они были добавлены
elif not is_checked and test_buttons:
# Останавливаем анимацию и удаляем виджеты тестовых кнопок
for btn in test_buttons:
if btn in self.icon_animators: if btn in self.icon_animators:
anim_data = self.icon_animators.pop(btn) anim_data = self.icon_animators.pop(btn)
if 'main_timer' in anim_data: if 'main_timer' in anim_data:
@@ -2249,29 +2300,22 @@ class WineHelperGUI(QMainWindow):
if 'animation' in anim_data and anim_data['animation']: if 'animation' in anim_data and anim_data['animation']:
anim_data['animation'].stop() anim_data['animation'].stop()
# Сбрасываем ссылку на активную кнопку, если она была удалена # Удаляем кнопку из основного списка
if self.current_active_button in data['buttons']: if btn in data['buttons']:
self.current_active_button = None data['buttons'].remove(btn)
# Очищаем старые кнопки и layout # Удаляем фрейм кнопки из layout
for btn in data['buttons']: frame = btn.parent()
btn.parent().deleteLater() if frame:
data['buttons'].clear() frame.deleteLater()
# Заполняем layout новыми кнопками # Очищаем список тестовых кнопок
scripts = [] data['test_buttons'].clear()
for folder in script_folders: # Обновляем список скриптов
script_path = os.path.join(Var.DATA_PATH, folder) self.autoinstall_scripts = [s for s in self.autoinstall_scripts if not os.path.exists(os.path.join(Var.DATA_PATH, "testinstall", s))]
if os.path.isdir(script_path):
try:
folder_scripts = sorted(os.listdir(script_path))
self._populate_install_grid(data['layout'], folder_scripts, folder, data['buttons'])
scripts.extend(folder_scripts)
except OSError as e:
print(f"Не удалось прочитать директорию {script_path}: {e}")
self.autoinstall_scripts = scripts # В любом случае применяем фильтр, чтобы скрыть/показать кнопки в соответствии с поиском
# Применяем текущий фильтр поиска к обновленному списку if data['test_checkbox'].isChecked():
self.filter_buttons('auto') self.filter_buttons('auto')
def create_installed_tab(self): def create_installed_tab(self):
@@ -3234,17 +3278,44 @@ class WineHelperGUI(QMainWindow):
help_subtabs = QTabWidget() help_subtabs = QTabWidget()
help_layout.addWidget(help_subtabs) help_layout.addWidget(help_subtabs)
# Подвкладка "Руководство" # Подвкладка "Общее"
guide_tab = QWidget() general_tab = QWidget()
guide_layout = QVBoxLayout(guide_tab) general_layout = QVBoxLayout(general_tab)
guide_text = QTextBrowser() general_text = QTextBrowser()
guide_text.setOpenExternalLinks(True) general_text.setOpenExternalLinks(True)
guide_text.setHtml("""
<h2>Руководство пользователя</h2> try:
<p>Подробное и актуальное руководство по использованию WineHelper смотрите на <a href="https://www.altlinux.org/Winehelper">https://www.altlinux.org/Winehelper</a></p> if not Var.GENERAL or not os.path.exists(Var.GENERAL):
""") raise FileNotFoundError
guide_layout.addWidget(guide_text)
help_subtabs.addTab(guide_tab, "Руководство") with open(Var.GENERAL, 'r', encoding='utf-8') as f:
general_content = f.read()
html_content = ""
url_re = re.compile(r'(https?://[^\s]+)')
for line in general_content.splitlines():
line = line.strip()
if not line:
html_content += "<br>"
continue
line = html.escape(line)
line = url_re.sub(r'<a href="\1">\1</a>', line)
if line.startswith('# '):
html_content += f'<h2>{line[2:]}</h2>'
elif line.startswith('Для '):
html_content += f'<p style="margin-left: 10px;">&bull; {line}</p>'
else:
html_content += f'<p>{line}</p>'
general_text.setHtml(html_content)
except (FileNotFoundError, TypeError):
general_text.setHtml(f'<h2>Ошибка</h2><p>Не удалось загрузить файл с общей информацией по пути:<br>{Var.GENERAL}</p>')
general_layout.addWidget(general_text)
help_subtabs.addTab(general_tab, "Общее")
# Подвкладка "Авторы" # Подвкладка "Авторы"
authors_tab = QWidget() authors_tab = QWidget()
@@ -4903,7 +4974,7 @@ def main():
window.server = server window.server = server
window.show() window.show()
# Создаем иконку в системном трее после создания окна # Создаем иконку в системном трее после создания окна
window.create_tray_icon() # window.create_tray_icon() # Временно отключено
return app.exec_() return app.exec_()
return 1 return 1