Spaces:
Sleeping
Sleeping
File size: 7,691 Bytes
4b6f4f0 2b4ec71 4b6f4f0 a62df18 05977dd 4b6f4f0 05977dd 4b6f4f0 2b4ec71 4b6f4f0 2b4ec71 4b6f4f0 2b4ec71 4b6f4f0 2b4ec71 4b6f4f0 2b4ec71 4b6f4f0 2b4ec71 4b6f4f0 05977dd 2b4ec71 4b6f4f0 05977dd 2b4ec71 4b6f4f0 2b4ec71 4b6f4f0 05977dd 4b6f4f0 05977dd 4b6f4f0 05977dd 4b6f4f0 05977dd 4b6f4f0 05977dd 4b6f4f0 05977dd 2b4ec71 4b6f4f0 2b4ec71 05977dd 4b6f4f0 05977dd 4b6f4f0 05977dd d02164f a62df18 4b6f4f0 d02164f 2b4ec71 4b6f4f0 a62df18 4b6f4f0 2b4ec71 05977dd 4b6f4f0 a62df18 4b6f4f0 a62df18 4b6f4f0 05977dd d02164f a364f6d d02164f 05977dd a62df18 a364f6d 05977dd d02164f a62df18 05977dd 4b6f4f0 d02164f 4b6f4f0 a364f6d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
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) |