Architecture ============ The Blender Development for PyCharm plugin is built as a set of services within the IntelliJ Platform. Core Services ------------- - **``BlenderService``**: The primary project-level facade. It coordinates all other services and provides a simplified API for common operations like starting Blender or reloading extensions. - **``BlenderDownloader``**: An application-level service responsible for downloading and managing different Blender versions from ``download.blender.org``. - **``BlenderLinker``**: Manages the connection between your project code and Blender. It creates symbolic links (or directory junctions on Windows) in Blender's extensions directory. - **``BlenderLauncher``**: Handles the process execution of Blender, including setting up environment variables for sandboxing and configuring app templates. - **``BlenderCommunicationService``**: A TCP server that listens for connections from the Blender instance and sends structured JSON commands (like ``reload``). - **``BlenderScriptGenerator``**: Generates the Python scripts that are injected into Blender at startup to initialize the communication and manage extensions. - **``BlenderLogger``**: Provides consistent logging to both the IntelliJ platform log and the project-local ``blender_plugin.log``. - **``BlenderTelemetryService``**: Collects and logs local-only telemetry for debugging and stability monitoring. - **``PythonLinterService``**: Manages the project's virtual environment (VENV) and handles the installation of ``fake-bpy-module`` for linting. Other Key Components -------------------- - **``BlenderScanner``**: Scans the system path and standard installation directories for already installed Blender versions. It uses version metadata caching for performance. - **``BlenderVersions``**: Provides a centralized source of truth for supported Blender versions and their download URLs. - **``ExternalProcessUtil``**: A utility class for safely executing external processes (like ``pip`` or ``venv`` creation) and capturing their output. Hot-Reloading Protocol ---------------------- The hot-reloading feature works through a TCP-based communication layer: 1. **Startup**: When Blender starts, the plugin injects a startup script that starts a background thread in Blender. 2. **Heartbeat**: Blender sends a "ready" signal upon startup to notify PyCharm that it is ready to receive commands. 3. **Connection**: The Blender side connects to the TCP server started by ``BlenderCommunicationService``. PyCharm utilizes a bidirectional heartbeat and retry logic (up to 5 attempts) to establish a stable connection. 4. **Trigger**: When you save a file or use the reload action, PyCharm sends a structured JSON message: ``{"type": "reload", "name": "your_extension"}``. 5. **Execution**: Blender receives the message and schedules a reload task on the main thread using ``bpy.app.timers``. 6. **Reload Cycle**: - **Disable**: The extension is temporarily disabled. - **Purge**: All modules related to the extension are removed from Python's ``sys.modules``. - **Refresh**: A repository refresh (``bpy.ops.extensions.repo_refresh_all()``) is forced to ensure Blender sees any new files. The plugin uses a dedicated extension repository named ``blender_pycharm``. - **Enable**: The extension is re-enabled, forcing a fresh import of the code. Error Handling -------------- Critical communication errors (e.g., connection failure, heartbeat timeout) are surfaced via IDE balloon notifications in addition to being logged in ``blender_plugin.log``. Sandboxing and Versioning ------------------------- The plugin supports multiple Blender versions (4.2+ and 5.0/5.1) and uses the ``BLENDER_USER_CONFIG`` and ``BLENDER_USER_SCRIPTS`` environment variables to isolate the development environment. When sandboxing is enabled: - Configuration is stored in ``.venv/blender_sandbox/config``. - Scripts and extensions are stored in ``.venv/blender_sandbox/scripts``. - An app template named ``pycharm`` is created within the sandbox to provide a clean environment and a custom splash screen (copied from ``images/sandbox_splash.png`` if present).