added base of example addon

This commit is contained in:
iLLiCiTiT 2021-09-07 11:53:59 +02:00
parent 478693d0ab
commit 3dd6903251
11 changed files with 291 additions and 0 deletions

View 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",
)

View 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")]
}

View 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

View file

@ -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!")

View file

@ -0,0 +1,6 @@
{
"project_settings/global": {
"type": "schema",
"name": "addon_with_settings/main"
}
}

View file

@ -0,0 +1,6 @@
{
"system_settings/modules": {
"type": "schema",
"name": "addon_with_settings/main"
}
}

View file

@ -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"
}
]
}
]
}

View file

@ -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
}
]
}
]

View file

@ -0,0 +1,14 @@
{
"type": "dict",
"key": "example_addon",
"label": "Example addon",
"collapsible": true,
"checkbox_key": "enabled",
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
}
]
}

View 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)