import gradio as gr from skidl import * import tempfile import os import time import sys import logging # Set up logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Check for required dependencies try: import gradio import skidl except ImportError as e: print(f"Missing dependency: {e}") print("Please install requirements: pip install gradio skidl") sys.exit(1) def check_device_lib(): """Check if device.lib exists in the current directory.""" current_dir = os.getcwd() device_lib_path = os.path.join(current_dir, 'device.lib') if os.path.exists(device_lib_path): logger.info(f"Found device.lib in current directory: {device_lib_path}") return True logger.warning(f"device.lib not found in current directory: {current_dir}") return False def configure_skidl_libs(): """Configure SKiDL to use device.lib from the current directory.""" current_dir = os.getcwd() # Clear existing library search paths lib_search_paths.clear() # Assume lib_search_paths is a dict (newer SKiDL) lib_key = getattr(skidl, 'LIB_SPICE', 'spice') lib_search_paths[lib_key] = [current_dir] logger.info(f"Using dict-based lib_search_paths with key '{lib_key}' and {current_dir}") # Conditionally set default library if set_default_lib exists try: set_default_lib('Device') logger.info("Set default library to Device using set_default_lib") except NameError: logger.info("set_default_lib not available; relying on lib_search_paths for Device library") # Suppress KiCad warnings by setting dummy environment variables os.environ['KICAD_SYMBOL_DIR'] = current_dir os.environ['KICAD6_SYMBOL_DIR'] = current_dir os.environ['KICAD7_SYMBOL_DIR'] = current_dir os.environ['KICAD8_SYMBOL_DIR'] = current_dir logger.info("Set dummy KiCad environment variables to suppress warnings") def cleanup_old_files(): """Clean up temporary files older than 1 hour.""" temp_dir = tempfile.gettempdir() for f in os.listdir(temp_dir): if f.endswith('.net') and os.path.getmtime(os.path.join(temp_dir, f)) < time.time() - 3600: try: os.remove(os.path.join(temp_dir, f)) except OSError: pass def generate_circuit(skidl_code): """Generate circuit netlist from SKiDL code.""" if not skidl_code.strip(): return "Error: Empty code provided", None reset() tmp_path = None try: configure_skidl_libs() if not check_device_lib(): return "Error: device.lib not found in the current directory. Please ensure it exists.", None try: compile(skidl_code, '', 'exec') except SyntaxError as e: return f"Syntax Error: {e}", None with tempfile.NamedTemporaryFile(delete=False, suffix='.py') as tmp: tmp.write(skidl_code.encode('utf-8')) tmp_path = tmp.name namespace = {} exec(open(tmp_path).read(), namespace) netlist_str = generate_netlist() if not netlist_str.strip(): return "Error: No netlist generated", None with tempfile.NamedTemporaryFile(delete=False, suffix='.net', mode='w') as out_file: out_file.write(netlist_str) netlist_file_path = out_file.name return "Netlist generated successfully!", netlist_file_path except SyntaxError as e: return f"Syntax Error: {e}", None except Exception as e: return f"Error: {str(e)}", None finally: if tmp_path and os.path.exists(tmp_path): try: os.remove(tmp_path) except OSError: pass # Gradio UI with gr.Blocks() as demo: gr.Markdown( """ # SKiDL Circuit Builder (No KiCad Needed) **Note**: Ensure `device.lib` is in the same directory as this script to resolve components like resistors, LEDs, etc. """ ) # Example code: 5 LEDs with resistors example_code = """ from skidl import * vcc = Vcc() gnd = Gnd() leds = [Part('Device', 'LED', value='LED') for _ in range(5)] resistors = [Part('Device', 'R', value='220') for _ in range(5)] for r, led in zip(resistors, leds): vcc & r & led & gnd """ skidl_input = gr.Textbox( label="Enter SKiDL Code Here", lines=20, placeholder=example_code ) output_message = gr.Textbox(label="Status / Error", interactive=False) download_btn = gr.File(label="Download Netlist (.net)") run_button = gr.Button("Build Circuit") run_button.click( fn=generate_circuit, inputs=[skidl_input], outputs=[output_message, download_btn] ) if __name__ == "__main__": cleanup_old_files() demo.launch()