gradio2toga / app.py
broadfield-dev's picture
Update app.py
a364f6d verified
raw
history blame
6.55 kB
import gradio as gr
import ast
import os
from io import StringIO
# Mapping of Gradio components to Toga equivalents
GRADIO_TO_TOGA = {
"Textbox": ("toga.TextInput", "style=Pack(padding=5)", "Text Input"),
"Image": ("toga.Label", "Placeholder: Image file picker", "Image Upload"),
"Button": ("toga.Button", "on_press=button_handler", "Submit"),
"Markdown": ("toga.Label", "style=Pack(padding=5, font_size=14)", "Markdown Text"),
"Audio": ("toga.Label", "Placeholder: Audio file picker", "Audio Upload"),
"Video": ("toga.Label", "Placeholder: Video file picker", "Video Upload"),
}
# Parse Gradio app code
def parse_gradio_code(code):
try:
tree = ast.parse(code)
components = []
functions = []
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
functions.append(node.name)
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 [], [f"Error parsing code: {str(e)}"]
# Generate Toga code
def generate_toga_code(components, functions):
toga_code = [
"import toga",
"from toga.style import Pack",
"from toga.style.pack import COLUMN",
"",
"# Original functions (copy these from your Gradio app)",
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, label_prefix = GRADIO_TO_TOGA.get(comp["type"], ("toga.Label", f"Placeholder: {comp['type']} not supported", "Unknown"))
if comp["type"] == "Textbox":
label = comp["kwargs"].get("label", f"'{label_prefix} {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}({extra})")
toga_code.append(f" box.add(label_{idx})")
toga_code.append(f" box.add(input_{idx})")
elif comp["type"] in ["Image", "Audio", "Video"]:
file_types = {
"Image": "['png', 'jpg', 'jpeg']",
"Audio": "['mp3', 'wav']",
"Video": "['mp4', 'avi']"
}.get(comp["type"], "['*']")
toga_code.append(f" {comp['type'].lower()}_label_{idx} = toga.Label('{label_prefix}: None selected', style=Pack(padding=5))")
toga_code.append(f" def select_{comp['type'].lower()}_{idx}(widget):")
toga_code.append(f" path = app.main_window.open_file_dialog('Select {comp['type']}', file_types={file_types})")
toga_code.append(f" if path:")
toga_code.append(f" {comp['type'].lower()}_label_{idx}.text = f'{label_prefix}: {{path}}'")
toga_code.append(f" {comp['type'].lower()}_button_{idx} = toga.Button('Upload {comp['type']}', on_press=select_{comp['type'].lower()}_{idx}, style=Pack(padding=5))")
toga_code.append(f" box.add(toga.Label('{label_prefix}', style=Pack(padding=(5, 5, 0, 5))))")
toga_code.append(f" box.add({comp['type'].lower()}_button_{idx})")
toga_code.append(f" box.add({comp['type'].lower()}_label_{idx})")
elif comp["type"] == "Button":
label = comp["kwargs"].get("value", f"'{label_prefix} {idx}'")
toga_code.append(f" def button_handler_{idx}(widget):")
toga_code.append(f" # Implement logic for {functions[0] if functions else 'function'}")
toga_code.append(f" pass")
toga_code.append(f" button_{idx} = {toga_comp}(label={label}, {extra})")
toga_code.append(f" box.add(button_{idx})")
elif comp["type"] == "Markdown":
text = comp["args"][0] if comp["args"] else f"'{label_prefix} {idx}'"
toga_code.append(f" markdown_{idx} = {toga_comp}({text}, {extra})")
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)
# Conversion function
def convert_gradio_to_toga(file):
if not file:
return None, "Please upload a Gradio app Python file."
try:
code = file.decode('utf-8')
components, functions = parse_gradio_code(code)
if not components and functions and isinstance(functions[0], str):
return None, f"Error: {functions[0]}"
toga_code = generate_toga_code(components, functions)
output_path = "/tmp/toga_app.py"
with open(output_path, "w") as f:
f.write(toga_code)
return output_path, "Conversion successful! Download the Toga app below."
except Exception as e:
return None, f"Error: {str(e)}"
# Gradio interface
with gr.Blocks(theme=gr.themes.Soft()) as interface:
gr.Markdown("# Gradio to Toga Converter")
gr.Markdown("Upload a Gradio app (.py) to convert it to a Toga desktop app.")
file_input = gr.File(label="Upload Gradio App", file_types=[".py"])
convert_button = gr.Button("Convert to Toga")
output = gr.File(label="Download Toga App")
status = gr.Textbox(label="Status", interactive=False)
convert_button.click(
fn=convert_gradio_to_toga,
inputs=file_input,
outputs=[output, status]
)
# Run Gradio directly for Hugging Face Spaces
if __name__ == "__main__":
interface.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)), quiet=True)