Files
HVideoTool/hvideotool/ui/marker_slider.py
T

67 lines
2.3 KiB
Python

"""A horizontal QSlider that paints marks at frames where censorship was found.
Used as the navigation scrubber under the image: the file list / `_results`
cache is projected onto the groove as small vertical ticks, so you can see at a
glance where the detected regions are along the whole sequence.
"""
from __future__ import annotations
from collections.abc import Iterable
from PySide6.QtCore import Qt
from PySide6.QtGui import QColor, QPainter
from PySide6.QtWidgets import QSlider, QStyle, QStyleOptionSlider
class MarkerSlider(QSlider):
def __init__(self, orientation=Qt.Horizontal, parent=None) -> None:
super().__init__(orientation, parent)
self._marks: set[int] = set()
self._mark_color = QColor(220, 70, 70)
def set_marks(self, marks: Iterable[int]) -> None:
marks = set(marks)
if marks != self._marks:
self._marks = marks
self.update()
def clear_marks(self) -> None:
if self._marks:
self._marks = set()
self.update()
def paintEvent(self, event) -> None:
super().paintEvent(event)
if not self._marks or self.maximum() <= self.minimum():
return
opt = QStyleOptionSlider()
self.initStyleOption(opt)
groove = self.style().subControlRect(
QStyle.CC_Slider, opt, QStyle.SC_SliderGroove, self
)
handle = self.style().subControlRect(
QStyle.CC_Slider, opt, QStyle.SC_SliderHandle, self
)
span = groove.width() - handle.width()
if span <= 0:
return
lo, hi = self.minimum(), self.maximum()
half = handle.width() // 2
top = groove.center().y() - 5
bottom = groove.center().y() + 5
painter = QPainter(self)
painter.setPen(self._mark_color)
# Many frames can collapse onto the same pixel column — dedupe to keep
# repaint cheap on big folders (tens of thousands of frames).
seen_x: set[int] = set()
for m in self._marks:
pos = QStyle.sliderPositionFromValue(lo, hi, m, span, opt.upsideDown)
x = groove.x() + half + pos
if x not in seen_x:
seen_x.add(x)
painter.drawLine(x, top, x, bottom)
painter.end()