Merge pull request #2238 from pypeclub/feature/static_interfaces

General: Static interfaces
This commit is contained in:
Jakub Trllo 2021-11-12 13:20:54 +01:00 committed by GitHub
commit 25c6d3732e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 43 additions and 148 deletions

View file

@ -107,12 +107,9 @@ class _InterfacesClass(_ModuleClass):
if attr_name in ("__path__", "__file__"):
return None
# Fake Interface if is not missing
self.__attributes__[attr_name] = type(
attr_name,
(MissingInteface, ),
{}
)
raise ImportError((
"cannot import name '{}' from 'openpype_interfaces'"
).format(attr_name))
return self.__attributes__[attr_name]
@ -212,54 +209,17 @@ def _load_interfaces():
_InterfacesClass(modules_key)
)
log = PypeLogger.get_logger("InterfacesLoader")
from . import interfaces
dirpaths = get_module_dirs()
interface_paths = []
interface_paths.append(
os.path.join(get_default_modules_dir(), "interfaces.py")
)
for dirpath in dirpaths:
if not os.path.exists(dirpath):
for attr_name in dir(interfaces):
attr = getattr(interfaces, attr_name)
if (
not inspect.isclass(attr)
or attr is OpenPypeInterface
or not issubclass(attr, OpenPypeInterface)
):
continue
for filename in os.listdir(dirpath):
if filename in ("__pycache__", ):
continue
full_path = os.path.join(dirpath, filename)
if not os.path.isdir(full_path):
continue
interfaces_path = os.path.join(full_path, "interfaces.py")
if os.path.exists(interfaces_path):
interface_paths.append(interfaces_path)
for full_path in interface_paths:
if not os.path.exists(full_path):
continue
try:
# Prepare module object where content of file will be parsed
module = import_filepath(full_path)
except Exception:
log.warning(
"Failed to load path: \"{0}\"".format(full_path),
exc_info=True
)
continue
for attr_name in dir(module):
attr = getattr(module, attr_name)
if (
not inspect.isclass(attr)
or attr is OpenPypeInterface
or not issubclass(attr, OpenPypeInterface)
):
continue
setattr(openpype_interfaces, attr_name, attr)
setattr(openpype_interfaces, attr_name, attr)
def load_modules(force=False):
@ -378,14 +338,6 @@ class OpenPypeInterface:
pass
class MissingInteface(OpenPypeInterface):
"""Class representing missing interface class.
Used when interface is not available from currently registered paths.
"""
pass
@six.add_metaclass(ABCMeta)
class OpenPypeModule:
"""Base class of pype module.

View file

@ -1,30 +0,0 @@
from abc import abstractmethod
from openpype.modules import OpenPypeInterface
class ISettingsChangeListener(OpenPypeInterface):
"""Module has plugin paths to return.
Expected result is dictionary with keys "publish", "create", "load" or
"actions" and values as list or string.
{
"publish": ["path/to/publish_plugins"]
}
"""
@abstractmethod
def on_system_settings_save(
self, old_value, new_value, changes, new_value_metadata
):
pass
@abstractmethod
def on_project_settings_save(
self, old_value, new_value, changes, project_name, new_value_metadata
):
pass
@abstractmethod
def on_project_anatomy_save(
self, old_value, new_value, changes, project_name, new_value_metadata
):
pass

View file

@ -1,10 +1,7 @@
import os
import platform
from openpype.modules import OpenPypeModule
from openpype_interfaces import (
ITimersManager,
ITrayService
)
from openpype_interfaces import ITrayService
from avalon.api import AvalonMongoDB

View file

@ -15,7 +15,6 @@ from openpype.modules import (
)
# Import interface defined by this addon to be able find other addons using it
from openpype_interfaces import (
IExampleInterface,
IPluginPaths,
ITrayAction
)
@ -75,19 +74,6 @@ class ExampleAddon(OpenPypeAddOn, IPluginPaths, ITrayAction):
self._create_dialog()
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:
@ -106,8 +92,6 @@ class ExampleAddon(OpenPypeAddOn, IPluginPaths, ITrayAction):
"""
# 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()

View file

@ -1,28 +0,0 @@
""" 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

@ -9,7 +9,8 @@ class MyExampleDialog(QtWidgets.QDialog):
self.setWindowTitle("Connected modules")
label_widget = QtWidgets.QLabel(self)
msg = "This is example dialog of example addon."
label_widget = QtWidgets.QLabel(msg, self)
ok_btn = QtWidgets.QPushButton("OK", self)
btns_layout = QtWidgets.QHBoxLayout()
@ -28,12 +29,3 @@ class MyExampleDialog(QtWidgets.QDialog):
def _on_ok_clicked(self):
self.done(1)
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)

View file

@ -263,3 +263,31 @@ class ITrayService(ITrayModule):
"""Change icon of an QAction to orange circle."""
if self.menu_action:
self.menu_action.setIcon(self.get_icon_idle())
class ISettingsChangeListener(OpenPypeInterface):
"""Module has plugin paths to return.
Expected result is dictionary with keys "publish", "create", "load" or
"actions" and values as list or string.
{
"publish": ["path/to/publish_plugins"]
}
"""
@abstractmethod
def on_system_settings_save(
self, old_value, new_value, changes, new_value_metadata
):
pass
@abstractmethod
def on_project_settings_save(
self, old_value, new_value, changes, project_name, new_value_metadata
):
pass
@abstractmethod
def on_project_anatomy_save(
self, old_value, new_value, changes, project_name, new_value_metadata
):
pass