Skip to content

Commit 28748e0

Browse files
committed
feat: add output path field to napari widget
1 parent 66ab927 commit 28748e0

1 file changed

Lines changed: 52 additions & 0 deletions

File tree

vesskel/_napari.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
load_pipeline_config,
2525
save_pipeline_config,
2626
)
27+
from vesskel._batch import _save_skeleton, _save_radius, _write_csv
2728
from vesskel.pipeline import analyze_binary_image
2829

2930

@@ -35,6 +36,7 @@ class VesselAnalysisWidget(Container):
3536
def __init__(self, napari_viewer):
3637
super().__init__()
3738
self.viewer = napari_viewer
39+
self._output_dir: Path | None = None
3840
self._setup_ui()
3941

4042
def _setup_ui(self):
@@ -205,6 +207,17 @@ def _output_params(
205207
output_group.append(self.write_node_csv_widget)
206208
output_group.append(self.write_radius_widget)
207209

210+
# ============================================================
211+
# Output Directory
212+
# ============================================================
213+
outdir_group = Container()
214+
outdir_group.label = "Output Directory"
215+
216+
self.select_outdir_btn = PushButton(text="Select Output Directory...")
217+
self.select_outdir_btn.clicked.connect(self._on_select_output_dir)
218+
219+
outdir_group.append(self.select_outdir_btn)
220+
208221
# ============================================================
209222
# Configuration Management
210223
# ============================================================
@@ -233,6 +246,7 @@ def _output_params(
233246
self.append(extraction_group)
234247
self.append(advanced_group)
235248
self.append(output_group)
249+
self.append(outdir_group)
236250
self.append(config_group)
237251
self.append(self.analyze_btn)
238252

@@ -330,6 +344,12 @@ def _on_save_config(self) -> None:
330344
except (ValueError, OSError) as e:
331345
show_error(f"Failed to save config: {e}")
332346

347+
def _on_select_output_dir(self) -> None:
348+
dir_path = QFileDialog.getExistingDirectory(None, "Select Output Directory")
349+
if dir_path:
350+
self._output_dir = Path(dir_path)
351+
self.select_outdir_btn.text = str(self._output_dir)
352+
333353
def _on_analyze(self) -> None:
334354
"""Execute analysis with current settings."""
335355
img = self.image_widget.value
@@ -379,5 +399,37 @@ def _on_analyze(self) -> None:
379399
show_info(
380400
f"Failed to add layer {meta.get('name', '<unnamed>')}: {e}"
381401
)
402+
403+
# -- save results to disk if output directory is set ----------
404+
if self._output_dir is not None:
405+
out = self._output_dir / img.name
406+
out.mkdir(parents=True, exist_ok=True)
407+
408+
o = pipeline_config.output
409+
if o.write_skeleton_npy or o.write_skeleton_png:
410+
_save_skeleton(
411+
out / f"{img.name}_skeleton",
412+
result.skeleton,
413+
npy=o.write_skeleton_npy,
414+
png=o.write_skeleton_png,
415+
)
416+
417+
if o.write_radius and result.radius_matrix is not None:
418+
_save_radius(out / f"{img.name}_radius", result.radius_matrix)
419+
420+
if o.write_branch_csv and result.branch_records:
421+
_write_csv(out / f"{img.name}_branches.csv", result.branch_records)
422+
423+
if o.write_node_csv and result.node_records:
424+
_write_csv(out / f"{img.name}_nodes.csv", result.node_records)
425+
426+
if o.write_summary_csv and result.summary_features:
427+
_write_csv(
428+
out / f"{img.name}_summary.csv",
429+
[{"image": img.name, **result.summary_features}],
430+
)
431+
432+
show_info(f"Results saved to {self._output_dir / img.name}")
433+
382434
except (ValueError, RuntimeError, OSError) as e:
383435
show_error(f"Analysis failed: {e}")

0 commit comments

Comments
 (0)