Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions docs/PLUGIN_SYSTEM_RESTRUCTURING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Plugin System Restructuring Guide

## Overview

This restructuring centralizes ImSwitch's plugin management system to make it more organized, maintainable, and easier to use for plugin developers.

## What Changed

### Before (Chaotic System)
- Plugin loading code scattered across multiple files
- Different loading patterns for managers, controllers, widgets
- No centralized registry or lifecycle management
- Inconsistent error handling

### After (Centralized System)
- Single `ImSwitchPluginManager` handles all plugin operations
- Standardized interfaces for all plugin types
- Centralized discovery, loading, and lifecycle management
- Consistent error handling and logging
- Full backwards compatibility

## For Plugin Developers

### Plugin Types Supported
1. **Managers** - Hardware/logic management (e.g., `my_plugin_manager`)
2. **Controllers** - UI business logic (e.g., `my_plugin_controller`)
3. **Widgets** - UI components (e.g., `my_plugin_widget`)
4. **Info Classes** - Configuration data classes (e.g., `my_plugin_info`)

### Example Plugin Structure

```python
# setup.py for your plugin
setup(
name="my-imswitch-plugin",
entry_points={
'imswitch.implugins': [
'my_plugin_manager = my_plugin.manager:MyPluginManager',
'my_plugin_controller = my_plugin.controller:MyPluginController',
'my_plugin_widget = my_plugin.widget:MyPluginWidget',
'my_plugin_info = my_plugin.info:MyPluginInfo',
],
},
)
```

### Plugin Implementation

```python
# my_plugin/manager.py
from imswitch.imcommon.model import PluginInterface

class MyPluginManager(PluginInterface):
def __init__(self, module_info):
self.module_info = module_info

def get_plugin_info(self):
return {
"name": "My Plugin",
"version": "1.0.0",
"description": "Example plugin"
}

# my_plugin/controller.py
class MyPluginController:
def __init__(self, widget, setupInfo, masterController, commChannel):
self.widget = widget
self.setupInfo = setupInfo
self.masterController = masterController
self.commChannel = commChannel

# my_plugin/widget.py
class MyPluginWidget:
def __init__(self):
pass

# For React components (headless mode)
class MyPluginReactWidget:
_ui_meta = {
"name": "my_plugin",
"icon": "plugin-icon",
"path": "/path/to/react/build"
}
```

## For ImSwitch Core Developers

### Using the Plugin Manager

```python
from imswitch.imcommon.model import get_plugin_manager

# Get the global plugin manager instance
pm = get_plugin_manager()

# Check if a plugin is available
if pm.is_plugin_available('my_plugin', 'manager'):
# Load the plugin
manager_class = pm.load_plugin('my_plugin', 'manager')
manager = manager_class(module_info)

# Get all available plugins of a type
widget_plugins = pm.get_available_plugins('widget')
for plugin in widget_plugins:
print(f"Found widget plugin: {plugin.name}")

# Get React widget plugins for UI
react_plugins = pm.get_react_widget_plugins()
for plugin in react_plugins:
manifest = pm.get_plugin_manifest_data(plugin.name)
if manifest:
print(f"React plugin: {manifest['name']}")
```

## Benefits

1. **Centralized Management**: All plugin operations in one place
2. **Better Organization**: Clear separation of concerns
3. **Type Safety**: Proper plugin classification and validation
4. **Performance**: On-demand loading with caching
5. **Error Handling**: Graceful failure for missing/broken plugins
6. **Backwards Compatible**: Existing plugins continue to work
7. **React Support**: Automatic React component discovery and serving

## Migration Guide

**For existing plugins**: No changes required! The new system maintains full backwards compatibility.

**For new plugins**: You can optionally implement the `PluginInterface` for better integration, but it's not required.

## API Reference

### ImSwitchPluginManager

- `get_available_plugins(plugin_type=None)` - List available plugins
- `is_plugin_available(name, type)` - Check plugin availability
- `load_plugin(name, type, info_class=None)` - Load a plugin class
- `get_plugin_manifest_data(name)` - Get React manifest data
- `get_react_widget_plugins()` - Get React-capable widget plugins

### PluginInterface (Optional Base Class)

- `get_plugin_info()` - Return plugin metadata (required method)

## Testing

The new system includes comprehensive testing to ensure:
- Plugin discovery works correctly
- Plugin loading handles errors gracefully
- Backwards compatibility is maintained
- React components are properly served

## Future Enhancements

- Plugin dependency management
- Plugin versioning and updates
- Hot reloading for development
- Plugin marketplace integration
- Enhanced React component integration
164 changes: 164 additions & 0 deletions examples/example_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
"""
Example Plugin for ImSwitch - Demonstrates the new centralized plugin system

This is a minimal example showing how to create plugins that work with
the new ImSwitchPluginManager system.
"""

from imswitch.imcommon.model import PluginInterface
from typing import Dict, Any


class ExamplePluginInfo:
"""Info class for configuration data"""

def __init__(self):
self.enabled = True
self.settings = {
"parameter1": "value1",
"parameter2": 42
}


class ExamplePluginManager(PluginInterface):
"""Example manager plugin"""

def __init__(self, module_info):
self.module_info = module_info
self.initialized = True

def get_plugin_info(self) -> Dict[str, Any]:
return {
"name": "Example Plugin",
"version": "1.0.0",
"description": "Demonstrates the new plugin system",
"type": "manager"
}

def do_something(self):
"""Example method"""
return "Manager is working!"


class ExamplePluginController:
"""Example controller plugin"""

def __init__(self, widget, setupInfo, masterController, commChannel):
self.widget = widget
self.setupInfo = setupInfo
self.masterController = masterController
self.commChannel = commChannel

def initialize(self):
"""Initialize the controller"""
print("Example plugin controller initialized")

def on_action(self):
"""Example action handler"""
return "Controller action executed"


class ExamplePluginWidget:
"""Example Qt widget plugin"""

def __init__(self):
self.initialized = True
print("Example plugin widget created")

def show_message(self):
"""Example widget method"""
return "Widget is active"


class ExamplePluginReactWidget:
"""Example React widget for headless mode"""

# UI metadata for React component serving
_ui_meta = {
"name": "example_plugin",
"icon": "example-icon",
"path": "/path/to/react/build/directory"
}

def __init__(self):
self.initialized = True
print("Example React widget created")

def get_react_props(self):
"""Return props for React component"""
return {
"title": "Example Plugin",
"data": [1, 2, 3, 4, 5]
}


# Example setup.py entry points would be:
"""
setup(
name="imswitch-example-plugin",
version="1.0.0",
entry_points={
'imswitch.implugins': [
'example_plugin_manager = example_plugin:ExamplePluginManager',
'example_plugin_controller = example_plugin:ExamplePluginController',
'example_plugin_widget = example_plugin:ExamplePluginWidget',
'example_plugin_info = example_plugin:ExamplePluginInfo',
],
},
)
"""


# Demo usage with the new plugin manager
def demo_plugin_usage():
"""Demonstrate how to use plugins with the new system"""
from imswitch.imcommon.model import get_plugin_manager

pm = get_plugin_manager()

# Check if plugin is available
if pm.is_plugin_available('example_plugin', 'manager'):
print("Example plugin manager is available")

# Load the plugin
manager_class = pm.load_plugin('example_plugin', 'manager')
if manager_class:
# Get info class if available
info_class = pm.get_plugin_info_class('example_plugin')
info = info_class() if info_class else None

# Create manager instance
manager = manager_class(info)

# Use the plugin
print(f"Plugin info: {manager.get_plugin_info()}")
print(f"Manager result: {manager.do_something()}")

# Check widget plugins for React components
react_plugins = pm.get_react_widget_plugins()
for plugin in react_plugins:
if plugin.name == 'example_plugin':
manifest = pm.get_plugin_manifest_data(plugin.name)
if manifest:
print(f"React component available at: {manifest['url']}")


if __name__ == "__main__":
demo_plugin_usage()


# Copyright (C) 2020-2024 ImSwitch developers
# This file is part of ImSwitch.
#
# ImSwitch is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ImSwitch is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
Loading