mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
added base of example addon
This commit is contained in:
parent
478693d0ab
commit
3dd6903251
11 changed files with 291 additions and 0 deletions
13
openpype/modules/example_addons/example_addon/__init__.py
Normal file
13
openpype/modules/example_addons/example_addon/__init__.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
""" Addon class definition and Settings definition must be imported here.
|
||||
|
||||
If addon class or settings definition won't be here their definition won't
|
||||
be found by OpenPype discovery.
|
||||
"""
|
||||
|
||||
from .addon import (
|
||||
AddonSettingsDef,
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"AddonSettingsDef",
|
||||
)
|
||||
124
openpype/modules/example_addons/example_addon/addon.py
Normal file
124
openpype/modules/example_addons/example_addon/addon.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
"""Addon definition is located here.
|
||||
|
||||
Import of python packages that may not be available should not be imported
|
||||
in global space here until are required or used.
|
||||
- Qt related imports
|
||||
- imports of Python 3 packages
|
||||
- we still support Python 2 hosts where addon definition should available
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from openpype.modules import (
|
||||
JsonFilesSettingsDef,
|
||||
OpenPypeAddOn
|
||||
)
|
||||
# Import interface defined by this addon to be able find other addons using it
|
||||
from openpype_interfaces import (
|
||||
IExampleInterface,
|
||||
IPluginPaths,
|
||||
ITrayAction
|
||||
)
|
||||
|
||||
|
||||
# Settings definiton of this addon using `JsonFilesSettingsDef`
|
||||
# - JsonFilesSettingsDef is prepared settings definiton using json files
|
||||
# to define settings and store defaul values
|
||||
class AddonSettingsDef(JsonFilesSettingsDef):
|
||||
# This will add prefix to every schema and template from `schemas`
|
||||
# subfolder.
|
||||
# - it is not required to fill the prefix but it is highly
|
||||
# recommended as schemas and templates may have name clashes across
|
||||
# multiple addons
|
||||
# - it is also recommended that prefix has addon name in it
|
||||
schema_prefix = "addon_with_settings"
|
||||
|
||||
def get_settings_root_path(self):
|
||||
"""Implemented abstract class of JsonFilesSettingsDef.
|
||||
|
||||
Return directory path where json files defying addon settings are
|
||||
located.
|
||||
"""
|
||||
return os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"settings"
|
||||
)
|
||||
|
||||
|
||||
class ExampleAddon(OpenPypeAddOn, IPluginPaths, ITrayAction):
|
||||
"""This Addon has defined it's settings and interface.
|
||||
|
||||
This example has system settings with enabled option. And use
|
||||
few other interfaces:
|
||||
- `IPluginPaths` to define custom plugin paths
|
||||
- `ITrayAction` to be shown in tray tool
|
||||
"""
|
||||
label = "Example Addon"
|
||||
name = "example_addon"
|
||||
|
||||
def initialize(self, settings):
|
||||
"""Initialization of addon."""
|
||||
module_settings = settings[self.name]
|
||||
# Enabled by settings
|
||||
self.enabled = module_settings.get("enabled", False)
|
||||
|
||||
# Prepare variables that can be used or set afterwards
|
||||
self._connected_modules = None
|
||||
# UI which must not be created at this time
|
||||
self._dialog = None
|
||||
|
||||
def connect_with_modules(self, enabled_modules):
|
||||
"""Method where you should find connected modules.
|
||||
|
||||
It is triggered by OpenPype modules manager at the best possible time.
|
||||
Some addons and modules may required to connect with other modules
|
||||
before their main logic is executed so changes would require to restart
|
||||
whole process.
|
||||
"""
|
||||
self._connected_modules = []
|
||||
for module in enabled_modules:
|
||||
if isinstance(module, IExampleInterface):
|
||||
self._connected_modules.append(module)
|
||||
|
||||
def _create_dialog(self):
|
||||
# Don't recreate dialog if already exists
|
||||
if self._dialog is not None:
|
||||
return
|
||||
|
||||
from .widgets import MyExampleDialog
|
||||
|
||||
self._dialog = MyExampleDialog()
|
||||
|
||||
def show_dialog(self):
|
||||
"""Show dialog with connected modules.
|
||||
|
||||
This can be called from anywhere but can also crash in headless mode.
|
||||
There is not way how to prevent addon to do invalid operations if he's
|
||||
not handling them.
|
||||
"""
|
||||
# Make sure dialog is created
|
||||
self._create_dialog()
|
||||
# Change value of dialog by current state
|
||||
self._dialog.set_connected_modules(self.get_connected_modules())
|
||||
# Show dialog
|
||||
self._dialog.open()
|
||||
|
||||
def get_connected_modules(self):
|
||||
"""Custom implementation of addon."""
|
||||
names = set()
|
||||
if self._connected_modules is not None:
|
||||
for module in self._connected_modules:
|
||||
names.add(module.name)
|
||||
return names
|
||||
|
||||
def on_action_trigger(self):
|
||||
"""Implementation of abstract method for `ITrayAction`."""
|
||||
self.show_dialog()
|
||||
|
||||
def get_plugin_paths(self):
|
||||
"""Implementation of abstract method for `IPluginPaths`."""
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
return {
|
||||
"publish": [os.path.join(current_dir, "plugins", "publish")]
|
||||
}
|
||||
28
openpype/modules/example_addons/example_addon/interfaces.py
Normal file
28
openpype/modules/example_addons/example_addon/interfaces.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
""" Using interfaces is one way of connecting multiple OpenPype Addons/Modules.
|
||||
|
||||
Interfaces must be in `interfaces.py` file (or folder). Interfaces should not
|
||||
import module logic or other module in global namespace. That is because
|
||||
all of them must be imported before all OpenPype AddOns and Modules.
|
||||
|
||||
Ideally they should just define abstract and helper methods. If interface
|
||||
require any logic or connection it should be defined in module.
|
||||
|
||||
Keep in mind that attributes and methods will be added to other addon
|
||||
attributes and methods so they should be unique and ideally contain
|
||||
addon name in it's name.
|
||||
"""
|
||||
|
||||
from abc import abstractmethod
|
||||
from openpype.modules import OpenPypeInterface
|
||||
|
||||
|
||||
class IExampleInterface(OpenPypeInterface):
|
||||
"""Example interface of addon."""
|
||||
_example_module = None
|
||||
|
||||
def get_example_module(self):
|
||||
return self._example_module
|
||||
|
||||
@abstractmethod
|
||||
def example_method_of_example_interface(self):
|
||||
pass
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class CollectExampleAddon(pyblish.api.ContextPlugin):
|
||||
order = pyblish.api.CollectorOrder + 0.4
|
||||
label = "Collect Example Addon"
|
||||
|
||||
def process(self, context):
|
||||
self.log.info("I'm in example addon's plugin!")
|
||||
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"project_settings/global": {
|
||||
"type": "schema",
|
||||
"name": "addon_with_settings/main"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"system_settings/modules": {
|
||||
"type": "schema",
|
||||
"name": "addon_with_settings/main"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"key": "exmaple_addon",
|
||||
"collapsible": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "number",
|
||||
"key": "number",
|
||||
"label": "This is your lucky number:",
|
||||
"minimum": 7,
|
||||
"maximum": 7,
|
||||
"decimals": 0
|
||||
},
|
||||
{
|
||||
"type": "template",
|
||||
"name": "example_addon/the_template",
|
||||
"template_data": [
|
||||
{
|
||||
"name": "color_1",
|
||||
"lable": "Color 1"
|
||||
},
|
||||
{
|
||||
"name": "color_2",
|
||||
"lable": "Color 2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
[
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "{name}",
|
||||
"label": "{label}",
|
||||
"object_types": [
|
||||
{
|
||||
"label": "Red",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Green",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Blue",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"key": "example_addon",
|
||||
"label": "Example addon",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
}
|
||||
]
|
||||
}
|
||||
30
openpype/modules/example_addons/example_addon/widgets.py
Normal file
30
openpype/modules/example_addons/example_addon/widgets.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
from Qt import QtWidgets
|
||||
|
||||
|
||||
class MyExampleDialog(QtWidgets.QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super(MyExampleDialog, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle("Connected modules")
|
||||
|
||||
label_widget = QtWidgets.QLabel(self)
|
||||
|
||||
ok_btn = QtWidgets.QPushButton("OK", self)
|
||||
btns_layout = QtWidgets.QHBoxLayout()
|
||||
btns_layout.addStretch(1)
|
||||
btns_layout.addWidget(ok_btn)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.addWidget(label_widget)
|
||||
layout.addLayout(btns_layout)
|
||||
|
||||
self._label_widget = label_widget
|
||||
|
||||
def set_connected_modules(self, connected_modules):
|
||||
if connected_modules:
|
||||
message = "\n".join(connected_modules)
|
||||
else:
|
||||
message = (
|
||||
"Other enabled modules/addons are not using my interface."
|
||||
)
|
||||
self._label_widget.setText(message)
|
||||
Loading…
Add table
Add a link
Reference in a new issue