mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-08-14 02:23:45 +02:00
Release v0.7 - Tools Hub, Engine Choice, NXM Link Handling, Native Component Install, NSF/CSF Support
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
"""
|
||||
Additional Tasks & Tools Screen
|
||||
|
||||
Simple screen for TTW automation only.
|
||||
Follows the same pattern as ModlistTasksScreen.
|
||||
Additional tools and automation. Follows the same pattern as ModlistTasksScreen.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -23,7 +22,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AdditionalTasksScreen(QWidget):
|
||||
"""Simple Additional Tasks screen for TTW only"""
|
||||
"""Additional Tasks screen for automation and standalone tools."""
|
||||
|
||||
def __init__(self, stacked_widget=None, main_menu_index=0, system_info: Optional[SystemInfo] = None,
|
||||
install_mo2_screen_index: int = 9):
|
||||
@@ -67,7 +66,7 @@ class AdditionalTasksScreen(QWidget):
|
||||
header_layout.addSpacing(10)
|
||||
|
||||
# Description area with fixed height
|
||||
desc = QLabel("TTW automation, Wabbajack installer, and additional tools.")
|
||||
desc = QLabel("Wabbajack installer, MO2 setup, and additional tools.")
|
||||
desc.setWordWrap(True)
|
||||
desc.setStyleSheet("color: #ccc; font-size: 13px;")
|
||||
desc.setAlignment(Qt.AlignHCenter)
|
||||
@@ -93,10 +92,11 @@ class AdditionalTasksScreen(QWidget):
|
||||
"""Set up the menu buttons section"""
|
||||
# Menu options
|
||||
MENU_ITEMS = [
|
||||
("Install TTW", "ttw_install", "Install Tale of Two Wastelands using TTW_Linux_Installer"),
|
||||
("Install Wabbajack", "wabbajack_install", "Install Wabbajack.exe via Proton (automated setup)"),
|
||||
("Setup Mod Organizer 2", "setup_mo2", "Download and configure a standalone MO2 instance"),
|
||||
("Run Install Verifier", "run_verifier", "Check an installed modlist for common configuration problems"),
|
||||
("Configure Tool Compatibility", "tool_config", "Apply xEdit, Pandora and DLL fixes to an existing modlist prefix"),
|
||||
("Setup Mod Organizer 2", "setup_mo2", "Download and configure a standalone MO2 instance"),
|
||||
("Install Wabbajack", "wabbajack_install", "Install Wabbajack.exe via Proton (automated setup)"),
|
||||
("Create Diagnostic Bundle", "diagnostic_bundle", "Package logs and system info for support reporting"),
|
||||
("Return to Main Menu", "return_main_menu", "Go back to the main menu"),
|
||||
]
|
||||
|
||||
@@ -148,25 +148,19 @@ class AdditionalTasksScreen(QWidget):
|
||||
|
||||
def _handle_button_click(self, action_id):
|
||||
"""Handle button clicks"""
|
||||
if action_id == "ttw_install":
|
||||
self._show_ttw_info()
|
||||
if action_id == "run_verifier":
|
||||
self._run_install_verifier()
|
||||
elif action_id == "wabbajack_install":
|
||||
self._show_wabbajack_installer()
|
||||
elif action_id == "setup_mo2":
|
||||
self._show_mo2_setup()
|
||||
elif action_id == "tool_config":
|
||||
self._show_tool_config()
|
||||
elif action_id == "coming_soon":
|
||||
self._show_coming_soon_info()
|
||||
elif action_id == "diagnostic_bundle":
|
||||
self._run_diagnostic_bundle()
|
||||
elif action_id == "return_main_menu":
|
||||
self._return_to_main_menu()
|
||||
|
||||
def _show_ttw_info(self):
|
||||
"""Navigate to TTW installation screen"""
|
||||
if self.stacked_widget:
|
||||
# Navigate to TTW installation screen (index 5)
|
||||
self.stacked_widget.setCurrentIndex(5)
|
||||
|
||||
def _show_wabbajack_installer(self):
|
||||
"""Navigate to Wabbajack installer screen"""
|
||||
if self.stacked_widget:
|
||||
@@ -178,20 +172,202 @@ class AdditionalTasksScreen(QWidget):
|
||||
if self.stacked_widget:
|
||||
self.stacked_widget.setCurrentIndex(self.install_mo2_screen_index)
|
||||
|
||||
def _show_coming_soon_info(self):
|
||||
"""Show coming soon info"""
|
||||
from ..services.message_service import MessageService
|
||||
MessageService.information(
|
||||
self,
|
||||
"Coming Soon",
|
||||
"Additional tools and features will be added in future updates.\n\n"
|
||||
"Check back later for more functionality!"
|
||||
)
|
||||
|
||||
def _show_tool_config(self):
|
||||
if self.stacked_widget:
|
||||
self.stacked_widget.setCurrentIndex(11)
|
||||
|
||||
def _run_install_verifier(self):
|
||||
"""Prompt user to pick a modlist, run the verifier, and show results."""
|
||||
from ..services.message_service import MessageService
|
||||
try:
|
||||
from jackify.backend.services.install_verifier_service import _load_verifier
|
||||
verifier_mod = _load_verifier()
|
||||
modlists = verifier_mod.discover_installed_modlists()
|
||||
except Exception as e:
|
||||
MessageService.critical(
|
||||
self,
|
||||
"Verifier Error",
|
||||
f"Could not load install verifier: {e}",
|
||||
)
|
||||
return
|
||||
|
||||
if not modlists:
|
||||
MessageService.information(
|
||||
self,
|
||||
"No Modlists Found",
|
||||
"No installed modlists were found in Steam shortcuts.\n\n"
|
||||
"Ensure ModOrganizer.exe shortcuts exist in Steam for your modlists.",
|
||||
)
|
||||
return
|
||||
|
||||
from PySide6.QtWidgets import QDialog, QVBoxLayout, QListWidget, QListWidgetItem, QPushButton, QLabel, QHBoxLayout
|
||||
picker = QDialog(self)
|
||||
picker.setWindowTitle("Select Modlist to Verify")
|
||||
picker.setMinimumWidth(480)
|
||||
picker.setMinimumHeight(260)
|
||||
picker_layout = QVBoxLayout(picker)
|
||||
picker_layout.addWidget(QLabel("Select a modlist to verify:"))
|
||||
|
||||
lw = QListWidget()
|
||||
for m in modlists:
|
||||
pfx_ok = m["pfx"] and m["pfx"].is_dir()
|
||||
suffix = "" if pfx_ok else " (prefix not found)"
|
||||
item = QListWidgetItem(f"{m['name']}{suffix}")
|
||||
item.setData(1000, m)
|
||||
lw.addItem(item)
|
||||
lw.setCurrentRow(0)
|
||||
picker_layout.addWidget(lw)
|
||||
|
||||
btn_row = QHBoxLayout()
|
||||
ok_btn = QPushButton("Run Verifier")
|
||||
cancel_btn = QPushButton("Cancel")
|
||||
btn_row.addStretch()
|
||||
btn_row.addWidget(ok_btn)
|
||||
btn_row.addWidget(cancel_btn)
|
||||
picker_layout.addLayout(btn_row)
|
||||
|
||||
ok_btn.clicked.connect(picker.accept)
|
||||
cancel_btn.clicked.connect(picker.reject)
|
||||
lw.itemDoubleClicked.connect(lambda _: picker.accept())
|
||||
|
||||
if picker.exec() != QDialog.Accepted:
|
||||
return
|
||||
|
||||
selected_item = lw.currentItem()
|
||||
if not selected_item:
|
||||
return
|
||||
selected = selected_item.data(1000)
|
||||
|
||||
pfx = selected.get("pfx")
|
||||
if not pfx or not pfx.is_dir():
|
||||
MessageService.warning(
|
||||
self,
|
||||
"Prefix Not Found",
|
||||
f"The Proton prefix for '{selected['name']}' was not found.\n\n"
|
||||
"Launch the modlist from Steam at least once to create the prefix.",
|
||||
)
|
||||
return
|
||||
|
||||
from PySide6.QtCore import QThread, Signal as _Signal
|
||||
|
||||
class _VerifierThread(QThread):
|
||||
done = _Signal(object)
|
||||
|
||||
def __init__(self, verifier_module, entry, parent=None):
|
||||
super().__init__(parent)
|
||||
self._verifier = verifier_module
|
||||
self._entry = entry
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
r = self._verifier.run_verification(
|
||||
pfx=self._entry["pfx"],
|
||||
modlist_dir=self._entry["modlist_dir"],
|
||||
game_type=self._entry["game_type"],
|
||||
appid=self._entry["appid"],
|
||||
modlist_name=self._entry.get("name", ""),
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning("On-demand verifier error: %s", exc)
|
||||
r = None
|
||||
self.done.emit(r)
|
||||
|
||||
from jackify.frontends.gui.services.message_service import MessageService as _MS
|
||||
progress_dlg = QDialog(self)
|
||||
progress_dlg.setWindowTitle("Verifying...")
|
||||
progress_dlg.setModal(True)
|
||||
prog_layout = QVBoxLayout(progress_dlg)
|
||||
prog_layout.addWidget(QLabel(f"Running verifier for '{selected['name']}'...\nThis may take a moment."))
|
||||
progress_dlg.setFixedSize(340, 100)
|
||||
progress_dlg.show()
|
||||
|
||||
self._verifier_ondemand_thread = _VerifierThread(verifier_mod, selected, parent=self)
|
||||
|
||||
def _on_done(results):
|
||||
progress_dlg.accept()
|
||||
self._verifier_ondemand_thread = None
|
||||
if results is None:
|
||||
MessageService.critical(
|
||||
self,
|
||||
"Verifier Error",
|
||||
"The verifier encountered an error and could not complete.",
|
||||
)
|
||||
return
|
||||
from jackify.frontends.gui.dialogs.verification_results_dialog import VerificationResultsDialog
|
||||
dlg = VerificationResultsDialog(results, parent=self)
|
||||
dlg.exec()
|
||||
|
||||
self._verifier_ondemand_thread.done.connect(_on_done)
|
||||
self._verifier_ondemand_thread.start()
|
||||
|
||||
def _run_diagnostic_bundle(self):
|
||||
"""Open the diagnostic bundle dialog; bundle is only created when the user confirms."""
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTextEdit
|
||||
)
|
||||
from PySide6.QtCore import QThread, Signal as _Signal
|
||||
|
||||
class _BundleThread(QThread):
|
||||
done = _Signal(object, str) # (bundle_path or None, error_msg)
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
from jackify.backend.services.diagnostic_service import build_bundle
|
||||
path = build_bundle()
|
||||
self.done.emit(path, "")
|
||||
except Exception as exc:
|
||||
self.done.emit(None, str(exc))
|
||||
|
||||
dlg = QDialog(self)
|
||||
dlg.setWindowTitle("Diagnostic Bundle")
|
||||
dlg.setMinimumWidth(600)
|
||||
dlg.setMinimumHeight(220)
|
||||
dlg.setStyleSheet("QDialog { background: #181818; color: #fff; }")
|
||||
|
||||
layout = QVBoxLayout(dlg)
|
||||
layout.setContentsMargins(20, 16, 20, 16)
|
||||
layout.setSpacing(10)
|
||||
|
||||
status_label = QLabel("Package logs and system info into a file for support reporting.")
|
||||
layout.addWidget(status_label)
|
||||
|
||||
path_box = QTextEdit()
|
||||
path_box.setReadOnly(True)
|
||||
path_box.setMinimumHeight(60)
|
||||
path_box.setVisible(False)
|
||||
layout.addWidget(path_box)
|
||||
|
||||
btn_row = QHBoxLayout()
|
||||
create_btn = QPushButton("Create Bundle")
|
||||
cancel_btn = QPushButton("Cancel")
|
||||
btn_row.addWidget(create_btn)
|
||||
btn_row.addWidget(cancel_btn)
|
||||
layout.addLayout(btn_row)
|
||||
|
||||
cancel_btn.clicked.connect(dlg.reject)
|
||||
|
||||
def _on_create():
|
||||
create_btn.setEnabled(False)
|
||||
cancel_btn.setEnabled(False)
|
||||
status_label.setText("Collecting logs and system info...")
|
||||
self._diag_thread = _BundleThread(parent=self)
|
||||
self._diag_thread.done.connect(_on_done)
|
||||
self._diag_thread.start()
|
||||
|
||||
def _on_done(bundle_path, error):
|
||||
self._diag_thread = None
|
||||
cancel_btn.setEnabled(True)
|
||||
cancel_btn.setText("Close")
|
||||
if not bundle_path:
|
||||
status_label.setText(f"Failed: {error}")
|
||||
return
|
||||
status_label.setText("Bundle created:")
|
||||
path_box.setPlainText(str(bundle_path))
|
||||
path_box.setVisible(True)
|
||||
|
||||
create_btn.clicked.connect(_on_create)
|
||||
dlg.exec()
|
||||
|
||||
def _return_to_main_menu(self):
|
||||
"""Return to main menu"""
|
||||
if self.stacked_widget:
|
||||
|
||||
Reference in New Issue
Block a user