Skip to content

feat: Add batch processing for multiple folders in GUI#22

Merged
Alpaca233 merged 7 commits intoCephla-Lab:mainfrom
Alpaca233:feature/batch-processing
Apr 8, 2026
Merged

feat: Add batch processing for multiple folders in GUI#22
Alpaca233 merged 7 commits intoCephla-Lab:mainfrom
Alpaca233:feature/batch-processing

Conversation

@Alpaca233
Copy link
Copy Markdown
Collaborator

Summary

  • Multi-drop support: Drop multiple folders/files onto the drop area to batch process them sequentially with shared parameters
  • Validation on drop: Each item is validated by loading metadata via TileFusion; invalid items are warned in the UI and skipped
  • Determinate progress: Progress bar shows N/total in batch mode, log messages prefixed with [1/5 folder_name] to track which item is processing
  • Graceful error handling: Per-item failures are logged and processing continues with remaining items
  • Batch mode UI: Preview, calculate-flatfield, registration z/t/channel controls, and Napari button are grayed out with explanatory tooltips in batch mode; single drop reverts to normal behavior

Test plan

  • Drop a single folder — verify existing behavior is unchanged
  • Drop multiple valid folders — verify batch mode activates, grayed-out controls, validation log
  • Drop a mix of valid and invalid items — verify invalid ones are warned and excluded
  • Run batch stitching — verify progress bar updates per item and log shows [N/total name] prefixes
  • Verify a failing item in the batch does not stop processing of remaining items
  • Drop a single item after batch — verify batch mode exits and all controls re-enable

🤖 Generated with Claude Code

Alpaca233 and others added 3 commits April 7, 2026 10:28
Drop multiple folders/files onto the drop area to process them
sequentially with shared parameters. Each item is validated on
drop (metadata loaded via TileFusion), with invalid items warned
and skipped. Progress bar shows determinate progress (N/total)
and log messages are prefixed with [1/5 folder_name]. Preview,
calculate-flatfield, reg z/t controls, and Napari are grayed out
in batch mode. Per-item failures are logged and processing
continues with remaining items.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract _run_fusion_pipeline() used by both FusionWorker and
  BatchFusionWorker, eliminating ~100 lines of duplicated pipeline
  logic. This also fixes batch mode silently dropping registration
  z/t/channel parameters.
- Make DropArea.file_path a property derived from file_paths list.
- Make StitcherGUI.is_batch_mode a property derived from batch_paths.
- Remove incorrect flatfield status reset in on_files_dropped (batch
  mode preserves any previously loaded flatfield for shared use).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CRITICAL: Remove assignment to read-only file_path property in
  setFiles() which caused AttributeError on every batch drop.
- Wrap BatchFusionWorker.run() in top-level try/except that emits
  error signal; connect it in _run_batch so UI doesn't freeze on
  unexpected thread death.
- Add MemoryError early exit in batch loop instead of continuing
  futile processing.
- Re-enable preview, calc-flatfield, and reg z/t controls in
  _on_batch_finished so UI isn't stuck after batch completes.
- Use context manager for TileFusion validation in on_files_dropped
  to prevent file handle leaks.
- Fix misleading signal comments (folder_name -> item_name,
  validated -> type-checked).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds batch-processing support to the stitcher GUI, allowing users to drop multiple datasets and process them sequentially with shared settings while providing per-item logging and determinate progress.

Changes:

  • Refactors the fusion/stitching pipeline into a shared _run_fusion_pipeline() used by both single and batch workers.
  • Adds BatchFusionWorker and multi-drop support in DropArea, including per-item progress/log prefixing.
  • Introduces batch-mode UI state (batch_paths) and disables certain controls while in batch mode.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

gui/app.py Outdated
Comment on lines +1624 to +1627
self.run_button.setEnabled(True)
self.preview_button.setEnabled(True)
self.calc_flatfield_button.setEnabled(True)
self.reg_zt_widget.setEnabled(True)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_on_batch_finished re-enables preview_button, calc_flatfield_button, and reg_zt_widget unconditionally, but batch_paths is not cleared, so the app is still in batch mode. This makes batch-only disabled controls usable again (e.g., Preview will run on drop_area.file_path, i.e., the first item) and contradicts the intended batch-mode UI state. Consider either clearing self.batch_paths to exit batch mode when the batch completes, or call _update_batch_mode_ui() here and only enable controls when not in batch mode (and when an input is selected).

Suggested change
self.run_button.setEnabled(True)
self.preview_button.setEnabled(True)
self.calc_flatfield_button.setEnabled(True)
self.reg_zt_widget.setEnabled(True)
# Batch processing is complete, so leave batch mode before restoring
# controls that are only valid for single-input operations.
self.batch_paths = []
has_input = bool(getattr(self.drop_area, "file_path", None))
self.run_button.setEnabled(True)
self.preview_button.setEnabled(has_input)
self.calc_flatfield_button.setEnabled(has_input)
self.reg_zt_widget.setEnabled(has_input)

Copilot uses AI. Check for mistakes.
Comment on lines +1269 to +1284
if not valid_paths:
self.log("No valid datasets found.")
self.run_button.setEnabled(False)
return

# Update DropArea display with validation results
self.drop_area.setFiles(valid_paths, invalid_names)
self.batch_paths = valid_paths
self._update_batch_mode_ui()
self.run_button.setEnabled(True)

self.dataset_n_z = 1
self.dataset_n_t = 1
self.dataset_n_channels = 1
self.dataset_channel_names = []

Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In on_files_dropped, if validation leaves exactly one valid_paths entry (e.g., user drops multiple items but only one is valid), the code still goes through the batch-mode path: setFiles(...), resets dataset dims to 1, and sets self.batch_paths = valid_paths while is_batch_mode will be false. This skips the single-item initialization done in on_file_dropped (dimension detection, flatfield auto-load, etc.) and can lead to inconsistent UI/behavior. Consider treating len(valid_paths) == 1 as a normal single selection (call self.drop_area.setFile(...) + on_file_dropped(...) or share the initialization logic).

Copilot uses AI. Check for mistakes.
Comment on lines +656 to +674
valid_paths = []
invalid_names = []
for url in urls:
file_path = url.toLocalFile()
if self._is_valid_path(file_path):
valid_paths.append(file_path)
else:
self.setStyleSheet(self._default_style)
else:
invalid_names.append(Path(file_path).name)

if not valid_paths:
self.setStyleSheet(self._default_style)
return

if len(valid_paths) == 1:
self.setFile(valid_paths[0])
self.fileDropped.emit(valid_paths[0])
else:
self.setFiles(valid_paths, invalid_names)
self.filesDropped.emit(valid_paths)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DropArea.dropEvent collects invalid_names (non-folder/non-TIFF) and shows them in setFiles(...), but filesDropped only emits valid_paths. StitcherGUI.on_files_dropped then re-renders the DropArea with its own invalid_names from TileFusion validation, which can overwrite and lose the original skipped names from the drop (and the log says Validating {len(paths)} where paths no longer includes those skipped items). If you want the UI/log to reflect all dropped invalid items, consider emitting both valid and invalid lists (or re-validating from the original urls list in the GUI) so skipped items aren't silently dropped from the later summary.

Copilot uses AI. Check for mistakes.
Comment on lines +1592 to +1595
total = len(self.batch_paths)
self.progress_bar.setRange(0, total)
self.progress_bar.setValue(0)
self.log(f"Starting batch processing: {total} items\n")
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Batch mode sets the progress bar to a determinate range (setRange(0, total)), but the default QProgressBar text format is percentage; the PR description says it should show N/total. Consider setting a value-based format for batch runs (e.g., %v/%m) and ensuring text visibility/layout supports it (the bar is currently only 6px tall, which may clip the text).

Copilot uses AI. Check for mistakes.
gui/app.py Outdated
Comment on lines +1166 to +1172
def _update_batch_mode_ui(self):
"""Update UI to reflect batch vs single mode."""
batch = self.is_batch_mode
self.preview_button.setEnabled(not batch)
self.calc_flatfield_button.setEnabled(not batch and self.drop_area.file_path is not None)
self.reg_zt_widget.setEnabled(not batch)
self.napari_button.setEnabled(False)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In _update_batch_mode_ui, batch mode disables reg_zt_widget but doesn't provide an explanatory tooltip the way Preview/Flatfield/Napari do. Since the PR description mentions tooltips for grayed-out registration z/t/channel controls, consider adding a tooltip to reg_zt_widget (or its child controls) when batch is true, and clearing it when leaving batch mode.

Copilot uses AI. Check for mistakes.
Comment on lines +543 to +550
def run(self):
try:
self._run_batch()
except Exception as e:
import traceback

self.error.emit(f"Error: {str(e)}\n{traceback.format_exc()}")
self.error.emit(f"Batch processing failed: {e}\n{traceback.format_exc()}")

Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BatchFusionWorker.run catches exceptions and emits error, but it does not emit any terminal signal (finished/item_finished) in that path. If an unexpected exception occurs before _run_batch completes, the GUI cleanup in _on_batch_finished will never run, and the progress bar may remain in determinate mode (and the app may stay in a partially-disabled state). Consider emitting finished (with failed counts) or a dedicated aborted signal from the exception handler so the UI can reliably reset batch-mode state.

Copilot uses AI. Check for mistakes.
Alpaca233 and others added 4 commits April 7, 2026 21:25
- Clear batch_paths in _on_batch_finished and call
  _update_batch_mode_ui() to properly exit batch mode and restore
  controls to correct state.
- Emit finished signal from BatchFusionWorker error handler so UI
  always resets (progress bar, disabled buttons) even on unexpected
  thread failure.
- Redirect single-valid-item from multi-drop to on_file_dropped
  so dimension detection and flatfield auto-load still work.
- Add tooltip for reg_zt_widget in batch mode for consistency with
  other disabled controls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove unconditional napari_button.setEnabled(False) from
  _update_batch_mode_ui so button stays available in batch mode.
- Re-enable napari button in _on_batch_finished.
- When no output_path is set, open an empty Napari viewer instead
  of silently returning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Alpaca233 Alpaca233 merged commit bd2cee0 into Cephla-Lab:main Apr 8, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants