feat: make clip count configurable (1–10, default 3)

Clips spinbox in settings row alongside Spread. Preview span and export
batch size adjust dynamically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-11 23:28:12 +02:00
parent 25250d6d8d
commit 9e07910df1
+26 -7
View File
@@ -17,7 +17,7 @@ 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, QToolTip, QListWidget, QListWidgetItem, QAbstractItemView, QSplitter, QToolTip,
QComboBox, QDialog, QPlainTextEdit, QCheckBox, QDoubleSpinBox, QComboBox, QDialog, QPlainTextEdit, QCheckBox, QSpinBox, QDoubleSpinBox,
) )
from PyQt6.QtCore import Qt, QThread, QTimer, pyqtSignal, QSettings from PyQt6.QtCore import Qt, QThread, QTimer, pyqtSignal, QSettings
from PyQt6.QtGui import QPainter, QColor, QPen, QDragEnterEvent, QDropEvent, QCursor, QFont, QKeySequence, QShortcut from PyQt6.QtGui import QPainter, QColor, QPen, QDragEnterEvent, QDropEvent, QCursor, QFont, QKeySequence, QShortcut
@@ -1178,7 +1178,7 @@ class MainWindow(QMainWindow):
self._mpv.file_loaded.connect(self._after_load) self._mpv.file_loaded.connect(self._after_load)
self._timeline = TimelineWidget() self._timeline = TimelineWidget()
self._timeline.setFixedHeight(160) self._timeline.setFixedHeight(160)
self._timeline.set_clip_span(8.0 + 2 * saved_spread) self._timeline.set_clip_span(8.0 + (saved_clips - 1) * saved_spread)
self._timeline.cursor_changed.connect(self._on_cursor_changed) self._timeline.cursor_changed.connect(self._on_cursor_changed)
self._timeline.marker_delete_requested.connect(self._on_delete_marker) self._timeline.marker_delete_requested.connect(self._on_delete_marker)
self._timeline.marker_clicked.connect(self._on_marker_clicked) self._timeline.marker_clicked.connect(self._on_marker_clicked)
@@ -1241,11 +1241,24 @@ class MainWindow(QMainWindow):
) )
self._cmb_format.currentTextChanged.connect(self._update_next_label) self._cmb_format.currentTextChanged.connect(self._update_next_label)
self._spn_clips = QSpinBox()
self._spn_clips.setRange(1, 10)
self._spn_clips.setToolTip("Number of overlapping 8s clips per export")
saved_clips = int(self._settings.value("clip_count", "3"))
self._spn_clips.setValue(saved_clips)
self._spn_clips.valueChanged.connect(
lambda v: self._settings.setValue("clip_count", str(v))
)
self._spn_clips.valueChanged.connect(
lambda: self._timeline.set_clip_span(self._clip_span)
)
self._spn_clips.valueChanged.connect(lambda: self._update_next_label())
self._spn_spread = QDoubleSpinBox() self._spn_spread = QDoubleSpinBox()
self._spn_spread.setRange(2.0, 8.0) self._spn_spread.setRange(2.0, 8.0)
self._spn_spread.setSingleStep(0.5) self._spn_spread.setSingleStep(0.5)
self._spn_spread.setSuffix("s") self._spn_spread.setSuffix("s")
self._spn_spread.setToolTip("Offset between the 3 overlapping 8s clips") self._spn_spread.setToolTip("Offset between overlapping 8s clips")
saved_spread = float(self._settings.value("spread", "3.0")) saved_spread = float(self._settings.value("spread", "3.0"))
self._spn_spread.setValue(saved_spread) self._spn_spread.setValue(saved_spread)
self._spn_spread.valueChanged.connect( self._spn_spread.valueChanged.connect(
@@ -1344,6 +1357,8 @@ class MainWindow(QMainWindow):
settings_row.addWidget(self._cmb_portrait) settings_row.addWidget(self._cmb_portrait)
settings_row.addWidget(QLabel("Format:")) settings_row.addWidget(QLabel("Format:"))
settings_row.addWidget(self._cmb_format) settings_row.addWidget(self._cmb_format)
settings_row.addWidget(QLabel("Clips:"))
settings_row.addWidget(self._spn_clips)
settings_row.addWidget(QLabel("Spread:")) settings_row.addWidget(QLabel("Spread:"))
settings_row.addWidget(self._spn_spread) settings_row.addWidget(self._spn_spread)
@@ -1558,8 +1573,8 @@ class MainWindow(QMainWindow):
@property @property
def _clip_span(self) -> float: def _clip_span(self) -> float:
"""Total time covered by the 3 overlapping clips.""" """Total time covered by the overlapping clips."""
return 8.0 + 2 * self._spn_spread.value() return 8.0 + (self._spn_clips.value() - 1) * self._spn_spread.value()
def _on_play(self): def _on_play(self):
if not self._file_path: if not self._file_path:
@@ -1618,8 +1633,12 @@ class MainWindow(QMainWindow):
if not os.path.exists(path): if not os.path.exists(path):
break break
self._export_counter += 1 self._export_counter += 1
n = self._spn_clips.value()
base = f"{name}_{self._export_counter:03d}" base = f"{name}_{self._export_counter:03d}"
self._lbl_next.setText(f"{base}_0/1/2") if n == 1:
self._lbl_next.setText(f"{base}_0")
else:
self._lbl_next.setText(f"{base}_0..{n - 1}")
def _on_export(self): def _on_export(self):
if not self._file_path: if not self._file_path:
@@ -1641,7 +1660,7 @@ class MainWindow(QMainWindow):
else: else:
name = self._txt_name.text() or "clip" name = self._txt_name.text() or "clip"
jobs = [] jobs = []
for sub in range(3): for sub in range(self._spn_clips.value()):
start = self._cursor + sub * spread start = self._cursor + sub * spread
if image_sequence: if image_sequence:
out = build_sequence_dir(folder, name, self._export_counter, sub=sub) out = build_sequence_dir(folder, name, self._export_counter, sub=sub)