Add end-of-clips dialog and --no-skip flag
Show a modal dialog when all clips have been processed and quit cleanly. Add --no-skip CLI flag to include already-annotated clips (default remains to skip them). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,7 @@ python -m river_annotation_tool.annotation_script
|
||||
| `--clips` | *(from config)* | Override `clips_file` from config |
|
||||
| `--clip` | *(first unannotated in list)* | Open a specific clip by stem name (e.g. `left_20230501`) |
|
||||
| `--extras` | off | Also save GIFs and extra PNGs (see Output section) |
|
||||
| `--no-skip` | off | Show already-annotated clips instead of skipping them |
|
||||
|
||||
### Typical workflows
|
||||
|
||||
@@ -109,7 +110,7 @@ Add, remove, or reorder questions directly in the YAML — the UI rebuilds autom
|
||||
|
||||
## Clip list file
|
||||
|
||||
`config/clips.txt` lists the clip filenames to annotate, one per line. Lines starting with `#` are ignored. Clips are processed in order; already-annotated clips (those with an existing `mask.png`) are skipped automatically.
|
||||
`config/clips.txt` lists the clip filenames to annotate, one per line. Lines starting with `#` are ignored. Clips are processed in order; already-annotated clips (those with an existing `mask.png`) are skipped automatically. Pass `--no-skip` to include them. When the last clip is reached, a dialog appears and the app exits.
|
||||
|
||||
```
|
||||
# Example clips.txt
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from matplotlib import use
|
||||
@@ -6,7 +7,7 @@ from matplotlib import use
|
||||
|
||||
use("QtAgg")
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
from PySide6.QtWidgets import QApplication, QMessageBox
|
||||
|
||||
from .annotator import Annotator
|
||||
from .config import load_config
|
||||
@@ -30,6 +31,11 @@ def parse_args():
|
||||
action="store_true",
|
||||
help="Also save GIFs, frame PNG, overlay PNG, and mask_vis PNG alongside the mask.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-skip",
|
||||
action="store_true",
|
||||
help="Show already-annotated clips instead of skipping them.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@@ -45,6 +51,15 @@ if __name__ == "__main__":
|
||||
cfg.clips_file = args.clips
|
||||
|
||||
app = QApplication([])
|
||||
win = Annotator(cfg, clip=args.clip, extras=args.extras)
|
||||
try:
|
||||
win = Annotator(
|
||||
cfg,
|
||||
clip=args.clip,
|
||||
extras=args.extras,
|
||||
skip_annotated=not args.no_skip,
|
||||
)
|
||||
except RuntimeError as e:
|
||||
QMessageBox.information(None, "No clips", str(e))
|
||||
sys.exit(0)
|
||||
win.show()
|
||||
app.exec()
|
||||
|
||||
@@ -6,11 +6,13 @@ import numpy as np
|
||||
from PIL import Image
|
||||
from PySide6.QtCore import QTimer
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QButtonGroup,
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QMainWindow,
|
||||
QMessageBox,
|
||||
QPushButton,
|
||||
QRadioButton,
|
||||
QVBoxLayout,
|
||||
@@ -29,6 +31,7 @@ class Annotator(QMainWindow):
|
||||
config: AppConfig,
|
||||
clip: str = None,
|
||||
extras: bool = False,
|
||||
skip_annotated: bool = True,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
@@ -42,6 +45,7 @@ class Annotator(QMainWindow):
|
||||
clips_file=Path(config.clips_file),
|
||||
mask_filename=config.filenames.mask,
|
||||
zip_extension=config.filenames.zip_extension,
|
||||
skip_annotated=skip_annotated,
|
||||
)
|
||||
|
||||
self.setWindowTitle("River Annotator")
|
||||
@@ -251,7 +255,16 @@ class Annotator(QMainWindow):
|
||||
self._set_answers(answers)
|
||||
|
||||
def _advance_clip(self):
|
||||
self._load_clip()
|
||||
try:
|
||||
self._load_clip()
|
||||
except RuntimeError:
|
||||
msg = QMessageBox(self)
|
||||
msg.setWindowTitle("All done!")
|
||||
msg.setText("You have reached the end of all clips.")
|
||||
msg.setStandardButtons(QMessageBox.StandardButton.Ok)
|
||||
msg.exec()
|
||||
QApplication.instance().quit()
|
||||
return
|
||||
self.frame_i = 0
|
||||
self.mc.load_clip(
|
||||
self.frames,
|
||||
|
||||
@@ -9,11 +9,13 @@ class ClipSelector:
|
||||
clips_file: Path,
|
||||
mask_filename: str = "mask.png",
|
||||
zip_extension: str = ".zip",
|
||||
skip_annotated: bool = True,
|
||||
):
|
||||
self.data_dir = data_dir
|
||||
self.out_dir = out_dir
|
||||
self.mask_filename = mask_filename
|
||||
self.zip_extension = zip_extension
|
||||
self.skip_annotated = skip_annotated
|
||||
self.clips = self._load_clips(clips_file)
|
||||
self.index = 0
|
||||
|
||||
@@ -46,6 +48,6 @@ class ClipSelector:
|
||||
while self.index < len(self.clips):
|
||||
clip = self.clips[self.index]
|
||||
self.index += 1
|
||||
if not self.is_annotated(clip):
|
||||
if not self.skip_annotated or not self.is_annotated(clip):
|
||||
return clip
|
||||
raise RuntimeError("No remaining clips to annotate")
|
||||
|
||||
Reference in New Issue
Block a user