feat: waveform view/selection state + band/handle painting

This commit is contained in:
2026-07-02 16:56:36 +02:00
parent ee7de4d83c
commit 7ca894d585
2 changed files with 66 additions and 9 deletions
+44 -3
View File
@@ -3946,29 +3946,70 @@ class AudioWaveform(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self._peaks: list[float] = []
self.setFixedHeight(48)
self._view_start = 0.0
self._view_dur = 0.0
self._sel: tuple[float, float] | None = None
self._playhead: float | None = None
self.setFixedHeight(96)
self.setToolTip("Waveform of the current audio area (↻ to refresh)")
def set_peaks(self, peaks: list[float]) -> None:
self._peaks = list(peaks or [])
self.update()
def set_view(self, start: float, dur: float) -> None:
self._view_start = start
self._view_dur = dur
self.update()
def set_selection(self, start: float, end: float) -> None:
if self._view_dur > 0:
lo = self._view_start
hi = self._view_start + self._view_dur
start = min(max(start, lo), hi)
end = min(max(end, lo), hi)
if start > end:
start, end = end, start
self._sel = (start, end)
self.update()
def selection(self) -> tuple[float, float] | None:
return self._sel
def set_playhead(self, t: float | None) -> None:
self._playhead = t
self.update()
def clear(self) -> None:
self._peaks = []
self._sel = None
self._playhead = None
self.update()
def paintEvent(self, _ev):
from core.waveform import t_to_x
p = QPainter(self)
p.fillRect(self.rect(), QColor(30, 30, 30))
if not self._peaks:
return
w = self.width(); h = self.height(); mid = h / 2
if self._peaks:
n = len(self._peaks)
p.setPen(QPen(QColor(0, 220, 190)))
for i, v in enumerate(self._peaks):
x = int(i * w / n)
bar_h = v * (h - 4) / 2
p.drawLine(x, int(mid - bar_h), x, int(mid + bar_h))
if self._sel and self._view_dur > 0:
x1 = t_to_x(self._sel[0], w, self._view_start, self._view_dur)
x2 = t_to_x(self._sel[1], w, self._view_start, self._view_dur)
p.fillRect(QRect(x1, 0, max(1, x2 - x1), h), QColor(0, 200, 180, 50))
p.setPen(QPen(QColor(0, 220, 190), 2))
p.drawLine(x1, 0, x1, h)
p.drawLine(x2, 0, x2, h)
if (self._playhead is not None and self._view_dur > 0
and self._view_start <= self._playhead <= self._view_start + self._view_dur):
px = t_to_x(self._playhead, w, self._view_start, self._view_dur)
p.setPen(QPen(QColor(255, 255, 255, 160), 1))
p.drawLine(px, 0, px, h)
class MainWindow(QMainWindow):
+16
View File
@@ -398,6 +398,22 @@ def test_waveform_strip_present_and_safe(win):
win._on_wave_refresh()
def test_waveform_view_and_selection(win):
w = win._wave
w.set_view(10.0, 4.0)
w.set_selection(11.0, 12.5)
assert w._view_start == 10.0 and w._view_dur == 4.0
assert w.selection() == (11.0, 12.5)
# selection clamps into the view window and keeps start < end
w.set_selection(9.0, 20.0)
s, e = w.selection()
assert s >= 10.0 and e <= 14.0 and s < e
w.set_peaks([0.1, 0.9, 0.3]) # still paints without crashing
w.set_playhead(11.5)
w.clear() # resets display state
assert w.selection() is None
def test_audition_button_present_and_safe(win):
from PyQt6.QtWidgets import QPushButton
assert isinstance(win._btn_audio_play, QPushButton)