- Clap detection moved from inline buttons to a Tools menu, plain labels (no more '(whole file)' / '(from cursor)' parentheticals) -- status messages moved to the window's status bar since the dedicated label widget went away with the button row. - ClickableSlider subclass: clicking anywhere along the seek track jumps straight there instead of QSlider's default page-by-a-step behavior; reposition-then-delegate-to-super keeps native drag behavior intact. - Dragging now seeks live via sliderMoved instead of waiting for sliderReleased. - PlayerWidget accepts drag-and-drop of a local file to load it. Verified with real Qt events (QMouseEvent clicks, a QDropEvent, a real mouse click via xdotool opening the Tools menu on an actual running window) rather than just calling the underlying methods directly.
87 lines
3.0 KiB
Python
87 lines
3.0 KiB
Python
"""Drives the real MainWindow's clap-detection buttons (not a copy of the
|
|
logic) against the synthetic fixture with a known beep at t=5.350s, then
|
|
simulates a real mouse click on the MarkerStrip widget to confirm
|
|
click-to-seek actually moves the player -- not just that a signal exists.
|
|
"""
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
from PyQt6.QtCore import Qt, QPointF
|
|
from PyQt6.QtGui import QMouseEvent
|
|
from PyQt6.QtWidgets import QApplication
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
import main as app_main
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
CLIP = str(ROOT / "test_media" / "sync_test_OUTOFSYNC_audio+350ms.mp4")
|
|
OUT_DIR = ROOT / "test_media" / "seek_validation"
|
|
|
|
|
|
def wait_for(predicate, qapp, timeout=20):
|
|
deadline = time.time() + timeout
|
|
while not predicate() and time.time() < deadline:
|
|
qapp.processEvents()
|
|
time.sleep(0.05)
|
|
return predicate()
|
|
|
|
|
|
def main():
|
|
OUT_DIR.mkdir(exist_ok=True)
|
|
qapp = QApplication(sys.argv)
|
|
win = app_main.MainWindow()
|
|
win.show()
|
|
qapp.processEvents()
|
|
|
|
win._load(CLIP)
|
|
qapp.processEvents()
|
|
time.sleep(0.3)
|
|
qapp.processEvents()
|
|
|
|
assert win.detect_all_action.isEnabled()
|
|
win.detect_all_action.trigger()
|
|
qapp.processEvents()
|
|
assert not win.detect_all_action.isEnabled(), "action should disable while detection runs"
|
|
|
|
ok = wait_for(lambda: win.detect_all_action.isEnabled(), qapp)
|
|
assert ok, "detection did not finish in time"
|
|
|
|
markers = win.marker_strip.markers()
|
|
print(f"detected markers: {markers}")
|
|
assert len(markers) >= 1, "expected at least one clap marker"
|
|
# ground truth beep is at t=5.350s in this fixture; onset detection has
|
|
# some inherent lag, so allow a generous window rather than exact match.
|
|
assert any(abs(m - 5.350) < 0.5 for m in markers), f"no marker near the known beep: {markers}"
|
|
|
|
win.grab().save(str(OUT_DIR / "clap_markers_on_timeline.png"))
|
|
|
|
# Simulate a real mouse click on the marker to prove click-to-seek works
|
|
# end to end, not just that the signal is wired.
|
|
target_time = min(markers, key=lambda m: abs(m - 5.350))
|
|
strip = win.marker_strip
|
|
strip.resize(win.seek_slider.width(), strip.height())
|
|
x = strip._x_for_time(target_time)
|
|
click_pos = QPointF(x, 5)
|
|
|
|
before_pos = win.player.get_position()
|
|
event = QMouseEvent(
|
|
QMouseEvent.Type.MouseButtonPress, click_pos, click_pos,
|
|
Qt.MouseButton.LeftButton, Qt.MouseButton.LeftButton, Qt.KeyboardModifier.NoModifier,
|
|
)
|
|
strip.mousePressEvent(event)
|
|
qapp.processEvents()
|
|
time.sleep(0.2)
|
|
qapp.processEvents()
|
|
|
|
after_pos = win.player.get_position()
|
|
print(f"clicked marker at x={x:.1f} (t={target_time:.3f}s); player position before={before_pos:.3f}s after={after_pos:.3f}s")
|
|
assert abs(after_pos - target_time) < 0.1, "clicking the marker should seek the player to that timestamp"
|
|
|
|
win.player.shutdown()
|
|
print("\nCLAP DETECTION SMOKE TEST PASSED")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|