mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-06-08 01:47:45 +02:00
163 lines
5.7 KiB
Python
163 lines
5.7 KiB
Python
"""
|
|
Main window dialogs and cleanup mixin.
|
|
Settings, About, open URL, cleanup_processes, closeEvent.
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
|
|
from jackify.frontends.gui.dialogs.settings_dialog import SettingsDialog
|
|
|
|
|
|
class MainWindowDialogsMixin:
|
|
"""Mixin for settings/about dialogs, open URL, and cleanup."""
|
|
def _stop_qthread(self, thread, thread_name: str, cooperative_timeout_ms: int = 5000):
|
|
"""Stop a QThread robustly to avoid teardown crashes on app exit."""
|
|
if thread is None:
|
|
return None
|
|
try:
|
|
if not thread.isRunning():
|
|
return None
|
|
except RuntimeError:
|
|
return None
|
|
|
|
try:
|
|
thread.requestInterruption()
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
thread.quit()
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
if thread.wait(cooperative_timeout_ms):
|
|
return None
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
thread.terminate()
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
if not thread.wait(10000):
|
|
print(f"WARNING: {thread_name} still running during shutdown")
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
def open_settings_dialog(self):
|
|
try:
|
|
if self._settings_dialog is not None:
|
|
try:
|
|
if self._settings_dialog.isVisible():
|
|
self._settings_dialog.raise_()
|
|
self._settings_dialog.activateWindow()
|
|
return
|
|
else:
|
|
self._settings_dialog = None
|
|
except RuntimeError:
|
|
self._settings_dialog = None
|
|
dlg = SettingsDialog(self)
|
|
self._settings_dialog = dlg
|
|
|
|
def on_dialog_finished():
|
|
self._settings_dialog = None
|
|
dlg.finished.connect(on_dialog_finished)
|
|
dlg.exec()
|
|
except Exception as e:
|
|
print(f"[ERROR] Exception in open_settings_dialog: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
self._settings_dialog = None
|
|
|
|
def open_about_dialog(self):
|
|
try:
|
|
from jackify.frontends.gui.dialogs.about_dialog import AboutDialog
|
|
if self._about_dialog is not None:
|
|
try:
|
|
if self._about_dialog.isVisible():
|
|
self._about_dialog.raise_()
|
|
self._about_dialog.activateWindow()
|
|
return
|
|
else:
|
|
self._about_dialog = None
|
|
except RuntimeError:
|
|
self._about_dialog = None
|
|
dlg = AboutDialog(self.system_info, self)
|
|
self._about_dialog = dlg
|
|
|
|
def on_dialog_finished():
|
|
self._about_dialog = None
|
|
dlg.finished.connect(on_dialog_finished)
|
|
dlg.exec()
|
|
except Exception as e:
|
|
print(f"[ERROR] Exception in open_about_dialog: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
self._about_dialog = None
|
|
|
|
def _open_url(self, url: str):
|
|
env = os.environ.copy()
|
|
appimage_vars = [
|
|
'LD_LIBRARY_PATH', 'PYTHONPATH', 'PYTHONHOME',
|
|
'QT_PLUGIN_PATH', 'QML2_IMPORT_PATH',
|
|
]
|
|
if 'APPIMAGE' in env or 'APPDIR' in env:
|
|
for var in appimage_vars:
|
|
env.pop(var, None)
|
|
subprocess.Popen(
|
|
['xdg-open', url],
|
|
env=env,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
start_new_session=True
|
|
)
|
|
|
|
def cleanup_processes(self):
|
|
try:
|
|
if hasattr(self, '_update_thread') and self._update_thread is not None:
|
|
self._update_thread = self._stop_qthread(self._update_thread, "_update_thread")
|
|
if hasattr(self, '_gallery_cache_preload_thread') and self._gallery_cache_preload_thread is not None:
|
|
self._gallery_cache_preload_thread = self._stop_qthread(
|
|
self._gallery_cache_preload_thread,
|
|
"_gallery_cache_preload_thread",
|
|
)
|
|
for service in self.gui_services.values():
|
|
if hasattr(service, 'cleanup'):
|
|
service.cleanup()
|
|
screens = [
|
|
getattr(self, 'modlist_tasks_screen', None),
|
|
getattr(self, 'additional_tasks_screen', None),
|
|
getattr(self, 'install_modlist_screen', None),
|
|
getattr(self, 'install_ttw_screen', None),
|
|
getattr(self, 'configure_new_modlist_screen', None),
|
|
getattr(self, 'wabbajack_installer_screen', None),
|
|
getattr(self, 'configure_existing_modlist_screen', None),
|
|
getattr(self, 'install_mo2_screen', None),
|
|
]
|
|
for screen in screens:
|
|
if screen is None:
|
|
continue
|
|
if hasattr(screen, 'cleanup_processes'):
|
|
screen.cleanup_processes()
|
|
elif hasattr(screen, 'cleanup'):
|
|
screen.cleanup()
|
|
elif hasattr(screen, 'worker'):
|
|
worker = getattr(screen, 'worker', None)
|
|
setattr(screen, 'worker', self._stop_qthread(worker, f"{screen.__class__.__name__}.worker"))
|
|
try:
|
|
subprocess.run(['pkill', '-f', 'jackify-engine'], timeout=5, capture_output=True)
|
|
except Exception:
|
|
pass
|
|
except Exception as e:
|
|
print(f"Error during cleanup: {e}")
|
|
|
|
def closeEvent(self, event):
|
|
self._save_geometry_on_quit()
|
|
self.cleanup_processes()
|
|
event.accept()
|