Skip to content

EZInput is a Python library that simplifies the creation of graphical user interfaces (GUIs) both in the terminal and in Jupyter notebooks. It provides a convenient way to add various types of widgets, making it easier to interact with your code and visualize results.

License

Notifications You must be signed in to change notification settings

HenriquesLab/EZInput

Repository files navigation

EZInput

PyPI Python Version Downloads Docs License Tests Coverage Contributors GitHub stars GitHub forks DOI

EZInput Logo

Write once, run everywhere - Create interactive GUIs that work seamlessly in both Jupyter notebooks and terminal environments with the exact same code.

EZInput is a Python library that simplifies creating user interfaces for data science, image analysis, and computational research. Whether you're prototyping in a notebook or deploying a command-line tool, your UI code remains identical.

✨ Key Features

  • 🔄 Unified API: Same code works in Jupyter notebooks (.ipynb) and terminal scripts (.py)
  • 📋 Copy-Pastable: Move code between notebooks and scripts without modification
  • 💾 Auto-Persistence: Widget values are automatically saved and restored between sessions
  • 🎯 Type-Safe: Validated inputs with type checking (int, float, text, paths, etc.)
  • 🎨 Rich Widgets: Sliders, dropdowns, text inputs, file pickers, and more
  • Zero Boilerplate: No environment detection code needed - it just works

Note: This project is currently in alpha stage and may undergo API changes.

🎯 Perfect For

  • Image analysis pipelines requiring user parameters
  • Data science workflows with interactive configuration
  • Research tools that need both notebook and CLI interfaces
  • Teaching and tutorials with reproducible parameter sets

📦 Installation

Install EZInput using pip:

pip install ezinput

For development with all optional dependencies:

pip install ezinput[all]

Requirements

  • Python >= 3.9
  • Dependencies are automatically installed:
    • ipywidgets and ipyfilechooser for Jupyter notebooks
    • prompt-toolkit for terminal interfaces
    • pyyaml for configuration persistence

🚀 Quick Start

The Simplest Example

from ezinput import EZInput

# Create a GUI (works in both Jupyter and terminal!)
gui = EZInput("my_app")

# Add widgets - order of adding determines display order
name = gui.add_text("name", "Enter your name:")
age = gui.add_int_range("age", "Select age:", 18, 100)
confirm = gui.add_check("confirm", "Proceed with analysis?")

# In Jupyter: display the widgets
gui.show()

# Access values
print(f"Hello {name.value}, you are {age.value} years old")
if confirm.value:
    print("Starting analysis...")

Running Jupyter Notebooks from Terminal

Execute notebooks as scripts using the ezinput command:

# Run a notebook from terminal
ezinput my_notebook.ipynb

# The notebook will execute with terminal-style prompts
# for all EZInput widgets

This is perfect for:

  • Running analysis pipelines in batch mode
  • Deploying notebook-based tools as CLI applications
  • CI/CD integration

💡 Copy-Paste Friendly: This exact code works in both:

  • Jupyter notebooks (.ipynb files) - displays interactive widgets
  • Terminal scripts (.py files) - shows interactive prompts

Run in terminal with:

python my_script.py

Real-World Example: Image Analysis

Here's a complete example for an image analysis pipeline:

from ezinput import EZInput
import numpy as np

# One EZInput instance per analysis task recommended
gui = EZInput("image_analysis_v1")

# Configuration section
gui.add_label(value="=== Image Processing Parameters ===")
input_file = gui.add_text("input", "Input image path:", 
                          placeholder="/path/to/image.tif")
threshold = gui.add_float_range("threshold", "Detection threshold:", 
                                0.0, 1.0)
min_size = gui.add_int_range("min_size", "Minimum object size (pixels):", 
                             10, 1000)

gui.add_label(value="=== Output Options ===")
save_results = gui.add_check("save", "Save results to disk?")
output_format = gui.add_dropdown("format", ["PNG", "TIFF", "JPEG"], 
                                 "Output format:")

# Display GUI (in Jupyter) or collect inputs (in terminal)
gui.show()

# Your analysis code using the values
def run_analysis(values):
    """Process image with user-provided parameters."""
    print(f"Processing {values['input'].value}")
    print(f"Using threshold: {values['threshold'].value}")
    print(f"Minimum size: {values['min_size'].value}")
    
    # Your image processing code here
    # ...
    
    if values['save'].value:
        print(f"Saving as {values['format'].value}")

# Add a callback button (in Jupyter: creates button, in terminal: executes after show())
gui.add_callback("process", run_analysis, gui.get_values(), 
                 description="Run Analysis")

Key Points:

  • Widget tags (like "input", "threshold") must be unique within each GUI instance
  • Create one EZInput instance per analysis task or computational pipeline
  • The order you add widgets is the order they appear on screen
  • Use remember_value=True parameter on widgets to persist values between runs

Terminal vs Jupyter: Same Code, Different Experience

In Jupyter Notebook:

  • Widgets appear as interactive UI elements
  • Sliders, dropdowns, and buttons are fully visual
  • Call gui.show() to display the interface

In Terminal:

  • Interactive prompts appear sequentially
  • Type-validated input with autocomplete
  • Press Enter to submit each value
  • Run with: python your_script.py

📚 Core Concepts

1. Automatic Environment Detection

EZInput automatically detects whether it's running in Jupyter or terminal:

from ezinput import EZInput

# No environment detection code needed!
gui = EZInput("my_app")

# Or import specific versions if needed:
from ezinput import EZInputJupyter  # Jupyter-specific
from ezinput import EZInputPrompt   # Terminal-specific

2. Value Persistence & Priority

Values are automatically saved to ~/.ezinput/{title}.yml. Priority order:

  1. Loaded parameters (from load_parameters())
  2. Remembered values (from previous runs, if remember_value=True parameter)
  3. Explicit defaults (passed via default= parameter)
  4. Widget defaults (empty string, 0, False, etc.)
# Values automatically persist when remember_value=True (default for most widgets)
gui = EZInput("my_app")
age = gui.add_int_range("age", "Age:", 0, 120, remember_value=True)
# Next time: shows the previously entered value!

# Load from specific configuration file
gui = EZInput("my_app")
gui.load_parameters("experiment_config.yml")

# Or save current values to share
gui.save_parameters("my_optimal_params.yml")

3. Configuration Files

Create shareable configuration files:

gui = EZInput("my_analysis")

# ... add widgets and collect values ...

# Save current values
gui.save_parameters("my_config.yml")  # Explicit filename
# or
gui.save_parameters("")  # Auto-named: my_analysis_parameters.yml

# Share this file with colleagues!
# They can load it:
gui2 = EZInput("my_analysis", params_file="my_config.yml")

Config file location:

  • Explicit saves: wherever you specify
  • Auto-saved settings: ~/.ezinput/{title}.yml

Resetting to defaults:

# Clear all remembered values - returns widgets to original defaults
gui.restore_defaults()

# ⚠️ Important: Re-run your cell (Jupyter) or script (terminal) to see the reset take effect
# The method removes the memory file but doesn't immediately change displayed widgets

🎨 Available Widgets

All widgets work identically in Jupyter and terminal. Here's the complete reference:

Text Input Widgets

Widget Description Example
add_text() Single-line text input gui.add_text("name", "Your name:")
add_text_area() Multi-line text input gui.add_text_area("notes", "Comments:")
# Text input with placeholder
email = gui.add_text("email", "Email address:", 
                     placeholder="user@example.com")

# Multi-line text area
notes = gui.add_text_area("description", "Project description:",
                          placeholder="Enter detailed description...")

Numeric Input Widgets

Widget Description Example
add_int_range() Integer slider/input with bounds gui.add_int_range("count", "Count:", 1, 100)
add_float_range() Float slider/input with bounds gui.add_float_range("alpha", "Alpha:", 0.0, 1.0)
add_int_text() Integer input (no bounds) gui.add_int_text("age", "Age:")
add_float_text() Float input (no bounds) gui.add_float_text("weight", "Weight:")
add_bounded_int_text() Integer input with validation gui.add_bounded_int_text("percent", "%:", 0, 100)
add_bounded_float_text() Float input with validation gui.add_bounded_float_text("ratio", "Ratio:", 0.0, 1.0)
# Slider-style (Jupyter) or validated prompt (terminal)
iterations = gui.add_int_range("iter", "Iterations:", 10, 1000)

# Free-form number input
temperature = gui.add_float_text("temp", "Temperature (°C):")

# Bounded input with automatic clamping
percentage = gui.add_bounded_int_text("coverage", "Coverage %:", 0, 100)

Selection Widgets

Widget Description Example
add_check() Boolean yes/no or checkbox gui.add_check("verbose", "Enable verbose output?")
add_dropdown() Single selection from list gui.add_dropdown("method", ["A", "B", "C"], "Method:")
# Boolean checkbox (Jupyter) or yes/no prompt (terminal)
debug = gui.add_check("debug", "Enable debug mode?")

# Dropdown with autocomplete in terminal
algorithm = gui.add_dropdown("algo", 
                             ["linear", "rbf", "polynomial"],
                             "Interpolation method:")

File & Path Widgets

Widget Environment Description
add_path_completer() Terminal only Path input with autocomplete
add_file_upload() Jupyter only Visual file picker
# Terminal: autocomplete-enabled path input
input_file = gui.add_path_completer("input", "Select input file:")

# Jupyter: visual file browser
input_file = gui.add_file_upload("input", accept="*.tif")

Display Widgets

Widget Description Example
add_label() Static text/header gui.add_label(value="=== Settings ===")
add_output() Output display area (Jupyter) gui.add_output("results")
add_HTML() Rich HTML content (Jupyter only) gui.add_HTML("info", "<b>Note:</b> ...")
# Section headers
gui.add_label(value="=== Input Parameters ===")

# Output area for results (Jupyter)
gui.add_output("results")
with gui["results"]:
    print("Analysis complete!")

# Rich formatting (Jupyter only)
gui.add_HTML("warning", '<p style="color:red">⚠️ Experimental feature</p>')

Callback & Actions

# add_callback ensures naming consistency between Jupyter and terminal
def process_data(values):
    """Process data with current parameter values."""
    threshold = values["threshold"].value
    method = values["method"].value
    print(f"Processing with {method} at threshold {threshold}")
    # Your processing code here...

# In Jupyter: creates a button
# In terminal: executes immediately
gui.add_callback("run", process_data, gui.get_values(), 
                 description="Start Processing")

Why add_callback? The name maintains API consistency between Jupyter (button-based) and terminal (immediate execution) interfaces.

💡 Best Practices

1. Unique Widget Tags

# ✅ Good: unique tags
gui.add_text("input_file", "File:")
gui.add_text("output_file", "Output:")

# ❌ Bad: duplicate tags
gui.add_text("file", "File:")
gui.add_text("file", "Output:")  # Overwrites the first one!

2. One GUI Per Task

# ✅ Good: separate GUIs for different analyses
preprocessing_gui = EZInput("preprocessing_v1")
analysis_gui = EZInput("main_analysis_v2")

# ❌ Avoid: reusing the same GUI instance for different purposes
gui = EZInput("multi_purpose")  # Config files may conflict

3. Meaningful Titles

# ✅ Good: descriptive, versioned titles
gui = EZInput("cell_segmentation_v2")
gui = EZInput("image_denoising_2024")

# ✅ Also good: user/project specific
gui = EZInput("john_experiment_setup")

# ❌ Avoid: generic titles that may conflict
gui = EZInput("test")
gui = EZInput("gui")

4. Display Order Matters

# Widgets appear in the order you add them
gui = EZInput("my_app")

# This order...
gui.add_label(value="=== Step 1 ===")
gui.add_text("name", "Name:")
gui.add_int_range("age", "Age:", 0, 120)
gui.add_label(value="=== Step 2 ===")  
gui.add_check("confirm", "Confirm?")

# ...is the order users see them in
gui.show()

📖 Documentation

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

📄 License

This project is licensed under the MIT License - see the LICENSE.txt file for details.


Made with ❤️ for the scientific Python community from ezinput.ezinput_jupyter import EZInputJupyter

gui = EZInputJupyter() gui.add_text('name', description='Name:', style={'description_width': 'initial'})


## Example: Adding a Custom Widget

```python
# Pass a widget class (with args/kwargs)
gui.add_custom_widget('slider', widgets.IntSlider, min=0, max=10, value=5)

# Or pass an already-instantiated widget
dropdown = widgets.Dropdown(options=['A', 'B', 'C'], value='A')
gui.add_custom_widget('dropdown', dropdown)

Examples

Terminal Example

from ezinput import EZInput

gui = EZInput(title="Terminal Example")
gui.add_text("username", "Enter your username:", remember_value=True)
gui.add_int_range("age", "Enter your age:", 18, 100, remember_value=True)
gui.save_settings()

Jupyter Notebook Example

from ezinput import EZInput

gui = EZInput(title="Jupyter Example")
gui.add_text("username", description="Enter your username:", remember_value=True)
gui.add_int_range("age", description="Enter your age:", vmin=18, vmax=100, remember_value=True)
gui.show()

License

This project is licensed under the MIT License. See the LICENSE file for details.

About

EZInput is a Python library that simplifies the creation of graphical user interfaces (GUIs) both in the terminal and in Jupyter notebooks. It provides a convenient way to add various types of widgets, making it easier to interact with your code and visualize results.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •