|
|
|
import gradio as gr |
|
from app import demo as app |
|
import os |
|
|
|
_docs = {'GradioDesigner': {'description': 'A visual designer component for building Gradio layouts with all components', 'members': {'__init__': {'value': {'type': 'dict | None', 'default': 'None', 'description': None}, 'label': {'type': 'str | None', 'default': 'None', 'description': None}}, 'postprocess': {'value': {'type': 'dict | None', 'description': None}}, 'preprocess': {'return': {'type': 'dict | None', 'description': None}, 'value': None}}, 'events': {'change': {'type': None, 'default': None, 'description': 'Triggered when the value of the GradioDesigner changes either because of user input (e.g. a user types in a textbox) OR because of a function update (e.g. an image receives a value from the output of an event trigger). See `.input()` for a listener that is only triggered by user input.'}, 'input': {'type': None, 'default': None, 'description': 'This listener is triggered when the user changes the value of the GradioDesigner.'}}}, '__meta__': {'additional_interfaces': {}, 'user_fn_refs': {'GradioDesigner': []}}} |
|
|
|
abs_path = os.path.join(os.path.dirname(__file__), "css.css") |
|
|
|
with gr.Blocks( |
|
css=abs_path, |
|
theme=gr.themes.Default( |
|
font_mono=[ |
|
gr.themes.GoogleFont("Inconsolata"), |
|
"monospace", |
|
], |
|
), |
|
) as demo: |
|
gr.Markdown( |
|
""" |
|
# `gradio_gradiodesigner` |
|
|
|
<div style="display: flex; gap: 7px;"> |
|
<img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.1%20-%20orange"> |
|
</div> |
|
|
|
gradio designer |
|
""", elem_classes=["md-custom"], header_links=True) |
|
app.render() |
|
gr.Markdown( |
|
""" |
|
## Installation |
|
|
|
```bash |
|
pip install gradio_gradiodesigner |
|
``` |
|
|
|
## Usage |
|
|
|
```python |
|
import gradio as gr |
|
from gradio_gradiodesigner import GradioDesigner |
|
import json |
|
|
|
def analyze_design(design_config): |
|
\"\"\"Analyze the design configuration\"\"\" |
|
if not design_config or not isinstance(design_config, dict): |
|
return "No design configuration provided" |
|
|
|
components = design_config.get('components', []) |
|
|
|
# Count components by type |
|
component_types = {} |
|
for comp in components: |
|
comp_type = comp.get('type', 'Unknown') |
|
component_types[comp_type] = component_types.get(comp_type, 0) + 1 |
|
|
|
# Calculate coverage area |
|
if components: |
|
positions = [(comp['position']['x'], comp['position']['y']) for comp in components] |
|
min_x, min_y = min(pos[0] for pos in positions), min(pos[1] for pos in positions) |
|
max_x, max_y = max(pos[0] for pos in positions), max(pos[1] for pos in positions) |
|
coverage = f"{max_x - min_x} x {max_y - min_y} pixels" |
|
else: |
|
coverage = "No components" |
|
|
|
analysis = f\"\"\"π **Design Analysis** |
|
|
|
**Component Summary:** |
|
β’ Total components: {len(components)} |
|
β’ Component types: {dict(component_types)} |
|
β’ Canvas coverage: {coverage} |
|
|
|
**Component Details:** |
|
\"\"\" |
|
|
|
for i, comp in enumerate(components, 1): |
|
analysis += f"\n{i}. **{comp['type']}** (`{comp['id']}`)" |
|
analysis += f"\n - Position: ({comp['position']['x']}, {comp['position']['y']})" |
|
analysis += f"\n - Size: {comp['size']['width']}Γ{comp['size']['height']}" |
|
if comp.get('props', {}).get('label'): |
|
analysis += f"\n - Label: \"{comp['props']['label']}\"" |
|
|
|
return analysis |
|
|
|
def generate_gradio_code(design_config): |
|
\"\"\"Generate complete Gradio code from design\"\"\" |
|
if not design_config or not isinstance(design_config, dict): |
|
return "# No design to generate code from" |
|
|
|
components = design_config.get('components', []) |
|
|
|
code = '''import gradio as gr |
|
|
|
def process_input(*args): |
|
\"\"\"Process the inputs from your app\"\"\" |
|
return "Hello from your generated app!" |
|
|
|
with gr.Blocks(title="Generated Gradio App") as demo: |
|
gr.Markdown("# π Generated Gradio App") |
|
gr.Markdown("This app was generated from your visual design!") |
|
|
|
''' |
|
|
|
# Sort components by position (top to bottom, left to right) |
|
sorted_components = sorted(components, key=lambda c: (c['position']['y'], c['position']['x'])) |
|
|
|
component_vars = [] |
|
|
|
for comp in sorted_components: |
|
comp_type = comp.get('type', 'Textbox') |
|
comp_id = comp.get('id', 'component') |
|
props = comp.get('props', {}) |
|
|
|
# Build component declaration |
|
prop_parts = [] |
|
for key, value in props.items(): |
|
if key in ['label', 'placeholder', 'value'] and isinstance(value, str): |
|
prop_parts.append(f'{key}="{value}"') |
|
elif key in ['minimum', 'maximum', 'step', 'lines', 'max_length', 'precision'] and isinstance(value, (int, float)): |
|
prop_parts.append(f'{key}={value}') |
|
elif key == 'choices' and isinstance(value, list): |
|
prop_parts.append(f'{key}={value}') |
|
elif isinstance(value, bool): |
|
prop_parts.append(f'{key}={value}') |
|
|
|
prop_string = ", ".join(prop_parts) if prop_parts else "" |
|
|
|
code += f" {comp_id} = gr.{comp_type}({prop_string})\n" |
|
component_vars.append(comp_id) |
|
|
|
# Add a simple interaction if there are components |
|
if component_vars: |
|
inputs = [var for var in component_vars if not var.startswith('button')] |
|
outputs = [var for var in component_vars if var.startswith('button')] |
|
|
|
if not outputs: |
|
outputs = inputs[:1] # Use first input as output if no buttons |
|
|
|
if inputs and outputs: |
|
code += f"\n # Add interactions\n" |
|
code += f" # Example: connect inputs to outputs\n" |
|
code += f" # {outputs[0]}.click(process_input, inputs=[{', '.join(inputs)}], outputs=[{outputs[0]}])\n" |
|
|
|
code += ''' |
|
if __name__ == "__main__": |
|
demo.launch() |
|
''' |
|
|
|
return code |
|
|
|
with gr.Blocks(title="Gradio Visual Designer Pro", theme=gr.themes.Soft()) as demo: |
|
gr.Markdown(\"\"\" |
|
# π¨ Gradio Visual Designer Pro |
|
|
|
**Build your Gradio apps visually!** Drag and drop components, customize properties, and generate production-ready code. |
|
|
|
**Features:** 25+ Gradio components β’ Real-time editing β’ Code generation β’ Export options |
|
\"\"\") |
|
|
|
with gr.Row(): |
|
designer = GradioDesigner( |
|
label="Visual App Designer", |
|
value={"components": [], "layout": "blocks"} |
|
) |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
analysis_output = gr.Markdown( |
|
value="Design analysis will appear here...", |
|
label="Design Analysis" |
|
) |
|
|
|
with gr.Column(scale=1): |
|
code_output = gr.Code( |
|
label="Generated Gradio Code", |
|
language="python", |
|
value="# Design your app above to see generated code", |
|
lines=20 |
|
) |
|
|
|
with gr.Row(): |
|
analyze_btn = gr.Button("π Analyze Design", variant="secondary") |
|
generate_btn = gr.Button("π Generate Code", variant="primary") |
|
clear_btn = gr.Button("ποΈ Clear All", variant="stop") |
|
|
|
# Event handlers |
|
designer.change( |
|
fn=analyze_design, |
|
inputs=[designer], |
|
outputs=[analysis_output] |
|
) |
|
|
|
analyze_btn.click( |
|
fn=analyze_design, |
|
inputs=[designer], |
|
outputs=[analysis_output] |
|
) |
|
|
|
generate_btn.click( |
|
fn=generate_gradio_code, |
|
inputs=[designer], |
|
outputs=[code_output] |
|
) |
|
|
|
clear_btn.click( |
|
fn=lambda: {"components": [], "layout": "blocks"}, |
|
outputs=[designer] |
|
) |
|
|
|
if __name__ == "__main__": |
|
demo.launch() |
|
|
|
``` |
|
""", elem_classes=["md-custom"], header_links=True) |
|
|
|
|
|
gr.Markdown(""" |
|
## `GradioDesigner` |
|
|
|
### Initialization |
|
""", elem_classes=["md-custom"], header_links=True) |
|
|
|
gr.ParamViewer(value=_docs["GradioDesigner"]["members"]["__init__"], linkify=[]) |
|
|
|
|
|
gr.Markdown("### Events") |
|
gr.ParamViewer(value=_docs["GradioDesigner"]["events"], linkify=['Event']) |
|
|
|
|
|
|
|
|
|
gr.Markdown(""" |
|
|
|
### User function |
|
|
|
The impact on the users predict function varies depending on whether the component is used as an input or output for an event (or both). |
|
|
|
- When used as an Input, the component only impacts the input signature of the user function. |
|
- When used as an output, the component only impacts the return signature of the user function. |
|
|
|
The code snippet below is accurate in cases where the component is used as both an input and an output. |
|
|
|
|
|
|
|
```python |
|
def predict( |
|
value: dict | None |
|
) -> dict | None: |
|
return value |
|
``` |
|
""", elem_classes=["md-custom", "GradioDesigner-user-fn"], header_links=True) |
|
|
|
|
|
|
|
|
|
demo.load(None, js=r"""function() { |
|
const refs = {}; |
|
const user_fn_refs = { |
|
GradioDesigner: [], }; |
|
requestAnimationFrame(() => { |
|
|
|
Object.entries(user_fn_refs).forEach(([key, refs]) => { |
|
if (refs.length > 0) { |
|
const el = document.querySelector(`.${key}-user-fn`); |
|
if (!el) return; |
|
refs.forEach(ref => { |
|
el.innerHTML = el.innerHTML.replace( |
|
new RegExp("\\b"+ref+"\\b", "g"), |
|
`<a href="#h-${ref.toLowerCase()}">${ref}</a>` |
|
); |
|
}) |
|
} |
|
}) |
|
|
|
Object.entries(refs).forEach(([key, refs]) => { |
|
if (refs.length > 0) { |
|
const el = document.querySelector(`.${key}`); |
|
if (!el) return; |
|
refs.forEach(ref => { |
|
el.innerHTML = el.innerHTML.replace( |
|
new RegExp("\\b"+ref+"\\b", "g"), |
|
`<a href="#h-${ref.toLowerCase()}">${ref}</a>` |
|
); |
|
}) |
|
} |
|
}) |
|
}) |
|
} |
|
|
|
""") |
|
|
|
demo.launch() |
|
|