image_preview_dialog.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. """
  2. Image Preview Dialog
  3. Dialog for displaying enlarged view of images/spectrograms with zoom functionality.
  4. """
  5. from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton, QHBoxLayout, QScrollArea
  6. from PyQt5.QtCore import Qt
  7. from PyQt5.QtGui import QPixmap, QFont
  8. class ImagePreviewDialog(QDialog):
  9. """
  10. Dialog for displaying enlarged image previews with zoom functionality.
  11. Features:
  12. - Enlarged view of spectrograms/images
  13. - Zoom in/out with buttons or keyboard (+/- or scroll wheel)
  14. - Click anywhere or press ESC to close
  15. - Clean, professional appearance
  16. """
  17. def __init__(self, pixmap: QPixmap, title: str = "Preview", parent=None):
  18. """
  19. Initialize the preview dialog.
  20. Args:
  21. pixmap: QPixmap to display
  22. title: Dialog title
  23. parent: Parent widget
  24. """
  25. super().__init__(parent)
  26. self.original_pixmap = pixmap
  27. self.setWindowTitle(title)
  28. self.setModal(True)
  29. self.zoom_level = 1.0
  30. self.min_zoom = 0.25
  31. self.max_zoom = 4.0
  32. self.init_ui()
  33. def init_ui(self):
  34. """Initialize the dialog UI."""
  35. layout = QVBoxLayout(self)
  36. layout.setContentsMargins(0, 0, 0, 0)
  37. layout.setSpacing(0)
  38. # Header
  39. header = QLabel(self.windowTitle())
  40. header.setAlignment(Qt.AlignCenter)
  41. header.setStyleSheet("""
  42. QLabel {
  43. background-color: #2c3e50;
  44. color: white;
  45. font-weight: bold;
  46. font-size: 12px;
  47. padding: 10px;
  48. }
  49. """)
  50. layout.addWidget(header)
  51. # Scroll area for image with zoom
  52. scroll_area = QScrollArea()
  53. scroll_area.setStyleSheet("""
  54. QScrollArea {
  55. background-color: #2c3e50;
  56. border: none;
  57. }
  58. """)
  59. scroll_area.setWidgetResizable(True)
  60. # Image display label
  61. self.image_label = QLabel()
  62. self.image_label.setAlignment(Qt.AlignCenter)
  63. self.image_label.setStyleSheet("""
  64. QLabel {
  65. background-color: #2c3e50;
  66. padding: 20px;
  67. }
  68. """)
  69. # Initial scaling to fit dialog (max 1200x800)
  70. initial_pixmap = self.original_pixmap.scaled(
  71. 1200, 800, Qt.KeepAspectRatio, Qt.SmoothTransformation
  72. )
  73. self.image_label.setPixmap(initial_pixmap)
  74. self.zoom_level = 1.0
  75. self.current_display_pixmap = initial_pixmap
  76. scroll_area.setWidget(self.image_label)
  77. layout.addWidget(scroll_area)
  78. # Control buttons and zoom info
  79. controls_layout = QHBoxLayout()
  80. controls_layout.setContentsMargins(10, 10, 10, 10)
  81. controls_layout.setSpacing(10)
  82. # Zoom out button
  83. zoom_out_button = QPushButton("🔍− Zoom Out")
  84. zoom_out_button.setFont(QFont("Arial", 10))
  85. zoom_out_button.setFixedHeight(35)
  86. zoom_out_button.setStyleSheet("""
  87. QPushButton {
  88. background-color: #34495e;
  89. color: white;
  90. border: none;
  91. font-weight: bold;
  92. border-radius: 4px;
  93. }
  94. QPushButton:hover {
  95. background-color: #2c3e50;
  96. }
  97. QPushButton:pressed {
  98. background-color: #1a252f;
  99. }
  100. """)
  101. zoom_out_button.clicked.connect(self.zoom_out)
  102. controls_layout.addWidget(zoom_out_button)
  103. # Zoom level label
  104. self.zoom_label = QLabel("100%")
  105. self.zoom_label.setAlignment(Qt.AlignCenter)
  106. self.zoom_label.setFont(QFont("Arial", 10, QFont.Bold))
  107. self.zoom_label.setStyleSheet("color: white; min-width: 60px;")
  108. controls_layout.addWidget(self.zoom_label)
  109. # Zoom in button
  110. zoom_in_button = QPushButton("Zoom In 🔍+")
  111. zoom_in_button.setFont(QFont("Arial", 10))
  112. zoom_in_button.setFixedHeight(35)
  113. zoom_in_button.setStyleSheet("""
  114. QPushButton {
  115. background-color: #34495e;
  116. color: white;
  117. border: none;
  118. font-weight: bold;
  119. border-radius: 4px;
  120. }
  121. QPushButton:hover {
  122. background-color: #2c3e50;
  123. }
  124. QPushButton:pressed {
  125. background-color: #1a252f;
  126. }
  127. """)
  128. zoom_in_button.clicked.connect(self.zoom_in)
  129. controls_layout.addWidget(zoom_in_button)
  130. # Reset zoom button
  131. reset_button = QPushButton("Reset (R)")
  132. reset_button.setFont(QFont("Arial", 10))
  133. reset_button.setFixedHeight(35)
  134. reset_button.setStyleSheet("""
  135. QPushButton {
  136. background-color: #7f8c8d;
  137. color: white;
  138. border: none;
  139. font-weight: bold;
  140. border-radius: 4px;
  141. }
  142. QPushButton:hover {
  143. background-color: #6c7a7d;
  144. }
  145. QPushButton:pressed {
  146. background-color: #5a6667;
  147. }
  148. """)
  149. reset_button.clicked.connect(self.reset_zoom)
  150. controls_layout.addWidget(reset_button)
  151. # Close button
  152. close_button = QPushButton("Close (ESC)")
  153. close_button.setFont(QFont("Arial", 10))
  154. close_button.setFixedHeight(35)
  155. close_button.setStyleSheet("""
  156. QPushButton {
  157. background-color: #e74c3c;
  158. color: white;
  159. border: none;
  160. font-weight: bold;
  161. border-radius: 4px;
  162. }
  163. QPushButton:hover {
  164. background-color: #c0392b;
  165. }
  166. QPushButton:pressed {
  167. background-color: #a93226;
  168. }
  169. """)
  170. close_button.clicked.connect(self.accept)
  171. controls_layout.addWidget(close_button)
  172. layout.addLayout(controls_layout)
  173. # Set dialog size
  174. self.resize(1280, 900)
  175. def zoom_in(self):
  176. """Increase zoom level."""
  177. if self.zoom_level < self.max_zoom:
  178. self.zoom_level *= 1.25
  179. if self.zoom_level > self.max_zoom:
  180. self.zoom_level = self.max_zoom
  181. self._update_display()
  182. def zoom_out(self):
  183. """Decrease zoom level."""
  184. if self.zoom_level > self.min_zoom:
  185. self.zoom_level /= 1.25
  186. if self.zoom_level < self.min_zoom:
  187. self.zoom_level = self.min_zoom
  188. self._update_display()
  189. def reset_zoom(self):
  190. """Reset to initial zoom level."""
  191. self.zoom_level = 1.0
  192. # Re-scale to fit dialog
  193. scaled_pixmap = self.original_pixmap.scaled(
  194. 1200, 800, Qt.KeepAspectRatio, Qt.SmoothTransformation
  195. )
  196. self.image_label.setPixmap(scaled_pixmap)
  197. self.current_display_pixmap = scaled_pixmap
  198. self._update_zoom_label()
  199. def _update_display(self):
  200. """Update the displayed image based on current zoom level."""
  201. # Calculate new size based on original pixmap and zoom level
  202. new_width = int(self.original_pixmap.width() * self.zoom_level)
  203. new_height = int(self.original_pixmap.height() * self.zoom_level)
  204. # Scale the image
  205. scaled_pixmap = self.original_pixmap.scaled(
  206. new_width, new_height, Qt.KeepAspectRatio, Qt.SmoothTransformation
  207. )
  208. self.image_label.setPixmap(scaled_pixmap)
  209. self.current_display_pixmap = scaled_pixmap
  210. self._update_zoom_label()
  211. def _update_zoom_label(self):
  212. """Update zoom percentage label."""
  213. zoom_percentage = int(self.zoom_level * 100)
  214. self.zoom_label.setText(f"{zoom_percentage}%")
  215. def mousePressEvent(self, event):
  216. """Close dialog on mouse click outside the scroll area."""
  217. # Only close if clicking on dialog background, not on scroll area
  218. if not self.image_label.geometry().contains(self.mapFromGlobal(event.globalPos())):
  219. self.accept()
  220. def wheelEvent(self, event):
  221. """Handle mouse wheel for zooming."""
  222. if event.angleDelta().y() > 0:
  223. self.zoom_in()
  224. else:
  225. self.zoom_out()
  226. def keyPressEvent(self, event):
  227. """Handle keyboard shortcuts for zooming."""
  228. if event.key() == Qt.Key_Escape:
  229. self.accept()
  230. elif event.key() == Qt.Key_Plus or event.key() == Qt.Key_Equal:
  231. self.zoom_in()
  232. elif event.key() == Qt.Key_Minus:
  233. self.zoom_out()
  234. elif event.key() == Qt.Key_R:
  235. self.reset_zoom()
  236. else:
  237. super().keyPressEvent(event)