Compare commits

..

24 Commits

Author SHA1 Message Date
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
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
Mikhail Tergoev
dfc6c1c836 added var WH_ICON_TRAY path to winehelper-symbolic.svg 2025-10-21 15:09:46 +03:00
Mikhail Tergoev
04187e9463 GUI: updated compatibility links and certificates 2025-10-21 15:00:53 +03:00
Mikhail Tergoev
5f915ab58d Merge branch 'minergenon-devel' 2025-10-21 14:11:22 +03:00
Sergey Palcheh
0e8ee7788a fixed: the Delete all WineHelper data button is active by default 2025-10-21 14:45:27 +06:00
Sergey Palcheh
49c1ac6846 fixed the text color change after an error in the log 2025-10-21 14:36:47 +06:00
Mikhail Tergoev
971bcd0f5a wine_run: added automatic detection of .reg and .dll 2025-10-21 11:30:48 +03:00
Sergey Palcheh
5b21015aee added compatibility links and certificates 2025-10-21 12:25:53 +06:00
Mikhail Tergoev
7dee08bcfb updated changelog to 0.7.0 2025-10-20 15:30:47 +03:00
Mikhail Tergoev
633deaf1c1 hided abc-*, ais and commfort-* scripts 2025-10-20 15:28:21 +03:00
Mikhail Tergoev
4df9508547 Merge branch 'minergenon-devel' 2025-10-20 15:07:30 +03:00
Sergey Palcheh
989f04cdd8 added a test list to the auto-installation 2025-10-20 17:59:40 +06:00
Mikhail Tergoev
95b189f5a2 less in used for print install list 2025-10-20 14:16:54 +03:00
Mikhail Tergoev
371553277a added WH_TESTINSTALL_DIR to scripts list 2025-10-20 13:09:42 +03:00
Mikhail Tergoev
dabf50103d hided unverifed install scripts 2025-10-20 12:45:39 +03:00
Mikhail Tergoev
590b37f1a3 rename var: INSTALL_MODE -> WH_INSTALL_MODE 2025-10-20 12:43:52 +03:00
Mikhail Tergoev
1f9b4a9146 added more info for daobit-cservice 2025-10-20 12:40:31 +03:00
Mikhail Tergoev
39f21373f1 added version for spravki-bk 2025-10-20 12:39:48 +03:00
Mikhail Tergoev
e984ffdb8c fixed: always return to DRIVE_C 2025-10-20 11:43:19 +03:00
Mikhail Tergoev
48834dc078 Merge branch 'minergenon-devel' 2025-10-20 11:35:11 +03:00
Sergey Palcheh
b62d06ed71 fixed typos 2025-10-19 15:39:43 +06:00
27 changed files with 262 additions and 84 deletions

View File

@@ -1,5 +1,16 @@
История изменений: История изменений:
0.7.0
* обновлен графический режим Qt5
- добавлена кнопка открытия каталога с резервными копиями и логами
- добавлена кнопка открытия каталога с префиксом
- добавлена блокировка кнопок для установленного приложения, если оно уже запущено
- добавлено отображения процесса установки сторонних компонентов с помощью winetricks
- добавлена возможность отображения и установки тестовых скриптов (выключено по умолчанию)
* добавлены скрипты установки для t-flex версии 18
* добавлен список тестовых скриптов установки ПО
* добавлена возможность ассоциации файлов для передачи в приложения запускаемых в WineHelper
0.6.0 0.6.0
* обновлен графический режим Qt5 * обновлен графический режим Qt5
* добавлен иконка в трее для графического режима Qt5 * добавлен иконка в трее для графического режима Qt5

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

@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# info_ru: Сервис обновления ПО # info_ru: Сервис обновления ПО: "R-Атлас", "R-Инфо" и "R-Тариф".
######################################################################## ########################################################################
export PROG_URL="https://daobit.ru" export PROG_URL="https://daobit.ru"
export WH_WINE_USE="wine_x_tkg_10-0_i586" # wine-9.0.9-alt1-i586" export WH_WINE_USE="wine_x_tkg_10-0_i586" # wine-9.0.9-alt1-i586"

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# info_ru: «Справки БК» — специальное программное обеспечение, предназначенное для заполнения справок о доходах, расходах, об имуществе и обязательствах имущественного характера. # info_ru: «Справки БК» (Windows версия 2.5.5) от 31.01.2024 — специальное программное обеспечение, предназначенное для заполнения справок о доходах, расходах, об имуществе и обязательствах имущественного характера.
######################################################################## ########################################################################
export PROG_URL="https://spravki-bk.ru" export PROG_URL="https://spravki-bk.ru"
export WH_WINE_USE="wine-9.0.14-alt1-i586-spravkibk" export WH_WINE_USE="wine-9.0.14-alt1-i586-spravkibk"

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
@@ -17,10 +17,12 @@ if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then
RUN_SCRIPT="/usr/bin/$SCRIPT_NAME" RUN_SCRIPT="/usr/bin/$SCRIPT_NAME"
DATA_PATH="/usr/share/$SCRIPT_NAME" DATA_PATH="/usr/share/$SCRIPT_NAME"
WH_ICON_PATH="/usr/share/icons/hicolor/scalable/apps/winehelper.svg" WH_ICON_PATH="/usr/share/icons/hicolor/scalable/apps/winehelper.svg"
WH_ICON_TRAY="/usr/share/icons/hicolor/symbolic/apps/winehelper-symbolic.svg"
CHANGELOG_FILE="/usr/share/doc/winehelper-$WH_VERSION/CHANGELOG" CHANGELOG_FILE="/usr/share/doc/winehelper-$WH_VERSION/CHANGELOG"
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"
@@ -28,9 +30,11 @@ else
DATA_PATH="$(dirname "$RUN_SCRIPT")" DATA_PATH="$(dirname "$RUN_SCRIPT")"
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"
WH_ICON_TRAY="$DATA_PATH/image/gui/winehelper-symbolic.svg"
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"
# минимальная проверка синтаксиса скриптов # минимальная проверка синтаксиса скриптов
@@ -1282,7 +1286,8 @@ init_wineprefix () {
echo "# переменные последнего использования префикса:" > "$WINEPREFIX/last.conf" echo "# переменные последнего использования префикса:" > "$WINEPREFIX/last.conf"
for var in WH_WINE_USE BASE_PFX WINEARCH WH_WINDOWS_VER WINEESYNC WINEFSYNC \ for var in WH_WINE_USE BASE_PFX WINEARCH WH_WINDOWS_VER WINEESYNC WINEFSYNC \
STAGING_SHARED_MEMORY WINE_LARGE_ADDRESS_AWARE WH_USE_SHADER_CACHE WH_USE_WINE_DXGI \ STAGING_SHARED_MEMORY WINE_LARGE_ADDRESS_AWARE WH_USE_SHADER_CACHE WH_USE_WINE_DXGI \
WINE_CPU_TOPOLOGY DXVK_VER VKD3D_VER WH_XDG_OPEN WH_USE_MESA_GL_OVERRIDE WINE_CPU_TOPOLOGY DXVK_VER VKD3D_VER WH_XDG_OPEN WH_USE_MESA_GL_OVERRIDE \
WH_USE_CPCSP_PROXY
do do
echo "export $var=\"${!var}\"" >> "$WINEPREFIX/last.conf" echo "export $var=\"${!var}\"" >> "$WINEPREFIX/last.conf"
done done
@@ -1386,15 +1391,32 @@ prepair_wine () {
[[ "$MANGOHUD" == 1 ]] && MANGOHUD_RUN="mangohud" [[ "$MANGOHUD" == 1 ]] && MANGOHUD_RUN="mangohud"
} }
wine_regfile () {
print_info "Запускаем команду: $WINELOADER $@"
"$WINELOADER" "$@" && print_ok "Выполнено." || fatal "Не выполнено: $WINELOADER $@"
wait_wineserver
if [[ "$WINEARCH" == "win64" ]] \
&& [[ -f "${WINELOADER}64" ]]
then
print_info "Запускаем команду: ${WINELOADER}64 $@"
"${WINELOADER}64" "$@" && print_ok "Выполнено." || fatal "Не выполнено: ${WINELOADER}64 $@"
wait_wineserver
fi
}
wine_run () { wine_run () {
local wh_add_args win_file_exec local wh_add_args win_file_exec win_file_path win_file_name
if [[ $1 =~ (winecfg|regedit|winefile|wineconsole) ]] ; then if [[ $1 =~ (winecfg|regedit|winefile|wineconsole) ]] ; then
win_file_exec="$1" win_file_exec="$1"
win_file_name="$win_file_exec" win_file_name="$win_file_exec"
win_file_path="$DRIVE_C"
wh_add_args="" wh_add_args=""
elif [[ $1 =~ \.dll$ ]] ; then
wine_regfile regsvr32 /s "$@"
return 0
elif [[ -f "$1" ]] ; then elif [[ -f "$1" ]] ; then
local win_file_path win_file_name
win_file_exec="$(readlink -f "$1")" win_file_exec="$(readlink -f "$1")"
win_file_path="$(dirname "$win_file_exec")" win_file_path="$(dirname "$win_file_exec")"
win_file_name="$(basename "$win_file_exec")" win_file_name="$(basename "$win_file_exec")"
@@ -1403,6 +1425,7 @@ wine_run () {
*.exe) wh_add_args="$WINE_WIN_START" ;; *.exe) wh_add_args="$WINE_WIN_START" ;;
*.msi) wh_add_args="msiexec /i" ;; *.msi) wh_add_args="msiexec /i" ;;
*.bat|*.cmd) wh_add_args="" ;; *.bat|*.cmd) wh_add_args="" ;;
*.reg) wine_regfile regedit "$@" ; return 0 ;;
*) fatal "Не удалось запустить файл $1. Проверьте расширение файла." ;; *) fatal "Не удалось запустить файл $1. Проверьте расширение файла." ;;
esac esac
@@ -1410,12 +1433,12 @@ wine_run () {
&& file "$win_file_exec" | grep -q "x86-64" && file "$win_file_exec" | grep -q "x86-64"
then fatal "Нельзя запустить 64-битное приложение в 32-битном префиксе!" then fatal "Нельзя запустить 64-битное приложение в 32-битном префиксе!"
fi fi
else else
fatal "Команда введена не правильно или не найден исполняемый файл $1" fatal "Команда введена не правильно или не найден исполняемый файл $1"
fi fi
shift shift
cd "$win_file_path"
if [[ $WINEDEBUG != "-all" ]] ; then if [[ $WINEDEBUG != "-all" ]] ; then
local log_dir log_file local log_dir log_file
@@ -1429,27 +1452,23 @@ wine_run () {
echo "##### Основные переменные #####" | tee -a "$log_file" echo "##### Основные переменные #####" | tee -a "$log_file"
env | grep -e "WH_" -e "WINE" -e "DXVK" -e "VKD3D" | tee -a "$log_file" env | grep -e "WH_" -e "WINE" -e "DXVK" -e "VKD3D" | tee -a "$log_file"
echo "##### Лог WINE #####" | tee -a "$log_file" echo "##### Лог WINE #####" | tee -a "$log_file"
(
cd "$win_file_path" $MANGOHUD_RUN "$WINELOADER" $wh_add_args "$win_file_exec" "$@" $LAUNCH_PARAMETERS 2>&1 | tee -a "$log_file"
$MANGOHUD_RUN "$WINELOADER" $wh_add_args "$win_file_exec" "$@" $LAUNCH_PARAMETERS 2>&1 | tee -a "$log_file"
)
else else
( $MANGOHUD_RUN "$WINELOADER" $wh_add_args "$win_file_exec" "$@" $LAUNCH_PARAMETERS
cd "$win_file_path"
$MANGOHUD_RUN "$WINELOADER" $wh_add_args "$win_file_exec" "$@" $LAUNCH_PARAMETERS
)
fi fi
wait_wineserver wait_wineserver
cd "$DRIVE_C"
} }
wine_run_install () { wine_run_install () {
print_info "Запускаем установку: $1." print_info "Запускаем установку: $1."
if [[ "$INSTALL_MODE" == "manual" ]] case "$WH_INSTALL_MODE" in
then print_warning "Рекомендуется не менять пути для установки приложения!" "manual") print_warning "Рекомендуется не менять пути для установки приложения!" ;;
fi "test") print_warning "Установка приложения из списка экспериментальных скриптов." ;;
esac
if [[ ! -f "$1" ]] if [[ ! -f "$1" ]]
then fatal "Нет файла для установки: $1" then fatal "Нет файла для установки: $1"
@@ -1472,46 +1491,49 @@ run_autoinstall () {
elif [[ -f "$WH_MANUALINSTALL_DIR/$INSTALL_SCRIPT_NAME" ]] ; then elif [[ -f "$WH_MANUALINSTALL_DIR/$INSTALL_SCRIPT_NAME" ]] ; then
INSTALL_SCRIPT="$WH_MANUALINSTALL_DIR/$INSTALL_SCRIPT_NAME" INSTALL_SCRIPT="$WH_MANUALINSTALL_DIR/$INSTALL_SCRIPT_NAME"
WH_INSTALL_MODE="manual" WH_INSTALL_MODE="manual"
elif [[ -d "$WH_TESTINSTALL_DIR" ]] \ elif [[ -f "$WH_TESTINSTALL_DIR/$INSTALL_SCRIPT_NAME" ]] ; then
&& [[ -f "$WH_TESTINSTALL_DIR/$INSTALL_SCRIPT_NAME" ]]
then
INSTALL_SCRIPT="$WH_TESTINSTALL_DIR/$INSTALL_SCRIPT_NAME" INSTALL_SCRIPT="$WH_TESTINSTALL_DIR/$INSTALL_SCRIPT_NAME"
WH_INSTALL_MODE="test" WH_INSTALL_MODE="test"
else else
INSTALL_SCRIPT="0" INSTALL_SCRIPT="0"
fi fi
export INSTALL_SCRIPT INSTALL_MODE export INSTALL_SCRIPT WH_INSTALL_MODE
if [[ $INSTALL_SCRIPT_NAME == "list" ]] || [[ -z "$INSTALL_SCRIPT_NAME" ]] ; then if [[ $INSTALL_SCRIPT_NAME == "list" ]] || [[ -z "$INSTALL_SCRIPT_NAME" ]] ; then
list_install_scripts() { print_install_list () {
local dir="$1" parse_install_scripts() {
local title="$2" local parse_dir="$1"
[[ ! -d "$dir" ]] || [[ -z "$(ls -A "$dir" 2>/dev/null)" ]] && return [[ ! -d "$parse_dir" ]] || [[ -z "$(ls -A "$parse_dir" 2>/dev/null)" ]] && return
print_info "$title" awk '
FNR==1 {
awk ' if (progname) {
FNR==1 { printf "\n%s - %s\n%s\n", filename, progname, info
if (progname) { }
printf "\n%s - %s\n%s\n", filename, progname, info progname=""; info=""; filename=FILENAME
sub(".*/", "", filename)
} }
progname=""; info=""; filename=FILENAME /info_ru:/ { sub(/.*info_ru: /, ""); info=$0 }
sub(".*/", "", filename) /PROG_NAME=/ { sub(/.*PROG_NAME=/, ""); progname=$0 }
} END {
/info_ru:/ { sub(/.*info_ru: /, ""); info=$0 } if (progname) {
/PROG_NAME=/ { sub(/.*PROG_NAME=/, ""); progname=$0 } printf "\n%s - %s\n%s\n", filename, progname, info
END { }
if (progname) {
printf "\n%s - %s\n%s\n", filename, progname, info
} }
} ' "$parse_dir"/*
' "$dir"/* echo
}
print_info "Список программ с возможностью автоматической установки:"
parse_install_scripts "$WH_AUTOINSTALL_DIR"
print_info "Список программ с возможностью установки из существующего дистрибутива:"
parse_install_scripts "$WH_MANUALINSTALL_DIR"
print_warning "Программы из списка экспериментальных скриптов:"
parse_install_scripts "$WH_TESTINSTALL_DIR"
} }
list_install_scripts "$WH_AUTOINSTALL_DIR" "Список программ с возможностью автоматической установки:" print_install_list | less -R --use-color
echo
list_install_scripts "$WH_MANUALINSTALL_DIR" "Список программ с возможностью установки из существующего дистрибутива:"
elif [[ "$INSTALL_SCRIPT" != "0" ]] ; then elif [[ "$INSTALL_SCRIPT" != "0" ]] ; then
if [[ $WH_USE_GUI == "1" ]] \ if [[ $WH_USE_GUI == "1" ]] \
&& [[ $(ps -o command= -p "$PPID" | awk '{print $2}') =~ "$DATA_PATH/winehelper_gui.py" ]] && [[ $(ps -o command= -p "$PPID" | awk '{print $2}') =~ "$DATA_PATH/winehelper_gui.py" ]]
@@ -2183,7 +2205,7 @@ select_component_version() {
} }
run_install_to_prefix() { run_install_to_prefix() {
if [[ -z "$1" ]] || [[ -z "$2" ]] || [[ -f "$2" ]] ; then if [[ -z "$1" ]] || [[ -z "$2" ]] || [[ ! -f "$2" ]] ; then
fatal "Использование: $SCRIPT_NAME install-to-prefix <имя_префикса> <путь_к_установщику>" fatal "Использование: $SCRIPT_NAME install-to-prefix <имя_префикса> <путь_к_установщику>"
fi fi

View File

@@ -14,7 +14,7 @@ from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QH
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
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QCursor from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QCursor, QTextCharFormat
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:
"""Класс для управления проверкой и установкой системных зависимостей.""" """Класс для управления проверкой и установкой системных зависимостей."""
@@ -870,6 +872,9 @@ class WinetricksManagerDialog(QDialog):
"Подробности смотрите в логе.", "Подробности смотрите в логе.",
QMessageBox.Warning, QMessageBox.Warning,
{"buttons": {"OK": QMessageBox.AcceptRole}}) {"buttons": {"OK": QMessageBox.AcceptRole}})
# Сбрасываем формат символов к значению по умолчанию.
# Это гарантирует, что следующий вызов append() не унаследует красный цвет.
self.log_output.setCurrentCharFormat(QTextCharFormat())
self.apply_button.setEnabled(True) self.apply_button.setEnabled(True)
self.close_button.setEnabled(True) self.close_button.setEnabled(True)
return return
@@ -1622,6 +1627,19 @@ class WineHelperGUI(QMainWindow):
"padding-left: 10px;", "padding-left: 15px;" "padding-left: 10px;", "padding-left: 15px;"
) )
# Стиль для кнопок тестовых программ
self.TEST_BUTTON_LIST_STYLE = """
QPushButton {
background-color: #ffdc64; /* Более темный желтый фон */
color: black; /* Черный цвет текста для контраста */
text-align: left;
padding-left: 10px;
padding-right: 10px;
height: 42px; min-height: 42px; max-height: 42px;
}
QPushButton::icon { padding-left: 10px; }
"""
# Стили для оберток кнопок (для рамки выделения) # Стили для оберток кнопок (для рамки выделения)
self.FRAME_STYLE_DEFAULT = "QFrame { border: 2px solid transparent; border-radius: 8px; padding: 0px; }" self.FRAME_STYLE_DEFAULT = "QFrame { border: 2px solid transparent; border-radius: 8px; padding: 0px; }"
self.FRAME_STYLE_SELECTED = "QFrame { border: 2px solid #0078d7; border-radius: 8px; padding: 0px; }" self.FRAME_STYLE_SELECTED = "QFrame { border: 2px solid #0078d7; border-radius: 8px; padding: 0px; }"
@@ -1729,7 +1747,7 @@ class WineHelperGUI(QMainWindow):
self.tray_icon = QSystemTrayIcon(self) self.tray_icon = QSystemTrayIcon(self)
icon_path = Var.WH_ICON_PATH icon_path = Var.WH_ICON_TRAY
if icon_path and os.path.exists(icon_path): if icon_path and os.path.exists(icon_path):
pixmap = QPixmap(icon_path) pixmap = QPixmap(icon_path)
if not pixmap.isNull(): if not pixmap.isNull():
@@ -1775,8 +1793,8 @@ class WineHelperGUI(QMainWindow):
if tab_name == "Автоматическая установка": if tab_name == "Автоматическая установка":
title = "Автоматическая установка" title = "Автоматическая установка"
html_content = ("<h3>Автоматическая установка</h3>" html_content = ("<h3>Автоматическая установка</h3>"
"<p>Скрипты из этого списка скачают, установят и настроят приложение за вас.</p>" "<p>Скрипты из этого списка скачают, установят и настроят приложение за вас. Просто выберите программу и нажмите «Установить».</p>"
"<p>Просто выберите программу и нажмите «Установить».</p>") "<p>Для доступа к экспериментальным скриптам установки отметьте опцию <b>«Показать тестовые версии»</b> внизу списка.</p>")
show_global = False show_global = False
elif tab_name == "Ручная установка": elif tab_name == "Ручная установка":
title = "Ручная установка" title = "Ручная установка"
@@ -2064,14 +2082,14 @@ class WineHelperGUI(QMainWindow):
return btn return btn
def _populate_install_grid(self, grid_layout, scripts_list, script_folder, button_list): def _populate_install_grid(self, grid_layout, scripts_list, script_folder, button_list, start_index=None):
""" """
Заполняет QGridLayout кнопками установщиков. Заполняет QGridLayout кнопками установщиков.
Кнопки создаются только для скриптов, в которых найдена переменная PROG_NAME. Кнопки создаются только для скриптов, в которых найдена переменная PROG_NAME.
:param grid_layout: QGridLayout для заполнения. :param grid_layout: QGridLayout для заполнения.
:param scripts_list: Список имен скриптов. :param scripts_list: Список имен скриптов.
:param script_folder: Имя папки со скриптами ('autoinstall' или 'manualinstall'). :param script_folder: Имя папки со скриптами ('autoinstall', 'manualinstall' или 'testinstall').
:param button_list: Список для хранения созданных кнопок. :param button_list: Список для хранения созданных кнопок.
""" """
button_index = 0 button_index = 0
@@ -2085,7 +2103,13 @@ class WineHelperGUI(QMainWindow):
icon_names = ScriptParser.extract_icons_from_script(script_path) icon_names = ScriptParser.extract_icons_from_script(script_path)
icon_paths = [os.path.join(Var.DATA_PATH, "image", f"{name}.png") for name in icon_names] icon_paths = [os.path.join(Var.DATA_PATH, "image", f"{name}.png") for name in icon_names]
btn = self._create_app_button(prog_name, icon_paths, self.BUTTON_LIST_STYLE)
# Выбираем стиль в зависимости от папки
if script_folder == 'testinstall':
style_sheet = self.TEST_BUTTON_LIST_STYLE
else:
style_sheet = self.BUTTON_LIST_STYLE
btn = self._create_app_button(prog_name, icon_paths, style_sheet)
# Обертка для рамки выделения # Обертка для рамки выделения
frame = QFrame() frame = QFrame()
@@ -2095,12 +2119,12 @@ class WineHelperGUI(QMainWindow):
layout.addWidget(btn) layout.addWidget(btn)
btn.clicked.connect(lambda _, s=script, b=btn: self.show_script_info(s, b)) btn.clicked.connect(lambda _, s=script, b=btn: self.show_script_info(s, b))
row, column = divmod(button_index, 2) row, column = divmod(len(button_list), 2)
grid_layout.addWidget(frame, row, column) grid_layout.addWidget(frame, row, column)
button_list.append(btn) button_list.append(btn)
button_index += 1 button_index += 1
def _create_searchable_grid_tab(self, placeholder_text, filter_slot): def _create_searchable_grid_tab(self, placeholder_text, filter_slot, add_stretch=True):
""" """
Создает стандартную вкладку с полем поиска и сеточным макетом с прокруткой. Создает стандартную вкладку с полем поиска и сеточным макетом с прокруткой.
Возвращает кортеж (главный виджет вкладки, сеточный макет, поле поиска, область прокрутки). Возвращает кортеж (главный виджет вкладки, сеточный макет, поле поиска, область прокрутки).
@@ -2134,11 +2158,12 @@ class WineHelperGUI(QMainWindow):
grid_layout.setColumnStretch(1, 1) grid_layout.setColumnStretch(1, 1)
v_scroll_layout.addLayout(grid_layout) v_scroll_layout.addLayout(grid_layout)
v_scroll_layout.addStretch(1) if add_stretch:
v_scroll_layout.addStretch(1)
return tab_widget, grid_layout, search_edit, scroll_area return tab_widget, grid_layout, search_edit, scroll_area
def _create_and_populate_install_tab(self, tab_title, script_folder, search_placeholder, filter_slot): def _create_and_populate_install_tab(self, tab_title, script_folders, search_placeholder, filter_slot):
""" """
Создает и заполняет вкладку для установки (автоматической или ручной). Создает и заполняет вкладку для установки (автоматической или ручной).
Возвращает кортеж со скриптами, кнопками и виджетами. Возвращает кортеж со скриптами, кнопками и виджетами.
@@ -2148,15 +2173,16 @@ class WineHelperGUI(QMainWindow):
) )
scripts = [] scripts = []
script_path = os.path.join(Var.DATA_PATH, script_folder)
if os.path.isdir(script_path):
try:
scripts = sorted(os.listdir(script_path))
except OSError as e:
print(f"Не удалось прочитать директорию {script_path}: {e}")
buttons_list = [] buttons_list = []
self._populate_install_grid(grid_layout, scripts, script_folder, buttons_list) for folder in script_folders:
script_path = os.path.join(Var.DATA_PATH, folder)
if os.path.isdir(script_path):
try:
folder_scripts = sorted(os.listdir(script_path))
scripts.extend(folder_scripts)
self._populate_install_grid(grid_layout, folder_scripts, folder, buttons_list)
except OSError as e:
print(f"Не удалось прочитать директорию {script_path}: {e}")
self.add_tab(tab_widget, tab_title) self.add_tab(tab_widget, tab_title)
@@ -2168,12 +2194,28 @@ class WineHelperGUI(QMainWindow):
scripts, buttons, layout, scripts, buttons, layout,
search_edit, scroll_area search_edit, scroll_area
) = self._create_and_populate_install_tab( ) = self._create_and_populate_install_tab(
"Автоматическая установка", "autoinstall", "Поиск скрипта автоматической установки...", partial(self.filter_buttons, 'auto') "Автоматическая установка", ["autoinstall"], "Поиск скрипта автоматической установки...", partial(self.filter_buttons, 'auto')
) )
self.autoinstall_scripts = scripts self.autoinstall_scripts = scripts
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.setToolTip("Показать/скрыть экспериментальные скрипты установки")
# Находим layout вкладки, чтобы добавить чекбокс
tab_widget = self.stacked_widget.widget(self.stacked_widget.count() - 1)
if tab_widget and tab_widget.layout():
tab_widget.layout().addWidget(test_checkbox)
# Подключаем сигнал к слоту обновления
test_checkbox.stateChanged.connect(self.update_auto_install_list)
# Сохраняем чекбокс для доступа в будущем
self.install_tabs_data['auto']['test_checkbox'] = test_checkbox
def create_manual_install_tab(self): def create_manual_install_tab(self):
"""Создает вкладку для ручной установки программ""" """Создает вкладку для ручной установки программ"""
@@ -2181,17 +2223,81 @@ class WineHelperGUI(QMainWindow):
scripts, buttons, layout, scripts, buttons, layout,
search_edit, scroll_area search_edit, scroll_area
) = self._create_and_populate_install_tab( ) = self._create_and_populate_install_tab(
"Ручная установка", "manualinstall", "Поиск скрипта ручной установки...", partial(self.filter_buttons, 'manual') "Ручная установка", ["manualinstall"], "Поиск скрипта ручной установки...", partial(self.filter_buttons, 'manual')
) )
self.manualinstall_scripts = scripts self.manualinstall_scripts = scripts
self.install_tabs_data['manual'] = { self.install_tabs_data['manual'] = {
'buttons': buttons, 'layout': layout, 'search_edit': search_edit, 'scroll_area': scroll_area 'buttons': buttons, 'layout': layout, 'search_edit': search_edit, 'scroll_area': scroll_area
} }
def update_auto_install_list(self):
"""Обновляет список на вкладке 'Автоматическая установка' при изменении чекбокса."""
data = self.install_tabs_data.get('auto')
if not data:
return
is_checked = data['test_checkbox'].isChecked()
test_buttons = data.get('test_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:
anim_data = self.icon_animators.pop(btn)
if 'main_timer' in anim_data:
anim_data['main_timer'].stop()
if 'animation' in anim_data and anim_data['animation']:
anim_data['animation'].stop()
# Удаляем кнопку из основного списка
if btn in data['buttons']:
data['buttons'].remove(btn)
# Удаляем фрейм кнопки из layout
frame = btn.parent()
if frame:
frame.deleteLater()
# Очищаем список тестовых кнопок
data['test_buttons'].clear()
# Обновляем список скриптов
self.autoinstall_scripts = [s for s in self.autoinstall_scripts if not os.path.exists(os.path.join(Var.DATA_PATH, "testinstall", s))]
# В любом случае применяем фильтр, чтобы скрыть/показать кнопки в соответствии с поиском
if data['test_checkbox'].isChecked():
self.filter_buttons('auto')
def create_installed_tab(self): def create_installed_tab(self):
"""Создает вкладку для отображения установленных программ в виде кнопок""" """Создает вкладку для отображения установленных программ в виде кнопок"""
installed_tab, self.installed_scroll_layout, self.installed_search_edit, self.installed_scroll_area = self._create_searchable_grid_tab( installed_tab, self.installed_scroll_layout, self.installed_search_edit, self.installed_scroll_area = self._create_searchable_grid_tab(
"Поиск установленной программы...", self.filter_installed_buttons "Поиск установленной программы...", self.filter_installed_buttons, add_stretch=True
) )
self.add_tab(installed_tab, "Установленные") self.add_tab(installed_tab, "Установленные")
@@ -2433,7 +2539,7 @@ class WineHelperGUI(QMainWindow):
prefix_names = [] prefix_names = []
self.created_prefix_selector.blockSignals(True) self.created_prefix_selector.blockSignals(True)
self.remove_all_button.setEnabled(bool(prefix_names)) self.remove_all_button.setEnabled(True)
self.created_prefix_selector.clear() self.created_prefix_selector.clear()
if prefix_names: if prefix_names:
self.created_prefix_selector.addItems(prefix_names) self.created_prefix_selector.addItems(prefix_names)
@@ -2449,7 +2555,7 @@ class WineHelperGUI(QMainWindow):
self.current_managed_prefix_name = None self.current_managed_prefix_name = None
self._setup_prefix_management_panel(None) self._setup_prefix_management_panel(None)
self.delete_prefix_button.setEnabled(False) self.delete_prefix_button.setEnabled(False)
self.remove_all_button.setEnabled(False) self.remove_all_button.setEnabled(True)
self.create_base_pfx_button.setEnabled(False) self.create_base_pfx_button.setEnabled(False)
self.open_prefix_folder_button.setEnabled(False) self.open_prefix_folder_button.setEnabled(False)
else: else:
@@ -3148,17 +3254,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()
@@ -3668,7 +3801,8 @@ class WineHelperGUI(QMainWindow):
msg_box.setText( msg_box.setText(
"Приложение будет запущено в режиме отладки.\n\n" "Приложение будет запущено в режиме отладки.\n\n"
"После закрытия приложения лог будет сохранен в папке 'winehelper_backup_log' " "После закрытия приложения лог будет сохранен в папке 'winehelper_backup_log' "
"в вашем домашнем каталоге под именем программы (например, 'program.log')." "в вашем домашнем каталоге под именем (пример: prefix_program.log).\n\n"
"Продолжить?"
) )
msg_box.addButton(yes_button, QMessageBox.YesRole) msg_box.addButton(yes_button, QMessageBox.YesRole)
msg_box.addButton(no_button, QMessageBox.NoRole) msg_box.addButton(no_button, QMessageBox.NoRole)
@@ -4238,6 +4372,8 @@ class WineHelperGUI(QMainWindow):
if script_name in self.autoinstall_scripts: if script_name in self.autoinstall_scripts:
script_path = os.path.join(Var.DATA_PATH, "autoinstall", script_name) script_path = os.path.join(Var.DATA_PATH, "autoinstall", script_name)
tab_type = 'auto' tab_type = 'auto'
if not os.path.exists(script_path): # Проверяем в testinstall, если не нашли в autoinstall
script_path = os.path.join(Var.DATA_PATH, "testinstall", script_name)
self.manual_install_path_widget.setVisible(False) self.manual_install_path_widget.setVisible(False)
else: else:
script_path = os.path.join(Var.DATA_PATH, "manualinstall", script_name) script_path = os.path.join(Var.DATA_PATH, "manualinstall", script_name)
@@ -4403,7 +4539,9 @@ class WineHelperGUI(QMainWindow):
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 os.path.exists(os.path.join(Var.DATA_PATH, "autoinstall", self.current_script))
else "testinstall" if os.path.exists(os.path.join(Var.DATA_PATH, "testinstall", self.current_script))
else "manualinstall",
self.current_script) self.current_script)
if not os.path.exists(winehelper_path): if not os.path.exists(winehelper_path):