feat: timeline markers with hover tooltip
This commit is contained in:
@@ -10,10 +10,10 @@ from pathlib import Path
|
|||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||||
QLabel, QPushButton, QLineEdit, QFileDialog, QFrame, QStatusBar,
|
QLabel, QPushButton, QLineEdit, QFileDialog, QFrame, QStatusBar,
|
||||||
QListWidget, QListWidgetItem, QAbstractItemView, QSplitter,
|
QListWidget, QListWidgetItem, QAbstractItemView, QSplitter, QToolTip,
|
||||||
)
|
)
|
||||||
from PyQt6.QtCore import Qt, QThread, QTimer, pyqtSignal
|
from PyQt6.QtCore import Qt, QThread, QTimer, pyqtSignal
|
||||||
from PyQt6.QtGui import QPainter, QColor, QPen, QDragEnterEvent, QDropEvent
|
from PyQt6.QtGui import QPainter, QColor, QPen, QDragEnterEvent, QDropEvent, QCursor, QFont
|
||||||
import mpv
|
import mpv
|
||||||
|
|
||||||
|
|
||||||
@@ -172,6 +172,7 @@ class TimelineWidget(QWidget):
|
|||||||
self.setMouseTracking(True)
|
self.setMouseTracking(True)
|
||||||
self._duration = 0.0
|
self._duration = 0.0
|
||||||
self._cursor = 0.0
|
self._cursor = 0.0
|
||||||
|
self._markers: list[tuple[float, int, str]] = []
|
||||||
|
|
||||||
def set_duration(self, duration: float):
|
def set_duration(self, duration: float):
|
||||||
self._duration = duration
|
self._duration = duration
|
||||||
@@ -182,6 +183,11 @@ class TimelineWidget(QWidget):
|
|||||||
self._cursor = max(0.0, min(seconds, max(0.0, self._duration - 8.0)))
|
self._cursor = max(0.0, min(seconds, max(0.0, self._duration - 8.0)))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def set_markers(self, markers: list[tuple[float, int, str]]) -> None:
|
||||||
|
"""markers: list of (start_time, number, output_path)"""
|
||||||
|
self._markers = markers
|
||||||
|
self.update()
|
||||||
|
|
||||||
def _pos_to_time(self, x: int) -> float:
|
def _pos_to_time(self, x: int) -> float:
|
||||||
if self._duration <= 0 or self.width() <= 0:
|
if self._duration <= 0 or self.width() <= 0:
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -192,8 +198,6 @@ class TimelineWidget(QWidget):
|
|||||||
p = QPainter(self)
|
p = QPainter(self)
|
||||||
try:
|
try:
|
||||||
w, h = self.width(), self.height()
|
w, h = self.width(), self.height()
|
||||||
|
|
||||||
# Background
|
|
||||||
p.fillRect(0, 0, w, h, QColor(30, 30, 30))
|
p.fillRect(0, 0, w, h, QColor(30, 30, 30))
|
||||||
|
|
||||||
if self._duration <= 0:
|
if self._duration <= 0:
|
||||||
@@ -209,6 +213,21 @@ class TimelineWidget(QWidget):
|
|||||||
pen.setWidth(2)
|
pen.setWidth(2)
|
||||||
p.setPen(pen)
|
p.setPen(pen)
|
||||||
p.drawLine(x_start, 0, x_start, h)
|
p.drawLine(x_start, 0, x_start, h)
|
||||||
|
|
||||||
|
# Markers
|
||||||
|
font = QFont()
|
||||||
|
font.setPixelSize(9)
|
||||||
|
p.setFont(font)
|
||||||
|
marker_pen = QPen(QColor(220, 60, 60))
|
||||||
|
marker_pen.setWidth(2)
|
||||||
|
for (t, num, _path) in self._markers:
|
||||||
|
if self._duration <= 0:
|
||||||
|
break
|
||||||
|
mx = int(t / self._duration * w)
|
||||||
|
p.setPen(marker_pen)
|
||||||
|
p.drawLine(mx, 0, mx, h)
|
||||||
|
p.setPen(QColor(255, 255, 255))
|
||||||
|
p.drawText(mx + 2, 10, str(num))
|
||||||
finally:
|
finally:
|
||||||
p.end()
|
p.end()
|
||||||
|
|
||||||
@@ -216,8 +235,20 @@ class TimelineWidget(QWidget):
|
|||||||
self._seek(event.position().x())
|
self._seek(event.position().x())
|
||||||
|
|
||||||
def mouseMoveEvent(self, event):
|
def mouseMoveEvent(self, event):
|
||||||
|
x = event.position().x()
|
||||||
|
# Check marker hover (±4px)
|
||||||
|
if self._duration > 0 and self._markers:
|
||||||
|
w = self.width()
|
||||||
|
for (t, _num, output_path) in self._markers:
|
||||||
|
mx = t / self._duration * w
|
||||||
|
if abs(x - mx) <= 4:
|
||||||
|
QToolTip.showText(QCursor.pos(), output_path, self)
|
||||||
|
if event.buttons():
|
||||||
|
self._seek(x)
|
||||||
|
return
|
||||||
|
QToolTip.hideText()
|
||||||
if event.buttons():
|
if event.buttons():
|
||||||
self._seek(event.position().x())
|
self._seek(x)
|
||||||
|
|
||||||
def _seek(self, x: float):
|
def _seek(self, x: float):
|
||||||
t = self._pos_to_time(int(x))
|
t = self._pos_to_time(int(x))
|
||||||
|
|||||||
Reference in New Issue
Block a user