gradio2toga / app.py
broadfield-dev's picture
Create app.py
4b6f4f0 verified
raw
history blame
6.92 kB
from flask import Flask, request, send_file, render_template_string
import gradio as gr
import ast
import os
from io import StringIO
app = Flask(__name__)
# Mapping of Gradio components to Toga equivalents
GRADIO_TO_TOGA = {
"Textbox": ("toga.TextInput", "placeholder='Enter Text'"),
"Image": ("toga.Label", "Placeholder: Use file picker for image"),
"Button": ("toga.Button", "on_press=button_handler"),
"Markdown": ("toga.Label", ""),
"Audio": ("toga.Label", "Placeholder: Audio not supported"),
"Video": ("toga.Label", "Placeholder: Video not supported"),
}
# Parse Gradio app code and extract components
def parse_gradio_code(code):
try:
tree = ast.parse(code)
components = []
functions = []
for node in ast.walk(tree):
# Extract function definitions
if isinstance(node, ast.FunctionDef):
functions.append(node.name)
# Extract Gradio component calls
if isinstance(node, ast.Call) and hasattr(node.func, 'attr'):
if node.func.attr in GRADIO_TO_TOGA:
component = node.func.attr
args = [ast.dump(arg, annotate_fields=False) for arg in node.args]
kwargs = {kw.arg: ast.dump(kw.value, annotate_fields=False) for kw in node.keywords}
components.append({"type": component, "args": args, "kwargs": kwargs})
return components, functions
except Exception as e:
return [], [str(e)]
# Generate Toga code from parsed components
def generate_toga_code(components, functions):
toga_code = [
"import toga",
"from toga.style import Pack",
"from toga.style.pack import COLUMN, ROW",
"import os",
"",
"# Placeholder for original functions",
"# Copy your original function definitions here",
f"# Detected functions: {', '.join(functions) if functions else 'None'}",
"",
"def build(app):",
" box = toga.Box(style=Pack(direction=COLUMN, padding=10))",
""
]
for idx, comp in enumerate(components):
toga_comp, extra = GRADIO_TO_TOGA.get(comp["type"], ("toga.Label", f"Placeholder: {comp['type']} not supported"))
# Handle component-specific logic
if comp["type"] == "Textbox":
label = comp["kwargs"].get("label", f"'Text Input {idx}'")
toga_code.append(f" label_{idx} = toga.Label({label}, style=Pack(padding=(5, 5, 0, 5)))")
toga_code.append(f" input_{idx} = {toga_comp}(style=Pack(padding=5))")
toga_code.append(f" box.add(label_{idx})")
toga_code.append(f" box.add(input_{idx})")
elif comp["type"] == "Image":
toga_code.append(f" image_label_{idx} = toga.Label('Image: None selected', style=Pack(padding=5))")
toga_code.append(f" def select_image_{idx}(widget):")
toga_code.append(f" path = app.main_window.open_file_dialog('Select Image', file_types=['png', 'jpg', 'jpeg'])")
toga_code.append(f" if path:")
toga_code.append(f" image_label_{idx}.text = f'Image: {{path}}'")
toga_code.append(f" image_button_{idx} = toga.Button('Upload Image', on_press=select_image_{idx}, style=Pack(padding=5))")
toga_code.append(f" box.add(toga.Label('Upload Image', style=Pack(padding=(5, 5, 0, 5))))")
toga_code.append(f" box.add(image_button_{idx})")
toga_code.append(f" box.add(image_label_{idx})")
elif comp["type"] == "Button":
label = comp["kwargs"].get("value", f"'Submit {idx}'")
toga_code.append(f" def button_handler_{idx}(widget):")
toga_code.append(f" # Placeholder: Implement logic for {functions[0] if functions else 'function'}")
toga_code.append(f" pass")
toga_code.append(f" button_{idx} = {toga_comp}(label={label}, style=Pack(padding=5))")
toga_code.append(f" box.add(button_{idx})")
elif comp["type"] == "Markdown":
text = comp["args"][0] if comp["args"] else "'Markdown Text'"
toga_code.append(f" markdown_{idx} = {toga_comp}({text}, style=Pack(padding=5, font_size=16))")
toga_code.append(f" box.add(markdown_{idx})")
else:
toga_code.append(f" placeholder_{idx} = {toga_comp}('{extra}', style=Pack(padding=5))")
toga_code.append(f" box.add(placeholder_{idx})")
toga_code.extend([
"",
" return box",
"",
"def main():",
" return toga.App('Gradio to Toga App', 'org.example.gradio2toga', startup=build)",
"",
"if __name__ == '__main__':",
" main().main_loop()"
])
return "\n".join(toga_code)
# Gradio interface for file upload
def convert_gradio_to_toga(file):
if not file:
return "Please upload a Gradio app Python file."
try:
# Read uploaded file
code = file.decode('utf-8')
components, functions = parse_gradio_code(code)
if not components and functions and isinstance(functions[0], str):
return f"Error parsing code: {functions[0]}"
# Generate Toga code
toga_code = generate_toga_code(components, functions)
# Save Toga code to a temporary file
output_path = "toga_app.py"
with open(output_path, "w") as f:
f.write(toga_code)
return send_file(output_path, as_attachment=True, download_name="toga_app.py")
except Exception as e:
return f"Error: {str(e)}"
# Gradio interface
def create_gradio_interface():
with gr.Blocks() as interface:
gr.Markdown("# Gradio to Toga Converter")
file_input = gr.File(label="Upload Gradio Python File")
convert_button = gr.Button("Convert to Toga")
output = gr.File(label="Download Toga App")
convert_button.click(
fn=convert_gradio_to_toga,
inputs=file_input,
outputs=output
)
return interface
# Flask route
@app.route('/')
def serve_gradio():
interface = create_gradio_interface()
# For Hugging Face Spaces, launch Gradio
interface.launch(share=False, server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
return render_template_string("""
<!DOCTYPE html>
<html>
<head>
<title>Gradio to Toga Converter</title>
</head>
<body>
<h1>Gradio to Toga Converter</h1>
<p>Access the Gradio interface to upload your Gradio app and download the Toga equivalent.</p>
</body>
</html>
""")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))