# SimLab Composer Python Addon Development Guide

## Table of Contents
1. [Introduction](#introduction)
2. [Prerequisites](#prerequisites)
3. [Addon Structure](#addon-structure)
4. [Step-by-Step Guide](#step-by-step-guide)
5. [Configuration Reference](#configuration-reference)
6. [Managing Dependencies](#managing-dependencies)
7. [Testing Your Addon](#testing-your-addon)
8. [Packaging and Distribution](#packaging-and-distribution)
9. [Installation via Addons Manager](#installation-via-addons-manager)
10. [Best Practices](#best-practices)
11. [Troubleshooting](#troubleshooting)

---

## Introduction

SimLab Composer supports custom Python-based addons that extend its functionality. This guide walks you through the complete process of creating, configuring, and deploying a Python addon for SimLab Composer using the built-in Addons Manager.

### What You'll Learn
- How to structure a SimLab Python addon
- How to configure addon metadata and menus
- How to manage external Python dependencies
- How to package and distribute your addon
- How to install addons using SimLab's Addons Manager

---

## Prerequisites

### Required Software
- **SimLab Composer** (with Addons Manager support)
- **Python 3.x** installed on your system

### Verify Python Installation

**Windows:**
```powershell
python --version
```

**macOS / Linux:**
```bash
python3 --version
```

If Python is not installed, download it from [python.org](https://www.python.org/downloads/)

> **⚠️ Important:** Make sure that **pip** (Python's package installer) is installed with your Python setup. Pip is required for managing addon dependencies. Most Python installations include pip by default, but you can verify by running:
> 
> ```powershell
> # Windows
> python -m pip --version
> # Or use python.exe -m pip --version
> ```
> 
> ```bash
> # macOS / Linux
> python3 -m pip --version
> ```
> 
> **Example output:**
> ```
> pip 25.2 from C:\Users\...\site-packages\pip (python 3.14)
> ```
> 
> If pip is not installed, refer to the [pip installation guide](https://pip.pypa.io/en/stable/installation/)

### Recommended Knowledge
- Basic Python programming
- Understanding of file structures and paths
- JSON format basics

---

## Addon Structure

A typical SimLab addon follows this directory structure:

```
MyAddon/
├── config.json              # Addon configuration (required)
├── main.py                  # Main entry script (required)
├── requirements.txt         # Python dependencies (optional)
├── resources/               # Resources folder (optional)
│   ├── icons/
│   │   └── addonIcon.png   # Addon icon (required)
│   └── images/
├── py/                      # Additional Python scripts
│   ├── settings.py
│   └── help.py
└── lib/                     # External dependencies (optional - see note below)
    └── site-packages/
```

> **📝 Note about `lib/site-packages/`:** This folder is only needed during **addon development** when you're using external libraries for local testing. It should **NOT** be included when packaging your addon for distribution. The Addons Manager will automatically install dependencies from `requirements.txt` when users install your addon.

### Key Components

| Component | Required | Description |
|-----------|----------|-------------|
| `config.json` | ✅ Yes | Addon metadata and configuration |
| `main.py` | ✅ Yes | Main entry point for the addon |
| `requirements.txt` | ⚠️ Optional | List of Python dependencies |
| `resources/` | ⚠️ Optional | Icons, images, and other assets |
| `lib/site-packages/` | ⚠️ Development Only | External Python packages for local testing (exclude from packaging) |

---

## Step-by-Step Guide

### Step 1: Create the Addon Folder

1. Create a new folder with your addon name (e.g., `MyAddon`)
2. This folder will serve as the root directory for all addon files

```
📁 MyAddon/
```

**Naming Convention:**
- Use descriptive names (e.g., `VRMenuCreator`, `ModelOptimizer`)
- Avoid spaces and special characters

---

### Step 2: Create the Configuration File

Inside your addon's root folder, create a file named `config.json`. This file contains all metadata about your addon.

#### Basic Configuration Template

```json
{
  "name": "My Addon",
  "description": "This addon creates a new SimLab feature.",
  "author": "Your Name",
  "version": "1.0",
  "icon": "resources/icons/addonIcon.png",
  "help_page": "https://your-website.com",
  "category": "VR",
  "main_script": "main.py",
  "menus": {
    "title": "My Addon",
    "sub_menus": [
      {
        "title": "Settings",
        "script": "py/settings.py"
      },
      {
        "title": "Help",
        "script": "py/help.py"
      }
    ]
  }
}
```

#### Configuration Fields Explained

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | String | ✅ Yes | Display name of your addon in the manager |
| `description` | String | ✅ Yes | Brief description of functionality |
| `author` | String | ✅ Yes | Creator's name or organization |
| `version` | String | ✅ Yes | Version number (e.g., "1.0", "2.1.3") |
| `icon` | String | ⚠️ Optional | Path to addon icon (PNG, 32x32 recommended) |
| `help_page` | String | ⚠️ Optional | URL to documentation or support |
| `category` | String | ✅ Yes | Category: "VR", "Modeling", "Animation", etc. |
| `main_script` | String | ✅ Yes | Entry point Python script |
| `title` | String | ✅ Yes | Display name of your addon in the addons menu |
| `menus` | Object | ⚠️ Optional | Menu structure definition |

**Important Notes:**
- All file paths in `config.json` are **relative** to the addon folder
- Use forward slashes (`/`) for paths, even on Windows
- Icon format: PNG, recommended size 32x32 pixels

---

### Step 3: Create the Main Script

Create `main.py` in your addon's root folder. This is the entry point for your addon.

#### Basic Main Script Template

```python
"""
My Addon - Main Script
Description: Brief description of what your addon does
Author: Your Name
Version: 1.0
"""

import sys
import os

# Import SimLab API
try:
    from simlabpy import *
    runtime = RunTime()
except ImportError:
    print("Warning: simlabpy not found. Running in standalone mode.")
    runtime = None

def main():
    """
    Main entry point for the addon.
    This function is called when the addon is launched from SimLab Composer.
    """
    if runtime:
        runtime.ui.alert("My Addon is running!")
    else:
        print("My Addon is running in standalone mode.")
    
    # Your addon code here
    print("Addon initialized successfully!")

# Execute main function
if __name__ == "__main__":
    main()
```

#### Advanced Main Script with GUI

```python
"""
My Addon - Main Script with PySide6 GUI
"""

import sys
import os
from PySide6.QtWidgets import QApplication, QDialog, QVBoxLayout, QLabel, QPushButton

# Import SimLab API
try:
    from simlabpy import *
    runtime = RunTime()
except ImportError:
    print("Warning: simlabpy not found.")
    runtime = None

class MyAddonDialog(QDialog):
    """Main dialog window for the addon."""
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("My Addon")
        self.setup_ui()
    
    def setup_ui(self):
        """Setup the user interface."""
        layout = QVBoxLayout()
        
        # Add widgets
        label = QLabel("Welcome to My Addon!")
        button = QPushButton("Run Feature")
        button.clicked.connect(self.on_button_clicked)
        
        layout.addWidget(label)
        layout.addWidget(button)
        self.setLayout(layout)
    
    def on_button_clicked(self):
        """Handle button click."""
        if runtime:
            runtime.ui.alert("Feature executed!")

def main():
    """Main entry point."""
    # Get SimLab main window if available
    if runtime:
        try:
            parent = getMainWindow()
        except:
            parent = None
    else:
        app = QApplication.instance() or QApplication(sys.argv)
        parent = None
    
    # Create and show dialog
    dialog = MyAddonDialog(parent)
    dialog.resize(400, 300)
    dialog.show()
    
    # Start event loop in standalone mode
    if not runtime:
        sys.exit(app.exec())

if __name__ == "__main__":
    main()
```

---

### Step 4: Add Menu Scripts (Optional)

If your addon has sub-menus defined in `config.json`, create the corresponding Python scripts.

#### Example: `py/settings.py`

```python
"""
Settings script for My Addon
"""

from simlabpy import *
from PySide6.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton, QCheckBox

class SettingsDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("My Addon - Settings")
        self.setup_ui()
    
    def setup_ui(self):
        layout = QVBoxLayout()
        
        label = QLabel("Addon Settings:")
        checkbox = QCheckBox("Enable Advanced Features")
        save_button = QPushButton("Save Settings")
        save_button.clicked.connect(self.save_settings)
        
        layout.addWidget(label)
        layout.addWidget(checkbox)
        layout.addWidget(save_button)
        self.setLayout(layout)
    
    def save_settings(self):
        runtime = RunTime()
        runtime.ui.alert("Settings saved!")
        self.accept()

def main():
    try:
        parent = getMainWindow()
    except:
        parent = None
    
    dialog = SettingsDialog(parent)
    dialog.show()

if __name__ == "__main__":
    main()
```

#### Example: `py/help.py`

```python
"""
Help script for My Addon
"""

from simlabpy import *
from PySide6.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton, QTextBrowser

class HelpDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("My Addon - Help")
        self.setup_ui()
    
    def setup_ui(self):
        layout = QVBoxLayout()
        
        help_text = QTextBrowser()
        help_text.setHtml("""
            <h2>My Addon Help</h2>
            <p>This addon provides the following features:</p>
            <ul>
                <li>Feature 1: Description</li>
                <li>Feature 2: Description</li>
                <li>Feature 3: Description</li>
            </ul>
            <p>For more information, visit our website.</p>
        """)
        
        close_button = QPushButton("Close")
        close_button.clicked.connect(self.accept)
        
        layout.addWidget(help_text)
        layout.addWidget(close_button)
        self.setLayout(layout)

def main():
    try:
        parent = getMainWindow()
    except:
        parent = None
    
    dialog = HelpDialog(parent)
    dialog.resize(500, 400)
    dialog.show()

if __name__ == "__main__":
    main()
```

---

## Configuration Reference

### Complete config.json Example

```json
{
  "name": "VR Menu Creator",
  "description": "Create and manage custom VR menus for immersive experiences",
  "author": "SimLab Soft",
  "version": "2.1.0",
  "icon": "resources/icons/vr_menu_icon.png",
  "help_page": "https://simlab-soft.com/addons/vr-menu-creator",
  "category": "VR",
  "main_script": "main.py",
  "menus": {
    "title": "VR Menu Creator",
    "sub_menus": [
      {
        "title": "Create Menu",
        "script": "py/create_menu.py"
      },
      {
        "title": "Edit Menu",
        "script": "py/edit_menu.py"
      },
      {
        "title": "Settings",
        "script": "py/settings.py"
      },
      {
        "title": "Documentation",
        "script": "py/help.py"
      }
    ]
  }
}
```

### Menu Configuration

The `menus` object defines how your addon appears in SimLab Composer's menu bar:

```json
"menus": {
  "title": "Addon Menu Title",
  "sub_menus": [
    {
      "title": "Sub-menu 1",
      "script": "path/to/script1.py"
    },
    {
      "title": "Sub-menu 2",
      "script": "path/to/script2.py"
    }
  ]
}
```

**Menu Guidelines:**
- Keep menu titles short and descriptive (2-4 words)
- Group related features under the same main menu
- Use standard names: "Settings", "Help", "About"

---

## Managing Dependencies

If your addon requires external Python packages (e.g., `requests`, `numpy`, `pillow`), follow these steps:

### Step 1: Install Dependencies During Addon Development

Navigate to your addon folder and install packages:

**Windows:**
```powershell
cd C:\path\to\MyAddon
python -m pip install requests==2.31.0 --target "lib\site-packages"
python -m pip install pillow==10.0.0 --target "lib\site-packages"
```

**macOS / Linux:**
```bash
cd /path/to/MyAddon
python3 -m pip install requests==2.31.0 --target "lib/python3.12/site-packages"
python3 -m pip install pillow==10.0.0 --target "lib/python3.12/site-packages"
```

**Notes:**
- Replace `python3.12` with your Python version
- Specify exact versions for reproducibility
- Dependencies are installed in the `lib/` folder

### Step 2: Generate requirements.txt

After installing all dependencies, generate the requirements file:

**Windows:**
```powershell
python -m pip freeze --path lib/site-packages > requirements.txt
```

**macOS / Linux:**
```bash
python3 -m pip freeze --path lib/python3.12/site-packages > requirements.txt
```

This creates a `requirements.txt` file in your addon's root folder.

#### Example requirements.txt

```
requests==2.31.0
pillow==10.0.0
certifi==2023.7.22
charset-normalizer==3.2.0
idna==3.4
urllib3==2.0.4
```

**Important:** The SimLab Addons Manager will automatically install these dependencies when users install your addon.

### Step 3: Using Dependencies in Your Code

```python
import requests
from simlabpy import *
runtime = RunTime()

# Define your request parameters
url = "https://httpbin.org/post"
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer your_token_here"
}
data = {
    "name": "Alice",
    "age": 30
}

# Send the request
response = requests.post(url, headers=headers, json=data)

# Print the echoed request details from httpbin
# Print your request details
runtime.ui.alert(str(response.json()["headers"]))
# Print body
runtime.ui.alert(str(response.json()["json"]))
```

### Example Dependencies

| Package | Use Case | Install Command |
|---------|----------|-----------------|
| `requests` | HTTP requests, API calls | `pip install requests` |
| `pillow` | Image processing | `pip install pillow` |
| `numpy` | Numerical computations | `pip install numpy` |
| `opencv-python` | Computer vision | `pip install opencv-python` |
| `matplotlib` | Data visualization | `pip install matplotlib` |
| `pandas` | Data analysis | `pip install pandas` |

---

## Testing Your Addon

### Local Testing

Before packaging, test your addon locally:

1. **Install via Addons Manager (Recommended):**
   - Package your addon as a ZIP file (see [Packaging and Distribution](#packaging-and-distribution))
   - Open SimLab Composer and access **Addons Manager**
   - Click **"Install"** and select your addon ZIP file
   - This method tests the complete installation process including dependency installation
   - **Recommended** especially for addons with external dependencies

2. **Copy to Addons Directory:**
   - Copy your addon folder to SimLab's Addons directory `C:\Users\YOUR_USERNAME\AppData\Roaming\SimLab\SimLab Composer Beta\data\Addons`
   - Restart SimLab Addons Manager
   - Look for your addon in the menu bar
   - **Note:** This method works best for addons without dependencies, or for addons that have already been installed before and have their dependencies set up

3. **Run main script directly:**
   ```bash
   python main.py
   ```

4. **Check for errors:**
   - Monitor SimLab's messages output
   - Test all menu items
   - Verify dependency imports

### Testing Checklist

- [ ] `config.json` is valid JSON format
- [ ] All file paths in config are correct
- [ ] Main script runs without errors
- [ ] All sub-menu scripts work correctly
- [ ] Icon displays properly (if provided)
- [ ] Dependencies load correctly
- [ ] Addon integrates with SimLab API
- [ ] GUI dialogs display correctly
- [ ] No error messages or warnings

---

## Packaging and Distribution

### Step 1: Prepare for Deployment

Before packaging, create a clean copy of your addon:

1. **Duplicate your addon folder:**
   ```
   MyAddon/           (Development version - keep this)
   MyAddon_Deploy/    (Deployment version)
   ```

2. **In the deployment folder, delete the `lib/` folder:**
   - The Addons Manager will reinstall dependencies from `requirements.txt`
   - This keeps your package size smaller

3. **Remove development files:**
   - Delete `__pycache__` folders
   - Remove `.pyc` compiled files
   - Delete test scripts or data
   - Remove version control files (`.git`, `.gitignore`)

### Step 2: Create the ZIP Package

**Windows (File Explorer):**
1. Navigate to the parent folder containing `MyAddon_Deploy`
2. Right-click on `MyAddon_Deploy` folder
3. Select "Send to" → "Compressed (zipped) folder"
4. Rename to `MyAddon.zip`

**Windows (Command Line):**
```powershell
Compress-Archive -Path MyAddon_Deploy -DestinationPath MyAddon.zip
```

**macOS:**
```bash
cd /path/to/parent/folder
zip -r MyAddon.zip MyAddon_Deploy/
```

**Linux:**
```bash
cd /path/to/parent/folder
zip -r MyAddon.zip MyAddon_Deploy/
```

### Package Contents Verification

Your ZIP file should contain:

```
MyAddon.zip
└── MyAddon_Deploy/
    ├── config.json
    ├── main.py
    ├── requirements.txt
    ├── resources/
    │   └── icons/
    │       └── addonIcon.png
    └── py/
        ├── settings.py
        └── help.py
```

**Note:** The `lib/` folder should NOT be in the deployment ZIP.

---

## Installation via Addons Manager

### Installing Your Addon

1. **Open SimLab Composer**

2. **Access Addons Manager:**
   - On the ribbon of **VR workbench**, go to **Addons** → **Manager**

3. **Install the Addon:**
   - In the **Addons Manager** dialog, click the **"Install"** button
   - Navigate to and open the zipped file `MyAddon.zip`

4. **Installation Process:**
   - Installation starts automatically
   - The Addons Manager will:
     - Extract the ZIP file
     - Read `config.json`
     - Install dependencies from `requirements.txt` (if present)
     - Register the addon in SimLab

5. **Installation Success:**
   - A message box will appear: **"Installation success: add-on installed successfully!"** if everything goes well
   - Click **OK** to close the message

6. **Verify Installation:**
   - The addon will appear in the **"Installed Add-ons"** list with:
     - **"Run the Addon"** button
     - **"Go to the add-on help page"** button
     - Addon title, description, author, and version information
   - In the **"Addon Menu"**, you will find the addon entry and its sub-menus (if configured in `config.json`)

### Uninstalling an Addon

1. Open **Addons Manager**
2. Select your addon from the list
3. Click **"Uninstall"**
4. Confirm the removal

---

## Best Practices

### Code Organization

1. **Modular Structure:**
   ```
   MyAddon/
   ├── main.py
   ├── core/
   │   ├── __init__.py
   │   ├── engine.py
   │   └── utils.py
   ├── ui/
   │   ├── __init__.py
   │   ├── dialogs.py
   │   └── widgets.py
   └── resources/
   ```

2. **Separate concerns:**
   - Keep UI code separate from business logic
   - Use utility modules for common functions
   - Create separate modules for different features

### Performance Considerations

1. **Lazy loading:**
   - Import heavy modules only when needed
   - Load large datasets on demand

2. **Background processing:**
   - Use threading for long operations
   - Show progress indicators

3. **Resource cleanup:**
   ```python
   def cleanup():
       """Clean up resources before closing."""
       # Close file handles
       # Release memory
       # Disconnect signals
   ```

### User Experience

1. **Provide feedback:**
   - Show progress bars for long operations
   - Display confirmation messages
   - Show helpful error messages

2. **Use consistent styling:**
   - Follow SimLab's UI conventions
   - Use standard icons and colors
   - Maintain consistent button sizes

3. **Add tooltips:**
   ```python
   button = QPushButton("Export")
   button.setToolTip("Export the current scene to file")
   ```

### Documentation

1. **Include README.md:**
   ```markdown
   # My Addon
   
   ## Description
   Brief description of your addon
   
   ## Installation
   Instructions for installation
   
   ## Usage
   How to use your addon
   
   ## Requirements
   - SimLab Composer version
   - Python dependencies
   
   ## License
   Your license information
   ```

2. **Add inline comments:**
   ```python
   # Calculate bounding box dimensions
   width = max_x - min_x  # Width in model units
   height = max_y - min_y  # Height in model units
   ```

3. **Write docstrings:**
   ```python
   def calculate_volume(mesh):
       """
       Calculate the volume of a mesh.
       
       Args:
           mesh: The mesh object to calculate volume for
           
       Returns:
           float: The volume in cubic units
           
       Raises:
           ValueError: If mesh is invalid
       """
       pass
   ```

---

## Troubleshooting

### Common Issues and Solutions

#### Issue 1: Addon Not Appearing in Menu

**Problem:** Addon installed but doesn't show in SimLab menu

**Solutions:**
- ✅ Enable the addon in the addons list of the Addons Manager
- ✅ Check `config.json` syntax (use a JSON validator)
- ✅ Verify `main_script` path is correct
- ✅ Ensure `menus.title` is defined
- ✅ Restart Addons Manager
- ✅ Check SimLab console for error messages

#### Issue 2: Dependencies Not Loading

**Problem:** Import errors when running addon

**Solutions:**
- ✅ Verify `requirements.txt` exists in addon root
- ✅ Check package names and versions are correct
- ✅ Ensure `lib/` folder was removed before packaging
- ✅ Reinstall the addon to trigger dependency installation
- ✅ Check Python path configuration in your script

#### Issue 3: Icon Not Displaying

**Problem:** Addon icon doesn't show in Addons Manager

**Solutions:**
- ✅ Verify icon path in `config.json` is correct
- ✅ Use PNG format (32x32 recommended)
- ✅ Check icon file is included in ZIP package
- ✅ Use relative path (e.g., `resources/icons/icon.png`)

#### Issue 4: Script Execution Errors

**Problem:** Python errors when running addon scripts

**Solutions:**
- ✅ Test scripts independently before packaging
- ✅ Check all imports are available
- ✅ Verify SimLab API functions are used correctly
- ✅ Add proper error handling
- ✅ Check file paths are relative to addon directory

#### Issue 5: ZIP File Won't Install

**Problem:** Addons Manager rejects the ZIP file

**Solutions:**
- ✅ Ensure ZIP contains the addon folder at root level
- ✅ Check `config.json` is in the addon root folder
- ✅ Verify ZIP file is not corrupted
- ✅ Remove any unnecessary files (`.DS_Store`, `.git`, etc.)
- ✅ Check file naming conventions (no special characters)

### Debugging Tips

Use SimLab's alert system to display debug messages:

```python
from simlabpy import *
runtime = RunTime()
runtime.ui.alert("debug message")
```

---

## Summary Checklist

Before packaging your addon for distribution, verify:

### Configuration
- [ ] `config.json` is complete and valid
- [ ] All required fields are filled
- [ ] File paths are relative and correct
- [ ] Version number is specified
- [ ] Category is appropriate

### Code
- [ ] `main.py` exists and runs without errors
- [ ] All sub-menu scripts are functional
- [ ] Dependencies are properly managed
- [ ] Error handling is implemented
- [ ] Code is documented with comments

### Resources
- [ ] Icon file exists and displays correctly
- [ ] All referenced files are included
- [ ] No development files (`.pyc`, `__pycache__`)
- [ ] File structure is clean and organized

### Dependencies
- [ ] `requirements.txt` generated if needed
- [ ] Dependencies tested and working
- [ ] `lib/` folder removed from deployment ZIP
- [ ] Package versions are specified

### Testing
- [ ] Addon works in SimLab Composer
- [ ] All menu items function correctly
- [ ] No console errors
- [ ] User interface is responsive
- [ ] Documentation is complete

### Packaging
- [ ] Clean deployment folder created
- [ ] ZIP file created correctly
- [ ] Package installs via Addons Manager
- [ ] Addon appears in menu after installation

---

## Additional Resources

### SimLab Documentation
- [SimLab Composer Official Website](https://simlab-soft.com)
- [SimLab API Documentation](https://www.simlab-soft.com/3d-products/docs/help/scripting/English/Python-Scripting-Documentation/)

### Python Resources
- [Python Official Documentation](https://docs.python.org/3/)
- [PySide6 Documentation](https://doc.qt.io/qtforpython/)
- [pip Documentation](https://pip.pypa.io/)

### Development Tools
- [JSON Validator](https://jsonlint.com/)
- [Python Virtual Environments](https://docs.python.org/3/tutorial/venv.html)
- [Git Version Control](https://git-scm.com/)

---

**Document Version:** 1.0  
**Last Updated:** October 27, 2025  
**Compatible with:** SimLab Composer with Addons Manager support

---

*For questions, support, or to report issues, please contact SimLab Software support.*
