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 extendtitle(str, optional): Window title for the GUIdescription(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 panelis_long(bool): Enable real-time output streaming for long-running commandsis_markdown(bool): Render return value as Markdownis_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 instancetitle(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 panelis_long(bool): Enable real-time output streamingis_markdown(bool): Render return value as Markdownis_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 instancetitle(str, optional): App titledescription(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
Uiclass 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=Truewill block the UI - No built-in progress bars (commands must print their own progress)
How It Works
-
Reflection: Typer-GUI uses Python's introspection (
inspectmodule) to analyze your Typer app, extracting command names, parameter types, defaults, and help text from function signatures. -
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. -
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).
-
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.
-
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:
Made with ❤️ for the Python CLI community
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
677fd026092f1c66432c0938c0c8220248f71642c15e07c64438bdad12dd95dc
|
|
| MD5 |
37f98dca5ff32e753da81075f379e037
|
|
| BLAKE2b-256 |
e2fc4b5e01bb1e241a618ea02fcb1a94312f1afc56833b729f136b7118e82513
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
952e0ba833a89fc433d5a5d9e052d13d74a6b3fbb7ebb848a2e1fbbc6b1f41aa
|
|
| MD5 |
6a7ec00c3720f17de346edc30832e3c5
|
|
| BLAKE2b-256 |
c6c744c76dcf7abfe3510d69dfea44759cb07c06815cd553df9e567916f525a7
|