Files
Jackify/jackify/frontends/gui/dialogs/protontricks_error_dialog.py
Omni cd591c14e3 Initial public release v0.1.0 - Linux Wabbajack Modlist Application
Jackify provides native Linux support for Wabbajack modlist installation
   and management with automated Steam integration and Proton configuration.

   Key Features:
   - Almost Native Linux implementation (texconv.exe run via proton)
   - Automated Steam shortcut creation and Proton prefix management
   - Both CLI and GUI interfaces, with Steam Deck optimization

   Supported Games:
   - Skyrim Special Edition
   - Fallout 4
   - Fallout New Vegas
   - Oblivion, Starfield, Enderal, and diverse other games

   Technical Architecture:
   - Clean separation between frontend and backend services
   - Powered by jackify-engine 0.3.x for Wabbajack-matching modlist installation
2025-09-05 20:46:24 +01:00

328 lines
11 KiB
Python

"""
Protontricks Error Dialog
Dialog shown when protontricks is not found, with options to install via Flatpak or get native installation guidance.
"""
from pathlib import Path
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame, QSizePolicy, QTextEdit, QProgressBar
)
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtGui import QPixmap, QIcon, QFont
from .. import shared_theme
class FlatpakInstallThread(QThread):
"""Thread for installing Flatpak protontricks"""
finished = Signal(bool, str) # success, message
def __init__(self, detection_service):
super().__init__()
self.detection_service = detection_service
def run(self):
success, message = self.detection_service.install_flatpak_protontricks()
self.finished.emit(success, message)
class ProtontricksErrorDialog(QDialog):
"""
Dialog shown when protontricks is not found
Provides options to install via Flatpak or get native installation guidance
"""
def __init__(self, detection_service, parent=None):
super().__init__(parent)
self.detection_service = detection_service
self.setWindowTitle("Protontricks Required")
self.setModal(True)
self.setFixedSize(550, 520)
self.install_thread = None
self._setup_ui()
def _setup_ui(self):
layout = QVBoxLayout(self)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
# Card background
card = QFrame(self)
card.setObjectName("protontricksCard")
card.setFrameShape(QFrame.StyledPanel)
card.setFrameShadow(QFrame.Raised)
card.setMinimumWidth(500)
card.setMinimumHeight(400)
card.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
card_layout = QVBoxLayout(card)
card_layout.setSpacing(16)
card_layout.setContentsMargins(28, 28, 28, 28)
card.setStyleSheet(
"QFrame#protontricksCard { "
" background: #2d2323; "
" border-radius: 12px; "
" border: 2px solid #e74c3c; "
"}"
)
# Error icon
icon_label = QLabel()
icon_label.setAlignment(Qt.AlignCenter)
icon_label.setText("!")
icon_label.setStyleSheet(
"QLabel { "
" font-size: 36px; "
" font-weight: bold; "
" color: #e74c3c; "
" margin-bottom: 4px; "
"}"
)
card_layout.addWidget(icon_label)
# Error title
title_label = QLabel("Protontricks Not Found")
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet(
"QLabel { "
" font-size: 20px; "
" font-weight: 600; "
" color: #e74c3c; "
" margin-bottom: 2px; "
"}"
)
card_layout.addWidget(title_label)
# Error message
message_text = QTextEdit()
message_text.setReadOnly(True)
message_text.setPlainText(
"Protontricks is required for Jackify to function properly. "
"It manages Wine prefixes for Steam games and is essential for modlist installation and configuration.\n\n"
"Choose an installation method below:"
)
message_text.setMinimumHeight(100)
message_text.setMaximumHeight(120)
message_text.setStyleSheet(
"QTextEdit { "
" font-size: 15px; "
" color: #e0e0e0; "
" background: transparent; "
" border: none; "
" line-height: 1.3; "
" margin-bottom: 6px; "
"}"
)
card_layout.addWidget(message_text)
# Progress bar (initially hidden)
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
self.progress_bar.setStyleSheet(
"QProgressBar { "
" border: 1px solid #555; "
" border-radius: 4px; "
" background: #23272e; "
" text-align: center; "
"} "
"QProgressBar::chunk { "
" background-color: #4fc3f7; "
" border-radius: 3px; "
"}"
)
card_layout.addWidget(self.progress_bar)
# Status label (initially hidden)
self.status_label = QLabel()
self.status_label.setVisible(False)
self.status_label.setAlignment(Qt.AlignCenter)
self.status_label.setStyleSheet(
"QLabel { "
" font-size: 14px; "
" color: #4fc3f7; "
" margin: 8px 0; "
"}"
)
card_layout.addWidget(self.status_label)
# Button layout
button_layout = QVBoxLayout()
button_layout.setSpacing(12)
# Flatpak install button
self.flatpak_btn = QPushButton("Install via Flatpak (Recommended)")
self.flatpak_btn.setFixedHeight(40)
self.flatpak_btn.clicked.connect(self._install_flatpak)
self.flatpak_btn.setStyleSheet(
"QPushButton { "
" background-color: #4fc3f7; "
" color: white; "
" border: none; "
" border-radius: 6px; "
" font-weight: bold; "
" font-size: 14px; "
" padding: 8px 16px; "
"} "
"QPushButton:hover { "
" background-color: #3498db; "
"} "
"QPushButton:pressed { "
" background-color: #2980b9; "
"} "
"QPushButton:disabled { "
" background-color: #555; "
" color: #888; "
"}"
)
button_layout.addWidget(self.flatpak_btn)
# Native install guidance button
self.native_btn = QPushButton("Show Native Installation Instructions")
self.native_btn.setFixedHeight(40)
self.native_btn.clicked.connect(self._show_native_guidance)
self.native_btn.setStyleSheet(
"QPushButton { "
" background-color: #95a5a6; "
" color: white; "
" border: none; "
" border-radius: 6px; "
" font-weight: bold; "
" font-size: 14px; "
" padding: 8px 16px; "
"} "
"QPushButton:hover { "
" background-color: #7f8c8d; "
"} "
"QPushButton:pressed { "
" background-color: #6c7b7d; "
"}"
)
button_layout.addWidget(self.native_btn)
card_layout.addLayout(button_layout)
# Bottom button layout
bottom_layout = QHBoxLayout()
bottom_layout.setSpacing(12)
# Re-detect button
self.redetect_btn = QPushButton("Re-detect")
self.redetect_btn.setFixedSize(120, 36)
self.redetect_btn.clicked.connect(self._redetect)
self.redetect_btn.setStyleSheet(
"QPushButton { "
" background-color: #27ae60; "
" color: white; "
" border: none; "
" border-radius: 4px; "
" font-weight: bold; "
" padding: 8px 16px; "
"} "
"QPushButton:hover { "
" background-color: #229954; "
"} "
"QPushButton:pressed { "
" background-color: #1e8449; "
"}"
)
bottom_layout.addWidget(self.redetect_btn)
bottom_layout.addStretch()
# Exit button
exit_btn = QPushButton("Exit Jackify")
exit_btn.setFixedSize(120, 36)
exit_btn.clicked.connect(self._exit_app)
exit_btn.setStyleSheet(
"QPushButton { "
" background-color: #e74c3c; "
" color: white; "
" border: none; "
" border-radius: 4px; "
" font-weight: bold; "
" padding: 8px 16px; "
"} "
"QPushButton:hover { "
" background-color: #c0392b; "
"} "
"QPushButton:pressed { "
" background-color: #a93226; "
"}"
)
bottom_layout.addWidget(exit_btn)
card_layout.addLayout(bottom_layout)
layout.addStretch()
layout.addWidget(card, alignment=Qt.AlignCenter)
layout.addStretch()
def _install_flatpak(self):
"""Install protontricks via Flatpak"""
# Disable buttons during installation
self.flatpak_btn.setEnabled(False)
self.native_btn.setEnabled(False)
self.redetect_btn.setEnabled(False)
# Show progress
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0) # Indeterminate progress
self.status_label.setVisible(True)
self.status_label.setText("Installing Flatpak protontricks...")
# Start installation thread
self.install_thread = FlatpakInstallThread(self.detection_service)
self.install_thread.finished.connect(self._on_install_finished)
self.install_thread.start()
def _on_install_finished(self, success, message):
"""Handle installation completion"""
# Hide progress
self.progress_bar.setVisible(False)
# Re-enable buttons
self.flatpak_btn.setEnabled(True)
self.native_btn.setEnabled(True)
self.redetect_btn.setEnabled(True)
if success:
self.status_label.setText("✓ Installation successful!")
self.status_label.setStyleSheet("QLabel { color: #27ae60; font-size: 14px; margin: 8px 0; }")
# Auto-redetect after successful installation
self._redetect()
else:
self.status_label.setText(f"✗ Installation failed: {message}")
self.status_label.setStyleSheet("QLabel { color: #e74c3c; font-size: 14px; margin: 8px 0; }")
def _show_native_guidance(self):
"""Show native installation guidance"""
from ..services.message_service import MessageService
guidance = self.detection_service.get_installation_guidance()
MessageService.information(self, "Native Installation", guidance, safety_level="low")
def _redetect(self):
"""Re-detect protontricks"""
self.detection_service.clear_cache()
is_installed, installation_type, details = self.detection_service.detect_protontricks(use_cache=False)
if is_installed:
self.status_label.setText("✓ Protontricks found!")
self.status_label.setStyleSheet("QLabel { color: #27ae60; font-size: 14px; margin: 8px 0; }")
self.status_label.setVisible(True)
self.accept() # Close dialog successfully
else:
self.status_label.setText("✗ Protontricks still not found")
self.status_label.setStyleSheet("QLabel { color: #e74c3c; font-size: 14px; margin: 8px 0; }")
self.status_label.setVisible(True)
def _exit_app(self):
"""Exit the application"""
self.reject()
import sys
sys.exit(1)
def closeEvent(self, event):
"""Handle dialog close event"""
if self.install_thread and self.install_thread.isRunning():
self.install_thread.terminate()
self.install_thread.wait()
event.accept()