"""
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()