added a file association button

This commit is contained in:
Sergey Palcheh
2025-09-25 13:29:56 +06:00
parent 9f994a8cc3
commit 97996fb67b
2 changed files with 177 additions and 4 deletions

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, QInputDialog)
QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser, QInputDialog, QDialogButtonBox)
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
@@ -1332,6 +1332,75 @@ class CreatePrefixDialog(QDialog):
self.accept()
class FileAssociationsDialog(QDialog):
"""Диалог для управления ассоциациями файлов (WH_XDG_OPEN)."""
def __init__(self, current_associations, parent=None):
super().__init__(parent)
self.setWindowTitle("Настройка ассоциаций файлов")
self.setMinimumWidth(450)
self.setModal(True)
self.new_associations = current_associations
layout = QVBoxLayout(self)
layout.setSpacing(10) # Добавляем вертикальный отступ между виджетами
info_label = QLabel(
"Укажите расширения файлов, которые должны открываться нативными<br>"
"приложениями Linux. Чтобы удалить все ассоциации, очистите поле.<br><br>"
"<b>Пример:</b> <code>pdf docx txt</code>"
)
info_label.setWordWrap(True)
info_label.setTextFormat(Qt.RichText)
layout.addWidget(info_label)
self.associations_edit = QLineEdit()
# Если ассоциации не заданы (значение "0"), поле будет пустым, чтобы показать подсказку
if current_associations != "0":
self.associations_edit.setText(current_associations)
self.associations_edit.setPlaceholderText("Введите расширения через пробел...")
layout.addWidget(self.associations_edit)
# Запрещенные расширения
forbidden_label = QLabel(
"<small><b>Запрещено использовать:</b> cpl, dll, exe, lnk, msi</small>"
)
forbidden_label.setTextFormat(Qt.RichText) # Включаем обработку HTML
layout.addWidget(forbidden_label)
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
button_box.accepted.connect(self.validate_and_accept)
button_box.rejected.connect(self.reject)
layout.addWidget(button_box)
def validate_and_accept(self):
"""Проверяет введенные данные перед закрытием."""
forbidden_extensions = {"cpl", "dll", "exe", "lnk", "msi"}
# Получаем введенные расширения, очищаем от лишних пробелов
input_text = self.associations_edit.text().lower().strip()
entered_extensions = {ext.strip() for ext in input_text.split() if ext.strip()}
found_forbidden = entered_extensions.intersection(forbidden_extensions)
if found_forbidden:
msg_box = QMessageBox(self)
msg_box.setIcon(QMessageBox.Warning)
msg_box.setWindowTitle("Недопустимые расширения")
msg_box.setTextFormat(Qt.RichText)
msg_box.setText(
"Следующие расширения запрещены и не могут быть использованы:<br><br>"
f"<b>{', '.join(sorted(list(found_forbidden)))}</b>"
)
msg_box.exec_()
return
# Сохраняем результат в виде отсортированной строки
self.new_associations = " ".join(sorted(list(entered_extensions)))
self.accept()
class ComponentVersionSelectionDialog(QDialog):
"""Диалог для выбора версии компонента (DXVK, VKD3D)."""
@@ -2119,6 +2188,13 @@ class WineHelperGUI(QMainWindow):
self.vkd3d_manage_button.setToolTip("Установка или удаление определенной версии vkd3d-proton в префиксе.")
management_layout.addWidget(self.vkd3d_manage_button, 5, 1)
self.file_associations_button = QPushButton("Ассоциации файлов")
self.file_associations_button.setMinimumHeight(32)
self.file_associations_button.clicked.connect(self.open_file_associations_manager)
self.file_associations_button.setToolTip(
"Настройка открытия определенных типов файлов с помощью нативных приложений Linux.")
management_layout.addWidget(self.file_associations_button, 6, 0, 1, 2)
# --- Правая сторона: Информационный блок и кнопки установки ---
right_column_widget = QWidget()
right_column_layout = QVBoxLayout(right_column_widget)
@@ -2151,7 +2227,7 @@ class WineHelperGUI(QMainWindow):
right_column_layout.setStretch(0, 1) # Информационное окно растягивается
right_column_layout.setStretch(1, 0) # Группа кнопок не растягивается
management_layout.addWidget(right_column_widget, 0, 2, 6, 1)
management_layout.addWidget(right_column_widget, 0, 2, 7, 1)
management_layout.setColumnStretch(0, 1)
management_layout.setColumnStretch(1, 1)
@@ -2358,8 +2434,9 @@ class WineHelperGUI(QMainWindow):
"VKD3D_VER": ("Версия VKD3D", lambda v: v if v else "Не установлено"),
"WINEESYNC": ("ESync", lambda v: "Включен" if v == "1" else "Выключен"),
"WINEFSYNC": ("FSync", lambda v: "Включен" if v == "1" else "Выключен"),
"WH_XDG_OPEN": ("Ассоциации файлов", lambda v: v if v and v != "0" else "Не заданы"),
}
display_order = ["WINEPREFIX", "WINEARCH", "WH_WINE_USE", "BASE_PFX", "DXVK_VER", "VKD3D_VER", "WINEESYNC", "WINEFSYNC"]
display_order = ["WINEPREFIX", "WINEARCH", "WH_WINE_USE", "BASE_PFX", "DXVK_VER", "VKD3D_VER", "WINEESYNC", "WINEFSYNC", "WH_XDG_OPEN"]
html_content = f'<p style="line-height: 1.3; font-size: 9pt;">'
html_content += f"<b>Имя:</b> {html.escape(prefix_name)}<br>"
@@ -2659,6 +2736,64 @@ class WineHelperGUI(QMainWindow):
if exit_code == 0:
self.update_prefix_info_display(prefix_name)
def open_file_associations_manager(self):
"""Открывает диалог для управления ассоциациями файлов."""
prefix_name = self.current_managed_prefix_name
if not prefix_name:
QMessageBox.warning(self, "Ошибка", "Сначала выберите префикс.")
return
current_associations = self._get_prefix_component_version(prefix_name, "WH_XDG_OPEN") or ""
dialog = FileAssociationsDialog(current_associations, self)
if dialog.exec_() == QDialog.Accepted:
new_associations = dialog.new_associations
# Запускаем обновление, только если значение изменилось
if new_associations != current_associations:
self.run_update_associations_command(prefix_name, new_associations)
def run_update_associations_command(self, prefix_name, new_associations):
"""Выполняет команду обновления ассоциаций файлов."""
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
self.command_dialog = QDialog(self)
self.command_dialog.setWindowTitle("Обновление ассоциаций файлов")
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(
lambda exit_code, exit_status: self._handle_component_install_finished(
prefix_name, exit_code, exit_status
)
)
env = QProcessEnvironment.systemEnvironment()
env.insert("WINEPREFIX", prefix_path)
# Устанавливаем новую переменную окружения для скрипта
env.insert("WH_XDG_OPEN", new_associations)
self.command_process.setProcessEnvironment(env)
args = ["init-prefix"]
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_launcher_for_prefix(self):
"""
Открывает диалог для создания ярлыка для приложения внутри выбранного префикса.