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
This commit is contained in:
@@ -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
|
||||
@@ -965,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
|
||||
@@ -1975,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("Обзор...")
|
||||
@@ -1983,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)
|
||||
|
||||
@@ -2001,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):
|
||||
@@ -2075,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):
|
||||
"""Сбрасывает состояние управления префиксом, когда пользователь вводит новое имя."""
|
||||
@@ -2116,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):
|
||||
@@ -2197,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()
|
||||
@@ -2503,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}) ===")
|
||||
|
||||
@@ -2962,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:
|
||||
@@ -3514,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:
|
||||
|
Reference in New Issue
Block a user