""" Visualization Widgets Reusable components for displaying analysis visualizations with click-to-enlarge functionality. """ from typing import Optional, Callable from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QFrame from PyQt5.QtCore import Qt from PyQt5.QtGui import QPixmap, QFont, QCursor from ui.dialogs.image_preview_dialog import ImagePreviewDialog class ClickableImageWidget(QFrame): """ A frame widget displaying an image that opens a preview dialog when clicked. Features: - Displays scaled image with aspect ratio preserved - Click to open full-size preview dialog - Visual indicator (cursor change, optional hover effect) - Configurable maximum dimensions """ def __init__(self, pixmap: QPixmap, title: str, max_width: int = 500, parent: QWidget = None): """ Initialize the clickable image widget. Args: pixmap: QPixmap to display title: Title for the preview dialog max_width: Maximum width in pixels for the scaled display image parent: Parent widget """ super().__init__(parent) self.original_pixmap = pixmap # Store original for preview dialog self.title = title self.max_width = max_width # Setup widget styling self.setFrameStyle(QFrame.Box) self.setStyleSheet("background-color: white; border: 1px solid #bdc3c7;") # Create layout layout = QVBoxLayout(self) layout.setAlignment(Qt.AlignCenter) # Title label title_label = QLabel(f"{title}") title_label.setFont(QFont("Arial", 12)) layout.addWidget(title_label) # Image label self.image_label = QLabel() self.image_label.setAlignment(Qt.AlignCenter) self.image_label.setCursor(Qt.PointingHandCursor) # Scale pixmap for display scaled_pixmap = self._scale_pixmap(pixmap, max_width) self.image_label.setPixmap(scaled_pixmap) self.image_label.setFixedSize(scaled_pixmap.width(), scaled_pixmap.height()) # Connect click event self.image_label.mousePressEvent = self._on_image_clicked layout.addWidget(self.image_label, alignment=Qt.AlignCenter) layout.addStretch() def _scale_pixmap(self, pixmap: QPixmap, max_width: int) -> QPixmap: """Scale pixmap to fit max_width while preserving aspect ratio.""" if pixmap.width() > max_width: return pixmap.scaledToWidth(max_width, Qt.SmoothTransformation) return pixmap def _on_image_clicked(self, event): """Handle image click to show preview dialog.""" dialog = ImagePreviewDialog(self.original_pixmap, title=self.title, parent=self) dialog.exec_() class VisualizationPanel(QFrame): """ A panel for displaying a single analysis visualization with metadata. Features: - Title and description - Clickable image - Optional metadata display """ def __init__(self, title: str, description: str = "", parent: QWidget = None): """ Initialize the visualization panel. Args: title: Panel title description: Optional description text parent: Parent widget """ super().__init__(parent) self.setFrameStyle(QFrame.Box) self.setStyleSheet("background-color: white; border: 1px solid #bdc3c7;") layout = QVBoxLayout(self) layout.setAlignment(Qt.AlignCenter) # Title title_label = QLabel(f"{title}") title_label.setFont(QFont("Arial", 12)) layout.addWidget(title_label) # Description (if provided) if description: desc_label = QLabel(description) desc_label.setStyleSheet("color: #7f8c8d; font-size: 10px;") layout.addWidget(desc_label) # Store reference to layout for adding image label later self.layout_ref = layout def add_image(self, pixmap: QPixmap, max_width: int = 500): """Add an image to the visualization panel.""" # Scale image if pixmap.width() > max_width: scaled_pixmap = pixmap.scaledToWidth(max_width, Qt.SmoothTransformation) else: scaled_pixmap = pixmap # Create clickable image label image_label = QLabel() image_label.setPixmap(scaled_pixmap) image_label.setFixedSize(scaled_pixmap.width(), scaled_pixmap.height()) image_label.setAlignment(Qt.AlignCenter) image_label.setCursor(Qt.PointingHandCursor) # Store pixmaps for click handler image_label.original_pixmap = pixmap image_label.title = self.layout_ref.itemAt(0).widget().text() if self.layout_ref.count() > 0 else "Image" # Add click handler def on_click(event): dialog = ImagePreviewDialog(image_label.original_pixmap, title=image_label.title, parent=self) dialog.exec_() image_label.mousePressEvent = on_click # Add to layout self.layout_ref.addWidget(image_label, alignment=Qt.AlignCenter) self.layout_ref.addStretch()