126 lines
4.2 KiB
Python
126 lines
4.2 KiB
Python
|
|
"""
|
||
|
|
Audio visualization widget with embedded matplotlib canvas.
|
||
|
|
Pure display responsibility - receives plotting data and shows graphs.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
|
||
|
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||
|
|
from matplotlib.figure import Figure
|
||
|
|
import matplotlib.pyplot as plt
|
||
|
|
|
||
|
|
|
||
|
|
class AudioVisualizationWidget(QWidget):
|
||
|
|
"""Widget for displaying audio analysis graphs with embedded matplotlib."""
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.initUI()
|
||
|
|
|
||
|
|
def initUI(self):
|
||
|
|
"""Initialize the UI components."""
|
||
|
|
layout = QVBoxLayout()
|
||
|
|
|
||
|
|
# Create matplotlib canvas
|
||
|
|
self.figure = Figure(figsize=(10, 4), facecolor='white')
|
||
|
|
self.canvas = FigureCanvas(self.figure)
|
||
|
|
|
||
|
|
# Add canvas to layout
|
||
|
|
layout.addWidget(self.canvas)
|
||
|
|
|
||
|
|
# Status label for feedback
|
||
|
|
self.status_label = QLabel("Ready for audio analysis...")
|
||
|
|
layout.addWidget(self.status_label)
|
||
|
|
|
||
|
|
self.setLayout(layout)
|
||
|
|
|
||
|
|
# Initialize with empty plot
|
||
|
|
self._create_empty_plot()
|
||
|
|
|
||
|
|
def _create_empty_plot(self):
|
||
|
|
"""Creates an empty placeholder plot."""
|
||
|
|
self.figure.clear()
|
||
|
|
ax = self.figure.add_subplot(111)
|
||
|
|
ax.text(0.5, 0.5, 'Drop an audio file to see analysis',
|
||
|
|
ha='center', va='center', transform=ax.transAxes,
|
||
|
|
fontsize=14, alpha=0.7)
|
||
|
|
ax.set_xlim(0, 1)
|
||
|
|
ax.set_ylim(0, 1)
|
||
|
|
ax.set_xticks([])
|
||
|
|
ax.set_yticks([])
|
||
|
|
self.canvas.draw()
|
||
|
|
|
||
|
|
def display_analysis_figure(self, figure):
|
||
|
|
"""
|
||
|
|
Display a matplotlib figure in the widget.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
figure: matplotlib.figure.Figure to display
|
||
|
|
"""
|
||
|
|
# Clear current figure
|
||
|
|
self.figure.clear()
|
||
|
|
|
||
|
|
# Copy the provided figure to our canvas
|
||
|
|
# Get the subplot from the provided figure
|
||
|
|
source_ax = figure.get_axes()[0]
|
||
|
|
|
||
|
|
# Create new subplot in our figure
|
||
|
|
ax = self.figure.add_subplot(111)
|
||
|
|
|
||
|
|
# Copy all the plot elements
|
||
|
|
for child in source_ax.get_children():
|
||
|
|
if hasattr(child, 'get_data'):
|
||
|
|
# Copy line plots
|
||
|
|
try:
|
||
|
|
x_data, y_data = child.get_data()
|
||
|
|
ax.plot(x_data, y_data, color=child.get_color(),
|
||
|
|
linewidth=child.get_linewidth())
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
|
||
|
|
# Copy collections (fill_between creates PolyCollection)
|
||
|
|
for collection in source_ax.collections:
|
||
|
|
ax.add_collection(collection)
|
||
|
|
|
||
|
|
# Copy axis properties
|
||
|
|
ax.set_xlim(source_ax.get_xlim())
|
||
|
|
ax.set_ylim(source_ax.get_ylim())
|
||
|
|
ax.set_xlabel(source_ax.get_xlabel())
|
||
|
|
ax.set_ylabel(source_ax.get_ylabel())
|
||
|
|
ax.set_title(source_ax.get_title())
|
||
|
|
|
||
|
|
# Copy colorbar if it exists
|
||
|
|
if hasattr(figure, '_colorbar') or len(figure.get_axes()) > 1:
|
||
|
|
# Try to copy colorbar
|
||
|
|
try:
|
||
|
|
cbar = figure.colorbar(source_ax.collections[-1], ax=ax, label='RMS Power')
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
|
||
|
|
self.figure.tight_layout()
|
||
|
|
self.canvas.draw()
|
||
|
|
self.status_label.setText("Analysis complete - displaying power graph")
|
||
|
|
|
||
|
|
def display_figure_direct(self, figure):
|
||
|
|
"""
|
||
|
|
Display a figure by replacing our canvas figure entirely.
|
||
|
|
More reliable than copying elements.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
figure: matplotlib.figure.Figure to display
|
||
|
|
"""
|
||
|
|
# Remove old canvas
|
||
|
|
layout = self.layout()
|
||
|
|
layout.removeWidget(self.canvas)
|
||
|
|
self.canvas.deleteLater()
|
||
|
|
|
||
|
|
# Create new canvas with the provided figure
|
||
|
|
self.figure = figure
|
||
|
|
self.canvas = FigureCanvas(self.figure)
|
||
|
|
layout.insertWidget(0, self.canvas) # Insert at position 0 (before status label)
|
||
|
|
|
||
|
|
self.canvas.draw()
|
||
|
|
self.status_label.setText("Analysis complete - displaying power graph")
|
||
|
|
|
||
|
|
def set_status(self, message):
|
||
|
|
"""Update the status label."""
|
||
|
|
self.status_label.setText(message)
|