Spaces:
Sleeping
Sleeping
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 | |
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))) |