feat: adapt export button for selection; show markers in review mode
- Scan panel button now reads "Export Selected (N)" while rows are selected, mirroring the clip-count estimate used for full exports. Selection changes fire an explicit signal so the label refreshes. - Export markers remain visible on the timeline in scan/review mode. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -716,6 +716,7 @@ class ScanResultsPanel(QWidget):
|
||||
negatives_removed = pyqtSignal(list) # emit list of start times to un-mark as negatives
|
||||
tab_changed = pyqtSignal() # active tab changed
|
||||
regions_edited = pyqtSignal() # a region was resized or toggled
|
||||
selection_changed = pyqtSignal() # user's row selection changed
|
||||
|
||||
# UserRole slots per item:
|
||||
# col 0: UserRole = row_id (int)
|
||||
@@ -1028,6 +1029,7 @@ class ScanResultsPanel(QWidget):
|
||||
|
||||
def _on_selection_changed(self, table: QTableWidget) -> None:
|
||||
"""Handle keyboard navigation (arrows) — seek to start of current row."""
|
||||
self.selection_changed.emit()
|
||||
cur = table.currentItem()
|
||||
if cur is None or not cur.isSelected():
|
||||
selected = table.selectedItems()
|
||||
@@ -1290,30 +1292,14 @@ class ScanResultsPanel(QWidget):
|
||||
table = self._current_table()
|
||||
if table is None:
|
||||
return
|
||||
selected_rows = sorted({idx.row() for idx in table.selectedIndexes()})
|
||||
if selected_rows:
|
||||
regions: list[tuple[float, float, float]] = []
|
||||
for r in selected_rows:
|
||||
item0 = table.item(r, 0)
|
||||
if item0 is None:
|
||||
continue
|
||||
if item0.data(Qt.ItemDataRole.UserRole + 2):
|
||||
continue # disabled
|
||||
start = item0.data(Qt.ItemDataRole.UserRole + 1)
|
||||
end = table.item(r, 1).data(Qt.ItemDataRole.UserRole)
|
||||
if start is None or end is None:
|
||||
continue
|
||||
if float(start) in self._neg_times:
|
||||
continue
|
||||
score = float(table.item(r, 2).text())
|
||||
regions.append((float(start), float(end), score))
|
||||
replace_all = False
|
||||
sel = self.selected_regions()
|
||||
if sel:
|
||||
self.export_requested.emit(sel, False)
|
||||
else:
|
||||
regions = [r for r in self._get_tab_regions(table)
|
||||
if r[0] not in self._neg_times]
|
||||
replace_all = True
|
||||
if regions:
|
||||
self.export_requested.emit(regions, replace_all)
|
||||
self.export_requested.emit(regions, True)
|
||||
|
||||
def current_regions(self) -> list[tuple[float, float, float]]:
|
||||
"""Return (start, end, score) for enabled rows in the active tab."""
|
||||
@@ -1345,13 +1331,37 @@ class ScanResultsPanel(QWidget):
|
||||
table.blockSignals(False)
|
||||
return
|
||||
|
||||
def set_export_count(self, n: int) -> None:
|
||||
def set_export_count(self, n: int, partial: bool = False) -> None:
|
||||
"""Update the export button label with estimated clip count."""
|
||||
if n > 0:
|
||||
if partial and n > 0:
|
||||
self._btn_export.setText(f"Export Selected ({n})")
|
||||
elif n > 0:
|
||||
self._btn_export.setText(f"Export Scan Results ({n})")
|
||||
else:
|
||||
self._btn_export.setText("Export Scan Results")
|
||||
|
||||
def selected_regions(self) -> list[tuple[float, float, float]]:
|
||||
"""Return (start, end, score) for rows selected in the active tab,
|
||||
excluding disabled and negative rows."""
|
||||
table = self._current_table()
|
||||
if table is None:
|
||||
return []
|
||||
rows = sorted({idx.row() for idx in table.selectedIndexes()})
|
||||
out: list[tuple[float, float, float]] = []
|
||||
for r in rows:
|
||||
item0 = table.item(r, 0)
|
||||
if item0 is None or item0.data(Qt.ItemDataRole.UserRole + 2):
|
||||
continue
|
||||
start = item0.data(Qt.ItemDataRole.UserRole + 1)
|
||||
end = table.item(r, 1).data(Qt.ItemDataRole.UserRole)
|
||||
if start is None or end is None:
|
||||
continue
|
||||
if float(start) in self._neg_times:
|
||||
continue
|
||||
score = float(table.item(r, 2).text())
|
||||
out.append((float(start), float(end), score))
|
||||
return out
|
||||
|
||||
def has_results(self) -> bool:
|
||||
return self._tabs.count() > 0
|
||||
|
||||
@@ -1817,7 +1827,6 @@ class TimelineWidget(QWidget):
|
||||
p.drawRect(ax1, rh + 1, max(ax2 - ax1, 1), h - rh - 2)
|
||||
|
||||
# ── export markers ────────────────────────────────────────────
|
||||
if not self._scan_mode:
|
||||
p.setFont(self._marker_font)
|
||||
for (t, num, _path) in self._markers:
|
||||
mx = int(t / self._duration * w)
|
||||
@@ -3308,6 +3317,7 @@ class MainWindow(QMainWindow):
|
||||
self._scan_panel.negatives_removed.connect(self._on_scan_negatives_removed)
|
||||
self._scan_panel.tab_changed.connect(self._on_scan_regions_edited)
|
||||
self._scan_panel.regions_edited.connect(self._on_scan_regions_edited)
|
||||
self._scan_panel.selection_changed.connect(self._update_scan_export_count)
|
||||
self._sld_threshold.valueChanged.connect(self._on_threshold_changed)
|
||||
|
||||
# Root: horizontal splitter
|
||||
@@ -4336,7 +4346,13 @@ class MainWindow(QMainWindow):
|
||||
def _update_scan_export_count(self) -> None:
|
||||
"""Recalculate and display estimated clip count on the export button."""
|
||||
neg = self._scan_panel._neg_times
|
||||
sel = self._scan_panel.selected_regions()
|
||||
if sel:
|
||||
regions = sel
|
||||
partial = True
|
||||
else:
|
||||
regions = [r for r in self._scan_panel.current_regions() if r[0] not in neg]
|
||||
partial = False
|
||||
if not regions:
|
||||
self._scan_panel.set_export_count(0)
|
||||
return
|
||||
@@ -4345,7 +4361,7 @@ class MainWindow(QMainWindow):
|
||||
spread=self._spn_spread.value(),
|
||||
)
|
||||
n = sum(len(g) for g in groups)
|
||||
self._scan_panel.set_export_count(n)
|
||||
self._scan_panel.set_export_count(n, partial=partial)
|
||||
|
||||
def _on_scan_export(self, regions: list, replace_all: bool = True) -> None:
|
||||
"""Export clips from scan results panel. replace_all=False for partial."""
|
||||
|
||||
Reference in New Issue
Block a user