Code/UI Bidirectional Editing
OpenHCS provides bidirectional editing between the TUI interface and Python code. This allows you to seamlessly switch between visual interface editing and code-based editing, leveraging the best of both approaches for bioimage analysis pipeline development.
Why This Matters: Most scientific tools provide one-way export from GUI to code. OpenHCS implements true bidirectional conversion, allowing you to edit in either representation while maintaining complete fidelity between them.
Quick Start (5 Minutes)
1. Open any TUI widget with a “Code” button
# Start OpenHCS TUI
openhcs-tui
# Navigate to Function List Editor, Pipeline Editor, or Plate Manager
# Look for the "Code" button in the interface
2. Click “Code” to generate Python code
The system automatically generates complete, executable Python code with all necessary imports:
# Generated from Function Pattern Editor
from openhcs.processing.backends.filters.gaussian_filter import gaussian_filter
pattern = gaussian_filter(sigma=2.0, preserve_dtype=True)
3. Edit the code in your preferred editor
Your $EDITOR opens automatically (vim, nano, emacs, VS Code, etc.):
# Edit parameters, add comments, modify the pattern
pattern = gaussian_filter(sigma=3.5, preserve_dtype=True) # Increased blur
4. Save and exit
The TUI automatically updates to reflect your changes. The interface now shows sigma=3.5 instead of sigma=2.0.
5. Continue working
Switch back and forth between TUI and code editing as needed. Each representation stays perfectly synchronized.
Function Pattern Editing
When to Use: Fine-tuning complex parameters, copying patterns between projects, or when you need syntax highlighting for complex function configurations.
Complete Workflow Example:
Start with TUI: Create a gaussian filter in the Function List Editor
Generate Code: Click “Code” button to see the pattern as Python code
Edit Parameters: Modify sigma, add preserve_dtype, adjust other parameters
Apply Changes: Save and exit editor to update the TUI
Verify Results: See the updated parameters reflected in the interface
Real Example - Parameter Tuning:
# Original pattern (from TUI)
from openhcs.processing.backends.filters.gaussian_filter import gaussian_filter
pattern = gaussian_filter(sigma=2.0)
# After editing (your changes)
pattern = gaussian_filter(
sigma=3.5, # Increased blur strength
preserve_dtype=True, # Maintain original data type
truncate=4.0 # Adjust truncation for performance
)
Benefits: - Syntax Highlighting: See parameter names and values clearly - Copy/Paste: Easy to duplicate patterns across projects - Comments: Add documentation directly in the pattern - Validation: Python syntax checking catches errors immediately
Pipeline Code Editing
When to Use: Bulk pipeline modifications, step reordering, adding multiple steps, or when you need to see the entire pipeline structure at once.
Complete Workflow Example:
Build Pipeline: Create a multi-step pipeline in the Pipeline Editor
Generate Code: Click “Code” button to see all steps as Python code
Bulk Edit: Add steps, reorder, modify multiple parameters at once
Apply Changes: Save to update the entire pipeline in the TUI
Real Example - Adding Cell Counting:
# Original pipeline (from TUI)
from openhcs.core.steps.function_step import FunctionStep
from openhcs.constants.constants import VariableComponents
from openhcs.processing.backends.filters.gaussian_filter import gaussian_filter
pipeline_steps = []
step_1 = FunctionStep(
func=gaussian_filter(sigma=2.0),
name="gaussian_filter",
variable_components=[VariableComponents.PLATE]
)
pipeline_steps.append(step_1)
# After editing - added cell counting step
from openhcs.processing.backends.analysis.cell_counting import count_cells
# ... existing step_1 ...
step_2 = FunctionStep(
func=count_cells(method="watershed", min_size=50),
name="count_cells",
variable_components=[VariableComponents.PLATE],
force_disk_output=True # Save counting results
)
pipeline_steps.append(step_2)
Advanced Pipeline Editing:
# Reorder steps by changing append order
pipeline_steps.append(step_2) # Count first
pipeline_steps.append(step_1) # Then filter
# Conditional steps
if analysis_type == "detailed":
step_3 = FunctionStep(func=detailed_analysis(), name="detailed")
pipeline_steps.append(step_3)
# Bulk parameter changes
for step in pipeline_steps:
step.variable_components = [VariableComponents.WELL] # Change all to well-level
Step Code Editing
When to Use: Fine-tuning individual step parameters, adjusting processing configurations, or when you need to see all available configuration options for a single step.
Complete Workflow Example:
Open Step Editor: Double-click a step in the Pipeline Editor or Dual Editor
Generate Code: Click “Code” button to see the step as Python code
Edit Parameters: Modify step parameters, processing config, or other settings
Apply Changes: Save to update the step in the editor
Real Example - Adjusting Processing Configuration:
# Original step (from UI)
from openhcs.core.steps.function_step import FunctionStep
from openhcs.processing.backends.filters.gaussian_filter import gaussian_filter
from openhcs.core.config import ProcessingConfig
from openhcs.constants.constants import GroupBy, VariableComponents
step = FunctionStep(
func=gaussian_filter(sigma=2.0),
name="gaussian_filter",
processing_config=ProcessingConfig(
group_by=None, # Inherits from pipeline config
variable_components=None # Inherits from pipeline config
)
)
# After editing - explicit configuration
step = FunctionStep(
func=gaussian_filter(sigma=3.5),
name="gaussian_filter",
processing_config=ProcessingConfig(
group_by=GroupBy.WELL, # Override pipeline default
variable_components=[VariableComponents.CHANNEL, VariableComponents.Z_INDEX]
)
)
Benefits:
See All Options: Non-clean mode shows all available fields, even when None
Explicit Configuration: Override pipeline defaults for specific steps
Nested Config Editing: Edit nested dataclass configurations directly
Clean Mode Toggle: Switch between full and minimal code representations
Clean Mode Toggle
What is Clean Mode: Clean mode generates minimal Python code by omitting fields that match their default values. This creates cleaner, more readable code for sharing and version control.
Toggling Clean Mode:
When viewing code in the code editor dialog, you can toggle between clean and full modes:
Full Mode (default): Shows all fields, including None values and nested dataclass structures
Clean Mode: Shows only explicitly set fields that differ from defaults
Example Comparison:
# Full Mode - shows all fields
step = FunctionStep(
func=gaussian_filter(sigma=2.0),
name="gaussian_filter",
processing_config=ProcessingConfig(
group_by=None,
variable_components=None,
input_source=None
),
step_materialization_config=StepMaterializationConfig(
enabled=None,
sub_dir=None
)
)
# Clean Mode - only non-default fields
step = FunctionStep(
func=gaussian_filter(sigma=2.0),
name="gaussian_filter"
)
When to Use Each Mode:
Full Mode: When you want to see all available configuration options
Clean Mode: When sharing code, committing to version control, or creating templates
Supported Contexts:
Function patterns
Pipeline steps
Individual steps
Configuration objects
Orchestrator Configuration
When to Use: Global configuration changes, multi-plate setup, advanced backend configuration, or when managing complex processing scenarios.
Complete Workflow Example:
Setup Plates: Configure multiple plates in the Plate Manager
Generate Code: Click “Code” button to see complete orchestrator configuration
Global Changes: Modify worker counts, backend settings, output configurations
Apply Changes: Save to update the entire system configuration
Real Example - Multi-Plate Processing:
# Generated orchestrator configuration
from openhcs.core.config import GlobalPipelineConfig, PathPlanningConfig
from openhcs.constants.constants import Backend
plate_paths = [
'/data/experiment1/plate1',
'/data/experiment1/plate2',
'/data/experiment2/plate1'
]
global_config = GlobalPipelineConfig(
num_workers=32, # Increased for large dataset
path_planning=PathPlanningConfig(
output_dir_suffix="_processed_v2",
global_output_folder="/results/batch_analysis"
),
vfs=VFSConfig(
default_backend=Backend.ZARR, # Use ZARR for large datasets
memory_limit_gb=64
)
)
# Pipeline data for each plate
pipeline_data = {}
for plate_path in plate_paths:
pipeline_data[plate_path] = [
# Same pipeline for all plates
step_1, step_2, step_3
]
Configuration Scenarios:
# High-performance configuration
global_config = GlobalPipelineConfig(
num_workers=64,
use_threading=False, # Use multiprocessing for CPU-bound tasks
vfs=VFSConfig(memory_limit_gb=128)
)
# Memory-constrained configuration
global_config = GlobalPipelineConfig(
num_workers=8,
vfs=VFSConfig(
default_backend=Backend.DISK,
memory_limit_gb=16
)
)
Plate Metadata Viewer
When to Use: Inspecting plate metadata, verifying microscope format detection, checking grid dimensions, pixel sizes, and available channels before processing.
Complete Workflow Example:
Initialize Plates: Add and initialize plates in the Plate Manager
Select Plates: Select one or more plates to view metadata
Click “Meta” Button: Opens a read-only metadata viewer for each selected plate
Inspect Metadata: Review all plate metadata fields in a structured form
Available Metadata Fields:
The metadata viewer displays all fields from the plate’s metadata using generic reflection:
Microscope Handler: The detected microscope format (ImageXpress, Opera Phenix, OpenHCS, etc.)
Source Filename Parser: The parser used to extract well/site/channel information
Grid Dimensions: Plate layout (rows × columns)
Pixel Size: Physical pixel dimensions (micrometers per pixel)
Image Files: List of all image files in the plate
Channels: Available imaging channels
Wells: Detected well positions
Sites: Number of sites per well
Z-Indexes: Available z-planes for z-stack imaging
Available Backends: Supported storage backends for this plate
Main Subdirectory: Primary data subdirectory (for OpenHCS format)
Multi-Subdirectory Plates:
For OpenHCS plates with multiple subdirectories (e.g., different processing outputs), the metadata viewer creates separate collapsible groups for each subdirectory, allowing you to inspect metadata for each independently.
Example Use Cases:
# Verify pixel size before analysis
# Open metadata viewer → Check "Pixel Size" field
# Ensure it matches expected microscope calibration
# Check available channels
# Open metadata viewer → Review "Channels" field
# Verify all expected fluorescence channels are present
# Inspect grid dimensions
# Open metadata viewer → Check "Grid Dimensions" field
# Confirm plate layout (e.g., 8×12 for 96-well plate)
Benefits:
Read-Only Display: Prevents accidental metadata modification
Generic Reflection: Automatically displays all metadata fields without custom formatting
Multi-Plate Support: Open metadata viewers for multiple plates simultaneously
Format Agnostic: Works with all supported microscope formats (ImageXpress, Opera Phenix, OpenHCS, etc.)
Structured View: Organized display using ParameterFormManager for consistent formatting
Editor Integration
Environment Setup:
# Set your preferred editor
export EDITOR=vim # For vim users
export EDITOR=nano # For nano users
export EDITOR=emacs # For emacs users
export EDITOR="code -w" # For VS Code users (with wait flag)
Supported Editors:
Terminal Editors: vim, nano, emacs, micro, helix
GUI Editors: VS Code (
code -w), Sublime Text, AtomSSH Compatible: All terminal editors work perfectly over SSH
Syntax Highlighting: Most editors automatically detect
.pyfiles
SSH Considerations:
# For SSH sessions, use terminal-only editors
export EDITOR=vim
# or
export EDITOR=nano
# Avoid GUI editors over SSH unless X11 forwarding is configured
File Associations:
The system creates temporary .py files, so your editor’s Python syntax highlighting will automatically activate, providing:
Keyword highlighting:
def,class,import, etc.String highlighting: Proper string and comment formatting
Indentation guides: Visual indentation assistance
Error detection: Basic syntax error highlighting
Best Practices
When to Use Code vs TUI:
Scenario |
Use TUI |
Use Code Editing |
|---|---|---|
Quick parameter changes |
✓ Fast visual editing |
✗ Overkill for simple changes |
Complex parameter tuning |
✗ Limited precision |
✓ Exact values, comments |
Adding single steps |
✓ Visual workflow |
✗ More overhead |
Bulk pipeline changes |
✗ Tedious repetition |
✓ Efficient bulk editing |
Learning the system |
✓ Visual feedback |
✗ Requires Python knowledge |
Sharing configurations |
✗ Hard to communicate |
✓ Copy/paste code snippets |
Version control |
✗ No direct integration |
✓ Perfect for git workflows |
Version Control Integration:
# Save generated code to version control
# 1. Generate code from TUI
# 2. Copy to your project repository
# 3. Commit with meaningful messages
git add pipeline_config.py
git commit -m "Add cell counting step to analysis pipeline"
# Share with collaborators
git push origin feature/cell-counting
Collaborative Workflows:
Code Sharing: Generate code and share via email, Slack, or version control
Configuration Templates: Create reusable configuration templates
Documentation: Add comments in code for team understanding
Review Process: Use code review tools for pipeline validation
Troubleshooting
Common Syntax Errors:
Error |
Solution |
|---|---|
|
Check for missing commas, parentheses, or quotes |
|
Ensure all imports are present at the top |
|
Fix Python indentation (use spaces, not tabs) |
|
Check function call syntax: |
Import Resolution Issues:
# Problem: Missing import
pattern = gaussian_filter(sigma=2.0) # NameError
# Solution: Add the import
from openhcs.processing.backends.filters.gaussian_filter import gaussian_filter
pattern = gaussian_filter(sigma=2.0) # Works
Variable Name Issues:
Widget |
Expected Variable |
Example |
|---|---|---|
Function List Editor |
|
|
Pipeline Editor |
|
|
Plate Manager |
|
All three variables required |
Editor Configuration Issues:
# Problem: Editor not found
# Error: "command not found: vim"
# Solution: Install editor or use available one
export EDITOR=nano # Use nano instead
# Problem: Editor doesn't wait
# VS Code exits immediately, changes not detected
# Solution: Add wait flag
export EDITOR="code -w" # Wait for file to close
State Synchronization Problems:
If the TUI doesn’t update after editing:
Check for syntax errors: Look for error dialogs in the TUI
Verify variable names: Ensure you’re using the correct variable name
Check imports: Make sure all required imports are present
Restart if needed: Close and reopen the TUI widget as a last resort
GlobalPipelineConfig Context Issues:
When editing GlobalPipelineConfig, changes are immediately reflected in the thread-local context:
Cancel Restoration: Clicking “Cancel” restores the original config and reverts context changes
Cross-Window Updates: Changes trigger updates in all open windows (steps, pipelines, etc.)
Context Synchronization: Form edits and code edits both update the global context
If you see stale values in other windows after editing GlobalPipelineConfig:
Check for errors: Look for error dialogs indicating failed context updates
Verify save: Ensure you clicked “Save” (not just edited and closed)
Refresh windows: Close and reopen affected windows if needed
Performance Issues:
For large pipelines or configurations:
Use clean mode: Add
clean_mode=Trueto reduce generated code sizeSimplify temporarily: Edit smaller sections at a time
Check memory: Large configurations may require more system memory
Advanced Usage
Custom Function Integration:
# Add your own functions to pipelines
def custom_preprocessing(image_array):
"""Custom preprocessing function."""
# Your custom logic here
return processed_array
# Use in pipeline
step_custom = FunctionStep(
func=custom_preprocessing,
name="custom_preprocessing",
variable_components=[VariableComponents.PLATE]
)
Configuration Templates:
# Create reusable configuration templates
HIGH_PERFORMANCE_CONFIG = GlobalPipelineConfig(
num_workers=64,
vfs=VFSConfig(memory_limit_gb=128)
)
MEMORY_EFFICIENT_CONFIG = GlobalPipelineConfig(
num_workers=8,
vfs=VFSConfig(default_backend=Backend.DISK)
)
# Use templates
global_config = HIGH_PERFORMANCE_CONFIG
Conditional Processing:
# Add conditional logic to pipelines
if experiment_type == "high_resolution":
pipeline_steps.append(high_res_step)
else:
pipeline_steps.append(standard_step)
# Plate-specific configurations
for plate_path in plate_paths:
if "control" in plate_path:
pipeline_data[plate_path] = control_pipeline
else:
pipeline_data[plate_path] = treatment_pipeline
See Also
Technical Details:
Code/UI Interconversion System - System architecture and design
API Reference - API reference (autogenerated from source code)
Related Guides:
Complete OpenHCS Examples - Complete workflow examples