Compare commits

...

19 Commits

Author SHA1 Message Date
Mikhail Tergoev
a15b2621b6 updated changelog to 0.5.4 2025-09-04 16:03:30 +03:00
Mikhail Tergoev
fd00b61c71 added function: find_prefix 2025-09-04 15:53:40 +03:00
Mikhail Tergoev
74eaf34c00 Merge branch 'minergenon-devel' 2025-09-04 12:27:48 +03:00
Sergey Palcheh
a69493df73 database: changed WINEPREFIX=tflex17 to WINEPREFIX=tflex 2025-09-03 10:22:11 +06:00
Sergey Palcheh
6b958dd7f1 added a bash-c call to improve script compatibility 2025-09-03 10:18:52 +06:00
Mikhail Tergoev
2483f4c66c Merge branch 'minergenon-devel' 2025-09-02 17:28:44 +03:00
Sergey Palcheh
ab0e23952e added a button to create a program shortcut
the function of extracting an icon from an exe file is combined with the creation of a desktop file
added the function of automatic prefix detection if it is not specified
2025-09-02 16:23:39 +06:00
Sergey Palcheh
92a682ba8a added a more explicit error message when installing dependencies 2025-09-02 10:44:37 +06:00
Sergey Palcheh
3381054c21 added a winehelper launch button when dependencies are successfully installed 2025-09-02 10:23:44 +06:00
Sergey Palcheh
76b7d4f046 removed the dependency installation success window 2025-09-02 10:11:47 +06:00
Sergey Palcheh
aeae544640 added 64bit versions of wine/proton to the 32bit prefix 2025-09-02 09:50:27 +06:00
Sergey Palcheh
eeb94523b3 the refresh list button has been removed 2025-09-02 09:33:35 +06:00
Sergey Palcheh
a2186b63cb renaming Proton/Wine Lg buttons to LG 2025-09-02 09:25:01 +06:00
Mikhail Tergoev
839b480c39 updated sha256sum.list 2025-09-01 12:06:10 +03:00
Mikhail Tergoev
83fe362b37 added all WINE, DXVK and VKD3D to sha256 list 2025-08-28 13:50:48 +03:00
Mikhail Tergoev
f7fdfca743 fixed typos and updated LICENSE_AGREEMENT 2025-08-26 12:01:35 +03:00
Mikhail Tergoev
ffc7098aba Merge branch 'minergenon-devel' 2025-08-26 11:13:21 +03:00
Mikhail Tergoev
50de9bdb14 Merge branch 'minergenon-tflex_wineprefix' 2025-08-26 11:12:30 +03:00
Sergey Palcheh
ddea68c11c renaming tflex prefixes 2025-08-13 10:01:18 +06:00
14 changed files with 315 additions and 132 deletions

View File

@@ -1,5 +1,14 @@
История изменений:
0.5.4:
* обновлен графический режим Qt5:
- добавлена возможность установки системных зависимостей
- исправлены/улучшены основные функции
- добавлено управление префиксами (в разработке)
- добавлена возможность создания ярлыка
* добавлено автоматическое создание иконки для ярлыков (desktop файлов)
* префикс tflex17 переименован в tflex (т.к. 18-я версия работает в том же префиксе)
0.5.3:
* исправлена установка grdcontrol для t-flex-*
* обновлен графический режим Qt5

View File

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

View File

@@ -8,7 +8,7 @@ export WH_WINE_USE="wine_wh_tflex_10-9_amd64"
export BASE_PFX="tflex17_pfx_x64_v02"
export WINEARCH="win64"
export WH_WINDOWS_VER="10"
export WINEPREFIX="tflex17"
export WINEPREFIX="tflex"
export WH_XDG_OPEN="log"
export WH_USE_MESA_GL_OVERRIDE="1"

View File

@@ -8,7 +8,7 @@ export WH_WINE_USE="wine_wh_tflex_10-9_amd64"
export BASE_PFX="tflex17_pfx_x64_v02"
export WINEARCH="win64"
export WH_WINDOWS_VER="10"
export WINEPREFIX="tflex17"
export WINEPREFIX="tflex"
export WH_XDG_OPEN="log"
export WH_USE_MESA_GL_OVERRIDE="1"

View File

@@ -8,7 +8,7 @@ export WH_WINDOWS_VER="10"
export WH_WINE_USE="wine_wh_tflex_10-9_amd64"
export BASE_PFX="tflex17_pfx_x64_v02"
export WINEARCH="win64"
export WINEPREFIX="tflex17"
export WINEPREFIX="tflex"
export WH_XDG_OPEN="log"
export WH_USE_MESA_GL_OVERRIDE="1"

View File

@@ -8,7 +8,7 @@ export WH_WINDOWS_VER="10"
export WH_WINE_USE="wine_wh_tflex_10-9_amd64"
export BASE_PFX="tflex17_pfx_x64_v02"
export WINEARCH="win64"
export WINEPREFIX="tflex17"
export WINEPREFIX="tflex"
export WH_XDG_OPEN="log"
export WH_USE_MESA_GL_OVERRIDE="1"

View File

@@ -8,7 +8,7 @@ export WH_WINE_USE="wine_wh_tflex_10-9_amd64"
export BASE_PFX="tflex17_pfx_x64_v02"
export WINEARCH="win64"
export WH_WINDOWS_VER="10"
export WINEPREFIX="tflex17"
export WINEPREFIX="tflex"
export WH_XDG_OPEN="log"
export WH_USE_MESA_GL_OVERRIDE="1"

View File

@@ -8,7 +8,7 @@ export WH_WINE_USE="wine_wh_tflex_10-9_amd64"
export BASE_PFX="tflex17_pfx_x64_v02"
export WINEARCH="win64"
export WH_WINDOWS_VER="10"
export WINEPREFIX="tflex17"
export WINEPREFIX="tflex"
export WH_XDG_OPEN="log"
export WH_USE_MESA_GL_OVERRIDE="1"

View File

@@ -8,7 +8,7 @@ export WH_WINE_USE="wine_wh_tflex_10-9_amd64"
export BASE_PFX="tflex17_pfx_x64_v02"
export WINEARCH="win64"
export WH_WINDOWS_VER="10"
export WINEPREFIX="tflex17"
export WINEPREFIX="tflex"
export WH_XDG_OPEN="log"
export WH_USE_MESA_GL_OVERRIDE="1"

View File

@@ -5,7 +5,7 @@ export WH_WINDOWS_VER="10"
export WH_WINE_USE="wine_wh_tflex_10-9_amd64"
export BASE_PFX="tflex17_pfx_x64_v02"
export WINEARCH="win64"
export WINEPREFIX="tflex17"
export WINEPREFIX="tflex"
export PROG_VERSION=""
export WH_XDG_OPEN="log"
export INSTALL_DLL="corefonts d3dcompiler_47 dotnet48 vcrun2022 ucrtbase2019 msxml6 fontsmooth=rgb baekmuk droid eufonts ipamona liberation lucida opensymbol sourcehansans tahoma takao uff unifont vlgothic wenquanyi wenquanyizenhei"

View File

@@ -7,7 +7,7 @@ export WH_WINE_USE="wine_wh_tflex_10-9_amd64"
export BASE_PFX="tflex17_pfx_x64_v02"
export WINEARCH="win64"
export WH_WINDOWS_VER="10"
export WINEPREFIX="tflex17"
export WINEPREFIX="tflex"
export WH_XDG_OPEN="log"
export WH_USE_MESA_GL_OVERRIDE="1"

0
sha256sum.list Executable file → Normal file
View File

View File

@@ -177,7 +177,7 @@ fi
##### CHECK NOEXEC FOR /HOME #####
if mount -l | grep -E "[[:space:]]/home[[:space:]]" | grep -q "noexec" ; then
fatal "/home примонтирован в /etc/fstab с аргументом noexec.\nЗапуск портативной версии wine не возможен из домашнего каталога."
fatal "/home примонтирован в /etc/fstab с аргументом noexec.\nЗапуск портативной версии wine невозможен из домашнего каталога."
fi
##### ROOT #####
@@ -190,7 +190,7 @@ su_run () {
((i++))
done
else
pkexec "$@" && return 0
pkexec bash -c "$@" && return 0
fi
fatal "Не удалось установить необходимые компоненты!"
}
@@ -259,13 +259,13 @@ try_copy_dir () {
try_force_link_file () {
if [[ ! -f "$1" ]] ; then
print_warning "нет файла для создания символьной ссылки: $1"
print_warning "нет файла для создания символической ссылки: $1"
if [[ -f "$2" ]] ; then
try_remove_file "$2"
print_warning "удаляем символьную ссылку: $2"
print_warning "удаляем символическую ссылку: $2"
fi
return 1
elif [[ -z "$2" ]] ; then fatal "нет пути для создания символьной ссылки на файл $1"
elif [[ -z "$2" ]] ; then fatal "нет пути для создания символической ссылки на файл $1"
else
try_remove_file "$2"
ln -s -f -r "$1" "$2"
@@ -275,11 +275,11 @@ try_force_link_file () {
}
try_force_link_dir () {
if [[ ! -d "$1" ]] ; then print_info "каталога $1 не существует для создания символьной сссылки"
elif [[ -z "$2" ]] ; then fatal "не указан путь для создания символьной ссылки на каталог $1"
if [[ ! -d "$1" ]] ; then print_info "каталога $1 не существует для создания символической сссылки"
elif [[ -z "$2" ]] ; then fatal "не указан путь для создания символической ссылки на каталог $1"
else
ln -s -f -r "$1" "$2"
[[ "$?" != 0 ]] && print_error "не удалось сделать символьную ссылку на каталог $1 по пути $2" || return 0
[[ "$?" != 0 ]] && print_error "не удалось сделать символическую ссылку на каталог $1 по пути $2" || return 0
fi
return 1
}
@@ -328,7 +328,7 @@ try_get_page () {
|| grep -q "Forbidden" "$OUT_PAGE_TMP"
then
try_remove_file "$OUT_PAGE_TMP"
fatal "Страница сайта $1 не доступна, или превышено количество запросов к странице."
fatal "Страница сайта $1 не доступна или превышено количество запросов к странице."
else
return 0
fi
@@ -509,63 +509,41 @@ var_ld_library_path_update () {
return 0
}
extract_icon() {
check_prefix_var
local exe_file="$1"
local ico_name="$(basename "$exe_file" .exe).ico"
local png_name="$(basename "$exe_file" .exe).png"
local tmp_ico_dir="$WH_TMP_DIR/icons"
local user_icons="$WINEPREFIX/icons"
create_new_dir "$tmp_ico_dir"
if ! wrestool -x -t 14 "$exe_file" -o "$tmp_ico_dir/$ico_name" ; then
print_warning "Не удалось извлечь иконку из $exe_file"
try_remove_file "$tmp_ico_dir"
return 1
find_prefix () {
# Автоматическое определение префикса, если он не задан
if [[ -z "$WINEPREFIX" ]] && [[ "$1" == "$WH_PREFIXES_DIR"* ]]; then
local extracted_prefix
extracted_prefix="$(echo "$1" | grep -o ".*/prefixes/[^/]*")"
if [[ -d "$extracted_prefix" ]]; then
export WINEPREFIX="$extracted_prefix"
print_info "Префикс автоматически определен: $(basename "$WINEPREFIX")"
fi
fi
if ! icotool -x -i 1 "$tmp_ico_dir/$ico_name" -o "$tmp_ico_dir/$png_name" ; then
print_warning "Не удалось извлечь иконку из $ico_name"
try_remove_file "$tmp_ico_dir"
return 1
fi
create_new_dir "$user_icons"
if ! try_copy_file "$tmp_ico_dir/$png_name" "$user_icons" ; then
print_warning "Не удалось копировать иконку в префикс"
try_remove_file "$user_icons"
return 1
fi
try_remove_dir "$tmp_ico_dir"
print_ok "Иконка сохранена: $user_icons/$png_name"
return 0
}
create_desktop () {
local name_desktop exe_file desktop_filename icon_file desktop_path
local name_desktop exe_file desktop_filename icon_file desktop_path icon_arg desktop_filename_arg
name_desktop="$1"
exe_file="$2"
if [[ -n $4 ]] && [[ $4 != "nocopy" ]];
then desktop_filename="$4"
else desktop_filename="$(basename "$exe_file" .exe | sed "s| |_|")"
fi
if [[ "$RESTORE_FROM_BACKUP" == "1" ]] && [[ -f "$3" ]]
then icon_file="$3"
elif [[ -f "$WH_IMAGE_PATH/$3.png" ]]
then icon_file="$WH_IMAGE_PATH/$3.png"
else icon_file="wine"
icon_arg="$3"
desktop_filename_arg="$4"
# Определяем имя desktop-файла
if [[ -n "$desktop_filename_arg" ]] && [[ "$desktop_filename_arg" != "nocopy" ]]; then
desktop_filename="$desktop_filename_arg"
else
desktop_filename="$(basename "$exe_file" .exe | sed "s| |_|g")"
fi
# Проверяем обязательные аргументы и наличие exe-файла
if [[ -z "$name_desktop" ]] || [[ -z "$exe_file" ]] ; then
fatal "Used: $SCRIPT_NAME --desktop \"desktop_name\" \"path_to_exe\" \"name_png_from_image\""
fatal "Использование: $0 desktop \"Имя ярлыка\" \"/путь/к/файлу.exe\" [иконка|auto] [имя_desktop_файла]"
elif [[ ! -f "$exe_file" ]] ; then
print_warning "Для создания ярлыка не найден исполняемый файл: $exe_file"
BASENAME_EXE="$(basename "$exe_file")"
local BASENAME_EXE="$(basename "$exe_file")"
print_info "Запускаем поиск $BASENAME_EXE"
local FIND_PATH
if [[ -z "$DRIVE_C" ]] || [[ ! -d "$DRIVE_C" ]]
then FIND_PATH="$WH_PREFIXES_DIR"
else FIND_PATH="$DRIVE_C"
@@ -575,11 +553,57 @@ create_desktop () {
-iname "$BASENAME_EXE")"
if [[ -z "$exe_file" ]] || [[ ! -f "$exe_file" ]]
then fatal "Для создания ярлыка не найден исполняемый файл: $BASENAME_EXE"
else print_ok "Исполняемый файл $BASENAME_EXE найден по пути $(dirname "$exe_file")/"
else print_ok "Исполняемый файл $BASENAME_EXE найден по пути: $(dirname "$exe_file")/"
fi
fi
# создаем .desktop файл
find_prefix "$exe_file"
# --- Логика обработки иконки ---
local user_icons_dir="$WINEPREFIX/icons"
create_new_dir "$user_icons_dir"
# Случай 1: Восстановление из бэкапа (передан явный путь)
if [[ "$RESTORE_FROM_BACKUP" == "1" ]] && [[ -f "$icon_arg" ]]; then
icon_file="$icon_arg"
# Случай 2: 'auto' или пустой аргумент - пытаемся извлечь из EXE
elif [[ -z "$icon_arg" ]] || [[ "$icon_arg" == "auto" ]]; then
print_info "Попытка извлечь иконку из $exe_file..."
local png_name="$(basename "$exe_file" .exe).png"
local extracted_icon_path="$user_icons_dir/$png_name"
# Проверяем, существует ли иконка, чтобы избежать повторного извлечения
if [[ -f "$extracted_icon_path" ]]; then
print_info "Иконка уже существует: $extracted_icon_path"
icon_file="$extracted_icon_path"
else
local tmp_ico_dir="$WH_TMP_DIR/icons_$$" # Используем PID для временного каталога
create_new_dir "$tmp_ico_dir"
local ico_name="$(basename "$exe_file" .exe).ico"
if wrestool -x -t 14 "$exe_file" -o "$tmp_ico_dir/$ico_name" &>/dev/null && \
icotool -x -i 1 "$tmp_ico_dir/$ico_name" -o "$tmp_ico_dir/$png_name" &>/dev/null && \
try_copy_file "$tmp_ico_dir/$png_name" "$user_icons_dir/"; then
icon_file="$extracted_icon_path"
print_ok "Иконка успешно извлечена и сохранена: $icon_file"
else
print_warning "Не удалось извлечь иконку из $exe_file. Используется иконка по умолчанию."
icon_file="wine" # Запасной вариант
fi
try_remove_dir "$tmp_ico_dir"
fi
# Случай 3: Передано конкретное имя иконки
elif [[ -f "$WH_IMAGE_PATH/$icon_arg.png" ]]; then
icon_file="$WH_IMAGE_PATH/$icon_arg.png"
# Случай 4: Запасной вариант по умолчанию
else
print_info "Иконка '$icon_arg' не найдена. Используется иконка по умолчанию."
icon_file="wine"
fi
# --- Конец логики обработки иконки ---
# Создаем .desktop файл
create_new_dir "$WH_MENU_DIR"
{
echo "[Desktop Entry]"
@@ -596,19 +620,29 @@ create_desktop () {
cp -f "$USER_WORK_PATH/$desktop_filename.desktop" "$WH_MENU_DIR/"
if [[ "$RESTORE_FROM_BACKUP" == "1" ]] ; then
print_info "Пропускаем обновление desktop.list (режим восстановления из бэкапа)"
print_info "Пропускаем обновление desktop.list (режим восстановления)"
else
# добавляем информацию о приложении в "$WINEPREFIX/desktop.list"
# Добавляем информацию о приложении в "$WINEPREFIX/desktop.list"
if [[ -f "$WINEPREFIX/desktop.list" ]] \
&& grep -qe "^${name_desktop}=" "$WINEPREFIX/desktop.list"
then sed -i "/^$name_desktop=/d" "$WINEPREFIX/desktop.list"
fi
create_new_dir "$WINEPREFIX/icons"
try_copy_file "$icon_file" "$WINEPREFIX/icons/"
echo "$name_desktop=${exe_file//$WINEPREFIX/}=$(basename "$icon_file")" >> "$WINEPREFIX/desktop.list"
# Копируем финальную иконку в директорию иконок префикса, если ее там нет
local final_icon_name
if [[ -f "$icon_file" ]]; then
final_icon_name=$(basename "$icon_file")
if [[ ! -f "$user_icons_dir/$final_icon_name" ]]; then
try_copy_file "$icon_file" "$user_icons_dir/"
fi
else
final_icon_name="$icon_file" # например, "wine"
fi
echo "$name_desktop=${exe_file//$WINEPREFIX/}=${final_icon_name}" >> "$WINEPREFIX/desktop.list"
fi
# создаем файл категории для меню
# Создаем файл категории для меню
create_new_dir "$HOME/.local/share/desktop-directories"
if [[ ! -f "$WH_MENU_CATEGORY" ]] ; then
cat > "$WH_MENU_CATEGORY" <<EOF
@@ -619,7 +653,7 @@ Icon=wine
EOF
fi
# Создаем файл меню для всех приложений
# Создаем файл меню для всех приложений WineHelper
create_new_dir "$HOME/.config/menus/applications-merged"
if [[ ! -f "$WH_MENU_CONFIG" ]] ; then
cat > "$WH_MENU_CONFIG" <<EOF
@@ -638,15 +672,15 @@ EOF
EOF
fi
# Обновляем кэш desktop файлов
# Обновляем кэш desktop-файлов
update-desktop-database "$HOME/.local/share/applications"
if [[ $4 != "nocopy" ]] ; then
if [[ "$desktop_filename_arg" != "nocopy" ]] ; then
desktop_path="$(xdg-user-dir DESKTOP)"
print_info "В меню и на рабочем столе создан $desktop_filename.desktop"
print_info "В меню и на рабочем столе создан ярлык: $desktop_filename.desktop"
cp -f "$USER_WORK_PATH/$desktop_filename.desktop" "$desktop_path"
else
print_info "В меню создан $desktop_filename.desktop"
print_info "В меню создан ярлык: $desktop_filename.desktop"
fi
if [[ -n "$INSTALL_SCRIPT_NAME" ]] \
@@ -1074,7 +1108,7 @@ init_wineprefix () {
if [[ ! -f "$WINEPREFIX/.firstboot" ]] ; then
create_new_dir "$WINEPREFIX"
if [[ "$CLEAR_PREFIX" == "1" ]]
then print_warning "Используется переменная \"CLEAR_PREFIX=1\" что принудительно создает чистый префикс с установкой компонентов с помощью winetricks."
then print_warning "Используется переменная \"CLEAR_PREFIX=1\", которая принудительно создает чистый префикс с установкой компонентов с помощью winetricks."
elif [[ "$BASE_PFX" != "none" ]]
then get_base_pfx "$BASE_PFX"
fi
@@ -1165,7 +1199,7 @@ init_wineprefix () {
for ext in $WH_XDG_OPEN ; do
get_and_set_reg_file --add "Software\Classes\.$ext" '@=' 'REG_SZ' "xdg-open" "system"
done
print_info "Используется ассоциации с нативными приложениями для файлов: \"$WH_XDG_OPEN\""
print_info "Используются ассоциации с нативными приложениями для файлов: \"$WH_XDG_OPEN\""
else
# удаление команды xdg-open из реестра
get_and_set_reg_file --delete "$XDG_OPEN_REG" '@='
@@ -1193,7 +1227,7 @@ init_wineprefix () {
"${cpcsp_proxy_cmd[@]}" | tee "$WINEPREFIX/cpcsp_setup.log"
local CPCSP_EXIT_STATUS="${PIPESTATUS[0]}"
if grep -q "failed to load /opt/cprocsp/" "$WINEPREFIX/cpcsp_setup.log" ; then
fatal "Проверьте правильность установки CryptoPro в системе.\n Инструкция: https://www.altlinux.org/CryptoPro"
fatal "Проверьте правильность установки КриптоПро в системе.\n Инструкция: https://www.altlinux.org/CryptoPro"
fi
if [[ $CPCSP_EXIT_STATUS == "0" ]] ; then
@@ -1535,9 +1569,7 @@ select_wine_version() {
group_versions+=("$version_name")
fi
else # win32
if [[ "$version_name" =~ i[3-6]86 ]]; then
group_versions+=("$version_name")
fi
group_versions+=("$version_name")
fi
fi
done < "$sha256_file"
@@ -1717,7 +1749,7 @@ remove_winehelper () {
echo " Это удалит:"
echo " - Все настройки WineHelper"
echo " - Все приложения/программы, установленные через WineHelper"
echo " - Все ярлыки из меню и с рабочего стола созданные с помощью WineHelper"
echo " - Все ярлыки из меню и с рабочего стола, созданные с помощью WineHelper"
echo "======================================================"
if print_confirmation "Продолжить?" ; then
echo "----------------------------------------------"
@@ -2046,7 +2078,8 @@ case "$arg1" in
*)
if [[ -f "$arg1" ]] ; then
WIN_FILE_EXEC="$(readlink -f "$arg1")"
WIN_FILE_NAME="$(basename "$arg1")"
WIN_FILE_NAME="$(basename "$WIN_FILE_EXEC")"
find_prefix "$WIN_FILE_EXEC"
case "${WIN_FILE_NAME,,}" in
*.exe) prepair_wine ; wine_run $WINE_WIN_START "$WIN_FILE_EXEC" "$@" ;;
*.msi) prepair_wine ; wine_run msiexec /i "$WIN_FILE_EXEC" "$@" ;;

View File

@@ -12,7 +12,7 @@ import hashlib
from functools import partial
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QTabWidget, QTabBar,
QTextEdit, QFileDialog, QMessageBox, QLineEdit, QCheckBox, QStackedWidget, QScrollArea, QFormLayout, QGroupBox, QRadioButton, QComboBox,
QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser)
QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser, QInputDialog)
from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment, QPropertyAnimation, QEasingCurve
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QDesktopServices
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
@@ -340,13 +340,8 @@ class DependencyManager:
still_missing = missing_packages
if not still_missing:
info_box = QMessageBox(dialog)
info_box.setWindowIcon(self.app_icon)
info_box.setIcon(QMessageBox.Information)
info_box.setWindowTitle("Успех")
info_box.setText("Все необходимые зависимости были успешно установлены.")
info_box.exec_()
installation_successful = True
close_button.setText("Запустить WineHelper")
else:
warn_box = QMessageBox(dialog)
warn_box.setWindowIcon(self.app_icon)
@@ -359,8 +354,16 @@ class DependencyManager:
)
warn_box.exec_()
else:
log_tag = "ПРЕРВАНО" if exit_status == QProcess.CrashExit else "ОШИБКА"
log_output.append(f"\n<b><font color='red'>=== {log_tag} (код: {exit_code}) ===</font></b>")
if exit_code == 127: # pkexec: пользователь отменил аутентификацию
log_output.append("\n<b><font color='orange'>=== УСТАНОВКА ОТМЕНЕНА ПОЛЬЗОВАТЕЛЕМ ===</font></b>")
log_output.append("Вы отменили ввод пароля. Установка зависимостей не была выполнена.")
elif exit_code == 126: # pkexec: у пользователя нет прав
log_output.append("\n<b><font color='red'>=== ОШИБКА: НЕДОСТАТОЧНО ПРАВ ===</font></b>")
log_output.append("У вашего пользователя нет прав для выполнения этой операции.")
else:
log_tag = "ПРЕРВАНО" if exit_status == QProcess.CrashExit else "ОШИБКА"
log_output.append(f"\n<b><font color='red'>=== {log_tag} (код: {exit_code}) ===</font></b>")
log_output.append("Произошла непредвиденная ошибка во время установки.")
log_output.ensureCursorVisible()
close_button.setEnabled(True)
@@ -962,7 +965,8 @@ class ScriptParser:
if part == 'create_desktop':
if len(parts) > i + 3:
icon_name = parts[i + 3]
if icon_name:
# Игнорируем служебные слова, которые не являются иконками
if icon_name and icon_name.lower() not in ('auto', 'nocopy'):
icon_names.append(icon_name)
except (ValueError, IndexError):
continue
@@ -1035,13 +1039,6 @@ class WineVersionSelectionDialog(QDialog):
self.version_tabs = QTabWidget()
main_layout.addWidget(self.version_tabs)
button_layout = QHBoxLayout()
self.refresh_button = QPushButton("Обновить список")
self.refresh_button.setIcon(QIcon.fromTheme("view-refresh"))
self.refresh_button.clicked.connect(self.load_versions)
button_layout.addStretch()
button_layout.addWidget(self.refresh_button)
main_layout.addLayout(button_layout)
self.load_versions()
@@ -1055,14 +1052,12 @@ class WineVersionSelectionDialog(QDialog):
loading_layout.addWidget(status_label)
self.version_tabs.addTab(loading_widget, "Загрузка...")
self.version_tabs.setEnabled(False)
self.refresh_button.setEnabled(False)
QApplication.processEvents()
self._parse_sha256_list()
self.populate_ui()
self.refresh_button.setEnabled(True)
self.version_tabs.setEnabled(True)
def _parse_sha256_list(self):
@@ -1150,14 +1145,16 @@ class WineVersionSelectionDialog(QDialog):
if is_win64:
if re_64bit.search(name) or not re_32bit.search(name):
filtered_versions.append(name)
else: # win32
if re_32bit.search(name):
filtered_versions.append(name)
else:
filtered_versions.append(name)
if not filtered_versions:
continue
pretty_key = key.replace('_', ' ').title()
if key.endswith('_LG'):
pretty_key = pretty_key.replace(' Lg', ' LG')
self._create_version_tab(pretty_key, filtered_versions)
self.filter_versions()
@@ -1979,7 +1976,7 @@ class WineHelperGUI(QMainWindow):
install_path_layout = QHBoxLayout()
self.prefix_install_path_edit = QLineEdit()
self.prefix_install_path_edit.setPlaceholderText("Путь к .exe или .msi файлу...")
self.prefix_install_path_edit.setPlaceholderText("Укажите путь к установочному файлу .exe или .msi...")
install_path_layout.addWidget(self.prefix_install_path_edit)
self.prefix_browse_button = QPushButton("Обзор...")
@@ -1987,10 +1984,21 @@ class WineHelperGUI(QMainWindow):
install_path_layout.addWidget(self.prefix_browse_button)
install_layout.addLayout(install_path_layout)
# Layout для кнопок установки и создания ярлыка
action_buttons_layout = QHBoxLayout()
self.prefix_install_button = QPushButton("Установить приложение в префикс")
self.prefix_install_button.setEnabled(False)
self.prefix_install_button.clicked.connect(self.run_prefix_installer)
install_layout.addWidget(self.prefix_install_button)
action_buttons_layout.addWidget(self.prefix_install_button)
self.create_launcher_button = QPushButton("Создать ярлык для приложения в префиксе")
self.create_launcher_button.setToolTip(
"Создает ярлык в меню и на вкладке 'Установленные' для .exe файла внутри префикса.")
self.create_launcher_button.clicked.connect(self.create_launcher_for_prefix)
self.create_launcher_button.setEnabled(False) # Изначально неактивна
action_buttons_layout.addWidget(self.create_launcher_button)
install_layout.addLayout(action_buttons_layout)
management_layout.addWidget(install_group, 4, 0, 1, 3)
@@ -2005,6 +2013,19 @@ class WineHelperGUI(QMainWindow):
self.wine_version_edit.textChanged.connect(self.update_create_prefix_button_state)
self.prefix_install_path_edit.textChanged.connect(self.update_prefix_install_button_state)
def _remove_prefix_from_gui_state(self, prefix_name):
"""Удаляет префикс из внутреннего состояния и пользовательского интерфейса вкладки 'Создать префикс'."""
if prefix_name in self.created_prefixes_info:
del self.created_prefixes_info[prefix_name]
index_to_remove = self.created_prefix_selector.findText(prefix_name)
if index_to_remove != -1:
self.created_prefix_selector.removeItem(index_to_remove)
# Сохраняем состояние после удаления. on_created_prefix_selected также вызовет сохранение,
# но этот вызов гарантирует сохранение, даже если сигналы были заблокированы.
self._save_state()
def _load_state(self):
"""Загружает последнее состояние GUI из файла."""
if not os.path.exists(self.state_file):
@@ -2079,31 +2100,59 @@ class WineHelperGUI(QMainWindow):
msg_box.setIcon(QMessageBox.Question)
msg_box.setWindowTitle('Подтверждение удаления')
msg_box.setText(f'Вы уверены, что хотите удалить префикс "{prefix_name}"?\n\n'
'Это действие необратимо и удалит все данные внутри префикса.')
'Это действие необратимо и удалит все данные внутри префикса, а также все связанные с ним ярлыки.')
yes_button = msg_box.addButton("Да", QMessageBox.YesRole)
yes_button = msg_box.addButton("Да, удалить", QMessageBox.YesRole)
no_button = msg_box.addButton("Нет", QMessageBox.NoRole)
msg_box.setDefaultButton(no_button)
msg_box.exec_()
# Если пользователь нажал не "Да", выходим
if msg_box.clickedButton() != yes_button:
return
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
# Используем модальный диалог для отображения процесса удаления
self.command_dialog = QDialog(self)
self.command_dialog.setWindowTitle(f"Удаление префикса: {prefix_name}")
self.command_dialog.setMinimumSize(750, 400)
self.command_dialog.setModal(True)
self.command_dialog.setWindowFlags(self.command_dialog.windowFlags() & ~Qt.WindowCloseButtonHint)
try:
if os.path.isdir(prefix_path):
shutil.rmtree(prefix_path)
if prefix_name in self.created_prefixes_info:
del self.created_prefixes_info[prefix_name]
index_to_remove = self.created_prefix_selector.findText(prefix_name)
if index_to_remove != -1:
self.created_prefix_selector.removeItem(index_to_remove)
QMessageBox.information(self, "Успех", f"Префикс '{prefix_name}' был успешно удален.")
except Exception as e:
QMessageBox.critical(self, "Ошибка удаления", f"Не удалось удалить префикс '{prefix_name}':\n{e}")
layout = QVBoxLayout()
self.command_log_output = QTextEdit()
self.command_log_output.setReadOnly(True)
self.command_log_output.setFont(QFont('DejaVu Sans Mono', 10))
layout.addWidget(self.command_log_output)
self.command_close_button = QPushButton("Закрыть")
self.command_close_button.setEnabled(False)
self.command_close_button.clicked.connect(self.command_dialog.close)
layout.addWidget(self.command_close_button)
self.command_dialog.setLayout(layout)
self.command_process = QProcess(self.command_dialog)
self.command_process.setProcessChannelMode(QProcess.MergedChannels)
self.command_process.readyReadStandardOutput.connect(self._handle_command_output)
self.command_process.finished.connect(
lambda exit_code, exit_status: self._handle_prefix_deletion_finished(prefix_name, exit_code, exit_status)
)
args = ["remove-prefix", prefix_name, "--force"]
self.command_log_output.append(f"Выполнение: {shlex.quote(self.winehelper_path)} {' '.join(shlex.quote(a) for a in args)}")
self.command_process.start(self.winehelper_path, args)
self.command_dialog.exec_()
def _handle_prefix_deletion_finished(self, prefix_name, exit_code, exit_status):
"""Обрабатывает завершение процесса удаления префикса."""
self._handle_command_finished(exit_code, exit_status)
if exit_code == 0:
# Успешное удаление, обновляем GUI
self._remove_prefix_from_gui_state(prefix_name)
self.update_installed_apps()
QMessageBox.information(self, "Успех", f"Префикс '{prefix_name}' и все связанные с ним данные были успешно удалены.")
else:
QMessageBox.critical(self, "Ошибка удаления", f"Не удалось удалить префикс '{prefix_name}'.\nПодробности смотрите в логе.")
def on_prefix_name_edited(self, text):
"""Сбрасывает состояние управления префиксом, когда пользователь вводит новое имя."""
@@ -2120,6 +2169,9 @@ class WineHelperGUI(QMainWindow):
self.prefix_management_groupbox.setEnabled(False)
self.prefix_info_display.clear()
self.prefix_install_path_edit.clear()
# Кнопка "Создать ярлык" должна быть активна, если выбран действительный префикс
is_prefix_selected = bool(prefix_name and prefix_name in self.created_prefixes_info)
self.create_launcher_button.setEnabled(is_prefix_selected)
self.update_prefix_install_button_state()
def update_prefix_info_display(self, prefix_name):
@@ -2201,6 +2253,79 @@ class WineHelperGUI(QMainWindow):
self.command_process.start(wine_executable, args)
self.command_dialog.exec_()
def create_launcher_for_prefix(self):
"""
Открывает диалог для создания ярлыка для приложения внутри выбранного префикса.
"""
prefix_name = self.current_managed_prefix_name
if not prefix_name:
QMessageBox.warning(self, "Ошибка", "Сначала выберите префикс.")
return
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
drive_c_path = os.path.join(prefix_path, "drive_c")
if not os.path.isdir(drive_c_path):
QMessageBox.critical(self, "Ошибка", f"Диск C: для префикса '{prefix_name}' не найден.")
return
# 1. Открываем диалог выбора файла для .exe
exe_path, _ = QFileDialog.getOpenFileName(
self,
"Выберите исполняемый файл (.exe) для создания ярлыка",
drive_c_path,
"Исполняемые файлы (*.exe)"
)
if not exe_path:
return # Пользователь отменил
# 2. Запрашиваем имя для ярлыка
app_name, ok = QInputDialog.getText(
self,
"Имя ярлыка",
"Введите имя для нового ярлыка:",
QLineEdit.Normal,
os.path.splitext(os.path.basename(exe_path))[0] # Предлагаем имя из .exe
)
if not ok or not app_name.strip():
return # Пользователь отменил или ввел пустое имя
# 3. Вызываем winehelper.sh create-desktop
self.command_dialog = QDialog(self)
self.command_dialog.setWindowTitle(f"Создание ярлыка для: {app_name}")
self.command_dialog.setMinimumSize(750, 400)
self.command_dialog.setModal(True)
self.command_dialog.setWindowFlags(self.command_dialog.windowFlags() & ~Qt.WindowCloseButtonHint)
layout = QVBoxLayout()
self.command_log_output = QTextEdit()
self.command_log_output.setReadOnly(True)
self.command_log_output.setFont(QFont('DejaVu Sans Mono', 10))
layout.addWidget(self.command_log_output)
self.command_close_button = QPushButton("Закрыть")
self.command_close_button.setEnabled(False)
self.command_close_button.clicked.connect(self.command_dialog.close)
layout.addWidget(self.command_close_button)
self.command_dialog.setLayout(layout)
self.command_process = QProcess(self.command_dialog)
self.command_process.setProcessChannelMode(QProcess.MergedChannels)
self.command_process.readyReadStandardOutput.connect(self._handle_command_output)
self.command_process.finished.connect(self._handle_launcher_creation_finished)
env = QProcessEnvironment.systemEnvironment()
env.insert("WINEPREFIX", prefix_path)
self.command_process.setProcessEnvironment(env)
args = ["desktop", app_name, exe_path, "auto"]
self.command_log_output.append(f"Выполнение: {shlex.quote(self.winehelper_path)} {' '.join(shlex.quote(a) for a in args)}")
self.command_process.start(self.winehelper_path, args)
self.command_dialog.exec_()
def create_help_tab(self):
"""Создает вкладку 'Справка' с подвкладками"""
help_tab = QWidget()
@@ -2507,6 +2632,7 @@ class WineHelperGUI(QMainWindow):
"""Обрабатывает завершение установки в префикс."""
if exit_code == 0:
self.command_log_output.append("\n=== Установка успешно завершена ===")
self.create_launcher_button.setEnabled(True) # Активируем кнопку создания ярлыка
else:
self.command_log_output.append(f"\n=== Ошибка выполнения (код: {exit_code}) ===")
@@ -2966,6 +3092,9 @@ class WineHelperGUI(QMainWindow):
except Exception as e:
raise RuntimeError(f"Ошибка удаления префикса: {str(e)}")
# Обновляем состояние на вкладке "Создать префикс"
self._remove_prefix_from_gui_state(prefix_name)
# 3. Удаляем ВСЕ найденные .desktop файлы, связанные с этим префиксом
removed_files = []
for file_path in all_desktop_files:
@@ -3518,6 +3647,18 @@ class WineHelperGUI(QMainWindow):
self.command_process = None
self.command_close_button.setEnabled(True)
def _handle_launcher_creation_finished(self, exit_code, exit_status):
"""Обрабатывает завершение создания ярлыка."""
self._handle_command_finished(exit_code, exit_status)
if exit_code == 0:
QMessageBox.information(self, "Успех", "Ярлык успешно создан.")
self.update_installed_apps()
# Переключаемся на вкладку "Установленные"
for i in range(self.tab_bar.count()):
if self.tab_bar.tabText(i) == "Установленные":
self.tab_bar.setCurrentIndex(i)
break
def _handle_restore_finished(self, exit_code, exit_status):
"""Обрабатывает завершение для диалога команды восстановления."""
if exit_code == 0: