@@ -474,10 +474,9 @@ class WinetricksManagerDialog(QDialog):
self . log_output . setText ( self . INFO_TEXT )
main_layout . addWidget ( self . log_output )
# Кнопки управления
# Кнопки управления, выровненные по правому краю
button_layout = QHBoxLayout ( )
self . status_label = QLabel ( " Загрузка компонентов... " )
button_layout . addWidget ( self . status_label , 1 )
button_layout . addStretch ( 1 )
self . apply_button = QPushButton ( " Применить " )
self . apply_button . setEnabled ( False )
@@ -548,7 +547,6 @@ class WinetricksManagerDialog(QDialog):
def load_all_categories ( self ) :
""" Запускает загрузку всех категорий. """
self . loading_count = len ( self . categories )
self . category_statuses = { name : " загрузка... " for name in self . categories . keys ( ) }
for internal_name in self . categories . values ( ) :
self . _start_load_process ( internal_name )
@@ -602,13 +600,6 @@ class WinetricksManagerDialog(QDialog):
process . finished . connect ( partial ( self . _on_load_finished , category ) )
process . start ( self . winetricks_path , [ category , " list " ] )
def _update_status_label ( self ) :
""" Обновляет текстовую метку состояния загрузки. """
status_parts = [ ]
for name , status in self . category_statuses . items ( ) :
status_parts . append ( f " { name } : { status } " )
self . status_label . setText ( " | " . join ( status_parts ) )
def _parse_winetricks_log ( self ) :
""" Читает winetricks.log и возвращает множество установленных компонентов. """
installed_verbs = set ( )
@@ -681,22 +672,15 @@ class WinetricksManagerDialog(QDialog):
if exit_code != 0 or exit_status != QProcess . NormalExit :
error_string = process . errorString ( ) if process else " N/A "
self . _log ( f " --- Ошибка загрузки категории ' { category } ' (код: { exit_code } ) --- " , " red " )
self . category_statuses [ category_display_name ] = " ошибка "
self . _update_status_label ( ) # Показываем ошибку в статусе
self . _log ( f " --- Ошибка загрузки категории ' { category_display_name } ' (код: { exit_code } ) --- " , " red " )
if exit_status == QProcess . CrashExit :
self . _log ( " --- Процесс winetricks завершился аварийно. --- " , " red " )
# По умолчанию используется "Неизвестная ошибка", которая не очень полезна.
if error_string != " Неизвестная ошибка " :
self . _log ( f " --- Системная ошибка: { error_string } --- " , " red " )
self . _log ( output if output . strip ( ) else " Winetricks не вернул вывод. Проверьте, что он работает корректно. " )
self . _log ( " -------------------------------------------------- " , " red " )
else :
self . category_statuses [ category_display_name ] = " готово "
installed_verbs = self . _parse_winetricks_log ( )
# Обновляем статус только если это была сетевая загрузка
if from_cache is None :
self . _update_status_label ( )
found_items = self . _parse_winetricks_list_output ( output , installed_verbs , list_widget )
if from_cache is None : # Только если мы не читали из кэша
@@ -721,7 +705,6 @@ class WinetricksManagerDialog(QDialog):
self . loading_count - = 1
if self . loading_count == 0 :
self . status_label . setText ( " Готово. " )
self . _update_ui_state ( )
def _on_item_changed ( self , item ) :
@@ -862,11 +845,6 @@ class WinetricksManagerDialog(QDialog):
# 3. Обрабатываем успех
self . _log ( " \n === В с е операции успешно завершены === " )
self . _show_message_box ( " Успех " ,
" Операции с компонентами были успешно выполнены. " ,
QMessageBox . Information ,
{ " buttons " : { " Да " : QMessageBox . AcceptRole } } )
self . apply_button . setEnabled ( True )
self . reinstall_button . setEnabled ( False ) # Сбрасываем в неактивное состояние
self . close_button . setEnabled ( True )
@@ -876,7 +854,6 @@ class WinetricksManagerDialog(QDialog):
search_edit . clear ( )
# Перезагружаем данные, чтобы обновить состояние
self . status_label . setText ( " Обновление данных... " )
self . initial_states . clear ( )
self . load_all_categories ( )
self . installation_finished = True
@@ -2142,61 +2119,49 @@ class WineHelperGUI(QMainWindow):
self . vkd3d_manage_button . setToolTip ( " Установка или удаление определенной версии vkd3d-proton в префиксе. " )
management_layout . addWidget ( self . vkd3d_manage_button , 5 , 1 )
# --- Правая сторона: Информационный блок ---
# --- Правая сторона: Информационный блок и кнопки установки ---
right_column_widget = QWidget ( )
right_column_layout = QVBoxLayout ( right_column_widget )
right_column_layout . setContentsMargins ( 0 , 0 , 0 , 0 )
right_column_layout . setSpacing ( 10 )
self . prefix_info_display = QTextBrowser ( )
self . prefix_info_display . setReadOnly ( True )
self . prefix_info_display . setFrameStyle ( QFrame . StyledPanel )
# Увеличиваем rowspan, чтобы охватить все строки с кнопками
management_layout . addWidget ( self . prefix_info_display , 0 , 2 , 6 , 1 )
management_layout . setColumnStretch ( 0 , 1 )
management_layout . setColumnStretch ( 1 , 1 )
management_layout . setColumnStretch ( 2 , 2 )
separator = QFrame ( )
separator . setFrameShape ( QFrame . HLine )
separator . setFrameShadow ( QFrame . Sunken )
management_layout . addWidget ( separator , 6 , 0 , 1 , 3 )
right_column_layout . addWidget ( self . prefix_info_display )
install_group = QWidget ( )
install_layout = QVBoxLayout ( install_group )
install_layout . setContentsMargins ( 0 , 5 , 0 , 0 )
install_layout . setContentsMargins ( 0 , 0 , 0 , 0 )
install_layout . setSpacing ( 5 )
install_path_layout = QHBoxLayout ( )
self . prefix_install_path_edit = QLineEdit ( )
self . prefix_install_path_edit . setPlaceholderText ( " Укажите путь к установочному файлу .exe или .msi... " )
install_path_layout . addWidget ( self . prefix_install_path_edit )
self . prefix_browse_button = QPushButton ( " Обзор... " )
self . prefix_browse_button . clicked . connect ( self . browse_for_prefix_installer )
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 )
action_buttons _layout. addWidget ( self . prefix_install_button )
self . prefix_install_button . clicked . connect ( self . browse_and_ run_prefix_installer)
install _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. addLayou t ( action_buttons_lay out )
management_layout . addWidget ( install_group , 7 , 0 , 1 , 3 )
install _layout. addWidget ( self . create_launcher_button )
right_column _layout. addWidge t ( install_gr oup )
right_column_layout . setStretch ( 0 , 1 ) # Информационное окно растягивается
right_column_layout . setStretch ( 1 , 0 ) # Группа кнопок не растягивается
management_layout . addWidget ( right_column_widget , 0 , 2 , 6 , 1 )
management_layout . setColumnStretch ( 0 , 1 )
management_layout . setColumnStretch ( 1 , 1 )
management_layout . setColumnStretch ( 2 , 2 )
container_layout . addWidget ( self . prefix_management_groupbox )
layout . addWidget ( self . management_container_groupbox )
layout . addStretch ( )
self . add_tab ( self . prefix_tab , " Менеджер префиксов " )
self . prefix_install_path_edit . textChanged . connect ( self . update_prefix_install_button_state )
def _get_current_prefixes ( self ) :
""" Возвращает множество имен существующих префиксов. """
prefixes_root_path = os . path . join ( Var . USER_WORK_PATH , " prefixes " )
@@ -2322,7 +2287,6 @@ class WineHelperGUI(QMainWindow):
# Успешное удаление, обновляем 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 Подробности смотрите в логе. " )
@@ -2331,18 +2295,16 @@ class WineHelperGUI(QMainWindow):
is_prefix_selected = bool ( prefix_name )
self . prefix_management_groupbox . setEnabled ( is_prefix_selected )
self . create_launcher_button . setEnabled ( is_prefix_selected )
self . prefix_install_button . setEnabled ( is_prefix_selected )
if is_prefix_selected :
self . update_prefix_info_display ( prefix_name )
else :
self . prefix_info_display . clear ( )
self . prefix_install_path_edit . clear ( )
# Сбрасываем состояние кнопок, когда префикс не выбран
self . esync_button . setChecked ( False )
self . fsync_button . setChecked ( False )
self . update_prefix_install_button_state ( )
def update_prefix_info_display ( self , prefix_name ) :
""" Обновляет информационный блок для созданного префикса, читая данные из last.conf. """
if not prefix_name :
@@ -2422,8 +2384,13 @@ class WineHelperGUI(QMainWindow):
html_content + = " </p> "
self . prefix_info_display . setHtml ( html_content )
def browse_for _prefix_installer ( self ) :
""" Открывает диалог выбора файла для установки в созданный префикс. """
def browse_and_run _prefix_installer ( self ) :
""" Открывает диалог выбора файла и запускает установку в созданный префикс. """
prefix_name = self . current_managed_prefix_name
if not prefix_name :
QMessageBox . warning ( self , " Ошибка " , " Сначала выберите префикс для установки. " )
return
file_path , _ = QFileDialog . getOpenFileName (
self ,
" Выберите установочный файл " ,
@@ -2431,18 +2398,11 @@ class WineHelperGUI(QMainWindow):
" Исполняемые файлы (*.exe *.msi);;В с е файлы (*) "
)
if file_path :
self . prefix_install_path_edit . setText ( file_path )
self . run_ prefix_installer ( file_path )
def update _prefix_install_button_state ( self ) :
""" Обновляет состояние кнопки установки в префикс ."""
path_ok = bool ( self . prefix_install_path_edit . text ( ) . strip ( ) )
prefix_selected = self . current_managed_prefix_name is not None
self . prefix_install_button . setEnabled ( path_ok and prefix_selected )
def run_prefix_installer ( self ) :
""" Запускает установку файла в выбранный префикс. """
def run _prefix_installer ( self , installer_path ) :
""" Запускает установку файла в выбранный префикс через скрипт winehelper ."""
prefix_name = self . current_managed_prefix_name
installer_path = self . prefix_install_path_edit . text ( ) . strip ( )
if not prefix_name :
QMessageBox . warning ( self , " Ошибка " , " Н е выбран префикс для установки." )
@@ -2451,9 +2411,6 @@ class WineHelperGUI(QMainWindow):
QMessageBox . warning ( self , " Ошибка " , " Указан неверный путь к установочному файлу. " )
return
prefix_path = os . path . join ( Var . USER_WORK_PATH , " prefixes " , prefix_name )
wine_executable = self . _get_wine_executable_for_prefix ( prefix_name )
self . command_dialog = QDialog ( self )
self . command_dialog . setWindowTitle ( f " Установка в префикс: { prefix_name } " )
self . command_dialog . setMinimumSize ( 750 , 400 )
@@ -2477,13 +2434,12 @@ class WineHelperGUI(QMainWindow):
self . command_process . readyReadStandardOutput . connect ( self . _handle_command_output )
self . command_process . finished . connect ( self . _handle_prefix_install_finished )
env = QProcessEnvironment . systemEnvironment ( )
env . insert ( " WINEPREFIX " , prefix_path )
self . command_process . setProcessEnvironment ( env )
# Окружение полностью настраивается скриптом winehelper
self . command_process . setProcessEnvironment ( QProcessEnvironment . systemEnvironment ( ) )
args = [ installer_path ]
self . command_log_output . append ( f " Запуск установки : { shlex . quote ( wine_executable ) } { shlex . quote ( installer_path ) } " )
self . command_process . start ( wine_executable , args )
args = [ " install-to-prefix " , prefix_name , installer_path ]
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 _get_prefix_component_version ( self , prefix_name , component_key ) :
@@ -2991,10 +2947,6 @@ class WineHelperGUI(QMainWindow):
if not self . management_container_groupbox . isVisible ( ) :
self . management_container_groupbox . setVisible ( True )
QMessageBox . information ( self , " Успех " ,
f " Префикс ' { prefix_name } ' успешно создан. \n "
" Теперь вы можете управлять им, выбрав е г о из выпадающего списка. " )
def update_installed_apps ( self ) :
""" Обновляет список установленных приложений в виде кнопок """
# Если активная кнопка находится в списке удаляемых, сбрасываем е е
@@ -3063,7 +3015,6 @@ class WineHelperGUI(QMainWindow):
self . command_process . deleteLater ( )
self . command_process = None
self . command_close_button . setEnabled ( True )
self . prefix_install_path_edit . clear ( )
self . update_installed_apps ( )
def _set_active_button ( self , button_widget ) :
@@ -3084,7 +3035,9 @@ class WineHelperGUI(QMainWindow):
def show_installed_app_info ( self , desktop_path , button_widget ) :
""" Показывает информацию о б установленном приложении в правой панели. """
self . _set_active_button ( button_widget )
# Очищаем поле поиска и принудительно обновляем список, чтобы показать все приложения
# Если в поиске был текст, очищаем е г о и перерисовываем список.
# Это предотвращает "прыжок", если список не был отфильтрован.
if self . installed_search_edit . text ( ) :
self . installed_search_edit . blockSignals ( True )
self . installed_search_edit . clear ( )
self . installed_search_edit . blockSignals ( False )
@@ -3758,11 +3711,14 @@ class WineHelperGUI(QMainWindow):
search_edit = tab_data [ ' search_edit ' ]
scroll_area = tab_data [ ' scroll_area ' ]
# Общая логика: очищаем поиск, обновляем список и прокручиваем к элементу
# Если в поиске был текст, очищаем е г о и перерисовываем список.
# Это предотвращает "прыжок", если список не был отфильтрован.
if search_edit . text ( ) :
search_edit . blockSignals ( True )
search_edit . clear ( )
search_edit . blockSignals ( False )
self . filter_buttons ( tab_type )
frame = button_widget . parent ( )
if isinstance ( frame , QFrame ) :
QTimer . singleShot ( 0 , lambda : scroll_area . ensureWidgetVisible ( frame ) )
@@ -4124,18 +4080,6 @@ class WineHelperGUI(QMainWindow):
self . created_prefix_selector . setCurrentIndex ( 0 )
# --- Конец обновления списка префиксов ---
# Создаем кастомный диалог, чтобы кнопка была на русском
success_box = QMessageBox ( self . install_dialog )
success_box . setWindowTitle ( " Успех " )
title_name = self . _get_current_app_title ( )
success_text = f " Программа « { title_name } » установлена успешно! "
if new_prefix_name :
success_text + = f " \n \n Новый префикс ' { new_prefix_name } ' был автоматически выбран в списке управления на вкладке ' Менеджер префиксов ' . "
success_box . setText ( success_text )
success_box . setIcon ( QMessageBox . Information )
success_box . addButton ( " Готово " , QMessageBox . AcceptRole )
success_box . exec_ ( )
self . update_installed_apps ( )
# Кнопка закрыть
@@ -4235,7 +4179,6 @@ class WineHelperGUI(QMainWindow):
""" Обрабатывает завершение создания ярлыка. """
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 ( ) ) :