Compare commits
9 Commits
0.8.2
...
1f4cb54f54
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f4cb54f54 | ||
|
|
af52bd74d0 | ||
|
|
ded62bd9ac | ||
|
|
15b6fdd216 | ||
|
|
33bba61891 | ||
|
|
cbf0bdbf8a | ||
|
|
5953d4b122 | ||
|
|
e12b24ccbc | ||
|
|
4d653cdd41 |
25
README.md
@@ -239,6 +239,13 @@ WineHelper предоставляет доступ к основным инст
|
|||||||
<p><em>Вкладка "Ручная установка"</em></p>
|
<p><em>Вкладка "Ручная установка"</em></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Так же на вкладке **Автоматическая установка** есть чекбокс с тестовыми версиями скриптов установки которые еще не прошли полную проверку на зависимости и совместимость, но уже можно попробовать в работе.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="image/handbook/auto_install_test.png">
|
||||||
|
<p><em>Тестовые версии скриптов установки</em></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
Для поиска нужной программы введите название в поле поиска.
|
Для поиска нужной программы введите название в поле поиска.
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
@@ -246,7 +253,7 @@ WineHelper предоставляет доступ к основным инст
|
|||||||
<p><em>Поле поиска</em></p>
|
<p><em>Поле поиска</em></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
При выборе программы из списка слева, в правой части окна отображается подробная информация о ней: описание, иконка и ссылка на официальный сайт.
|
При выборе программы из списка слева, в правой части окна отображается подробная информация о ней: описание и ссылка на официальный сайт.
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="image/handbook/info.png">
|
<img src="image/handbook/info.png">
|
||||||
@@ -271,7 +278,7 @@ WineHelper предоставляет доступ к основным инст
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="image/handbook/log.png">
|
<img src="image/handbook/log.png">
|
||||||
<p><em>Окно установки с логом</em></м</em></p>
|
<p><em>Окно установки с логом</em></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
После установки приложения и нажатия кнопки **Закрыть** в окне установки приложения, ярлык приложения появится в списке установленных приложений во вкладке **Установленные** а также в меню приложений и на рабочем столе если это разрешено в рабочем окружении.
|
После установки приложения и нажатия кнопки **Закрыть** в окне установки приложения, ярлык приложения появится в списке установленных приложений во вкладке **Установленные** а также в меню приложений и на рабочем столе если это разрешено в рабочем окружении.
|
||||||
@@ -323,7 +330,7 @@ WineHelper предоставляет доступ к основным инст
|
|||||||
* **Версию Wine/Proton** из доступного списка.
|
* **Версию Wine/Proton** из доступного списка.
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="image/handbook/create_prefix.png">
|
<img src="image/handbook/prefix_create.png">
|
||||||
<p><em>Диалог создания нового префикса</em></p>
|
<p><em>Диалог создания нового префикса</em></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -339,9 +346,9 @@ WineHelper предоставляет доступ к основным инст
|
|||||||
* `Файловый менеджер (winefile)`
|
* `Файловый менеджер (winefile)`
|
||||||
* **Управлять компонентами**:
|
* **Управлять компонентами**:
|
||||||
* **Менеджер компонентов (Winetricks)**: Удобный интерфейс для установки и переустановки библиотек, шрифтов и настроек.
|
* **Менеджер компонентов (Winetricks)**: Удобный интерфейс для установки и переустановки библиотек, шрифтов и настроек.
|
||||||
* **Управление Wine/Proton**: Смена версии Wine или Proton для выбранного префикса.
|
* **Управление Wine/Proton**: Смена версии Wine или Proton для выбранного префикса. Диалог выбора позволяет как выбрать уже установленную версию, так и скачать новую из списков, сгруппированных по архитектуре.
|
||||||
* **Управление DXVK/VKD3D**: Установка или удаление конкретных версий DXVK и VKD3D.
|
* **Управление DXVK/VKD3D**: Установка или удаление конкретных версий DXVK и vkd3d-proton.
|
||||||
* **Ассоциации файлов**: Настройка открытия определенных типов файлов (например, `.pdf`, `.docx`) нативными приложениями Linux.
|
* **Ассоциации файлов**: Настройка открытия определенных типов файлов (например, `.pdf`, `.docx`) нативными приложениями Linux, чтобы избежать их открытия внутри Wine.
|
||||||
* **Включать/выключать ESync и FSync**.
|
* **Включать/выключать ESync и FSync**.
|
||||||
* **Устанавливать приложения**: Установить любой `.exe` или `.msi` файл напрямую в выбранный префикс.
|
* **Устанавливать приложения**: Установить любой `.exe` или `.msi` файл напрямую в выбранный префикс.
|
||||||
* **Создавать ярлыки**: Создать ярлык для любого исполняемого файла внутри префикса.
|
* **Создавать ярлыки**: Создать ярлык для любого исполняемого файла внутри префикса.
|
||||||
@@ -349,10 +356,14 @@ WineHelper предоставляет доступ к основным инст
|
|||||||
|
|
||||||
Справа отображается подробная информация о конфигурации выбранного префикса.
|
Справа отображается подробная информация о конфигурации выбранного префикса.
|
||||||
|
|
||||||
|
В самом низу вкладки находится кнопка **«Удалить все данные WineHelper»**.
|
||||||
|
> [!CAUTION]
|
||||||
|
> Эта функция полностью и безвозвратно удаляет все префиксы, настройки, кэш и ярлыки, созданные WineHelper. Используйте с осторожностью! Программа запросит двойное подтверждение для предотвращения случайного удаления.
|
||||||
|
|
||||||
### Вкладка «Справка»
|
### Вкладка «Справка»
|
||||||
|
|
||||||
Содержит полезную информацию о проекте:
|
Содержит полезную информацию о проекте:
|
||||||
* **Руководство**: Ссылка на официальную документацию.
|
* **Общее**: Ссылка на официальную документацию.
|
||||||
* **Авторы**: Список разработчиков и участников проекта.
|
* **Авторы**: Список разработчиков и участников проекта.
|
||||||
* **Лицензия**: Текст лицензии WineHelper и информация о сторонних компонентах.
|
* **Лицензия**: Текст лицензии WineHelper и информация о сторонних компонентах.
|
||||||
* **История изменений**: Changelog пакета.
|
* **История изменений**: Changelog пакета.
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 243 KiB |
BIN
image/handbook/auto_install_test.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
|
Before Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 227 KiB After Width: | Height: | Size: 234 KiB |
BIN
image/handbook/prefix_create.png
Normal file
|
After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 125 KiB |
@@ -180,9 +180,6 @@ dfb44ce5e5af7dba1686932c63d6b05e5dd6919a21c78130a7d1d0271b93958e audiorecstatio
|
|||||||
# create with wine_x_tkg_10-0_amd64 (universal user: xuser)
|
# create with wine_x_tkg_10-0_amd64 (universal user: xuser)
|
||||||
# winetricks dotnet48 gdiplus vcrun6sp6 vcrun2005 vcrun2019 d3dx11_42 d3dx11_43 d3dx9 d3dcompiler_42 d3dcompiler_43 d3dcompiler_46 d3dcompiler_47 richtx32 riched30 riched20 msxml6 dotnet20
|
# winetricks dotnet48 gdiplus vcrun6sp6 vcrun2005 vcrun2019 d3dx11_42 d3dx11_43 d3dx9 d3dcompiler_42 d3dcompiler_43 d3dcompiler_46 d3dcompiler_47 richtx32 riched30 riched20 msxml6 dotnet20
|
||||||
|
|
||||||
# addons with ODBC, SSH, *.reg
|
|
||||||
0f4ef434df07bc338ae308af44330590eaa1d9c94b64850514e55b960642d0eb scadoffice_addons_v02.tar.xz
|
|
||||||
|
|
||||||
ef7e8f1ba785d48e4ea287feed5b79bd630d423e59efadb43da9653adefef218 ais-lpu-client_pfx_x86_v01.tar.xz
|
ef7e8f1ba785d48e4ea287feed5b79bd630d423e59efadb43da9653adefef218 ais-lpu-client_pfx_x86_v01.tar.xz
|
||||||
# create with wine_x_tkg_10-0_i586 (universal user: xuser)
|
# create with wine_x_tkg_10-0_i586 (universal user: xuser)
|
||||||
# winetricks vcrun2005 vcrun2008 dotnet20sp2 dotnet40 mfc42 7zip
|
# winetricks vcrun2005 vcrun2008 dotnet20sp2 dotnet40 mfc42 7zip
|
||||||
@@ -191,3 +188,7 @@ f18864014fdb2fead0b45b5e70e95073072b89168df8cd6debba89081ac51a2a ksamu_pfx_x64_
|
|||||||
# create with wine_x_tkg_10-0_i586 (universal user: xuser)
|
# create with wine_x_tkg_10-0_i586 (universal user: xuser)
|
||||||
# winetricks msxml6 msxml4 msxml3 riched30 msls31 riched20 msftedit richtx32 fontsmooth=gray
|
# winetricks msxml6 msxml4 msxml3 riched30 msls31 riched20 msftedit richtx32 fontsmooth=gray
|
||||||
# + manuall installed riched32
|
# + manuall installed riched32
|
||||||
|
|
||||||
|
##### ADDONS #####
|
||||||
|
# addons with ODBC, SSH, *.reg
|
||||||
|
0f4ef434df07bc338ae308af44330590eaa1d9c94b64850514e55b960642d0eb scadoffice_addons_v02.tar.xz
|
||||||
|
|||||||
159
winehelper
@@ -101,7 +101,7 @@ if [[ "$1" == "--debug" ]] ; then
|
|||||||
export DXVK_NVAPI_LOG_LEVEL="error"
|
export DXVK_NVAPI_LOG_LEVEL="error"
|
||||||
shift
|
shift
|
||||||
else
|
else
|
||||||
check_variables WINEDEBUG "-all"
|
check_variables WINEDEBUG "-all,err+all"
|
||||||
check_variables DXVK_LOG_LEVEL "none"
|
check_variables DXVK_LOG_LEVEL "none"
|
||||||
check_variables VKD3D_SHADER_DEBUG "none"
|
check_variables VKD3D_SHADER_DEBUG "none"
|
||||||
check_variables VKD3D_DEBUG "none"
|
check_variables VKD3D_DEBUG "none"
|
||||||
@@ -460,7 +460,7 @@ try_copy_wine_dll_to_pfx_64 () {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
try_copy_wine_dll_to_pfx_32() {
|
try_copy_wine_dll_to_pfx_32 () {
|
||||||
if [[ -d "$WINEDIR/lib/wine/i386-windows" ]] ; then
|
if [[ -d "$WINEDIR/lib/wine/i386-windows" ]] ; then
|
||||||
WINE_BUILD_DLL_32="$WINEDIR/lib/wine/i386-windows"
|
WINE_BUILD_DLL_32="$WINEDIR/lib/wine/i386-windows"
|
||||||
elif [[ -d "$WINEDIR/lib64/wine/i386-windows" ]] ; then
|
elif [[ -d "$WINEDIR/lib64/wine/i386-windows" ]] ; then
|
||||||
@@ -1694,14 +1694,16 @@ select_wine_version() {
|
|||||||
local total_versions_found=0
|
local total_versions_found=0
|
||||||
|
|
||||||
# --- System ---
|
# --- System ---
|
||||||
local system_wine_display_name="system"
|
# Добавляем системную версию только для 64-битного префикса
|
||||||
if command -v wine &>/dev/null; then
|
if [[ "$WINEARCH" == "win64" ]]; then
|
||||||
local system_wine_version
|
local system_wine_display_name="system"
|
||||||
system_wine_version=$(wine --version 2>/dev/null)
|
if command -v wine &>/dev/null; then
|
||||||
[[ -n "$system_wine_version" ]] && system_wine_display_name="$system_wine_version"
|
local system_wine_version
|
||||||
|
system_wine_version=$(wine --version 2>/dev/null)
|
||||||
|
[[ -n "$system_wine_version" ]] && system_wine_display_name="$system_wine_version"
|
||||||
|
fi
|
||||||
|
options+=("--- SYSTEM ---" "$system_wine_display_name")
|
||||||
fi
|
fi
|
||||||
options+=("--- System ---")
|
|
||||||
options+=("$system_wine_display_name")
|
|
||||||
|
|
||||||
# --- Other versions from sha256sum.list ---
|
# --- Other versions from sha256sum.list ---
|
||||||
local current_group=""
|
local current_group=""
|
||||||
@@ -1718,14 +1720,28 @@ select_wine_version() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
if [[ "$line" =~ ^#+[[:space:]]([^#[:space:]]+)[[:space:]]#* ]]; then
|
if [[ "$line" =~ ^#+[[:space:]](.*[^#[:space:]])[[:space:]]#* ]] ; then
|
||||||
flush_group
|
flush_group
|
||||||
current_group="${BASH_REMATCH[1]}"
|
current_group="${BASH_REMATCH[1]}"
|
||||||
# Отображаем только группы, которые являются сборками WINE или PROTON
|
# Отображаем только группы, которые являются сборками WINE или PROTON
|
||||||
case "$current_group" in
|
case "$current_group" in
|
||||||
WINE|WINE_LG|PROTON_LG|PROTON_STEAM)
|
"WINE WOW64"|"WINE AMD64"|"WINE I586")
|
||||||
local pretty_key=$(echo "$current_group" | tr '_' ' ' | sed -e "s/\b\(.\)/\u\1/g")
|
# Фильтрация групп в зависимости от архитектуры префикса
|
||||||
options+=("--- $pretty_key ---")
|
if [[ "$WINEARCH" == "win64" ]]; then
|
||||||
|
# Для 64-битного префикса скрываем группу I586
|
||||||
|
if [[ "$current_group" != "WINE I586" ]]; then
|
||||||
|
local pretty_key="$current_group"
|
||||||
|
options+=("--- $pretty_key ---")
|
||||||
|
else
|
||||||
|
current_group="" # Игнорируем группу I586
|
||||||
|
fi
|
||||||
|
elif [[ "$WINEARCH" == "win32" ]]; then
|
||||||
|
# Для 32-битного префикса скрываем только группу WOW64
|
||||||
|
if [[ "$current_group" != "WINE WOW64" ]]; then
|
||||||
|
local pretty_key="$current_group"
|
||||||
|
options+=("--- $pretty_key ---")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
current_group=""
|
current_group=""
|
||||||
@@ -1736,11 +1752,14 @@ select_wine_version() {
|
|||||||
local version_name=${filename%.tar.xz}
|
local version_name=${filename%.tar.xz}
|
||||||
|
|
||||||
if [[ "$WINEARCH" == "win64" ]]; then
|
if [[ "$WINEARCH" == "win64" ]]; then
|
||||||
if [[ "$version_name" =~ (amd64|x86_64|wow64) ]] || ! [[ "$version_name" =~ i[3-6]86 ]]; then
|
# Для 64-битного префикса показываем только 64-битные версии
|
||||||
|
if ! [[ "$version_name" =~ i[3-6]86 ]]; then
|
||||||
group_versions+=("$version_name")
|
group_versions+=("$version_name")
|
||||||
fi
|
fi
|
||||||
else # win32
|
else # Для 32-битного префикса показываем и i586, и amd64
|
||||||
group_versions+=("$version_name")
|
if [[ "$version_name" =~ (i[3-6]86|amd64) ]]; then
|
||||||
|
group_versions+=("$version_name")
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done < "$sha256_file"
|
done < "$sha256_file"
|
||||||
@@ -1824,13 +1843,7 @@ select_wine_version() {
|
|||||||
print_info "Операция отменена."
|
print_info "Операция отменена."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
local selected_opt
|
export WH_WINE_USE="${selectable_options[$user_choice]}"
|
||||||
selected_opt="${selectable_options[$user_choice]}"
|
|
||||||
if [[ "$selected_opt" == "$system_wine_display_name" ]]; then
|
|
||||||
export WH_WINE_USE="system"
|
|
||||||
else
|
|
||||||
export WH_WINE_USE="$selected_opt"
|
|
||||||
fi
|
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
print_error "Неверный выбор. Введите число от 0 до $max_choice."
|
print_error "Неверный выбор. Введите число от 0 до $max_choice."
|
||||||
@@ -1839,6 +1852,67 @@ select_wine_version() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select_prepared_prefix() {
|
||||||
|
local arch="$1"
|
||||||
|
local sha256_file="$DATA_PATH/sha256sum.list"
|
||||||
|
[[ ! -f "$sha256_file" ]] && fatal "Файл с описаниями префиксов не найден: $sha256_file"
|
||||||
|
|
||||||
|
options=()
|
||||||
|
descriptions=()
|
||||||
|
|
||||||
|
options+=("none")
|
||||||
|
descriptions+=("Создать чистый префикс без дополнительных библиотек")
|
||||||
|
|
||||||
|
local in_prefix_section=false
|
||||||
|
local current_description=""
|
||||||
|
local current_prefix_name=""
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [[ "$line" =~ ^#####[[:space:]]PREFIX[[:space:]]#####$ ]] ; then
|
||||||
|
in_prefix_section=true
|
||||||
|
continue
|
||||||
|
elif [[ "$line" =~ ^#####.* ]] ; then
|
||||||
|
in_prefix_section=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$in_prefix_section" == true ]] ; then
|
||||||
|
if [[ "$line" =~ ^[a-f0-9]{64} ]] ; then
|
||||||
|
# Если у нас есть имя предыдущего префикса, добавляем его описание
|
||||||
|
if [[ -n "$current_prefix_name" ]] ; then
|
||||||
|
descriptions+=("$(echo -e "${current_description}" | sed 's/\\n$//')")
|
||||||
|
fi
|
||||||
|
current_description=""
|
||||||
|
|
||||||
|
local filename
|
||||||
|
filename=$(echo "$line" | awk '{print $2}')
|
||||||
|
current_prefix_name=${filename%.tar.xz}
|
||||||
|
|
||||||
|
if [[ "$arch" == "win32" ]] && ([[ "$current_prefix_name" == *"_x86_"* ]] || [[ "$current_prefix_name" == *"_i586_"* ]]) ; then
|
||||||
|
options+=("$current_prefix_name")
|
||||||
|
elif [[ "$arch" == "win64" ]] && ([[ "$current_prefix_name" == *"_x64_"* ]] || [[ "$current_prefix_name" == *"_amd64_"* ]]) ; then
|
||||||
|
options+=("$current_prefix_name")
|
||||||
|
else
|
||||||
|
# Если архитектура не совпадает, сбрасываем имя, чтобы не добавлять описание
|
||||||
|
current_prefix_name=""
|
||||||
|
fi
|
||||||
|
elif [[ "$line" =~ ^#[[:space:]] ]] ; then
|
||||||
|
local comment_line=${line:2} # Удаляем '# '
|
||||||
|
current_description+="$comment_line\n"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < "$sha256_file"
|
||||||
|
# Добавляем описание для самого последнего префикса в файле
|
||||||
|
if [[ -n "$current_prefix_name" ]] ; then
|
||||||
|
descriptions+=("$(echo -e "${current_description}" | sed 's/\\n$//')")
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
print_info "Выберите тип создаваемого префикса:"
|
||||||
|
for i in "${!options[@]}" ; do
|
||||||
|
printf "\n\E[36m %s) %s \e[0m\n" "$((i+1))" "${options[$i]}"
|
||||||
|
[[ -n "${descriptions[$i]}" ]] && printf " \E[33m%s\e[0m\n" "$(echo -e "${descriptions[$i]}" | sed 's/^/ /g')"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
create_prefix() {
|
create_prefix() {
|
||||||
print_info "Существующие префиксы:"
|
print_info "Существующие префиксы:"
|
||||||
local prefixes=()
|
local prefixes=()
|
||||||
@@ -1888,19 +1962,22 @@ create_prefix() {
|
|||||||
|
|
||||||
select_wine_version || exit 0
|
select_wine_version || exit 0
|
||||||
|
|
||||||
print_info "Выберите тип создаваемого префикса:"
|
select_prepared_prefix "$WINEARCH"
|
||||||
echo " 0) Отмена создания префикса"
|
local max_choice=${#options[@]}
|
||||||
echo " 1) Чистый префикс (без библиотек)"
|
local user_choice
|
||||||
echo " 2) С рекомендуемыми библиотеками"
|
while true; do
|
||||||
echo
|
read -p "Ваш выбор [1-$max_choice] (0 для отмены): " user_choice
|
||||||
local pfx_type_choice
|
if [[ "$user_choice" == "0" ]]; then
|
||||||
read -p "Ваш выбор [0-2] (по умолчанию 1): " pfx_type_choice
|
print_info "Создание префикса отменено." ; exit 0
|
||||||
case "${pfx_type_choice:-1}" in
|
elif [[ "$user_choice" -ge 1 && "$user_choice" -le "$max_choice" ]]; then
|
||||||
0) print_info "Создание префикса отменено." ; exit 0 ;;
|
export BASE_PFX="${options[$((user_choice-1))]}"
|
||||||
1) export BASE_PFX="none" ;;
|
break
|
||||||
2) ;; # Оставляем BASE_PFX пустым, чтобы init_wineprefix использовал значение по умолчанию
|
else
|
||||||
*) fatal "Неверный выбор. Операция отменена." ;;
|
print_error "Неверный выбор. Введите число от 1 до $max_choice."
|
||||||
esac
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
print_license_agreement || exit 0
|
||||||
|
|
||||||
export WINEPREFIX="$WH_PREFIXES_DIR/$prefix_name"
|
export WINEPREFIX="$WH_PREFIXES_DIR/$prefix_name"
|
||||||
|
|
||||||
@@ -2097,7 +2174,6 @@ restore_prefix() {
|
|||||||
local backup_archive_path="$1"
|
local backup_archive_path="$1"
|
||||||
local temp_extract_dir prefix_name
|
local temp_extract_dir prefix_name
|
||||||
|
|
||||||
|
|
||||||
if [[ -z "$backup_archive_path" ]] ; then
|
if [[ -z "$backup_archive_path" ]] ; then
|
||||||
read -e -p "Укажите путь к архиву резервной копии (/путь/к/архиву.whpack): " backup_archive_path
|
read -e -p "Укажите путь к архиву резервной копии (/путь/к/архиву.whpack): " backup_archive_path
|
||||||
backup_archive_path=$(echo "$backup_archive_path" | sed "s/'//g; s/\"//g")
|
backup_archive_path=$(echo "$backup_archive_path" | sed "s/'//g; s/\"//g")
|
||||||
@@ -2168,6 +2244,15 @@ restore_prefix() {
|
|||||||
else
|
else
|
||||||
print_ok "Префикс $prefix_name восстановлен."
|
print_ok "Префикс $prefix_name восстановлен."
|
||||||
|
|
||||||
|
export WINEPREFIX="$WH_PREFIXES_DIR/$prefix_name"
|
||||||
|
if [[ -f "$WINEPREFIX/last.conf" ]] ; then
|
||||||
|
source "$WINEPREFIX/last.conf"
|
||||||
|
prepair_wine
|
||||||
|
if [[ -f "$WINEPREFIX/restore_pfx.sh" ]] ; then
|
||||||
|
source "$WINEPREFIX/restore_pfx.sh"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
print_info "Восстановление ярлыков для префикса $prefix_name..."
|
print_info "Восстановление ярлыков для префикса $prefix_name..."
|
||||||
export RESTORE_FROM_BACKUP="1" # Устанавливаем флаг восстановления
|
export RESTORE_FROM_BACKUP="1" # Устанавливаем флаг восстановления
|
||||||
while IFS='=' read -r name_desktop exe_path icon_name ; do
|
while IFS='=' read -r name_desktop exe_path icon_name ; do
|
||||||
|
|||||||
@@ -642,9 +642,9 @@ class WinetricksManagerDialog(QDialog):
|
|||||||
settings_blacklist_pattern = None
|
settings_blacklist_pattern = None
|
||||||
|
|
||||||
if category == 'dlls':
|
if category == 'dlls':
|
||||||
# Исключаем d3d*, directx9, dont_use, dxvk*, vkd3d*, galliumnine, faudio*, Foundation
|
# Исключаем dont_use, dxvk*, vkd3d*, galliumnine, faudio*, Foundation
|
||||||
dlls_blacklist_pattern = re.compile(
|
dlls_blacklist_pattern = re.compile(
|
||||||
r'^(d3d|directx9|dont_use|dxvk|vkd3d|galliumnine|faudio|foundation)', re.IGNORECASE
|
r'^(dont_use|dxvk|vkd3d|galliumnine|faudio|foundation)', re.IGNORECASE
|
||||||
)
|
)
|
||||||
elif category == 'fonts':
|
elif category == 'fonts':
|
||||||
fonts_blacklist_pattern = re.compile(
|
fonts_blacklist_pattern = re.compile(
|
||||||
@@ -1129,28 +1129,28 @@ class WineVersionSelectionDialog(QDialog):
|
|||||||
self.search_edit.textChanged.connect(self.filter_versions)
|
self.search_edit.textChanged.connect(self.filter_versions)
|
||||||
main_layout.addWidget(self.search_edit)
|
main_layout.addWidget(self.search_edit)
|
||||||
|
|
||||||
self.version_tabs = QTabWidget()
|
self.main_tabs = QTabWidget()
|
||||||
main_layout.addWidget(self.version_tabs)
|
main_layout.addWidget(self.main_tabs)
|
||||||
|
|
||||||
self.load_versions()
|
self.load_versions()
|
||||||
|
|
||||||
def load_versions(self):
|
def load_versions(self):
|
||||||
"""Запускает процесс получения списка версий Wine."""
|
"""Запускает процесс получения списка версий Wine."""
|
||||||
self.version_tabs.clear()
|
self.main_tabs.clear()
|
||||||
loading_widget = QWidget()
|
loading_widget = QWidget()
|
||||||
loading_layout = QVBoxLayout(loading_widget)
|
loading_layout = QVBoxLayout(loading_widget)
|
||||||
status_label = QLabel("Загрузка, пожалуйста, подождите...")
|
status_label = QLabel("Загрузка, пожалуйста, подождите...")
|
||||||
status_label.setAlignment(Qt.AlignCenter)
|
status_label.setAlignment(Qt.AlignCenter)
|
||||||
loading_layout.addWidget(status_label)
|
loading_layout.addWidget(status_label)
|
||||||
self.version_tabs.addTab(loading_widget, "Загрузка...")
|
self.main_tabs.addTab(loading_widget, "Загрузка...")
|
||||||
self.version_tabs.setEnabled(False)
|
self.main_tabs.setEnabled(False)
|
||||||
|
|
||||||
QApplication.processEvents()
|
QApplication.processEvents()
|
||||||
|
|
||||||
self._parse_sha256_list()
|
self._parse_sha256_list()
|
||||||
self.populate_ui()
|
self.populate_ui()
|
||||||
|
|
||||||
self.version_tabs.setEnabled(True)
|
self.main_tabs.setEnabled(True)
|
||||||
|
|
||||||
def _parse_sha256_list(self):
|
def _parse_sha256_list(self):
|
||||||
"""Парсит sha256sum.list для получения списка версий."""
|
"""Парсит sha256sum.list для получения списка версий."""
|
||||||
@@ -1170,10 +1170,10 @@ class WineVersionSelectionDialog(QDialog):
|
|||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.match(r'^#+\s*([^#]+?)\s*#*$', line)
|
match = re.match(r'^#+\s*(.*?)\s*#*$', line)
|
||||||
if match:
|
if match:
|
||||||
group_name = match.group(1)
|
group_name = match.group(1).strip()
|
||||||
allowed_groups = {"WINE", "WINE_LG", "PROTON_LG", "PROTON_STEAM"}
|
allowed_groups = {"WINE WOW64", "WINE AMD64", "WINE I586"}
|
||||||
# Отображаем только группы, которые являются сборками WINE или PROTON
|
# Отображаем только группы, которые являются сборками WINE или PROTON
|
||||||
if group_name in allowed_groups:
|
if group_name in allowed_groups:
|
||||||
current_group = group_name
|
current_group = group_name
|
||||||
@@ -1194,9 +1194,59 @@ class WineVersionSelectionDialog(QDialog):
|
|||||||
QMessageBox.warning(self, "Ошибка", f"Не удалось прочитать файл версий:\n{e}")
|
QMessageBox.warning(self, "Ошибка", f"Не удалось прочитать файл версий:\n{e}")
|
||||||
self.wine_versions_data = {}
|
self.wine_versions_data = {}
|
||||||
|
|
||||||
|
def _get_installed_versions(self):
|
||||||
|
"""Возвращает список локально установленных версий Wine."""
|
||||||
|
dist_path = os.path.join(Var.USER_WORK_PATH, "dist")
|
||||||
|
if not os.path.isdir(dist_path):
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
return sorted([
|
||||||
|
name for name in os.listdir(dist_path)
|
||||||
|
if os.path.isdir(os.path.join(dist_path, name))
|
||||||
|
], reverse=True)
|
||||||
|
except OSError:
|
||||||
|
return []
|
||||||
|
|
||||||
def populate_ui(self):
|
def populate_ui(self):
|
||||||
"""Заполняет UI отфильтрованными версиями."""
|
"""Заполняет UI отфильтрованными версиями."""
|
||||||
self.version_tabs.clear()
|
self.main_tabs.clear()
|
||||||
|
|
||||||
|
# --- Вкладка "Установленные" ---
|
||||||
|
installed_tab = QWidget()
|
||||||
|
self.main_tabs.addTab(installed_tab, "Установленные")
|
||||||
|
installed_layout = QVBoxLayout(installed_tab)
|
||||||
|
installed_scroll_area = QScrollArea()
|
||||||
|
installed_scroll_area.setWidgetResizable(True)
|
||||||
|
installed_layout.addWidget(installed_scroll_area)
|
||||||
|
installed_content = QWidget()
|
||||||
|
installed_scroll_area.setWidget(installed_content)
|
||||||
|
self.installed_grid_layout = QGridLayout(installed_content)
|
||||||
|
self.installed_grid_layout.setAlignment(Qt.AlignTop)
|
||||||
|
|
||||||
|
installed_versions_for_grid = []
|
||||||
|
|
||||||
|
# Системная версия
|
||||||
|
if shutil.which('wine'):
|
||||||
|
try:
|
||||||
|
result = subprocess.run(['wine', '--version'], capture_output=True, text=True, check=True, encoding='utf-8')
|
||||||
|
self.system_wine_display_name = result.stdout.strip()
|
||||||
|
except (FileNotFoundError, subprocess.CalledProcessError) as e:
|
||||||
|
print(f"Не удалось получить версию системного wine: {e}")
|
||||||
|
installed_versions_for_grid.append((self.system_wine_display_name, "system"))
|
||||||
|
|
||||||
|
# Локально установленные версии
|
||||||
|
local_versions = self._get_installed_versions()
|
||||||
|
installed_versions_for_grid.extend(local_versions)
|
||||||
|
|
||||||
|
self._create_grid_buttons(self.installed_grid_layout, installed_versions_for_grid)
|
||||||
|
|
||||||
|
# --- Вкладка "Скачать" ---
|
||||||
|
download_tab = QWidget()
|
||||||
|
download_layout = QVBoxLayout(download_tab)
|
||||||
|
self.version_tabs = QTabWidget() # Вложенные табы для категорий
|
||||||
|
download_layout.addWidget(self.version_tabs)
|
||||||
|
download_tab.setLayout(download_layout)
|
||||||
|
self.main_tabs.addTab(download_tab, "Скачать")
|
||||||
|
|
||||||
if not self.wine_versions_data:
|
if not self.wine_versions_data:
|
||||||
error_widget = QWidget()
|
error_widget = QWidget()
|
||||||
@@ -1205,55 +1255,33 @@ class WineVersionSelectionDialog(QDialog):
|
|||||||
error_label.setAlignment(Qt.AlignCenter)
|
error_label.setAlignment(Qt.AlignCenter)
|
||||||
error_layout.addWidget(error_label)
|
error_layout.addWidget(error_label)
|
||||||
self.version_tabs.addTab(error_widget, "Ошибка")
|
self.version_tabs.addTab(error_widget, "Ошибка")
|
||||||
|
self.filter_versions()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# --- Фильтрация и сортировка групп в зависимости от архитектуры ---
|
||||||
is_win64 = self.architecture == "win64"
|
is_win64 = self.architecture == "win64"
|
||||||
re_32bit = re.compile(r'i[3-6]86|x86(?!_64)')
|
|
||||||
re_64bit = re.compile(r'amd64|x86_64|wow64')
|
|
||||||
|
|
||||||
# --- System Tab ---
|
if is_win64:
|
||||||
if shutil.which('wine'):
|
allowed_groups_for_arch = {"WINE AMD64", "WINE WOW64"}
|
||||||
self.system_wine_display_name = "Системная версия"
|
tab_order = ["WINE AMD64", "WINE WOW64"]
|
||||||
try:
|
else: # win32
|
||||||
# Пытаемся получить версию системного wine
|
allowed_groups_for_arch = {"WINE I586","WINE AMD64"}
|
||||||
result = subprocess.run(['wine', '--version'], capture_output=True, text=True, check=True, encoding='utf-8')
|
tab_order = ["WINE I586","WINE AMD64"]
|
||||||
version_line = result.stdout.strip()
|
|
||||||
# Вывод обычно "wine-X.Y.Z"
|
|
||||||
self.system_wine_display_name = version_line
|
|
||||||
except (FileNotFoundError, subprocess.CalledProcessError) as e:
|
|
||||||
print(f"Не удалось получить версию системного wine: {e}")
|
|
||||||
# Если wine возвращает ошибку, используем имя по умолчанию "Системная версия"
|
|
||||||
|
|
||||||
self._create_version_tab("Системный", [(self.system_wine_display_name, "system")])
|
# Получаем только те ключи, которые есть в данных и разрешены для данной архитектуры
|
||||||
|
available_keys = [key for key in self.wine_versions_data.keys() if key in allowed_groups_for_arch]
|
||||||
# Определяем желаемый порядок вкладок
|
# Сортируем доступные ключи в соответствии с заданным порядком
|
||||||
tab_order = ["WINE", "WINE_LG", "PROTON_LG", "PROTON_STEAM"]
|
group_keys = sorted(available_keys, key=lambda k: tab_order.index(k))
|
||||||
# Сортируем ключи в соответствии с заданным порядком
|
|
||||||
group_keys = sorted(self.wine_versions_data.keys(), key=lambda k: tab_order.index(k) if k in tab_order else len(tab_order))
|
|
||||||
|
|
||||||
for key in group_keys:
|
for key in group_keys:
|
||||||
versions = self.wine_versions_data.get(key, [])
|
versions = self.wine_versions_data.get(key, [])
|
||||||
|
if not versions:
|
||||||
filtered_versions = []
|
|
||||||
for name in sorted(versions, reverse=True):
|
|
||||||
if is_win64:
|
|
||||||
if re_64bit.search(name) or not re_32bit.search(name):
|
|
||||||
filtered_versions.append(name)
|
|
||||||
else:
|
|
||||||
filtered_versions.append(name)
|
|
||||||
|
|
||||||
if not filtered_versions:
|
|
||||||
continue
|
continue
|
||||||
|
self._create_version_tab(key, sorted(versions, reverse=True))
|
||||||
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()
|
self.filter_versions()
|
||||||
|
|
||||||
def _create_version_tab(self, title, versions_list):
|
def _create_version_tab(self, title, versions_list, is_download_tab=True):
|
||||||
"""Создает вкладку с сеткой кнопок для переданного списка версий."""
|
"""Создает вкладку с сеткой кнопок для переданного списка версий."""
|
||||||
tab_page = QWidget()
|
tab_page = QWidget()
|
||||||
tab_layout = QVBoxLayout(tab_page)
|
tab_layout = QVBoxLayout(tab_page)
|
||||||
@@ -1269,6 +1297,14 @@ class WineVersionSelectionDialog(QDialog):
|
|||||||
grid_layout = QGridLayout(scroll_content)
|
grid_layout = QGridLayout(scroll_content)
|
||||||
grid_layout.setAlignment(Qt.AlignTop)
|
grid_layout.setAlignment(Qt.AlignTop)
|
||||||
|
|
||||||
|
self._create_grid_buttons(grid_layout, versions_list, is_download_tab=is_download_tab)
|
||||||
|
self.version_tabs.addTab(tab_page, title)
|
||||||
|
|
||||||
|
def _create_grid_buttons(self, grid_layout, versions_list, is_download_tab=False):
|
||||||
|
is_win64_prefix = self.architecture == "win64"
|
||||||
|
is_win32_prefix = self.architecture == "win32"
|
||||||
|
re_32bit_version = re.compile(r'i[3-6]86|i586')
|
||||||
|
|
||||||
num_columns = 3
|
num_columns = 3
|
||||||
row, col = 0, 0
|
row, col = 0, 0
|
||||||
for version_data in versions_list:
|
for version_data in versions_list:
|
||||||
@@ -1279,36 +1315,64 @@ class WineVersionSelectionDialog(QDialog):
|
|||||||
|
|
||||||
btn = QPushButton(display_name)
|
btn = QPushButton(display_name)
|
||||||
btn.clicked.connect(partial(self.on_version_selected, value_name))
|
btn.clicked.connect(partial(self.on_version_selected, value_name))
|
||||||
|
|
||||||
|
# Выделяем системную версию Wine
|
||||||
|
if value_name == "system":
|
||||||
|
btn.setText(f"{display_name} (Системный)")
|
||||||
|
btn.setToolTip("Системная версия Wine, установленная в вашей ОС.")
|
||||||
|
|
||||||
|
# Отключение 32-битных версий Wine при выбранном 64-битного префикса (вкладка "Установленные")
|
||||||
|
if not is_download_tab and is_win64_prefix:
|
||||||
|
if re_32bit_version.search(value_name):
|
||||||
|
btn.setEnabled(False)
|
||||||
|
btn.setToolTip("Эта 32-битная версия Wine несовместима с 64-битным префиксом.")
|
||||||
|
|
||||||
|
# Отключение 64-битных версий Wine (системной и wow64) при выборе 32-битного префикса
|
||||||
|
if not is_download_tab and is_win32_prefix:
|
||||||
|
if value_name == "system" or 'wow64' in value_name:
|
||||||
|
btn.setEnabled(False)
|
||||||
|
btn.setToolTip("Эта 64-битная версия Wine несовместима с 32-битным префиксом.")
|
||||||
|
|
||||||
grid_layout.addWidget(btn, row, col)
|
grid_layout.addWidget(btn, row, col)
|
||||||
col += 1
|
col += 1
|
||||||
if col >= num_columns:
|
if col >= num_columns:
|
||||||
col = 0
|
col = 0
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
self.version_tabs.addTab(tab_page, title)
|
|
||||||
|
|
||||||
def filter_versions(self):
|
def filter_versions(self):
|
||||||
"""Фильтрует видимость кнопок версий на основе текста поиска."""
|
"""Фильтрует видимость кнопок версий на основе текста поиска."""
|
||||||
search_text = self.search_edit.text().lower()
|
search_text = self.search_edit.text().lower()
|
||||||
|
|
||||||
for i in range(self.version_tabs.count()):
|
# Фильтр для вкладки "Установленные"
|
||||||
tab_widget = self.version_tabs.widget(i)
|
if hasattr(self, 'installed_grid_layout'):
|
||||||
# The grid layout is inside a scroll area content widget
|
self._filter_grid(self.installed_grid_layout, search_text)
|
||||||
grid_layout = tab_widget.findChild(QGridLayout)
|
|
||||||
if not grid_layout:
|
|
||||||
continue
|
|
||||||
|
|
||||||
any_visible_in_tab = False
|
# Фильтр для вкладок "Скачать"
|
||||||
for j in range(grid_layout.count()):
|
if hasattr(self, 'version_tabs'):
|
||||||
btn_widget = grid_layout.itemAt(j).widget()
|
for i in range(self.version_tabs.count()):
|
||||||
|
tab_widget = self.version_tabs.widget(i)
|
||||||
|
grid_layout = tab_widget.findChild(QGridLayout)
|
||||||
|
if grid_layout:
|
||||||
|
any_visible = self._filter_grid(grid_layout, search_text)
|
||||||
|
self.version_tabs.setTabEnabled(i, any_visible)
|
||||||
|
|
||||||
|
def _filter_grid(self, grid_layout, search_text):
|
||||||
|
"""Helper-функция для фильтрации кнопок в одной сетке."""
|
||||||
|
any_visible_in_grid = False
|
||||||
|
if not grid_layout:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for j in range(grid_layout.count()):
|
||||||
|
item = grid_layout.itemAt(j)
|
||||||
|
if item:
|
||||||
|
btn_widget = item.widget()
|
||||||
if isinstance(btn_widget, QPushButton):
|
if isinstance(btn_widget, QPushButton):
|
||||||
is_match = search_text in btn_widget.text().lower()
|
is_match = search_text in btn_widget.text().lower()
|
||||||
btn_widget.setVisible(is_match)
|
btn_widget.setVisible(is_match)
|
||||||
if is_match:
|
if is_match:
|
||||||
any_visible_in_tab = True
|
any_visible_in_grid = True
|
||||||
|
|
||||||
# Enable/disable tab based on content
|
return any_visible_in_grid
|
||||||
self.version_tabs.setTabEnabled(i, any_visible_in_tab)
|
|
||||||
|
|
||||||
def on_version_selected(self, version_name):
|
def on_version_selected(self, version_name):
|
||||||
"""Обрабатывает выбор версии."""
|
"""Обрабатывает выбор версии."""
|
||||||
@@ -1333,6 +1397,7 @@ class CreatePrefixDialog(QDialog):
|
|||||||
self.prefix_name = None
|
self.prefix_name = None
|
||||||
self.wine_arch = None
|
self.wine_arch = None
|
||||||
self.base_pfx = None
|
self.base_pfx = None
|
||||||
|
self.prepared_prefixes = {}
|
||||||
self.selected_wine_version_value = None
|
self.selected_wine_version_value = None
|
||||||
self.selected_wine_version_display = None
|
self.selected_wine_version_display = None
|
||||||
|
|
||||||
@@ -1367,18 +1432,10 @@ class CreatePrefixDialog(QDialog):
|
|||||||
arch_layout.addWidget(self.arch_win64_radio)
|
arch_layout.addWidget(self.arch_win64_radio)
|
||||||
form_layout.addRow("<b>Разрядность:</b>", arch_widget)
|
form_layout.addRow("<b>Разрядность:</b>", arch_widget)
|
||||||
|
|
||||||
type_widget = QWidget()
|
# Виджет для выбора типа префикса (шаблона)
|
||||||
type_layout = QHBoxLayout(type_widget)
|
self.prefix_template_selector = QComboBox()
|
||||||
type_layout.setContentsMargins(0, 0, 0, 0)
|
self.prefix_template_selector.setToolTip("Выберите шаблон для создания префикса.")
|
||||||
self.type_clean_radio = QRadioButton("Чистый")
|
form_layout.addRow("<b>Шаблон префикса:</b>", self.prefix_template_selector)
|
||||||
self.type_clean_radio.setToolTip("Создает пустой префикс Wine без каких-либо дополнительных компонентов.")
|
|
||||||
self.type_recommended_radio = QRadioButton("С рекомендуемыми библиотеками")
|
|
||||||
tooltip_text = "Устанавливает базовый набор компонентов, необходимый для большинства приложений"
|
|
||||||
self.type_recommended_radio.setToolTip(tooltip_text)
|
|
||||||
self.type_clean_radio.setChecked(True)
|
|
||||||
type_layout.addWidget(self.type_clean_radio)
|
|
||||||
type_layout.addWidget(self.type_recommended_radio)
|
|
||||||
form_layout.addRow("<b>Наполнение:</b>", type_widget)
|
|
||||||
|
|
||||||
self.wine_version_edit = QLineEdit()
|
self.wine_version_edit = QLineEdit()
|
||||||
self.wine_version_edit.setReadOnly(True)
|
self.wine_version_edit.setReadOnly(True)
|
||||||
@@ -1411,10 +1468,82 @@ class CreatePrefixDialog(QDialog):
|
|||||||
layout.addLayout(button_layout)
|
layout.addLayout(button_layout)
|
||||||
|
|
||||||
# Connect signals
|
# Connect signals
|
||||||
self.arch_win32_radio.toggled.connect(self.clear_wine_version_selection)
|
self.arch_win32_radio.toggled.connect(self.on_architecture_changed)
|
||||||
self.prefix_name_edit.textChanged.connect(self.validate_prefix_name)
|
self.prefix_name_edit.textChanged.connect(self.validate_prefix_name)
|
||||||
self.wine_version_edit.textChanged.connect(self.update_create_button_state)
|
self.wine_version_edit.textChanged.connect(self.update_create_button_state)
|
||||||
|
|
||||||
|
# Загружаем и настраиваем шаблоны префиксов
|
||||||
|
self._load_prepared_prefixes()
|
||||||
|
self.on_architecture_changed()
|
||||||
|
|
||||||
|
def _load_prepared_prefixes(self):
|
||||||
|
"""Загружает и парсит шаблоны префиксов из sha256sum.list."""
|
||||||
|
self.prepared_prefixes = {'win32': [], 'win64': []}
|
||||||
|
sha256_file = os.path.join(Var.DATA_PATH, "sha256sum.list")
|
||||||
|
if not os.path.exists(sha256_file):
|
||||||
|
QMessageBox.warning(self, "Ошибка", f"Файл с описаниями префиксов не найден: {sha256_file}")
|
||||||
|
return
|
||||||
|
|
||||||
|
in_prefix_section = False
|
||||||
|
current_description = ""
|
||||||
|
current_prefix_name = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(sha256_file, 'r', encoding='utf-8') as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if "##### PREFIX #####" in line:
|
||||||
|
in_prefix_section = True
|
||||||
|
continue
|
||||||
|
elif line.startswith("#####"):
|
||||||
|
in_prefix_section = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_prefix_section:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if re.match(r'^[a-f0-9]{64}', line):
|
||||||
|
# Если у нас есть имя и описание для предыдущего префикса, добавляем его
|
||||||
|
if current_prefix_name and current_description:
|
||||||
|
arch_for_prev_prefix = None
|
||||||
|
if "_x86_" in current_prefix_name or "_i586_" in current_prefix_name:
|
||||||
|
arch_for_prev_prefix = 'win32'
|
||||||
|
elif "_x64_" in current_prefix_name or "_amd64_" in current_prefix_name:
|
||||||
|
arch_for_prev_prefix = 'win64'
|
||||||
|
|
||||||
|
if arch_for_prev_prefix:
|
||||||
|
description_to_add = current_description.strip().replace('\\n', '\n')
|
||||||
|
self.prepared_prefixes[arch_for_prev_prefix].append((current_prefix_name, description_to_add))
|
||||||
|
|
||||||
|
# Сбрасываем описание для нового префикса
|
||||||
|
current_description = ""
|
||||||
|
|
||||||
|
# Получаем имя нового префикса
|
||||||
|
current_prefix_name = line.split(maxsplit=1)[1].replace('.tar.xz', '') if len(line.split(maxsplit=1)) > 1 else None
|
||||||
|
|
||||||
|
elif line.startswith('# '):
|
||||||
|
# Накапливаем описание
|
||||||
|
comment_line = line[2:].strip()
|
||||||
|
if current_description:
|
||||||
|
current_description += "\n" + comment_line
|
||||||
|
else:
|
||||||
|
current_description = comment_line
|
||||||
|
|
||||||
|
# После цикла добавляем описание для самого последнего префикса, если оно есть
|
||||||
|
if current_prefix_name and current_description:
|
||||||
|
arch = None
|
||||||
|
if "_x86_" in current_prefix_name or "_i586_" in current_prefix_name: arch = 'win32'
|
||||||
|
elif "_x64_" in current_prefix_name or "_amd64_" in current_prefix_name: arch = 'win64'
|
||||||
|
if arch:
|
||||||
|
self.prepared_prefixes[arch].append((current_prefix_name, current_description.strip().replace('\\n', '\n')))
|
||||||
|
|
||||||
|
except IOError as e:
|
||||||
|
QMessageBox.warning(self, "Ошибка", f"Не удалось прочитать файл с описаниями префиксов: {e}")
|
||||||
|
|
||||||
|
# Добавляем опцию "Чистый префикс" для обеих архитектур
|
||||||
|
self.prepared_prefixes['win32'].insert(0, ('none', 'Создать чистый префикс без дополнительных библиотек'))
|
||||||
|
self.prepared_prefixes['win64'].insert(0, ('none', 'Создать чистый префикс без дополнительных библиотек'))
|
||||||
|
|
||||||
def open_wine_version_dialog(self):
|
def open_wine_version_dialog(self):
|
||||||
"""Открывает диалог выбора версии Wine."""
|
"""Открывает диалог выбора версии Wine."""
|
||||||
architecture = "win32" if self.arch_win32_radio.isChecked() else "win64"
|
architecture = "win32" if self.arch_win32_radio.isChecked() else "win64"
|
||||||
@@ -1423,11 +1552,29 @@ class CreatePrefixDialog(QDialog):
|
|||||||
self.wine_version_edit.setText(dialog.selected_display_text)
|
self.wine_version_edit.setText(dialog.selected_display_text)
|
||||||
self.selected_wine_version_value = dialog.selected_version
|
self.selected_wine_version_value = dialog.selected_version
|
||||||
|
|
||||||
def clear_wine_version_selection(self):
|
def on_architecture_changed(self):
|
||||||
"""Сбрасывает выбор версии Wine."""
|
"""Обновляет список шаблонов и сбрасывает выбор версии Wine при смене архитектуры."""
|
||||||
|
# Сбрасываем выбор версии
|
||||||
self.wine_version_edit.clear()
|
self.wine_version_edit.clear()
|
||||||
self.selected_wine_version_value = None
|
self.selected_wine_version_value = None
|
||||||
|
|
||||||
|
# Обновляем список шаблонов
|
||||||
|
self.prefix_template_selector.blockSignals(True)
|
||||||
|
self.prefix_template_selector.clear()
|
||||||
|
|
||||||
|
architecture = "win32" if self.arch_win32_radio.isChecked() else "win64"
|
||||||
|
templates = self.prepared_prefixes.get(architecture, [])
|
||||||
|
|
||||||
|
for value, description in templates:
|
||||||
|
# Для 'none' используем более понятное имя
|
||||||
|
display_name = "Чистый префикс" if value == "none" else value
|
||||||
|
self.prefix_template_selector.addItem(display_name, userData={'value': value, 'tooltip': description})
|
||||||
|
|
||||||
|
self.prefix_template_selector.blockSignals(False)
|
||||||
|
# Обновляем тултип при смене элемента
|
||||||
|
self.prefix_template_selector.currentIndexChanged.connect(lambda i: self.prefix_template_selector.setToolTip(self.prefix_template_selector.itemData(i)['tooltip']))
|
||||||
|
self.prefix_template_selector.setToolTip(self.prefix_template_selector.itemData(0)['tooltip'])
|
||||||
|
|
||||||
def validate_prefix_name(self, text):
|
def validate_prefix_name(self, text):
|
||||||
"""Проверяет имя префикса в реальном времени и показывает/скрывает предупреждение."""
|
"""Проверяет имя префикса в реальном времени и показывает/скрывает предупреждение."""
|
||||||
valid_pattern = r'^[a-zA-Z0-9_-]*$'
|
valid_pattern = r'^[a-zA-Z0-9_-]*$'
|
||||||
@@ -1471,7 +1618,10 @@ class CreatePrefixDialog(QDialog):
|
|||||||
# Save data
|
# Save data
|
||||||
self.prefix_name = prefix_name
|
self.prefix_name = prefix_name
|
||||||
self.wine_arch = "win32" if self.arch_win32_radio.isChecked() else "win64"
|
self.wine_arch = "win32" if self.arch_win32_radio.isChecked() else "win64"
|
||||||
self.base_pfx = "none" if self.type_clean_radio.isChecked() else ""
|
|
||||||
|
# Получаем значение `BASE_PFX` из выбранного шаблона
|
||||||
|
current_index = self.prefix_template_selector.currentIndex()
|
||||||
|
self.base_pfx = self.prefix_template_selector.itemData(current_index)['value']
|
||||||
self.selected_wine_version_display = self.wine_version_edit.text()
|
self.selected_wine_version_display = self.wine_version_edit.text()
|
||||||
|
|
||||||
self.accept()
|
self.accept()
|
||||||
@@ -4989,6 +5139,13 @@ class WineHelperGUI(QMainWindow):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Основная точка входа в приложение."""
|
"""Основная точка входа в приложение."""
|
||||||
|
# Включаем поддержку HiDPI до создания QApplication для надежного и предсказуемого масштабирования.
|
||||||
|
# AA_EnableHighDpiScaling включает автоматическое масштабирование на основе DPI дисплея.
|
||||||
|
if hasattr(Qt, 'AA_EnableHighDpiScaling'):
|
||||||
|
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
||||||
|
# AA_UseHighDpiPixmaps позволяет использовать высококачественные иконки (например, @2x).
|
||||||
|
if hasattr(Qt, 'AA_UseHighDpiPixmaps'):
|
||||||
|
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
||||||
# QApplication должен быть создан до использования любых других частей Qt
|
# QApplication должен быть создан до использования любых других частей Qt
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
|||||||