Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import ast | |
| import astor | |
| 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 = [] | |
| imports = [] | |
| for node in ast.walk(tree): | |
| # Extract function definitions | |
| if isinstance(node, ast.FunctionDef): | |
| functions.append(node) | |
| # 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}) | |
| # Extract imports | |
| if isinstance(node, (ast.Import, ast.ImportFrom)): | |
| imports.append(node) | |
| return components, functions, imports | |
| except Exception as e: | |
| return [], [], [f"Error parsing code: {str(e)}"] | |
| # Generate Toga code | |
| def generate_toga_code(components, functions, imports): | |
| toga_code = [ | |
| "import toga", | |
| "from toga.style import Pack", | |
| "from toga.style.pack import COLUMN", | |
| "" | |
| ] | |
| # Add imports | |
| toga_code.append("# Dependencies from Gradio app") | |
| for imp in imports: | |
| toga_code.append(astor.to_source(imp).strip()) | |
| toga_code.append("") | |
| # Add function definitions | |
| toga_code.append("# Functions from Gradio app") | |
| for func in functions: | |
| toga_code.append(astor.to_source(func).strip()) | |
| toga_code.append("") | |
| # Generate UI | |
| toga_code.extend([ | |
| "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}'") | |
| # Check for associated function in click events | |
| click_fn = comp["kwargs"].get("fn", f"{functions[0].name if functions else 'function'}") | |
| toga_code.append(f" def button_handler_{idx}(widget):") | |
| toga_code.append(f" # Call {click_fn} with inputs") | |
| toga_code.append(f" pass # Implement logic using input values") | |
| 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, code_text): | |
| if not file and not code_text: | |
| return None, None, "Please upload a Gradio app Python file or paste code in the textbox." | |
| try: | |
| # Use file if provided, else use text | |
| code = file.decode('utf-8') if file else code_text | |
| components, functions, imports = parse_gradio_code(code) | |
| if not components and functions and isinstance(functions[0], str): | |
| return None, None, f"Error: {functions[0]}" | |
| toga_code = generate_toga_code(components, functions, imports) | |
| output_path = "/tmp/toga_app.py" | |
| with open(output_path, "w") as f: | |
| f.write(toga_code) | |
| return output_path, toga_code, "Conversion successful! Preview the Toga code below and download the app." | |
| except Exception as e: | |
| return None, 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) or paste the code below to convert to a Toga desktop app.") | |
| file_input = gr.File(label="Upload Gradio App", file_types=[".py"]) | |
| code_input = gr.Textbox(label="Paste Gradio Code", lines=10, placeholder="Paste your Gradio Python code here...") | |
| convert_button = gr.Button("Convert to Toga") | |
| toga_preview = gr.Textbox(label="Toga Code Preview", lines=15, interactive=False) | |
| 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, code_input], | |
| outputs=[output, toga_preview, status] | |
| ) | |
| # Run Gradio 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) |