diff --git a/README.md b/README.md
index 163a7438..a4b251d3 100644
--- a/README.md
+++ b/README.md
@@ -239,6 +239,13 @@ WineHelper предоставляет доступ к основным инст

@@ -271,7 +278,7 @@ WineHelper предоставляет доступ к основным инст

-
Окно установки с логомм
+
Окно установки с логом
После установки приложения и нажатия кнопки **Закрыть** в окне установки приложения, ярлык приложения появится в списке установленных приложений во вкладке **Установленные** а также в меню приложений и на рабочем столе если это разрешено в рабочем окружении.
@@ -323,7 +330,7 @@ WineHelper предоставляет доступ к основным инст
* **Версию Wine/Proton** из доступного списка.
-

+
Диалог создания нового префикса
@@ -339,9 +346,9 @@ WineHelper предоставляет доступ к основным инст
* `Файловый менеджер (winefile)`
* **Управлять компонентами**:
* **Менеджер компонентов (Winetricks)**: Удобный интерфейс для установки и переустановки библиотек, шрифтов и настроек.
- * **Управление Wine/Proton**: Смена версии Wine или Proton для выбранного префикса.
- * **Управление DXVK/VKD3D**: Установка или удаление конкретных версий DXVK и VKD3D.
- * **Ассоциации файлов**: Настройка открытия определенных типов файлов (например, `.pdf`, `.docx`) нативными приложениями Linux.
+ * **Управление Wine/Proton**: Смена версии Wine или Proton для выбранного префикса. Диалог выбора позволяет как выбрать уже установленную версию, так и скачать новую из списков, сгруппированных по архитектуре.
+ * **Управление DXVK/VKD3D**: Установка или удаление конкретных версий DXVK и vkd3d-proton.
+ * **Ассоциации файлов**: Настройка открытия определенных типов файлов (например, `.pdf`, `.docx`) нативными приложениями Linux, чтобы избежать их открытия внутри Wine.
* **Включать/выключать ESync и FSync**.
* **Устанавливать приложения**: Установить любой `.exe` или `.msi` файл напрямую в выбранный префикс.
* **Создавать ярлыки**: Создать ярлык для любого исполняемого файла внутри префикса.
@@ -349,10 +356,14 @@ WineHelper предоставляет доступ к основным инст
Справа отображается подробная информация о конфигурации выбранного префикса.
+В самом низу вкладки находится кнопка **«Удалить все данные WineHelper»**.
+> [!CAUTION]
+> Эта функция полностью и безвозвратно удаляет все префиксы, настройки, кэш и ярлыки, созданные WineHelper. Используйте с осторожностью! Программа запросит двойное подтверждение для предотвращения случайного удаления.
+
### Вкладка «Справка»
Содержит полезную информацию о проекте:
-* **Руководство**: Ссылка на официальную документацию.
+* **Общее**: Ссылка на официальную документацию.
* **Авторы**: Список разработчиков и участников проекта.
* **Лицензия**: Текст лицензии WineHelper и информация о сторонних компонентах.
* **История изменений**: Changelog пакета.
diff --git a/image/handbook/auto_install.png b/image/handbook/auto_install.png
index 63c389a6..599f0f90 100644
Binary files a/image/handbook/auto_install.png and b/image/handbook/auto_install.png differ
diff --git a/image/handbook/auto_install_test.png b/image/handbook/auto_install_test.png
new file mode 100644
index 00000000..b8d92131
Binary files /dev/null and b/image/handbook/auto_install_test.png differ
diff --git a/image/handbook/create_prefix.png b/image/handbook/create_prefix.png
deleted file mode 100644
index 2f66f0f1..00000000
Binary files a/image/handbook/create_prefix.png and /dev/null differ
diff --git a/image/handbook/election_installed.png b/image/handbook/election_installed.png
index e6742c5d..b75b41ba 100644
Binary files a/image/handbook/election_installed.png and b/image/handbook/election_installed.png differ
diff --git a/image/handbook/folder_log_backup.png b/image/handbook/folder_log_backup.png
index b4017429..cdda3e66 100644
Binary files a/image/handbook/folder_log_backup.png and b/image/handbook/folder_log_backup.png differ
diff --git a/image/handbook/help.png b/image/handbook/help.png
index 9a5a8b44..c894940e 100644
Binary files a/image/handbook/help.png and b/image/handbook/help.png differ
diff --git a/image/handbook/info.png b/image/handbook/info.png
index 2226e90d..d507417c 100644
Binary files a/image/handbook/info.png and b/image/handbook/info.png differ
diff --git a/image/handbook/prefix_create.png b/image/handbook/prefix_create.png
new file mode 100644
index 00000000..51273fc3
Binary files /dev/null and b/image/handbook/prefix_create.png differ
diff --git a/image/handbook/prefix_manager.png b/image/handbook/prefix_manager.png
index 25d0803d..9849ea9c 100644
Binary files a/image/handbook/prefix_manager.png and b/image/handbook/prefix_manager.png differ
diff --git a/image/handbook/search.png b/image/handbook/search.png
index 1c72cc37..2b6fc69b 100644
Binary files a/image/handbook/search.png and b/image/handbook/search.png differ
diff --git a/winehelper b/winehelper
index 202a08de..94527600 100755
--- a/winehelper
+++ b/winehelper
@@ -1700,7 +1700,7 @@ select_wine_version() {
system_wine_version=$(wine --version 2>/dev/null)
[[ -n "$system_wine_version" ]] && system_wine_display_name="$system_wine_version"
fi
- options+=("--- System ---")
+ options+=("--- SYSTEM ---")
options+=("$system_wine_display_name")
# --- Other versions from sha256sum.list ---
@@ -1718,13 +1718,13 @@ select_wine_version() {
}
while IFS= read -r line; do
- if [[ "$line" =~ ^#+[[:space:]]([^#[:space:]]+)[[:space:]]#* ]]; then
+ if [[ "$line" =~ ^#+[[:space:]](.*[^#[:space:]])[[:space:]]#* ]] ; then
flush_group
current_group="${BASH_REMATCH[1]}"
# Отображаем только группы, которые являются сборками WINE или PROTON
case "$current_group" in
- WINE|WINE_LG|PROTON_LG|PROTON_STEAM)
- local pretty_key=$(echo "$current_group" | tr '_' ' ' | sed -e "s/\b\(.\)/\u\1/g")
+ "WINE WOW64"|"WINE AMD64"|"WINE I586")
+ local pretty_key="$current_group"
options+=("--- $pretty_key ---")
;;
*)
diff --git a/winehelper_gui.py b/winehelper_gui.py
index 45841b53..93362c32 100644
--- a/winehelper_gui.py
+++ b/winehelper_gui.py
@@ -1129,28 +1129,28 @@ class WineVersionSelectionDialog(QDialog):
self.search_edit.textChanged.connect(self.filter_versions)
main_layout.addWidget(self.search_edit)
- self.version_tabs = QTabWidget()
- main_layout.addWidget(self.version_tabs)
+ self.main_tabs = QTabWidget()
+ main_layout.addWidget(self.main_tabs)
self.load_versions()
def load_versions(self):
"""Запускает процесс получения списка версий Wine."""
- self.version_tabs.clear()
+ self.main_tabs.clear()
loading_widget = QWidget()
loading_layout = QVBoxLayout(loading_widget)
status_label = QLabel("Загрузка, пожалуйста, подождите...")
status_label.setAlignment(Qt.AlignCenter)
loading_layout.addWidget(status_label)
- self.version_tabs.addTab(loading_widget, "Загрузка...")
- self.version_tabs.setEnabled(False)
+ self.main_tabs.addTab(loading_widget, "Загрузка...")
+ self.main_tabs.setEnabled(False)
QApplication.processEvents()
self._parse_sha256_list()
self.populate_ui()
- self.version_tabs.setEnabled(True)
+ self.main_tabs.setEnabled(True)
def _parse_sha256_list(self):
"""Парсит sha256sum.list для получения списка версий."""
@@ -1170,10 +1170,10 @@ class WineVersionSelectionDialog(QDialog):
if not line:
continue
- match = re.match(r'^#+\s*([^#]+?)\s*#*$', line)
+ match = re.match(r'^#+\s*(.*?)\s*#*$', line)
if match:
- group_name = match.group(1)
- allowed_groups = {"WINE", "WINE_LG", "PROTON_LG", "PROTON_STEAM"}
+ group_name = match.group(1).strip()
+ allowed_groups = {"WINE WOW64", "WINE AMD64", "WINE I586"}
# Отображаем только группы, которые являются сборками WINE или PROTON
if group_name in allowed_groups:
current_group = group_name
@@ -1194,9 +1194,59 @@ class WineVersionSelectionDialog(QDialog):
QMessageBox.warning(self, "Ошибка", f"Не удалось прочитать файл версий:\n{e}")
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):
"""Заполняет 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}")
+ self.system_wine_display_name = "Системная версия"
+ 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:
error_widget = QWidget()
@@ -1205,55 +1255,33 @@ class WineVersionSelectionDialog(QDialog):
error_label.setAlignment(Qt.AlignCenter)
error_layout.addWidget(error_label)
self.version_tabs.addTab(error_widget, "Ошибка")
+ self.filter_versions()
return
+ # --- Фильтрация и сортировка групп в зависимости от архитектуры ---
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 shutil.which('wine'):
- self.system_wine_display_name = "Системная версия"
- try:
- # Пытаемся получить версию системного wine
- result = subprocess.run(['wine', '--version'], capture_output=True, text=True, check=True, encoding='utf-8')
- 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 возвращает ошибку, используем имя по умолчанию "Системная версия"
+ if is_win64:
+ allowed_groups_for_arch = {"WINE AMD64", "WINE WOW64"}
+ tab_order = ["WINE AMD64", "WINE WOW64"]
+ else: # win32
+ allowed_groups_for_arch = {"WINE I586","WINE AMD64", "WINE WOW64"}
+ tab_order = ["WINE I586","WINE AMD64", "WINE WOW64"]
- self._create_version_tab("Системный", [(self.system_wine_display_name, "system")])
-
- # Определяем желаемый порядок вкладок
- tab_order = ["WINE", "WINE_LG", "PROTON_LG", "PROTON_STEAM"]
- # Сортируем ключи в соответствии с заданным порядком
- group_keys = sorted(self.wine_versions_data.keys(), key=lambda k: tab_order.index(k) if k in tab_order else len(tab_order))
+ # Получаем только те ключи, которые есть в данных и разрешены для данной архитектуры
+ available_keys = [key for key in self.wine_versions_data.keys() if key in allowed_groups_for_arch]
+ # Сортируем доступные ключи в соответствии с заданным порядком
+ group_keys = sorted(available_keys, key=lambda k: tab_order.index(k))
for key in group_keys:
versions = self.wine_versions_data.get(key, [])
-
- 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:
+ if not 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._create_version_tab(key, sorted(versions, reverse=True))
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_layout = QVBoxLayout(tab_page)
@@ -1269,6 +1297,13 @@ class WineVersionSelectionDialog(QDialog):
grid_layout = QGridLayout(scroll_content)
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"
+ re_32bit_version = re.compile(r'i[3-6]86|i586')
+
num_columns = 3
row, col = 0, 0
for version_data in versions_list:
@@ -1279,36 +1314,58 @@ class WineVersionSelectionDialog(QDialog):
btn = QPushButton(display_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-битным префиксом.")
+
grid_layout.addWidget(btn, row, col)
col += 1
if col >= num_columns:
col = 0
row += 1
- self.version_tabs.addTab(tab_page, title)
-
def filter_versions(self):
"""Фильтрует видимость кнопок версий на основе текста поиска."""
search_text = self.search_edit.text().lower()
- for i in range(self.version_tabs.count()):
- tab_widget = self.version_tabs.widget(i)
- # The grid layout is inside a scroll area content widget
- grid_layout = tab_widget.findChild(QGridLayout)
- if not grid_layout:
- continue
+ # Фильтр для вкладки "Установленные"
+ if hasattr(self, 'installed_grid_layout'):
+ self._filter_grid(self.installed_grid_layout, search_text)
- any_visible_in_tab = False
- for j in range(grid_layout.count()):
- btn_widget = grid_layout.itemAt(j).widget()
+ # Фильтр для вкладок "Скачать"
+ if hasattr(self, 'version_tabs'):
+ 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):
is_match = search_text in btn_widget.text().lower()
btn_widget.setVisible(is_match)
if is_match:
- any_visible_in_tab = True
+ any_visible_in_grid = True
- # Enable/disable tab based on content
- self.version_tabs.setTabEnabled(i, any_visible_in_tab)
+ return any_visible_in_grid
def on_version_selected(self, version_name):
"""Обрабатывает выбор версии."""