Skip to main content

Automatically generate desktop GUIs for Typer CLI applications using Flet

Project description

Typer-GUI

Automatically generate desktop GUIs for existing Typer CLI applications using Flet.

Overview

Typer-GUI is a Python library that bridges the gap between command-line interfaces and graphical user interfaces. If you have a Typer-based CLI application, you can instantly create a desktop GUI for it with just a few lines of code.

Key features:

  • Zero or minimal code changes to your existing Typer app
  • Automatic GUI generation from Typer commands and parameters
  • Type-aware controls - text fields, dropdowns, checkboxes based on parameter types
  • Real-time output streaming - see output as it's produced for long-running commands
  • Markdown rendering - rich formatted output with headings, lists, code blocks, and more
  • Auto-execution - commands that run immediately when selected
  • Custom styling - highlight important commands as buttons
  • Clean, modern interface powered by Flet

Installation

Using pip

pip install typer-gui

This will automatically install typer and flet as dependencies if they're not already present.

For development

If you're contributing to typer-gui or want to try it locally, you can use uv (optional but recommended for faster dependency management):

# Clone the repository
git clone https://github.com/yourusername/typer-gui.git
cd typer-gui

# Create a virtual environment with uv (optional)
uv venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install in development mode
uv pip install -e .

# Or with standard pip
pip install -e .

Quick Start

Here's a minimal example to get you started:

Option 1: Integrated API (Recommended)

# my_app.py
import typer
import typer_gui

# Create the Typer app
app = typer.Typer()

# Create the UI wrapper
ui = typer_gui.Ui(
    app,
    title="My App GUI",
    description="A graphical interface for my CLI app"
)

@app.command()
def greet(name: str, excited: bool = False):
    """Greet someone with a friendly message."""
    punctuation = "!" if excited else "."
    print(f"Hello {name}{punctuation}")

@app.command()
def add(a: int, b: int):
    """Add two numbers together."""
    result = a + b
    print(f"{a} + {b} = {result}")

if __name__ == "__main__":
    # Launch the GUI (or use app() for CLI mode)
    ui.app()

Run it:

python my_app.py

Option 2: Separate GUI Runner

If you prefer to keep your CLI and GUI code separate:

# my_app.py
import typer

app = typer.Typer()

@app.command()
def greet(name: str, excited: bool = False):
    """Greet someone with a friendly message."""
    punctuation = "!" if excited else "."
    print(f"Hello {name}{punctuation}")

if __name__ == "__main__":
    app()
# my_app_gui.py
from typer_gui import run_gui
from my_app import app

if __name__ == "__main__":
    run_gui(
        app,
        title="My App GUI",
        description="A graphical interface for my CLI app"
    )

Run it:

python my_app_gui.py

That's it! Your CLI app now has a fully functional GUI.

GUI Layout

The generated GUI has three main areas:

┌─────────────────────────────────────────┐
│  Title and Description (if provided)    │
├──────────┬──────────────────────────────┤
│          │                              │
│ Commands │  Parameter Form              │
│  List    │  - Input fields              │
│          │  - Dropdowns                 │
│          │  - Checkboxes                │
│          │  - [Run Command] button      │
│          │                              │
│          ├──────────────────────────────┤
│          │  Output Console              │
│          │  (stdout/stderr)             │
└──────────┴──────────────────────────────┘

GUI Customization

Typer-GUI provides decorators to customize how commands appear and behave in the GUI. These work alongside your Typer decorators.

Using @ui.command()

The @ui.command() decorator adds GUI-specific options to your commands:

import typer
import typer_gui

app = typer.Typer()
ui = typer_gui.Ui(app, title="My App")

@app.command()
@ui.command(is_button=True, is_long=True)
def process():
    """Long-running process with real-time output."""
    for i in range(10):
        print(f"Processing step {i}...")
        time.sleep(1)

Available options:

  • is_button (bool): Display command as a highlighted button instead of a text link

    @ui.command(is_button=True)
    
  • is_long (bool): Enable real-time output streaming for long-running commands

    @ui.command(is_long=True)
    
  • is_markdown (bool): Render the command's return value as Markdown

    @app.command()
    @ui.command(is_markdown=True)
    def info() -> str:
        return "# Hello\n\nThis is **bold** text!"
    
  • is_auto_exec (bool): Execute automatically when selected (hides "Run Command" button)

    @app.command()
    @ui.command(is_auto_exec=True)
    def status():
        print(f"Current time: {datetime.now()}")
    

Using @gui_command() (Alternative)

If you're using the separate runner approach, use @gui_command() instead:

from typer_gui import gui_command
import typer

app = typer.Typer()

@app.command()
@gui_command(is_button=True, is_long=True)
def process():
    """Long-running process."""
    for i in range(10):
        print(f"Step {i}")
        time.sleep(1)

The options are identical to @ui.command().

Markdown Output Example

@app.command()
@ui.command(is_markdown=True)
def report() -> str:
    """Generate a formatted report."""
    return """
# System Report

## Status
- **CPU**: OK
- **Memory**: OK
- **Disk**: OK

## Details

| Component | Status | Usage |
|-----------|--------|-------|
| CPU       | ✓      | 45%   |
| Memory    | ✓      | 60%   |
| Disk      | ✓      | 70%   |

```python
# Example code block
print("Hello World")

"""


## Supported Parameter Types

Typer-GUI automatically maps Python types to appropriate GUI controls:

| Python Type | GUI Control | Notes |
|-------------|-------------|-------|
| `str` | Text field | For string input |
| `int` | Text field | Numeric keyboard, validates integers |
| `float` | Text field | Numeric keyboard, validates floats |
| `bool` | Checkbox | For boolean flags |
| `Enum` | Dropdown | Shows all enum values as options |

### Example with Enums

```python
from enum import Enum
import typer

class Color(str, Enum):
    RED = "red"
    GREEN = "green"
    BLUE = "blue"

app = typer.Typer()

@app.command()
def paint(color: Color):
    """Paint with a color."""
    print(f"Painting with {color.value}!")

In the GUI, the color parameter will appear as a dropdown with "red", "green", and "blue" as options.

API Reference

Ui Class

The main entry point for typer-gui's integrated API.

Constructor:

Ui(app, *, title=None, description=None)

Parameters:

  • app (typer.Typer): The Typer application instance to extend
  • title (str, optional): Window title for the GUI
  • description (str, optional): Description text shown at the top of the GUI

Methods:

ui.command(*, is_button=False, is_long=False, is_markdown=False, is_auto_exec=False)

Decorator to add GUI-specific options to a Typer command.

Parameters:

  • is_button (bool): Display as a button in the left panel
  • is_long (bool): Enable real-time output streaming for long-running commands
  • is_markdown (bool): Render return value as Markdown
  • is_auto_exec (bool): Execute automatically when selected, hide 'Run Command' button

Example:

import typer
import typer_gui

app = typer.Typer()
ui = typer_gui.Ui(app, title="My App")

@app.command()
@ui.command(is_button=True, is_long=True)
def process():
    for i in range(10):
        print(f"Step {i}")
        time.sleep(1)

if __name__ == "__main__":
    ui.app()

ui.app()

Launch the GUI application.

Example:

if __name__ == "__main__":
    ui.app()

run_gui(app, *, title=None, description=None)

Launch a Flet GUI for a Typer application (standalone function).

Parameters:

  • app (typer.Typer): A Typer application instance
  • title (str, optional): Window title (defaults to "Typer GUI")
  • description (str, optional): Description text shown at the top of the GUI

Example:

from typer_gui import run_gui

run_gui(app, title="My App", description="Welcome to my application!")

gui_command(*, is_button=False, is_long=False, is_markdown=False, is_auto_exec=False)

Decorator to add GUI-specific options to a Typer command (standalone decorator).

Parameters:

  • is_button (bool): Display as a button in the left panel
  • is_long (bool): Enable real-time output streaming
  • is_markdown (bool): Render return value as Markdown
  • is_auto_exec (bool): Execute automatically when selected

Example:

from typer_gui import gui_command
import typer

app = typer.Typer()

@app.command()
@gui_command(is_button=True, is_markdown=True)
def info() -> str:
    return "# Hello **World**!"

build_gui_model(app, *, title=None, description=None)

Build a structured representation of a Typer app (useful for testing or custom implementations).

Parameters:

  • app (typer.Typer): A Typer application instance
  • title (str, optional): App title
  • description (str, optional): App description

Returns:

  • GuiApp: A structured model containing commands and parameters

Example:

from typer_gui import build_gui_model

gui_model = build_gui_model(app)
for command in gui_model.commands:
    print(f"Command: {command.name}")
    for param in command.params:
        print(f"  - {param.name}: {param.param_type}")

Markdown Class

A return type for commands that produce Markdown output (alternative to is_markdown=True).

Example:

from typer_gui import Markdown

@app.command()
def info() -> Markdown:
    return Markdown("# Hello\n\nThis is **bold** text!")

Note: Using is_markdown=True and returning a string is the preferred approach.

Examples

Check out the examples/ directory for comprehensive working examples:

  • examples/basic_typer_app.py - A complete demonstration featuring:

    • Multiple command types (greet, add, calculate, paint)
    • Enum parameters with dropdown selection
    • Button-styled commands (is_button=True)
    • Long-running commands with real-time output (is_long=True)
    • Markdown-formatted output (is_markdown=True)
    • Auto-executing commands (is_auto_exec=True)
    • Combination features (e.g., button + long-running)
  • examples/basic_gui_runner.py - Alternative approach showing separate GUI runner

To run the main example:

# Using the integrated approach
python examples/basic_typer_app.py

# Or using the separate runner
python examples/basic_gui_runner.py

For development with auto-reload:

uv run flet run examples/basic_typer_app.py --reload

Running Tests

The library includes unit tests for the core reflection logic:

# Install dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

Features & Limitations

Current Features

  • ✅ Automatic reflection of Typer commands and parameters
  • ✅ Support for str, int, float, bool, and Enum types
  • ✅ Required and optional parameters with validation
  • ✅ Default values
  • ✅ Help text and descriptions
  • ✅ Live output capture (stdout/stderr)
  • Real-time output streaming for long-running commands (is_long=True)
  • Markdown rendering for rich formatted output (is_markdown=True)
  • Auto-execution - commands that run on selection (is_auto_exec=True)
  • Button styling for important commands (is_button=True)
  • Integrated API with Ui class for cleaner code
  • ✅ Output clearing when switching between commands
  • ✅ Clean, modern UI powered by Flet
  • ✅ Cross-platform (Windows, macOS, Linux)

Planned Features

  • Path/file selection widgets
  • Support for list/multiple value parameters
  • Date/time pickers
  • Custom themes and color schemes
  • Nested/grouped commands
  • Command history
  • Progress bars and status indicators
  • Command search/filtering

Known Limitations

  • Unsupported parameter types fall back to text input
  • Long-running commands without is_long=True will block the UI
  • No built-in progress bars (commands must print their own progress)

How It Works

  1. Reflection: Typer-GUI uses Python's introspection (inspect module) to analyze your Typer app, extracting command names, parameter types, defaults, and help text from function signatures.

  2. Decorator Metadata: The @ui.command() or @gui_command() decorators attach GUI-specific metadata (button styling, streaming, markdown, auto-exec) to your functions without affecting Typer's CLI behavior.

  3. GUI Generation: Based on the reflection data and decorator metadata, it generates a Flet-based GUI with appropriate controls for each parameter type (text fields, dropdowns, checkboxes).

  4. Direct Execution: When you click "Run" (or when auto-exec triggers), the GUI calls your command function directly (not via subprocess), passing the parsed and validated parameters.

  5. Output Capture:

    • Regular commands: stdout and stderr are buffered and displayed when the command completes
    • Long-running commands (is_long=True): Output is streamed in real-time as it's produced using custom IO writers
    • Markdown commands (is_markdown=True): Return values are rendered with GitHub-flavored markdown formatting

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

License

MIT License - see LICENSE file for details.

Credits

Built with:

  • Typer - CLI framework by Sebastián Ramírez
  • Flet - GUI framework based on Flutter

Made with ❤️ for the Python CLI community

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

typer_gui-0.1.0.tar.gz (22.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

typer_gui-0.1.0-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file typer_gui-0.1.0.tar.gz.

File metadata

  • Download URL: typer_gui-0.1.0.tar.gz
  • Upload date:
  • Size: 22.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for typer_gui-0.1.0.tar.gz
Algorithm Hash digest
SHA256 677fd026092f1c66432c0938c0c8220248f71642c15e07c64438bdad12dd95dc
MD5 37f98dca5ff32e753da81075f379e037
BLAKE2b-256 e2fc4b5e01bb1e241a618ea02fcb1a94312f1afc56833b729f136b7118e82513

See more details on using hashes here.

File details

Details for the file typer_gui-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: typer_gui-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 17.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for typer_gui-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 952e0ba833a89fc433d5a5d9e052d13d74a6b3fbb7ebb848a2e1fbbc6b1f41aa
MD5 6a7ec00c3720f17de346edc30832e3c5
BLAKE2b-256 c6c744c76dcf7abfe3510d69dfea44759cb07c06815cd553df9e567916f525a7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page