audio_spectrogram_panel.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. """
  2. Audio Spectrogram Panel
  3. Panel for displaying audio spectrogram visualization.
  4. """
  5. from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QFrame, QSizePolicy
  6. from PyQt5.QtCore import Qt, pyqtSignal
  7. from PyQt5.QtGui import QFont, QPixmap, QCursor
  8. from ui.widgets.panel_header import PanelHeader
  9. class ClickableLabel(QLabel):
  10. """Label that emits a signal when clicked."""
  11. clicked = pyqtSignal()
  12. def __init__(self, parent=None):
  13. super().__init__(parent)
  14. self.setCursor(QCursor(Qt.PointingHandCursor))
  15. def mousePressEvent(self, event):
  16. """Emit clicked signal on mouse press."""
  17. self.clicked.emit()
  18. super().mousePressEvent(event)
  19. class AudioSpectrogramPanel(QWidget):
  20. """
  21. Panel for audio spectrogram display and settings.
  22. Signals:
  23. spectrogram_clicked: Emitted when spectrogram is clicked for enlarged view
  24. """
  25. spectrogram_clicked = pyqtSignal()
  26. def __init__(self, parent=None):
  27. super().__init__(parent)
  28. self.current_pixmap = None
  29. self.current_audio_path = None
  30. self.init_ui()
  31. def init_ui(self):
  32. """Initialize the panel UI."""
  33. # Set size policy to expand equally
  34. self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
  35. layout = QVBoxLayout(self)
  36. layout.setContentsMargins(0, 0, 0, 0)
  37. layout.setSpacing(0)
  38. # Main panel container with card styling
  39. self.setStyleSheet("""
  40. QWidget {
  41. background-color: white;
  42. border: 1px solid #ddd;
  43. }
  44. """)
  45. # Header
  46. header = QWidget()
  47. header.setFixedHeight(25)
  48. header.setStyleSheet("background-color: #16a085;")
  49. header_layout = QHBoxLayout(header)
  50. header_layout.setContentsMargins(10, 0, 10, 0)
  51. header_layout.setSpacing(0)
  52. title = QLabel("Audio Spectrogram")
  53. title.setStyleSheet("color: white; font-weight: bold; font-size: 16px;")
  54. status_indicator = QWidget()
  55. status_indicator.setFixedSize(10, 10)
  56. status_indicator.setStyleSheet("background-color: #27ae60; border-radius: 5px;")
  57. header_layout.addWidget(title)
  58. header_layout.addStretch()
  59. header_layout.addWidget(status_indicator)
  60. # Visualization area
  61. visualization = QWidget()
  62. visualization.setMinimumSize(250, 250)
  63. # visualization.setMaximumSize(500, 500)
  64. visualization.setStyleSheet("""
  65. background-color: #2c3e50;
  66. border: 1px solid #34495e;
  67. border-top: none;
  68. """)
  69. visualization_layout = QVBoxLayout(visualization)
  70. visualization_layout.setContentsMargins(0, 0, 0, 0)
  71. visualization_layout.setSpacing(0)
  72. # Frequency range label
  73. range_label = QLabel("0-8kHz Range")
  74. range_label.setStyleSheet("color: #bdc3c7; font-size: 12px;")
  75. range_label.setAlignment(Qt.AlignCenter)
  76. # range_label.setFixedHeight(15)
  77. visualization_layout.addWidget(range_label)
  78. # Spectrogram display (clickable)
  79. self.spectrogram_label = ClickableLabel()
  80. self.spectrogram_label.setAlignment(Qt.AlignCenter)
  81. self.spectrogram_label.setMinimumHeight(200)
  82. self.spectrogram_label.setScaledContents(False)
  83. self.spectrogram_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
  84. self.spectrogram_label.setStyleSheet("""
  85. QLabel {
  86. background-color: #2c3e50;
  87. border: none;
  88. color: #bdc3c7;
  89. }
  90. QLabel:hover {
  91. background-color: #34495e;
  92. }
  93. """)
  94. self.spectrogram_label.setText("No audio loaded\n\nClick to view details")
  95. self.spectrogram_label.setToolTip("Click to enlarge and view audio details")
  96. self.spectrogram_label.clicked.connect(self._on_spectrogram_clicked)
  97. visualization_layout.addWidget(self.spectrogram_label, 1)
  98. layout.addWidget(header)
  99. layout.addWidget(visualization, 1)
  100. def update_spectrogram(self, pixmap: QPixmap, sample_rate: float = 44100,
  101. duration: float = 0, audio_path: str = None):
  102. """
  103. Update the spectrogram display.
  104. Args:
  105. pixmap: Spectrogram image as QPixmap
  106. sample_rate: Audio sample rate in Hz
  107. duration: Audio duration in seconds
  108. audio_path: Path to the audio file for playback
  109. """
  110. # Store original pixmap and audio path for enlargement
  111. self.current_pixmap = pixmap
  112. self.current_audio_path = audio_path
  113. # Force widget to update geometry first
  114. self.spectrogram_label.updateGeometry()
  115. # Get actual dimensions, with better fallbacks
  116. label_width = self.spectrogram_label.width()
  117. label_height = self.spectrogram_label.height()
  118. # Use parent widget size if label size not yet determined
  119. if label_width < 100:
  120. parent_width = self.width() if self.width() > 100 else 380
  121. label_width = parent_width - 20
  122. if label_height < 100:
  123. label_height = 220
  124. # Scale to fit width while preserving aspect ratio
  125. scaled_pixmap = pixmap.scaledToWidth(
  126. int(label_width),
  127. Qt.SmoothTransformation
  128. )
  129. self.spectrogram_label.setPixmap(scaled_pixmap)
  130. def clear_spectrogram(self):
  131. """Clear the spectrogram display."""
  132. self.current_pixmap = None
  133. self.current_audio_path = None
  134. self.spectrogram_label.clear()
  135. self.spectrogram_label.setText("No audio loaded\n\nClick to view details")
  136. def _on_spectrogram_clicked(self):
  137. """Handle spectrogram click to show enlarged view with waveform and playback."""
  138. if self.current_pixmap is not None:
  139. from ui.dialogs.spectrogram_preview_dialog import SpectrogramPreviewDialog
  140. dialog = SpectrogramPreviewDialog(
  141. self.current_pixmap,
  142. self.current_audio_path,
  143. self
  144. )
  145. dialog.exec_()