""" EU-Utility - Plugins View (Core Framework Component) Built-in plugins interface with Installed and Store tabs. """ from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QCheckBox, QTabWidget, QFrame, QScrollArea, QGridLayout, QSizePolicy ) from PyQt6.QtCore import Qt from core.icon_manager import get_icon_manager class PluginsView(QWidget): """Main plugins interface - built into the framework. Features: - Installed tab: Manage installed plugins (enable/disable) - Store tab: Browse and install new plugins """ def __init__(self, overlay_window, parent=None): super().__init__(parent) self.overlay = overlay_window self.plugin_manager = overlay_window.plugin_manager if hasattr(overlay_window, 'plugin_manager') else None self.icon_manager = get_icon_manager() self._setup_ui() def _setup_ui(self): """Create the plugins UI.""" layout = QVBoxLayout(self) layout.setSpacing(16) layout.setContentsMargins(24, 24, 24, 24) # Header with icon header_layout = QHBoxLayout() header_layout.setSpacing(12) header_icon = QLabel() header_pixmap = self.icon_manager.get_pixmap("plugins", size=28) header_icon.setPixmap(header_pixmap) header_layout.addWidget(header_icon) header = QLabel("Plugins") header.setStyleSheet("font-size: 24px; font-weight: bold; color: white;") header_layout.addWidget(header) header_layout.addStretch() layout.addLayout(header_layout) # Tabs - Installed and Store self.tabs = QTabWidget() self.tabs.setStyleSheet(""" QTabBar::tab { background-color: rgba(20, 31, 35, 0.95); color: rgba(255,255,255,150); padding: 10px 20px; border-top-left-radius: 6px; border-top-right-radius: 6px; } QTabBar::tab:selected { background-color: #ff8c42; color: white; font-weight: bold; } """) # Add tabs self.tabs.addTab(self._create_installed_tab(), "Installed") self.tabs.addTab(self._create_store_tab(), "Store") layout.addWidget(self.tabs) def _create_installed_tab(self): """Create installed plugins tab.""" tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(16) # Info label info = QLabel("Manage your installed plugins. Enable or disable as needed.") info.setStyleSheet("color: rgba(255,255,255,150); font-size: 13px;") info.setWordWrap(True) layout.addWidget(info) # Scroll area for plugin list scroll = QScrollArea() scroll.setWidgetResizable(True) scroll.setStyleSheet("background: transparent; border: none;") scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.installed_container = QWidget() self.installed_layout = QVBoxLayout(self.installed_container) self.installed_layout.setAlignment(Qt.AlignmentFlag.AlignTop) self.installed_layout.setSpacing(8) self._populate_installed_plugins() scroll.setWidget(self.installed_container) layout.addWidget(scroll) return tab def _populate_installed_plugins(self): """Populate the installed plugins list.""" # Clear existing while self.installed_layout.count(): item = self.installed_layout.takeAt(0) if item.widget(): item.widget().deleteLater() if not self.plugin_manager: error = QLabel("Plugin Manager not available") error.setStyleSheet("color: #ff4757;") self.installed_layout.addWidget(error) return # Get discovered plugins try: all_plugins = self.plugin_manager.get_all_discovered_plugins() except AttributeError: # Fallback - try different method try: all_plugins = {p.plugin_id: p for p in self.plugin_manager.plugins.values()} except: all_plugins = {} if not all_plugins: no_plugins = QLabel("No plugins installed.\n\nVisit the Store tab to install plugins.") no_plugins.setStyleSheet("color: rgba(255,255,255,100); padding: 40px;") no_plugins.setAlignment(Qt.AlignmentFlag.AlignCenter) self.installed_layout.addWidget(no_plugins) return # Sort by name sorted_plugins = sorted(all_plugins.items(), key=lambda x: x[1].name if hasattr(x[1], 'name') else str(x[0])) for plugin_id, plugin_class in sorted_plugins: row = QHBoxLayout() row.setSpacing(12) # Enable/Disable checkbox cb = QCheckBox() try: is_enabled = self.plugin_manager.is_plugin_enabled(plugin_id) except: is_enabled = True cb.setChecked(is_enabled) cb.stateChanged.connect(lambda state, pid=plugin_id: self._toggle_plugin(pid, state)) row.addWidget(cb) # Plugin info info_layout = QVBoxLayout() info_layout.setSpacing(2) name_layout = QHBoxLayout() # Name name = QLabel(getattr(plugin_class, 'name', str(plugin_id))) name.setStyleSheet("color: white; font-size: 14px; font-weight: 500;") name_layout.addWidget(name) # Version version = getattr(plugin_class, 'version', '?.?.?') version_label = QLabel(f"v{version}") version_label.setStyleSheet("color: rgba(255,255,255,100); font-size: 11px;") name_layout.addWidget(version_label) name_layout.addStretch() info_layout.addLayout(name_layout) # Description desc = getattr(plugin_class, 'description', 'No description available') desc_label = QLabel(desc) desc_label.setStyleSheet("color: rgba(255,255,255,120); font-size: 12px;") desc_label.setWordWrap(True) info_layout.addWidget(desc_label) # Add Open/Configure buttons btn_layout = QHBoxLayout() # Open button open_btn = QPushButton("Open") open_btn.setStyleSheet(""" QPushButton { background: rgba(255, 140, 66, 0.2); color: #ff8c42; border: 1px solid rgba(255, 140, 66, 0.3); border-radius: 4px; padding: 4px 12px; font-size: 11px; } QPushButton:hover { background: rgba(255, 140, 66, 0.3); } """) open_btn.clicked.connect(lambda checked, pid=plugin_id: self._open_plugin(pid)) btn_layout.addWidget(open_btn) # Configure button (if plugin has settings) if hasattr(plugin_class, 'get_settings_widget') or hasattr(plugin_class, 'settings'): config_btn = QPushButton("Settings") config_btn.setStyleSheet(""" QPushButton { background: rgba(100, 100, 100, 0.2); color: rgba(255, 255, 255, 0.7); border: 1px solid rgba(100, 100, 100, 0.3); border-radius: 4px; padding: 4px 12px; font-size: 11px; } QPushButton:hover { background: rgba(100, 100, 100, 0.3); } """) config_btn.clicked.connect(lambda checked, pid=plugin_id: self._configure_plugin(pid)) btn_layout.addWidget(config_btn) row.addLayout(btn_layout) self.installed_layout.addLayout(row) # Separator sep = QFrame() sep.setFrameShape(QFrame.Shape.HLine) sep.setStyleSheet("background-color: rgba(255, 140, 66, 0.1);") sep.setFixedHeight(1) self.installed_layout.addWidget(sep) self.installed_layout.addStretch() def _open_plugin(self, plugin_id: str): """Open a plugin's main interface.""" print(f"[PluginsView] Opening plugin: {plugin_id}") if not self.plugin_manager: return try: # Get the plugin instance plugin = self.plugin_manager.get_plugin(plugin_id) if not plugin: print(f"[PluginsView] Plugin not found: {plugin_id}") return # Check if plugin has a show_ui or open method if hasattr(plugin, 'show_ui'): plugin.show_ui() elif hasattr(plugin, 'open'): plugin.open() elif hasattr(plugin, 'get_widget'): widget = plugin.get_widget() if widget: widget.show() widget.raise_() widget.activateWindow() else: # Try to create and show the plugin's main window print(f"[PluginsView] Plugin {plugin_id} has no standard UI method") except Exception as e: print(f"[PluginsView] Error opening plugin {plugin_id}: {e}") import traceback traceback.print_exc() def _configure_plugin(self, plugin_id: str): """Open a plugin's settings/configuration.""" print(f"[PluginsView] Configuring plugin: {plugin_id}") if not self.plugin_manager: return try: plugin = self.plugin_manager.get_plugin(plugin_id) if not plugin: return # Check for settings widget if hasattr(plugin, 'get_settings_widget'): settings_widget = plugin.get_settings_widget() if settings_widget: settings_widget.show() settings_widget.raise_() settings_widget.activateWindow() elif hasattr(plugin, 'settings'): # Open generic settings dialog self._show_generic_settings(plugin) except Exception as e: print(f"[PluginsView] Error configuring plugin {plugin_id}: {e}") def _show_generic_settings(self, plugin): """Show a generic settings dialog for a plugin.""" from PyQt6.QtWidgets import QDialog, QVBoxLayout, QLabel, QDialogButtonBox dialog = QDialog(self) dialog.setWindowTitle(f"{plugin.name} - Settings") dialog.setMinimumSize(400, 300) layout = QVBoxLayout(dialog) # Plugin info name_label = QLabel(f"{plugin.name}") name_label.setStyleSheet("font-size: 16px;") layout.addWidget(name_label) desc_label = QLabel(plugin.description) desc_label.setWordWrap(True) layout.addWidget(desc_label) version_label = QLabel(f"Version: {plugin.version}") layout.addWidget(version_label) layout.addStretch() # Buttons buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok) buttons.accepted.connect(dialog.accept) layout.addWidget(buttons) dialog.exec() def _toggle_plugin(self, plugin_id: str, state: int): """Enable or disable a plugin.""" if not self.plugin_manager: return try: if state == Qt.CheckState.Checked.value: self.plugin_manager.enable_plugin(plugin_id) print(f"[PluginsView] Enabled plugin: {plugin_id}") else: self.plugin_manager.disable_plugin(plugin_id) print(f"[PluginsView] Disabled plugin: {plugin_id}") except Exception as e: print(f"[PluginsView] Error toggling plugin {plugin_id}: {e}") def _create_store_tab(self): """Create plugin store tab.""" try: from core.plugin_store import PluginStoreUI if self.plugin_manager: return PluginStoreUI(self.plugin_manager) except ImportError: pass # Fallback tab = QWidget() layout = QVBoxLayout(tab) info = QLabel("Plugin Store") info.setStyleSheet("font-size: 18px; font-weight: bold; color: white;") layout.addWidget(info) desc = QLabel("Browse and install plugins from the community repository.") desc.setStyleSheet("color: rgba(255,255,255,150);") desc.setWordWrap(True) layout.addWidget(desc) # Placeholder placeholder = QLabel("Plugin Store interface will appear here.") placeholder.setStyleSheet("color: rgba(255,255,255,100); padding: 40px;") placeholder.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(placeholder) layout.addStretch() return tab