mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 13:24:54 +01:00
Merge
This commit is contained in:
commit
d5cc025da5
295 changed files with 1140 additions and 712 deletions
10
.gitmodules
vendored
10
.gitmodules
vendored
|
|
@ -4,9 +4,9 @@
|
|||
[submodule "repos/avalon-unreal-integration"]
|
||||
path = repos/avalon-unreal-integration
|
||||
url = https://github.com/pypeclub/avalon-unreal-integration.git
|
||||
[submodule "openpype/modules/ftrack/python2_vendor/ftrack-python-api"]
|
||||
path = openpype/modules/ftrack/python2_vendor/ftrack-python-api
|
||||
[submodule "openpype/modules/default_modules/ftrack/python2_vendor/arrow"]
|
||||
path = openpype/modules/default_modules/ftrack/python2_vendor/arrow
|
||||
url = git@github.com:arrow-py/arrow.git
|
||||
[submodule "openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api"]
|
||||
path = openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api
|
||||
url = https://bitbucket.org/ftrack/ftrack-python-api.git
|
||||
[submodule "openpype/modules/ftrack/python2_vendor/arrow"]
|
||||
path = openpype/modules/ftrack/python2_vendor/arrow
|
||||
url = https://github.com/arrow-py/arrow.git
|
||||
89
CHANGELOG.md
89
CHANGELOG.md
|
|
@ -1,10 +1,21 @@
|
|||
# Changelog
|
||||
|
||||
## [3.3.1-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
## [3.4.0-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.3.0...HEAD)
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.3.1...HEAD)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
**Merged pull requests:**
|
||||
|
||||
- Maya: Add Xgen family support [\#1947](https://github.com/pypeclub/OpenPype/pull/1947)
|
||||
- Add face sets to exported alembics [\#1942](https://github.com/pypeclub/OpenPype/pull/1942)
|
||||
- Environments: Tool environments in alphabetical order [\#1910](https://github.com/pypeclub/OpenPype/pull/1910)
|
||||
- Dynamic modules [\#1872](https://github.com/pypeclub/OpenPype/pull/1872)
|
||||
|
||||
## [3.3.1](https://github.com/pypeclub/OpenPype/tree/3.3.1) (2021-08-20)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.3.1-nightly.1...3.3.1)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- TVPaint: Fixed rendered frame indexes [\#1946](https://github.com/pypeclub/OpenPype/pull/1946)
|
||||
- Maya: Menu actions fix [\#1945](https://github.com/pypeclub/OpenPype/pull/1945)
|
||||
|
|
@ -15,80 +26,66 @@
|
|||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.3.0-nightly.11...3.3.0)
|
||||
|
||||
**🚀 Enhancements**
|
||||
**Merged pull requests:**
|
||||
|
||||
- Python console interpreter [\#1940](https://github.com/pypeclub/OpenPype/pull/1940)
|
||||
- Fix - make AE workfile publish to Ftrack configurable [\#1937](https://github.com/pypeclub/OpenPype/pull/1937)
|
||||
- Fix - ftrack family was added incorrectly in some cases [\#1935](https://github.com/pypeclub/OpenPype/pull/1935)
|
||||
- Settings UI: Breadcrumbs in settings [\#1932](https://github.com/pypeclub/OpenPype/pull/1932)
|
||||
- Fix - Deadline publish on Linux started Tray instead of headless publishing [\#1930](https://github.com/pypeclub/OpenPype/pull/1930)
|
||||
- Maya: Validate Model Name - repair accident deletion in settings defaults [\#1929](https://github.com/pypeclub/OpenPype/pull/1929)
|
||||
- Global: Updated logos and Default settings [\#1927](https://github.com/pypeclub/OpenPype/pull/1927)
|
||||
- Nuke: submit to farm failed due `ftrack` family remove [\#1926](https://github.com/pypeclub/OpenPype/pull/1926)
|
||||
- Check for missing ✨ Python when using `pyenv` [\#1925](https://github.com/pypeclub/OpenPype/pull/1925)
|
||||
- Maya: Scene patching 🩹on submission to Deadline [\#1923](https://github.com/pypeclub/OpenPype/pull/1923)
|
||||
- Fix - validate takes repre\["files"\] as list all the time [\#1922](https://github.com/pypeclub/OpenPype/pull/1922)
|
||||
- Settings: Default values for enum [\#1920](https://github.com/pypeclub/OpenPype/pull/1920)
|
||||
- Settings UI: Modifiable dict view enhance [\#1919](https://github.com/pypeclub/OpenPype/pull/1919)
|
||||
- standalone: validator asset parents [\#1917](https://github.com/pypeclub/OpenPype/pull/1917)
|
||||
- Nuke: update video file crassing [\#1916](https://github.com/pypeclub/OpenPype/pull/1916)
|
||||
- Fix - texture validators for workfiles triggers only for textures workfiles [\#1914](https://github.com/pypeclub/OpenPype/pull/1914)
|
||||
- submodules: avalon-core update [\#1911](https://github.com/pypeclub/OpenPype/pull/1911)
|
||||
- Settings UI: List order works as expected [\#1906](https://github.com/pypeclub/OpenPype/pull/1906)
|
||||
- Add support for multiple Deadline ☠️➖ servers [\#1905](https://github.com/pypeclub/OpenPype/pull/1905)
|
||||
- Hiero: loaded clip was not set colorspace from version data [\#1904](https://github.com/pypeclub/OpenPype/pull/1904)
|
||||
- Pyblish UI: Fix collecting stage processing [\#1903](https://github.com/pypeclub/OpenPype/pull/1903)
|
||||
- Burnins: Use input's bitrate in h624 [\#1902](https://github.com/pypeclub/OpenPype/pull/1902)
|
||||
- Feature AE local render [\#1901](https://github.com/pypeclub/OpenPype/pull/1901)
|
||||
- Ftrack: Where I run action enhancement [\#1900](https://github.com/pypeclub/OpenPype/pull/1900)
|
||||
- Ftrack: Private project server actions [\#1899](https://github.com/pypeclub/OpenPype/pull/1899)
|
||||
- Support nested studio plugins paths. [\#1898](https://github.com/pypeclub/OpenPype/pull/1898)
|
||||
- Bug: fixed python detection [\#1893](https://github.com/pypeclub/OpenPype/pull/1893)
|
||||
- Settings: global validators with options [\#1892](https://github.com/pypeclub/OpenPype/pull/1892)
|
||||
- Settings: Conditional dict enum positioning [\#1891](https://github.com/pypeclub/OpenPype/pull/1891)
|
||||
- global: integrate name missing default template [\#1890](https://github.com/pypeclub/OpenPype/pull/1890)
|
||||
- publisher: editorial plugins fixes [\#1889](https://github.com/pypeclub/OpenPype/pull/1889)
|
||||
- Expose stop timer through rest api. [\#1886](https://github.com/pypeclub/OpenPype/pull/1886)
|
||||
- TVPaint: Increment workfile [\#1885](https://github.com/pypeclub/OpenPype/pull/1885)
|
||||
- Allow Multiple Notes to run on tasks. [\#1882](https://github.com/pypeclub/OpenPype/pull/1882)
|
||||
- Normalize path returned from Workfiles. [\#1880](https://github.com/pypeclub/OpenPype/pull/1880)
|
||||
- Prepare for pyside2 [\#1869](https://github.com/pypeclub/OpenPype/pull/1869)
|
||||
- Filter hosts in settings host-enum [\#1868](https://github.com/pypeclub/OpenPype/pull/1868)
|
||||
- Local actions with process identifier [\#1867](https://github.com/pypeclub/OpenPype/pull/1867)
|
||||
- Workfile tool start at host launch support [\#1865](https://github.com/pypeclub/OpenPype/pull/1865)
|
||||
- Maya: support for configurable `dirmap` 🗺️ [\#1859](https://github.com/pypeclub/OpenPype/pull/1859)
|
||||
- Settings list can use template or schema as object type [\#1815](https://github.com/pypeclub/OpenPype/pull/1815)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- Fix - ftrack family was added incorrectly in some cases [\#1935](https://github.com/pypeclub/OpenPype/pull/1935)
|
||||
- Fix - Deadline publish on Linux started Tray instead of headless publishing [\#1930](https://github.com/pypeclub/OpenPype/pull/1930)
|
||||
- Maya: Validate Model Name - repair accident deletion in settings defaults [\#1929](https://github.com/pypeclub/OpenPype/pull/1929)
|
||||
- Nuke: submit to farm failed due `ftrack` family remove [\#1926](https://github.com/pypeclub/OpenPype/pull/1926)
|
||||
- Fix - validate takes repre\["files"\] as list all the time [\#1922](https://github.com/pypeclub/OpenPype/pull/1922)
|
||||
- standalone: validator asset parents [\#1917](https://github.com/pypeclub/OpenPype/pull/1917)
|
||||
- Nuke: update video file crassing [\#1916](https://github.com/pypeclub/OpenPype/pull/1916)
|
||||
- Fix - texture validators for workfiles triggers only for textures workfiles [\#1914](https://github.com/pypeclub/OpenPype/pull/1914)
|
||||
- Fix - validators for textures workfiles trigger only for textures workfiles [\#1913](https://github.com/pypeclub/OpenPype/pull/1913)
|
||||
- Settings UI: List order works as expected [\#1906](https://github.com/pypeclub/OpenPype/pull/1906)
|
||||
- Hiero: loaded clip was not set colorspace from version data [\#1904](https://github.com/pypeclub/OpenPype/pull/1904)
|
||||
- Pyblish UI: Fix collecting stage processing [\#1903](https://github.com/pypeclub/OpenPype/pull/1903)
|
||||
- Burnins: Use input's bitrate in h624 [\#1902](https://github.com/pypeclub/OpenPype/pull/1902)
|
||||
- Bug: fixed python detection [\#1893](https://github.com/pypeclub/OpenPype/pull/1893)
|
||||
- global: integrate name missing default template [\#1890](https://github.com/pypeclub/OpenPype/pull/1890)
|
||||
- publisher: editorial plugins fixes [\#1889](https://github.com/pypeclub/OpenPype/pull/1889)
|
||||
- Normalize path returned from Workfiles. [\#1880](https://github.com/pypeclub/OpenPype/pull/1880)
|
||||
- Workfiles tool event arguments fix [\#1862](https://github.com/pypeclub/OpenPype/pull/1862)
|
||||
- Maya: don't add reference members as connections to the container set 📦 [\#1855](https://github.com/pypeclub/OpenPype/pull/1855)
|
||||
- Settings error dialog on show [\#1798](https://github.com/pypeclub/OpenPype/pull/1798)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix - make AE workfile publish to Ftrack configurable [\#1937](https://github.com/pypeclub/OpenPype/pull/1937)
|
||||
- Settings UI: Breadcrumbs in settings [\#1932](https://github.com/pypeclub/OpenPype/pull/1932)
|
||||
- Add support for multiple Deadline ☠️➖ servers [\#1905](https://github.com/pypeclub/OpenPype/pull/1905)
|
||||
- Maya: add support for `RedshiftNormalMap` node, fix `tx` linear space 🚀 [\#1863](https://github.com/pypeclub/OpenPype/pull/1863)
|
||||
- Workfiles tool event arguments fix [\#1862](https://github.com/pypeclub/OpenPype/pull/1862)
|
||||
- Maya: support for configurable `dirmap` 🗺️ [\#1859](https://github.com/pypeclub/OpenPype/pull/1859)
|
||||
- Maya: don't add reference members as connections to the container set 📦 [\#1855](https://github.com/pypeclub/OpenPype/pull/1855)
|
||||
- Settings list can use template or schema as object type [\#1815](https://github.com/pypeclub/OpenPype/pull/1815)
|
||||
- Maya: expected files -\> render products ⚙️ overhaul [\#1812](https://github.com/pypeclub/OpenPype/pull/1812)
|
||||
- Settings error dialog on show [\#1798](https://github.com/pypeclub/OpenPype/pull/1798)
|
||||
|
||||
## [3.2.0](https://github.com/pypeclub/OpenPype/tree/3.2.0) (2021-07-13)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.2.0-nightly.7...3.2.0)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- Nuke: ftrack family plugin settings preset [\#1805](https://github.com/pypeclub/OpenPype/pull/1805)
|
||||
- Standalone publisher last project [\#1799](https://github.com/pypeclub/OpenPype/pull/1799)
|
||||
- Ftrack Multiple notes as server action [\#1795](https://github.com/pypeclub/OpenPype/pull/1795)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- nuke: fixing wrong name of family folder when `used existing frames` [\#1803](https://github.com/pypeclub/OpenPype/pull/1803)
|
||||
- Collect ftrack family bugs [\#1801](https://github.com/pypeclub/OpenPype/pull/1801)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Build: don't add Poetry to `PATH` [\#1808](https://github.com/pypeclub/OpenPype/pull/1808)
|
||||
- Nuke: ftrack family plugin settings preset [\#1805](https://github.com/pypeclub/OpenPype/pull/1805)
|
||||
- nuke: fixing wrong name of family folder when `used existing frames` [\#1803](https://github.com/pypeclub/OpenPype/pull/1803)
|
||||
- Collect ftrack family bugs [\#1801](https://github.com/pypeclub/OpenPype/pull/1801)
|
||||
- Standalone publisher last project [\#1799](https://github.com/pypeclub/OpenPype/pull/1799)
|
||||
|
||||
## [2.18.4](https://github.com/pypeclub/OpenPype/tree/2.18.4) (2021-06-24)
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ def patched_discover(superclass):
|
|||
def install():
|
||||
"""Install Pype to Avalon."""
|
||||
from pyblish.lib import MessageHandler
|
||||
from openpype.modules import load_modules
|
||||
|
||||
# Make sure modules are loaded
|
||||
load_modules()
|
||||
|
||||
def modified_emit(obj, record):
|
||||
"""Method replacing `emit` in Pyblish's MessageHandler."""
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class CreateAnimation(plugin.Creator):
|
|||
|
||||
# Write vertex colors with the geometry.
|
||||
self.data["writeColorSets"] = False
|
||||
self.data["writeFaceSets"] = False
|
||||
|
||||
# Include only renderable visible shapes.
|
||||
# Skips locators and empty transforms
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class CreateModel(plugin.Creator):
|
|||
|
||||
# Vertex colors with the geometry
|
||||
self.data["writeColorSets"] = False
|
||||
self.data["writeFaceSets"] = False
|
||||
|
||||
# Include attributes by attribute name or prefix
|
||||
self.data["attr"] = ""
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class CreatePointCache(plugin.Creator):
|
|||
self.data.update(lib.collect_animation_data())
|
||||
|
||||
self.data["writeColorSets"] = False # Vertex colors with the geometry.
|
||||
self.data["writeFaceSets"] = False # Vertex colors with the geometry.
|
||||
self.data["renderableOnly"] = False # Only renderable visible shapes
|
||||
self.data["visibleOnly"] = False # only nodes that are visible
|
||||
self.data["includeParentHierarchy"] = False # Include parent groups
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ class ExtractAnimation(openpype.api.Extractor):
|
|||
"uvWrite": True,
|
||||
"selection": True,
|
||||
"worldSpace": instance.data.get("worldSpace", True),
|
||||
"writeColorSets": instance.data.get("writeColorSets", False)
|
||||
"writeColorSets": instance.data.get("writeColorSets", False),
|
||||
"writeFaceSets": instance.data.get("writeFaceSets", False)
|
||||
}
|
||||
|
||||
if not instance.data.get("includeParentHierarchy", True):
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class ExtractModel(openpype.api.Extractor):
|
|||
hosts = ["maya"]
|
||||
families = ["model"]
|
||||
scene_type = "ma"
|
||||
optional = True
|
||||
|
||||
def process(self, instance):
|
||||
"""Plugin entry point."""
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class ExtractAlembic(openpype.api.Extractor):
|
|||
|
||||
# Get extra export arguments
|
||||
writeColorSets = instance.data.get("writeColorSets", False)
|
||||
writeFaceSets = instance.data.get("writeFaceSets", False)
|
||||
|
||||
self.log.info("Extracting pointcache..")
|
||||
dirname = self.staging_dir(instance)
|
||||
|
|
@ -53,6 +54,7 @@ class ExtractAlembic(openpype.api.Extractor):
|
|||
"writeVisibility": True,
|
||||
"writeCreases": True,
|
||||
"writeColorSets": writeColorSets,
|
||||
"writeFaceSets": writeFaceSets,
|
||||
"uvWrite": True,
|
||||
"selection": True,
|
||||
"worldSpace": instance.data.get("worldSpace", True)
|
||||
|
|
|
|||
|
|
@ -52,9 +52,11 @@ from .vendor_bin_utils import (
|
|||
)
|
||||
|
||||
from .python_module_tools import (
|
||||
import_filepath,
|
||||
modules_from_path,
|
||||
recursive_bases_from_class,
|
||||
classes_from_module
|
||||
classes_from_module,
|
||||
import_module_from_dirpath
|
||||
)
|
||||
|
||||
from .avalon_context import (
|
||||
|
|
@ -170,9 +172,11 @@ __all__ = [
|
|||
"get_ffmpeg_tool_path",
|
||||
"ffprobe_streams",
|
||||
|
||||
"import_filepath",
|
||||
"modules_from_path",
|
||||
"recursive_bases_from_class",
|
||||
"classes_from_module",
|
||||
"import_module_from_dirpath",
|
||||
|
||||
"CURRENT_DOC_SCHEMAS",
|
||||
"PROJECT_NAME_ALLOWED_SYMBOLS",
|
||||
|
|
|
|||
|
|
@ -1107,7 +1107,7 @@ def prepare_host_environments(data, implementation_envs=True):
|
|||
asset_doc = data.get("asset_doc")
|
||||
# Add tools environments
|
||||
groups_by_name = {}
|
||||
tool_by_group_name = collections.defaultdict(list)
|
||||
tool_by_group_name = collections.defaultdict(dict)
|
||||
if asset_doc:
|
||||
# Make sure each tool group can be added only once
|
||||
for key in asset_doc["data"].get("tools_env") or []:
|
||||
|
|
@ -1115,12 +1115,14 @@ def prepare_host_environments(data, implementation_envs=True):
|
|||
if not tool:
|
||||
continue
|
||||
groups_by_name[tool.group.name] = tool.group
|
||||
tool_by_group_name[tool.group.name].append(tool)
|
||||
tool_by_group_name[tool.group.name][tool.name] = tool
|
||||
|
||||
for group_name, group in groups_by_name.items():
|
||||
for group_name in sorted(groups_by_name.keys()):
|
||||
group = groups_by_name[group_name]
|
||||
environments.append(group.environment)
|
||||
added_env_keys.add(group_name)
|
||||
for tool in tool_by_group_name[group_name]:
|
||||
for tool_name in sorted(tool_by_group_name[group_name].keys()):
|
||||
tool = tool_by_group_name[group_name][tool_name]
|
||||
environments.append(tool.environment)
|
||||
added_env_keys.add(tool.name)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,38 @@ log = logging.getLogger(__name__)
|
|||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
|
||||
def import_filepath(filepath, module_name=None):
|
||||
"""Import python file as python module.
|
||||
|
||||
Python 2 and Python 3 compatibility.
|
||||
|
||||
Args:
|
||||
filepath(str): Path to python file.
|
||||
module_name(str): Name of loaded module. Only for Python 3. By default
|
||||
is filled with filename of filepath.
|
||||
"""
|
||||
if module_name is None:
|
||||
module_name = os.path.splitext(os.path.basename(filepath))[0]
|
||||
|
||||
# Prepare module object where content of file will be parsed
|
||||
module = types.ModuleType(module_name)
|
||||
|
||||
if PY3:
|
||||
# Use loader so module has full specs
|
||||
module_loader = importlib.machinery.SourceFileLoader(
|
||||
module_name, filepath
|
||||
)
|
||||
module_loader.exec_module(module)
|
||||
else:
|
||||
# Execute module code and store content to module
|
||||
with open(filepath) as _stream:
|
||||
# Execute content and store it to module object
|
||||
exec(_stream.read(), module.__dict__)
|
||||
|
||||
module.__file__ = filepath
|
||||
return module
|
||||
|
||||
|
||||
def modules_from_path(folder_path):
|
||||
"""Get python scripts as modules from a path.
|
||||
|
||||
|
|
@ -55,23 +87,7 @@ def modules_from_path(folder_path):
|
|||
continue
|
||||
|
||||
try:
|
||||
# Prepare module object where content of file will be parsed
|
||||
module = types.ModuleType(mod_name)
|
||||
|
||||
if PY3:
|
||||
# Use loader so module has full specs
|
||||
module_loader = importlib.machinery.SourceFileLoader(
|
||||
mod_name, full_path
|
||||
)
|
||||
module_loader.exec_module(module)
|
||||
else:
|
||||
# Execute module code and store content to module
|
||||
with open(full_path) as _stream:
|
||||
# Execute content and store it to module object
|
||||
exec(_stream.read(), module.__dict__)
|
||||
|
||||
module.__file__ = full_path
|
||||
|
||||
module = import_filepath(full_path, mod_name)
|
||||
modules.append((full_path, module))
|
||||
|
||||
except Exception:
|
||||
|
|
@ -127,3 +143,96 @@ def classes_from_module(superclass, module):
|
|||
|
||||
classes.append(obj)
|
||||
return classes
|
||||
|
||||
|
||||
def _import_module_from_dirpath_py2(dirpath, module_name, dst_module_name):
|
||||
"""Import passed dirpath as python module using `imp`."""
|
||||
if dst_module_name:
|
||||
full_module_name = "{}.{}".format(dst_module_name, module_name)
|
||||
dst_module = sys.modules[dst_module_name]
|
||||
else:
|
||||
full_module_name = module_name
|
||||
dst_module = None
|
||||
|
||||
if full_module_name in sys.modules:
|
||||
return sys.modules[full_module_name]
|
||||
|
||||
import imp
|
||||
|
||||
fp, pathname, description = imp.find_module(module_name, [dirpath])
|
||||
module = imp.load_module(full_module_name, fp, pathname, description)
|
||||
if dst_module is not None:
|
||||
setattr(dst_module, module_name, module)
|
||||
|
||||
return module
|
||||
|
||||
|
||||
def _import_module_from_dirpath_py3(dirpath, module_name, dst_module_name):
|
||||
"""Import passed dirpath as python module using Python 3 modules."""
|
||||
if dst_module_name:
|
||||
full_module_name = "{}.{}".format(dst_module_name, module_name)
|
||||
dst_module = sys.modules[dst_module_name]
|
||||
else:
|
||||
full_module_name = module_name
|
||||
dst_module = None
|
||||
|
||||
# Skip import if is already imported
|
||||
if full_module_name in sys.modules:
|
||||
return sys.modules[full_module_name]
|
||||
|
||||
import importlib.util
|
||||
from importlib._bootstrap_external import PathFinder
|
||||
|
||||
# Find loader for passed path and name
|
||||
loader = PathFinder.find_module(full_module_name, [dirpath])
|
||||
|
||||
# Load specs of module
|
||||
spec = importlib.util.spec_from_loader(
|
||||
full_module_name, loader, origin=dirpath
|
||||
)
|
||||
|
||||
# Create module based on specs
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
|
||||
# Store module to destination module and `sys.modules`
|
||||
# WARNING this mus be done before module execution
|
||||
if dst_module is not None:
|
||||
setattr(dst_module, module_name, module)
|
||||
|
||||
sys.modules[full_module_name] = module
|
||||
|
||||
# Execute module import
|
||||
loader.exec_module(module)
|
||||
|
||||
return module
|
||||
|
||||
|
||||
def import_module_from_dirpath(dirpath, folder_name, dst_module_name=None):
|
||||
"""Import passed directory as a python module.
|
||||
|
||||
Python 2 and 3 compatible.
|
||||
|
||||
Imported module can be assigned as a child attribute of already loaded
|
||||
module from `sys.modules` if has support of `setattr`. That is not default
|
||||
behavior of python modules so parent module must be a custom module with
|
||||
that ability.
|
||||
|
||||
It is not possible to reimport already cached module. If you need to
|
||||
reimport module you have to remove it from caches manually.
|
||||
|
||||
Args:
|
||||
dirpath(str): Parent directory path of loaded folder.
|
||||
folder_name(str): Folder name which should be imported inside passed
|
||||
directory.
|
||||
dst_module_name(str): Parent module name under which can be loaded
|
||||
module added.
|
||||
"""
|
||||
if PY3:
|
||||
module = _import_module_from_dirpath_py3(
|
||||
dirpath, folder_name, dst_module_name
|
||||
)
|
||||
else:
|
||||
module = _import_module_from_dirpath_py2(
|
||||
dirpath, folder_name, dst_module_name
|
||||
)
|
||||
return module
|
||||
|
|
|
|||
|
|
@ -1,7 +1,19 @@
|
|||
# Pype modules
|
||||
Pype modules should contain separated logic of specific kind of implementation, like Ftrack connection and usage code or Deadline farm rendering.
|
||||
# OpenPype modules/addons
|
||||
OpenPype modules should contain separated logic of specific kind of implementation, like Ftrack connection and usage code or Deadline farm rendering or may contain only special plugins. Addons work the same way currently there is no difference in module and addon.
|
||||
|
||||
## Base class `PypeModule`
|
||||
## Modules concept
|
||||
- modules and addons are dynamically imported to virtual python module `openpype_modules` from which it is possible to import them no matter where is the modulo located
|
||||
- modules or addons should never be imported directly even if you know possible full import path
|
||||
- it is because all of their content must be imported in specific order and should not be imported without defined functions as it may also break few implementation parts
|
||||
|
||||
### TODOs
|
||||
- add module/addon manifest
|
||||
- definition of module (not 100% defined content e.g. minimum require OpenPype version etc.)
|
||||
- defying that folder is content of a module or an addon
|
||||
- module/addon have it's settings schemas and default values outside OpenPype
|
||||
- add general setting of paths to modules
|
||||
|
||||
## Base class `OpenPypeModule`
|
||||
- abstract class as base for each module
|
||||
- implementation should be module's api withou GUI parts
|
||||
- may implement `get_global_environments` method which should return dictionary of environments that are globally appliable and value is the same for whole studio if launched at any workstation (except os specific paths)
|
||||
|
|
@ -17,6 +29,16 @@ Pype modules should contain separated logic of specific kind of implementation,
|
|||
- interface is class that has defined abstract methods to implement and may contain preimplemented helper methods
|
||||
- module that inherit from an interface must implement those abstract methods otherwise won't be initialized
|
||||
- it is easy to find which module object inherited from which interfaces withh 100% chance they have implemented required methods
|
||||
- interfaces can be defined in `interfaces.py` inside module directory
|
||||
- the file can't use relative imports or import anything from other parts
|
||||
of module itself at the header of file
|
||||
- this is one of reasons why modules/addons can't be imported directly without using defined functions in OpenPype modules implementation
|
||||
|
||||
## Base class `OpenPypeInterface`
|
||||
- has nothing implemented
|
||||
- has ABCMeta as metaclass
|
||||
- is defined to be able find out classes which inherit from this base to be
|
||||
able tell this is an Interface
|
||||
|
||||
## Global interfaces
|
||||
- few interfaces are implemented for global usage
|
||||
|
|
@ -70,7 +92,7 @@ Pype modules should contain separated logic of specific kind of implementation,
|
|||
- Clockify has more inharitance it's class definition looks like
|
||||
```
|
||||
class ClockifyModule(
|
||||
PypeModule, # Says it's Pype module so ModulesManager will try to initialize.
|
||||
OpenPypeModule, # Says it's Pype module so ModulesManager will try to initialize.
|
||||
ITrayModule, # Says has special implementation when used in tray.
|
||||
IPluginPaths, # Says has plugin paths that want to register (paths to clockify actions for launcher).
|
||||
IFtrackEventHandlerPaths, # Says has Ftrack actions/events for user/server.
|
||||
|
|
|
|||
|
|
@ -1,86 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from .base import (
|
||||
PypeModule,
|
||||
ITrayModule,
|
||||
ITrayAction,
|
||||
ITrayService,
|
||||
IPluginPaths,
|
||||
ILaunchHookPaths,
|
||||
OpenPypeModule,
|
||||
OpenPypeInterface,
|
||||
|
||||
load_modules,
|
||||
|
||||
ModulesManager,
|
||||
TrayModulesManager
|
||||
)
|
||||
from .settings_action import (
|
||||
SettingsAction,
|
||||
ISettingsChangeListener,
|
||||
LocalSettingsAction
|
||||
)
|
||||
from .webserver import (
|
||||
WebServerModule,
|
||||
IWebServerRoutes
|
||||
)
|
||||
from .idle_manager import (
|
||||
IdleManager,
|
||||
IIdleManager
|
||||
)
|
||||
from .timers_manager import (
|
||||
TimersManager,
|
||||
ITimersManager
|
||||
)
|
||||
from .avalon_apps import AvalonModule
|
||||
from .launcher_action import LauncherAction
|
||||
from .ftrack import (
|
||||
FtrackModule,
|
||||
IFtrackEventHandlerPaths
|
||||
)
|
||||
from .clockify import ClockifyModule
|
||||
from .log_viewer import LogViewModule
|
||||
from .muster import MusterModule
|
||||
from .deadline import DeadlineModule
|
||||
from .project_manager_action import ProjectManagerAction
|
||||
from .standalonepublish_action import StandAlonePublishAction
|
||||
from .python_console_interpreter import PythonInterpreterAction
|
||||
from .sync_server import SyncServerModule
|
||||
from .slack import SlackIntegrationModule
|
||||
|
||||
|
||||
__all__ = (
|
||||
"PypeModule",
|
||||
"ITrayModule",
|
||||
"ITrayAction",
|
||||
"ITrayService",
|
||||
"IPluginPaths",
|
||||
"ILaunchHookPaths",
|
||||
"OpenPypeModule",
|
||||
"OpenPypeInterface",
|
||||
|
||||
"load_modules",
|
||||
|
||||
"ModulesManager",
|
||||
"TrayModulesManager",
|
||||
|
||||
"SettingsAction",
|
||||
"LocalSettingsAction",
|
||||
|
||||
"WebServerModule",
|
||||
"IWebServerRoutes",
|
||||
|
||||
"IdleManager",
|
||||
"IIdleManager",
|
||||
|
||||
"TimersManager",
|
||||
"ITimersManager",
|
||||
|
||||
"AvalonModule",
|
||||
"LauncherAction",
|
||||
|
||||
"FtrackModule",
|
||||
"IFtrackEventHandlerPaths",
|
||||
|
||||
"ClockifyModule",
|
||||
"IdleManager",
|
||||
"LogViewModule",
|
||||
"MusterModule",
|
||||
"DeadlineModule",
|
||||
"ProjectManagerAction",
|
||||
"StandAlonePublishAction",
|
||||
"PythonInterpreterAction",
|
||||
|
||||
"SyncServerModule",
|
||||
|
||||
"SlackIntegrationModule"
|
||||
"TrayModulesManager"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Base class for Pype Modules."""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import inspect
|
||||
import logging
|
||||
import threading
|
||||
import collections
|
||||
from uuid import uuid4
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
|
@ -11,11 +14,305 @@ import six
|
|||
import openpype
|
||||
from openpype.settings import get_system_settings
|
||||
from openpype.lib import PypeLogger
|
||||
from openpype import resources
|
||||
|
||||
|
||||
# Inherit from `object` for Python 2 hosts
|
||||
class _ModuleClass(object):
|
||||
"""Fake module class for storing OpenPype modules.
|
||||
|
||||
Object of this class can be stored to `sys.modules` and used for storing
|
||||
dynamically imported modules.
|
||||
"""
|
||||
def __init__(self, name):
|
||||
# Call setattr on super class
|
||||
super(_ModuleClass, self).__setattr__("name", name)
|
||||
|
||||
# Where modules and interfaces are stored
|
||||
super(_ModuleClass, self).__setattr__("__attributes__", dict())
|
||||
super(_ModuleClass, self).__setattr__("__defaults__", set())
|
||||
|
||||
super(_ModuleClass, self).__setattr__("_log", None)
|
||||
|
||||
def __getattr__(self, attr_name):
|
||||
if attr_name not in self.__attributes__:
|
||||
if attr_name in ("__path__"):
|
||||
return None
|
||||
raise ImportError("No module named {}.{}".format(
|
||||
self.name, attr_name
|
||||
))
|
||||
return self.__attributes__[attr_name]
|
||||
|
||||
def __iter__(self):
|
||||
for module in self.values():
|
||||
yield module
|
||||
|
||||
def __setattr__(self, attr_name, value):
|
||||
if attr_name in self.__attributes__:
|
||||
self.log.warning(
|
||||
"Duplicated name \"{}\" in {}. Overriding.".format(
|
||||
self.name, attr_name
|
||||
)
|
||||
)
|
||||
self.__attributes__[attr_name] = value
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.__setattr__(key, value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
|
||||
@property
|
||||
def log(self):
|
||||
if self._log is None:
|
||||
super(_ModuleClass, self).__setattr__(
|
||||
"_log", PypeLogger.get_logger(self.name)
|
||||
)
|
||||
return self._log
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self.__attributes__.get(key, default)
|
||||
|
||||
def keys(self):
|
||||
return self.__attributes__.keys()
|
||||
|
||||
def values(self):
|
||||
return self.__attributes__.values()
|
||||
|
||||
def items(self):
|
||||
return self.__attributes__.items()
|
||||
|
||||
|
||||
class _InterfacesClass(_ModuleClass):
|
||||
"""Fake module class for storing OpenPype interfaces.
|
||||
|
||||
MissingInterface object is returned if interfaces does not exists.
|
||||
- this is because interfaces must be available even if are missing
|
||||
implementation
|
||||
"""
|
||||
def __getattr__(self, attr_name):
|
||||
if attr_name not in self.__attributes__:
|
||||
# Fake Interface if is not missing
|
||||
self.__attributes__[attr_name] = type(
|
||||
attr_name,
|
||||
(MissingInteface, ),
|
||||
{}
|
||||
)
|
||||
|
||||
return self.__attributes__[attr_name]
|
||||
|
||||
|
||||
class _LoadCache:
|
||||
interfaces_lock = threading.Lock()
|
||||
modules_lock = threading.Lock()
|
||||
interfaces_loaded = False
|
||||
modules_loaded = False
|
||||
|
||||
|
||||
def get_default_modules_dir():
|
||||
"""Path to default OpenPype modules."""
|
||||
current_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
return os.path.join(current_dir, "default_modules")
|
||||
|
||||
|
||||
def get_module_dirs():
|
||||
"""List of paths where OpenPype modules can be found."""
|
||||
dirpaths = [
|
||||
get_default_modules_dir()
|
||||
]
|
||||
return dirpaths
|
||||
|
||||
|
||||
def load_interfaces(force=False):
|
||||
"""Load interfaces from modules into `openpype_interfaces`.
|
||||
|
||||
Only classes which inherit from `OpenPypeInterface` are loaded and stored.
|
||||
|
||||
Args:
|
||||
force(bool): Force to load interfaces even if are already loaded.
|
||||
This won't update already loaded and used (cached) interfaces.
|
||||
"""
|
||||
|
||||
if _LoadCache.interfaces_loaded and not force:
|
||||
return
|
||||
|
||||
if not _LoadCache.interfaces_lock.locked():
|
||||
with _LoadCache.interfaces_lock:
|
||||
_load_interfaces()
|
||||
_LoadCache.interfaces_loaded = True
|
||||
else:
|
||||
# If lock is locked wait until is finished
|
||||
while _LoadCache.interfaces_lock.locked():
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
def _load_interfaces():
|
||||
# Key under which will be modules imported in `sys.modules`
|
||||
from openpype.lib import import_filepath
|
||||
|
||||
modules_key = "openpype_interfaces"
|
||||
|
||||
sys.modules[modules_key] = openpype_interfaces = (
|
||||
_InterfacesClass(modules_key)
|
||||
)
|
||||
|
||||
log = PypeLogger.get_logger("InterfacesLoader")
|
||||
|
||||
dirpaths = get_module_dirs()
|
||||
|
||||
interface_paths = []
|
||||
interface_paths.append(
|
||||
os.path.join(get_default_modules_dir(), "interfaces.py")
|
||||
)
|
||||
for dirpath in dirpaths:
|
||||
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)
|
||||
|
||||
|
||||
def load_modules(force=False):
|
||||
"""Load OpenPype modules as python modules.
|
||||
|
||||
Modules does not load only classes (like in Interfaces) because there must
|
||||
be ability to use inner code of module and be able to import it from one
|
||||
defined place.
|
||||
|
||||
With this it is possible to import module's content from predefined module.
|
||||
|
||||
Function makes sure that `load_interfaces` was triggered. Modules import
|
||||
has specific order which can't be changed.
|
||||
|
||||
Args:
|
||||
force(bool): Force to load modules even if are already loaded.
|
||||
This won't update already loaded and used (cached) modules.
|
||||
"""
|
||||
|
||||
if _LoadCache.modules_loaded and not force:
|
||||
return
|
||||
|
||||
# First load interfaces
|
||||
# - modules must not be imported before interfaces
|
||||
load_interfaces(force)
|
||||
|
||||
if not _LoadCache.modules_lock.locked():
|
||||
with _LoadCache.modules_lock:
|
||||
_load_modules()
|
||||
_LoadCache.modules_loaded = True
|
||||
else:
|
||||
# If lock is locked wait until is finished
|
||||
while _LoadCache.modules_lock.locked():
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
def _load_modules():
|
||||
# Import helper functions from lib
|
||||
from openpype.lib import (
|
||||
import_filepath,
|
||||
import_module_from_dirpath
|
||||
)
|
||||
|
||||
# Key under which will be modules imported in `sys.modules`
|
||||
modules_key = "openpype_modules"
|
||||
|
||||
# Change `sys.modules`
|
||||
sys.modules[modules_key] = openpype_modules = _ModuleClass(modules_key)
|
||||
|
||||
log = PypeLogger.get_logger("ModulesLoader")
|
||||
|
||||
# Look for OpenPype modules in paths defined with `get_module_dirs`
|
||||
dirpaths = get_module_dirs()
|
||||
|
||||
for dirpath in dirpaths:
|
||||
if not os.path.exists(dirpath):
|
||||
log.warning((
|
||||
"Could not find path when loading OpenPype modules \"{}\""
|
||||
).format(dirpath))
|
||||
continue
|
||||
|
||||
for filename in os.listdir(dirpath):
|
||||
# Ignore filenames
|
||||
if filename in ("__pycache__", ):
|
||||
continue
|
||||
|
||||
fullpath = os.path.join(dirpath, filename)
|
||||
basename, ext = os.path.splitext(filename)
|
||||
|
||||
# TODO add more logic how to define if folder is module or not
|
||||
# - check manifest and content of manifest
|
||||
if os.path.isdir(fullpath):
|
||||
import_module_from_dirpath(dirpath, filename, modules_key)
|
||||
|
||||
elif ext in (".py", ):
|
||||
module = import_filepath(fullpath)
|
||||
setattr(openpype_modules, basename, module)
|
||||
|
||||
|
||||
class _OpenPypeInterfaceMeta(ABCMeta):
|
||||
"""OpenPypeInterface meta class to print proper string."""
|
||||
def __str__(self):
|
||||
return "<'OpenPypeInterface.{}'>".format(self.__name__)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
@six.add_metaclass(_OpenPypeInterfaceMeta)
|
||||
class OpenPypeInterface:
|
||||
"""Base class of Interface that can be used as Mixin with abstract parts.
|
||||
|
||||
This is way how OpenPype module or addon can tell that has implementation
|
||||
for specific part or for other module/addon.
|
||||
|
||||
Child classes of OpenPypeInterface may be used as mixin in different
|
||||
OpenPype modules which means they have to have implemented methods defined
|
||||
in the interface. By default interface does not have any abstract parts.
|
||||
"""
|
||||
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 PypeModule:
|
||||
class OpenPypeModule:
|
||||
"""Base class of pype module.
|
||||
|
||||
Attributes:
|
||||
|
|
@ -38,7 +335,7 @@ class PypeModule:
|
|||
def __init__(self, manager, settings):
|
||||
self.manager = manager
|
||||
|
||||
self.log = PypeLogger().get_logger(self.name)
|
||||
self.log = PypeLogger.get_logger(self.name)
|
||||
|
||||
self.initialize(settings)
|
||||
|
||||
|
|
@ -70,265 +367,8 @@ class PypeModule:
|
|||
return {}
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class IPluginPaths:
|
||||
"""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"]
|
||||
}
|
||||
"""
|
||||
# TODO validation of an output
|
||||
@abstractmethod
|
||||
def get_plugin_paths(self):
|
||||
pass
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class ILaunchHookPaths:
|
||||
"""Module has launch hook paths to return.
|
||||
|
||||
Expected result is list of paths.
|
||||
["path/to/launch_hooks_dir"]
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_launch_hook_paths(self):
|
||||
pass
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class ITrayModule:
|
||||
"""Module has special procedures when used in Pype Tray.
|
||||
|
||||
IMPORTANT:
|
||||
The module still must be usable if is not used in tray even if
|
||||
would do nothing.
|
||||
"""
|
||||
tray_initialized = False
|
||||
_tray_manager = None
|
||||
|
||||
@abstractmethod
|
||||
def tray_init(self):
|
||||
"""Initialization part of tray implementation.
|
||||
|
||||
Triggered between `initialization` and `connect_with_modules`.
|
||||
|
||||
This is where GUIs should be loaded or tray specific parts should be
|
||||
prepared.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def tray_menu(self, tray_menu):
|
||||
"""Add module's action to tray menu."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def tray_start(self):
|
||||
"""Start procedure in Pype tray."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def tray_exit(self):
|
||||
"""Cleanup method which is executed on tray shutdown.
|
||||
|
||||
This is place where all threads should be shut.
|
||||
"""
|
||||
pass
|
||||
|
||||
def execute_in_main_thread(self, callback):
|
||||
""" Pushes callback to the queue or process 'callback' on a main thread
|
||||
|
||||
Some callbacks need to be processed on main thread (menu actions
|
||||
must be added on main thread or they won't get triggered etc.)
|
||||
"""
|
||||
# called without initialized tray, still main thread needed
|
||||
if not self.tray_initialized:
|
||||
try:
|
||||
callback = self._main_thread_callbacks.popleft()
|
||||
callback()
|
||||
except:
|
||||
self.log.warning(
|
||||
"Failed to execute {} in main thread".format(callback),
|
||||
exc_info=True)
|
||||
|
||||
return
|
||||
self.manager.tray_manager.execute_in_main_thread(callback)
|
||||
|
||||
def show_tray_message(self, title, message, icon=None, msecs=None):
|
||||
"""Show tray message.
|
||||
|
||||
Args:
|
||||
title (str): Title of message.
|
||||
message (str): Content of message.
|
||||
icon (QSystemTrayIcon.MessageIcon): Message's icon. Default is
|
||||
Information icon, may differ by Qt version.
|
||||
msecs (int): Duration of message visibility in miliseconds.
|
||||
Default is 10000 msecs, may differ by Qt version.
|
||||
"""
|
||||
if self._tray_manager:
|
||||
self._tray_manager.show_tray_message(title, message, icon, msecs)
|
||||
|
||||
def add_doubleclick_callback(self, callback):
|
||||
if hasattr(self.manager, "add_doubleclick_callback"):
|
||||
self.manager.add_doubleclick_callback(self, callback)
|
||||
|
||||
|
||||
class ITrayAction(ITrayModule):
|
||||
"""Implementation of Tray action.
|
||||
|
||||
Add action to tray menu which will trigger `on_action_trigger`.
|
||||
It is expected to be used for showing tools.
|
||||
|
||||
Methods `tray_start`, `tray_exit` and `connect_with_modules` are overriden
|
||||
as it's not expected that action will use them. But it is possible if
|
||||
necessary.
|
||||
"""
|
||||
|
||||
admin_action = False
|
||||
_admin_submenu = None
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def label(self):
|
||||
"""Service label showed in menu."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_action_trigger(self):
|
||||
"""What happens on actions click."""
|
||||
pass
|
||||
|
||||
def tray_menu(self, tray_menu):
|
||||
from Qt import QtWidgets
|
||||
|
||||
if self.admin_action:
|
||||
menu = self.admin_submenu(tray_menu)
|
||||
action = QtWidgets.QAction(self.label, menu)
|
||||
menu.addAction(action)
|
||||
if not menu.menuAction().isVisible():
|
||||
menu.menuAction().setVisible(True)
|
||||
|
||||
else:
|
||||
action = QtWidgets.QAction(self.label, tray_menu)
|
||||
tray_menu.addAction(action)
|
||||
|
||||
action.triggered.connect(self.on_action_trigger)
|
||||
|
||||
def tray_start(self):
|
||||
return
|
||||
|
||||
def tray_exit(self):
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def admin_submenu(tray_menu):
|
||||
if ITrayAction._admin_submenu is None:
|
||||
from Qt import QtWidgets
|
||||
|
||||
admin_submenu = QtWidgets.QMenu("Admin", tray_menu)
|
||||
admin_submenu.menuAction().setVisible(False)
|
||||
ITrayAction._admin_submenu = admin_submenu
|
||||
return ITrayAction._admin_submenu
|
||||
|
||||
|
||||
class ITrayService(ITrayModule):
|
||||
# Module's property
|
||||
menu_action = None
|
||||
|
||||
# Class properties
|
||||
_services_submenu = None
|
||||
_icon_failed = None
|
||||
_icon_running = None
|
||||
_icon_idle = None
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def label(self):
|
||||
"""Service label showed in menu."""
|
||||
pass
|
||||
|
||||
# TODO be able to get any sort of information to show/print
|
||||
# @abstractmethod
|
||||
# def get_service_info(self):
|
||||
# pass
|
||||
|
||||
@staticmethod
|
||||
def services_submenu(tray_menu):
|
||||
if ITrayService._services_submenu is None:
|
||||
from Qt import QtWidgets
|
||||
|
||||
services_submenu = QtWidgets.QMenu("Services", tray_menu)
|
||||
services_submenu.menuAction().setVisible(False)
|
||||
ITrayService._services_submenu = services_submenu
|
||||
return ITrayService._services_submenu
|
||||
|
||||
@staticmethod
|
||||
def add_service_action(action):
|
||||
ITrayService._services_submenu.addAction(action)
|
||||
if not ITrayService._services_submenu.menuAction().isVisible():
|
||||
ITrayService._services_submenu.menuAction().setVisible(True)
|
||||
|
||||
@staticmethod
|
||||
def _load_service_icons():
|
||||
from Qt import QtGui
|
||||
ITrayService._failed_icon = QtGui.QIcon(
|
||||
resources.get_resource("icons", "circle_red.png")
|
||||
)
|
||||
ITrayService._icon_running = QtGui.QIcon(
|
||||
resources.get_resource("icons", "circle_green.png")
|
||||
)
|
||||
ITrayService._icon_idle = QtGui.QIcon(
|
||||
resources.get_resource("icons", "circle_orange.png")
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_icon_running():
|
||||
if ITrayService._icon_running is None:
|
||||
ITrayService._load_service_icons()
|
||||
return ITrayService._icon_running
|
||||
|
||||
@staticmethod
|
||||
def get_icon_idle():
|
||||
if ITrayService._icon_idle is None:
|
||||
ITrayService._load_service_icons()
|
||||
return ITrayService._icon_idle
|
||||
|
||||
@staticmethod
|
||||
def get_icon_failed():
|
||||
if ITrayService._failed_icon is None:
|
||||
ITrayService._load_service_icons()
|
||||
return ITrayService._failed_icon
|
||||
|
||||
def tray_menu(self, tray_menu):
|
||||
from Qt import QtWidgets
|
||||
action = QtWidgets.QAction(
|
||||
self.label,
|
||||
self.services_submenu(tray_menu)
|
||||
)
|
||||
self.menu_action = action
|
||||
|
||||
self.add_service_action(action)
|
||||
|
||||
self.set_service_running_icon()
|
||||
|
||||
def set_service_running_icon(self):
|
||||
"""Change icon of an QAction to green circle."""
|
||||
if self.menu_action:
|
||||
self.menu_action.setIcon(self.get_icon_running())
|
||||
|
||||
def set_service_failed_icon(self):
|
||||
"""Change icon of an QAction to red circle."""
|
||||
if self.menu_action:
|
||||
self.menu_action.setIcon(self.get_icon_failed())
|
||||
|
||||
def set_service_idle_icon(self):
|
||||
"""Change icon of an QAction to orange circle."""
|
||||
if self.menu_action:
|
||||
self.menu_action.setIcon(self.get_icon_idle())
|
||||
class OpenPypeAddOn(OpenPypeModule):
|
||||
pass
|
||||
|
||||
|
||||
class ModulesManager:
|
||||
|
|
@ -357,6 +397,11 @@ class ModulesManager:
|
|||
|
||||
def initialize_modules(self):
|
||||
"""Import and initialize modules."""
|
||||
# Make sure modules are loaded
|
||||
load_modules()
|
||||
|
||||
import openpype_modules
|
||||
|
||||
self.log.debug("*** Pype modules initialization.")
|
||||
# Prepare settings for modules
|
||||
system_settings = getattr(self, "_system_settings", None)
|
||||
|
|
@ -368,33 +413,43 @@ class ModulesManager:
|
|||
time_start = time.time()
|
||||
prev_start_time = time_start
|
||||
|
||||
# Go through globals in `pype.modules`
|
||||
for name in dir(openpype.modules):
|
||||
modules_item = getattr(openpype.modules, name, None)
|
||||
# Filter globals that are not classes which inherit from PypeModule
|
||||
if (
|
||||
not inspect.isclass(modules_item)
|
||||
or modules_item is openpype.modules.PypeModule
|
||||
or not issubclass(modules_item, openpype.modules.PypeModule)
|
||||
):
|
||||
continue
|
||||
module_classes = []
|
||||
for module in openpype_modules:
|
||||
# Go through globals in `pype.modules`
|
||||
for name in dir(module):
|
||||
modules_item = getattr(module, name, None)
|
||||
# Filter globals that are not classes which inherit from
|
||||
# OpenPypeModule
|
||||
if (
|
||||
not inspect.isclass(modules_item)
|
||||
or modules_item is OpenPypeModule
|
||||
or not issubclass(modules_item, OpenPypeModule)
|
||||
):
|
||||
continue
|
||||
|
||||
# Check if class is abstract (Developing purpose)
|
||||
if inspect.isabstract(modules_item):
|
||||
# Find missing implementations by convetion on `abc` module
|
||||
not_implemented = []
|
||||
for attr_name in dir(modules_item):
|
||||
attr = getattr(modules_item, attr_name, None)
|
||||
if attr and getattr(attr, "__isabstractmethod__", None):
|
||||
not_implemented.append(attr_name)
|
||||
# Check if class is abstract (Developing purpose)
|
||||
if inspect.isabstract(modules_item):
|
||||
# Find missing implementations by convetion on `abc` module
|
||||
not_implemented = []
|
||||
for attr_name in dir(modules_item):
|
||||
attr = getattr(modules_item, attr_name, None)
|
||||
abs_method = getattr(
|
||||
attr, "__isabstractmethod__", None
|
||||
)
|
||||
if attr and abs_method:
|
||||
not_implemented.append(attr_name)
|
||||
|
||||
# Log missing implementations
|
||||
self.log.warning((
|
||||
"Skipping abstract Class: {}. Missing implementations: {}"
|
||||
).format(name, ", ".join(not_implemented)))
|
||||
continue
|
||||
# Log missing implementations
|
||||
self.log.warning((
|
||||
"Skipping abstract Class: {}."
|
||||
" Missing implementations: {}"
|
||||
).format(name, ", ".join(not_implemented)))
|
||||
continue
|
||||
module_classes.append(modules_item)
|
||||
|
||||
for modules_item in module_classes:
|
||||
try:
|
||||
name = modules_item.__name__
|
||||
# Try initialize module
|
||||
module = modules_item(self, modules_settings)
|
||||
# Store initialized object
|
||||
|
|
@ -492,6 +547,8 @@ class ModulesManager:
|
|||
and "actions" each containing list of paths.
|
||||
"""
|
||||
# Output structure
|
||||
from openpype_interfaces import IPluginPaths
|
||||
|
||||
output = {
|
||||
"publish": [],
|
||||
"create": [],
|
||||
|
|
@ -544,6 +601,8 @@ class ModulesManager:
|
|||
Returns:
|
||||
list: Paths to launch hook directories.
|
||||
"""
|
||||
from openpype_interfaces import ILaunchHookPaths
|
||||
|
||||
str_type = type("")
|
||||
expected_types = (list, tuple, set)
|
||||
|
||||
|
|
@ -711,6 +770,7 @@ class TrayModulesManager(ModulesManager):
|
|||
self.modules_by_id = {}
|
||||
self.modules_by_name = {}
|
||||
self._report = {}
|
||||
|
||||
self.tray_manager = None
|
||||
|
||||
self.doubleclick_callbacks = {}
|
||||
|
|
@ -743,6 +803,8 @@ class TrayModulesManager(ModulesManager):
|
|||
self.tray_menu(tray_menu)
|
||||
|
||||
def get_enabled_tray_modules(self):
|
||||
from openpype_interfaces import ITrayModule
|
||||
|
||||
output = []
|
||||
for module in self.modules:
|
||||
if module.enabled and isinstance(module, ITrayModule):
|
||||
|
|
@ -818,6 +880,8 @@ class TrayModulesManager(ModulesManager):
|
|||
self._report["Tray menu"] = report
|
||||
|
||||
def start_modules(self):
|
||||
from openpype_interfaces import ITrayService
|
||||
|
||||
report = {}
|
||||
time_start = time.time()
|
||||
prev_start_time = time_start
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import os
|
||||
import openpype
|
||||
from openpype import resources
|
||||
from .. import (
|
||||
PypeModule,
|
||||
from openpype.modules import OpenPypeModule
|
||||
from openpype_interfaces import (
|
||||
ITrayModule,
|
||||
IWebServerRoutes
|
||||
)
|
||||
|
||||
|
||||
class AvalonModule(PypeModule, ITrayModule, IWebServerRoutes):
|
||||
class AvalonModule(OpenPypeModule, ITrayModule, IWebServerRoutes):
|
||||
name = "avalon"
|
||||
|
||||
def initialize(self, modules_settings):
|
||||
|
|
@ -1,16 +1,13 @@
|
|||
import os
|
||||
import re
|
||||
import json
|
||||
import datetime
|
||||
|
||||
import bson
|
||||
from bson.objectid import ObjectId
|
||||
import bson.json_util
|
||||
|
||||
from aiohttp.web_response import Response
|
||||
|
||||
from avalon.api import AvalonMongoDB
|
||||
from openpype.modules.webserver.base_routes import RestApiEndpoint
|
||||
from openpype_modules.webserver.base_routes import RestApiEndpoint
|
||||
|
||||
|
||||
class _RestApiEndpoint(RestApiEndpoint):
|
||||
|
|
@ -7,8 +7,8 @@ from .constants import (
|
|||
CLOCKIFY_FTRACK_USER_PATH,
|
||||
CLOCKIFY_FTRACK_SERVER_PATH
|
||||
)
|
||||
from openpype.modules import (
|
||||
PypeModule,
|
||||
from openpype.modules import OpenPypeModule
|
||||
from openpype_interfaces import (
|
||||
ITrayModule,
|
||||
IPluginPaths,
|
||||
IFtrackEventHandlerPaths,
|
||||
|
|
@ -17,7 +17,7 @@ from openpype.modules import (
|
|||
|
||||
|
||||
class ClockifyModule(
|
||||
PypeModule,
|
||||
OpenPypeModule,
|
||||
ITrayModule,
|
||||
IPluginPaths,
|
||||
IFtrackEventHandlerPaths,
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
import json
|
||||
from openpype.modules.ftrack.lib import ServerAction
|
||||
from openpype.modules.clockify.clockify_api import ClockifyAPI
|
||||
from openpype_modules.ftrack.lib import ServerAction
|
||||
from openpype_modules.clockify.clockify_api import ClockifyAPI
|
||||
|
||||
|
||||
class SyncClocifyServer(ServerAction):
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import json
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype.modules.clockify.clockify_api import ClockifyAPI
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.clockify.clockify_api import ClockifyAPI
|
||||
|
||||
|
||||
class SyncClocifyLocal(BaseAction):
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from avalon import api, io
|
||||
from openpype.api import Logger
|
||||
from openpype.modules.clockify.clockify_api import ClockifyAPI
|
||||
from openpype_modules.clockify.clockify_api import ClockifyAPI
|
||||
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from avalon import api, io
|
||||
from openpype.modules.clockify.clockify_api import ClockifyAPI
|
||||
from openpype_modules.clockify.clockify_api import ClockifyAPI
|
||||
from openpype.api import Logger
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import os
|
||||
from openpype.modules import (
|
||||
PypeModule, IPluginPaths)
|
||||
from openpype.modules import OpenPypeModule
|
||||
from openpype_interfaces import IPluginPaths
|
||||
|
||||
|
||||
class DeadlineModule(PypeModule, IPluginPaths):
|
||||
class DeadlineModule(OpenPypeModule, IPluginPaths):
|
||||
name = "deadline"
|
||||
|
||||
def __init__(self, manager, settings):
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
from .ftrack_module import (
|
||||
FtrackModule,
|
||||
IFtrackEventHandlerPaths,
|
||||
FTRACK_MODULE_DIR
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"FtrackModule",
|
||||
"IFtrackEventHandlerPaths",
|
||||
"FTRACK_MODULE_DIR"
|
||||
)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import json
|
||||
|
||||
from openpype.modules.ftrack.lib import ServerAction
|
||||
from openpype_modules.ftrack.lib import ServerAction
|
||||
|
||||
|
||||
def clone_review_session(session, entity):
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from openpype.modules.ftrack.lib import ServerAction
|
||||
from openpype_modules.ftrack.lib import ServerAction
|
||||
|
||||
|
||||
class MultipleNotesServer(ServerAction):
|
||||
|
|
@ -4,7 +4,7 @@ from avalon.api import AvalonMongoDB
|
|||
from openpype.api import ProjectSettings
|
||||
from openpype.lib import create_project
|
||||
|
||||
from openpype.modules.ftrack.lib import (
|
||||
from openpype_modules.ftrack.lib import (
|
||||
ServerAction,
|
||||
get_openpype_attr,
|
||||
CUST_ATTR_AUTO_SYNC
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from openpype.modules.ftrack.lib import ServerAction
|
||||
from openpype_modules.ftrack.lib import ServerAction
|
||||
|
||||
|
||||
class PrivateProjectDetectionAction(ServerAction):
|
||||
|
|
@ -2,7 +2,7 @@ import sys
|
|||
import json
|
||||
import collections
|
||||
import ftrack_api
|
||||
from openpype.modules.ftrack.lib import ServerAction
|
||||
from openpype_modules.ftrack.lib import ServerAction
|
||||
|
||||
|
||||
class PushHierValuesToNonHier(ServerAction):
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import time
|
||||
import traceback
|
||||
|
||||
from openpype.modules.ftrack.lib import ServerAction
|
||||
from openpype.modules.ftrack.lib.avalon_sync import SyncEntitiesFactory
|
||||
from openpype_modules.ftrack.lib import ServerAction
|
||||
from openpype_modules.ftrack.lib.avalon_sync import SyncEntitiesFactory
|
||||
|
||||
|
||||
class SyncToAvalonServer(ServerAction):
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from openpype.modules.ftrack.lib import BaseEvent
|
||||
from openpype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
|
||||
from openpype.modules.ftrack.event_handlers_server.event_sync_to_avalon import (
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
from openpype_modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
|
||||
from openpype_modules.ftrack.event_handlers_server.event_sync_to_avalon import (
|
||||
SyncToAvalonEvent
|
||||
)
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from openpype.modules.ftrack.lib import BaseEvent
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
class FirstVersionStatus(BaseEvent):
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import collections
|
||||
from openpype.modules.ftrack.lib import BaseEvent
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
class NextTaskUpdate(BaseEvent):
|
||||
|
|
@ -2,7 +2,7 @@ import collections
|
|||
import datetime
|
||||
|
||||
import ftrack_api
|
||||
from openpype.modules.ftrack.lib import (
|
||||
from openpype_modules.ftrack.lib import (
|
||||
BaseEvent,
|
||||
query_custom_attributes
|
||||
)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import ftrack_api
|
||||
from openpype.modules.ftrack.lib import BaseEvent
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
class RadioButtons(BaseEvent):
|
||||
|
|
@ -17,7 +17,7 @@ import ftrack_api
|
|||
from avalon import schema
|
||||
from avalon.api import AvalonMongoDB
|
||||
|
||||
from openpype.modules.ftrack.lib import (
|
||||
from openpype_modules.ftrack.lib import (
|
||||
get_openpype_attr,
|
||||
CUST_ATTR_ID_KEY,
|
||||
CUST_ATTR_AUTO_SYNC,
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import collections
|
||||
from openpype.modules.ftrack.lib import BaseEvent
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
class TaskStatusToParent(BaseEvent):
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import collections
|
||||
from openpype.modules.ftrack.lib import BaseEvent
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
class TaskToVersionStatus(BaseEvent):
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import collections
|
||||
from openpype.modules.ftrack.lib import BaseEvent
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
class ThumbnailEvents(BaseEvent):
|
||||
|
|
@ -2,8 +2,8 @@ import os
|
|||
import re
|
||||
import subprocess
|
||||
|
||||
from openpype.modules.ftrack.lib import BaseEvent
|
||||
from openpype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
from openpype_modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
|
||||
from avalon.api import AvalonMongoDB
|
||||
|
||||
from bson.objectid import ObjectId
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from openpype.modules.ftrack.lib import BaseEvent
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
class VersionToTaskStatus(BaseEvent):
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
from uuid import uuid4
|
||||
|
||||
from openpype.modules.ftrack.lib import BaseAction
|
||||
from openpype_modules.ftrack.lib import BaseAction
|
||||
from openpype.lib import (
|
||||
ApplicationManager,
|
||||
ApplicationLaunchFailed,
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
Taken from https://github.com/tokejepsen/ftrack-hooks/tree/master/batch_tasks
|
||||
"""
|
||||
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
|
||||
|
||||
class BatchTasksAction(BaseAction):
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import collections
|
||||
import ftrack_api
|
||||
from openpype.modules.ftrack.lib import (
|
||||
from openpype_modules.ftrack.lib import (
|
||||
BaseAction,
|
||||
statics_icon,
|
||||
get_openpype_attr
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
try:
|
||||
from functools import cmp_to_key
|
||||
except Exception:
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
|
||||
|
||||
class ComponentOpen(BaseAction):
|
||||
|
|
@ -2,7 +2,7 @@ import collections
|
|||
import json
|
||||
import arrow
|
||||
import ftrack_api
|
||||
from openpype.modules.ftrack.lib import (
|
||||
from openpype_modules.ftrack.lib import (
|
||||
BaseAction,
|
||||
statics_icon,
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ dictionary level, task's attributes are nested more.
|
|||
|
||||
group (string)
|
||||
- name of group
|
||||
- based on attribute `openpype.modules.ftrack.lib.CUST_ATTR_GROUP`
|
||||
- based on attribute `openpype_modules.ftrack.lib.CUST_ATTR_GROUP`
|
||||
- "pype" by default
|
||||
|
||||
*** Required ***************************************************************
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import os
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
from avalon import lib as avalonlib
|
||||
from openpype.api import (
|
||||
Anatomy,
|
||||
|
|
@ -2,7 +2,7 @@ import os
|
|||
import re
|
||||
import json
|
||||
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype.api import Anatomy, get_project_settings
|
||||
|
||||
|
||||
|
|
@ -4,7 +4,7 @@ from datetime import datetime
|
|||
from queue import Queue
|
||||
|
||||
from bson.objectid import ObjectId
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
from avalon.api import AvalonMongoDB
|
||||
|
||||
|
||||
|
|
@ -5,7 +5,7 @@ import uuid
|
|||
import clique
|
||||
from pymongo import UpdateOne
|
||||
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
from avalon.api import AvalonMongoDB
|
||||
from openpype.api import Anatomy
|
||||
|
||||
|
|
@ -6,8 +6,8 @@ import collections
|
|||
from bson.objectid import ObjectId
|
||||
|
||||
from openpype.api import Anatomy, config
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
|
||||
from openpype.lib.delivery import (
|
||||
path_from_representation,
|
||||
get_format_dict,
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
import subprocess
|
||||
from operator import itemgetter
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
|
||||
|
||||
class DJVViewAction(BaseAction):
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import json
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
|
||||
|
||||
class JobKiller(BaseAction):
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
|
||||
|
||||
class MultipleNotes(BaseAction):
|
||||
|
|
@ -4,7 +4,7 @@ from avalon.api import AvalonMongoDB
|
|||
from openpype.api import ProjectSettings
|
||||
from openpype.lib import create_project
|
||||
|
||||
from openpype.modules.ftrack.lib import (
|
||||
from openpype_modules.ftrack.lib import (
|
||||
BaseAction,
|
||||
statics_icon,
|
||||
get_openpype_attr,
|
||||
|
|
@ -3,7 +3,7 @@ import subprocess
|
|||
import traceback
|
||||
import json
|
||||
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
import ftrack_api
|
||||
from avalon import io, api
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
from operator import itemgetter
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
|
||||
|
||||
class SeedDebugProject(BaseAction):
|
||||
|
|
@ -4,11 +4,11 @@ import json
|
|||
import requests
|
||||
|
||||
from bson.objectid import ObjectId
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype.api import Anatomy
|
||||
from avalon.api import AvalonMongoDB
|
||||
|
||||
from openpype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
|
||||
from openpype_modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
|
||||
|
||||
|
||||
class StoreThumbnailsToAvalon(BaseAction):
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import time
|
||||
import traceback
|
||||
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype.modules.ftrack.lib.avalon_sync import SyncEntitiesFactory
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib.avalon_sync import SyncEntitiesFactory
|
||||
|
||||
|
||||
class SyncToAvalonLocal(BaseAction):
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
|
||||
|
||||
class TestAction(BaseAction):
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import json
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
|
||||
|
||||
class ThumbToChildren(BaseAction):
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import json
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction, statics_icon
|
||||
|
||||
|
||||
class ThumbToParent(BaseAction):
|
||||
|
|
@ -2,7 +2,7 @@ import platform
|
|||
import socket
|
||||
import getpass
|
||||
|
||||
from openpype.modules.ftrack.lib import BaseAction, statics_icon
|
||||
from openpype_modules.ftrack.lib import BaseAction
|
||||
|
||||
|
||||
class ActionWhereIRun(BaseAction):
|
||||
|
|
@ -1,35 +1,24 @@
|
|||
import os
|
||||
import json
|
||||
import collections
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import six
|
||||
import openpype
|
||||
from openpype.modules import (
|
||||
PypeModule,
|
||||
from openpype.modules import OpenPypeModule
|
||||
|
||||
from openpype_interfaces import (
|
||||
ITrayModule,
|
||||
IPluginPaths,
|
||||
ITimersManager,
|
||||
ILaunchHookPaths,
|
||||
ISettingsChangeListener
|
||||
ISettingsChangeListener,
|
||||
IFtrackEventHandlerPaths
|
||||
)
|
||||
from openpype.settings import SaveWarningExc
|
||||
|
||||
FTRACK_MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class IFtrackEventHandlerPaths:
|
||||
"""Other modules interface to return paths to ftrack event handlers.
|
||||
|
||||
Expected output is dictionary with "server" and "user" keys.
|
||||
"""
|
||||
@abstractmethod
|
||||
def get_event_handler_paths(self):
|
||||
pass
|
||||
|
||||
|
||||
class FtrackModule(
|
||||
PypeModule,
|
||||
OpenPypeModule,
|
||||
ITrayModule,
|
||||
IPluginPaths,
|
||||
ITimersManager,
|
||||
|
|
@ -242,7 +231,7 @@ class FtrackModule(
|
|||
return
|
||||
|
||||
import ftrack_api
|
||||
from openpype.modules.ftrack.lib import get_openpype_attr
|
||||
from openpype_modules.ftrack.lib import get_openpype_attr
|
||||
|
||||
try:
|
||||
session = self.create_ftrack_session()
|
||||
|
|
@ -18,17 +18,10 @@ from openpype.lib import (
|
|||
get_pype_execute_args,
|
||||
OpenPypeMongoConnection
|
||||
)
|
||||
from openpype.modules.ftrack import FTRACK_MODULE_DIR
|
||||
from openpype.modules.ftrack.lib import (
|
||||
credentials,
|
||||
get_ftrack_url_from_settings
|
||||
)
|
||||
from openpype.modules.ftrack.ftrack_server.lib import (
|
||||
check_ftrack_url,
|
||||
get_ftrack_event_mongo_info
|
||||
)
|
||||
|
||||
from openpype.modules.ftrack.ftrack_server import socket_thread
|
||||
from openpype_modules.ftrack import FTRACK_MODULE_DIR
|
||||
from openpype_modules.ftrack.lib import credentials
|
||||
from openpype_modules.ftrack.ftrack_server.lib import check_ftrack_url
|
||||
from openpype_modules.ftrack.ftrack_server import socket_thread
|
||||
|
||||
|
||||
class MongoPermissionsError(Exception):
|
||||
|
|
@ -22,7 +22,7 @@ try:
|
|||
from weakref import WeakMethod
|
||||
except ImportError:
|
||||
from ftrack_api._weakref import WeakMethod
|
||||
from openpype.modules.ftrack.lib import get_ftrack_event_mongo_info
|
||||
from openpype_modules.ftrack.lib import get_ftrack_event_mongo_info
|
||||
|
||||
from openpype.lib import OpenPypeMongoConnection
|
||||
from openpype.api import Logger
|
||||
12
openpype/modules/default_modules/ftrack/interfaces.py
Normal file
12
openpype/modules/default_modules/ftrack/interfaces.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from abc import abstractmethod
|
||||
from openpype.modules import OpenPypeInterface
|
||||
|
||||
|
||||
class IFtrackEventHandlerPaths(OpenPypeInterface):
|
||||
"""Other modules interface to return paths to ftrack event handlers.
|
||||
|
||||
Expected output is dictionary with "server" and "user" keys.
|
||||
"""
|
||||
@abstractmethod
|
||||
def get_event_handler_paths(self):
|
||||
pass
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
from openpype.lib import PreLaunchHook
|
||||
from openpype.modules.ftrack import FTRACK_MODULE_DIR
|
||||
from openpype_modules.ftrack import FTRACK_MODULE_DIR
|
||||
|
||||
|
||||
class PrePython2Support(PreLaunchHook):
|
||||
|
|
@ -10,7 +10,7 @@ from openpype.api import Logger
|
|||
from openpype.settings import get_project_settings
|
||||
|
||||
import ftrack_api
|
||||
from openpype.modules.ftrack import ftrack_server
|
||||
from openpype_modules.ftrack import ftrack_server
|
||||
|
||||
|
||||
class MissingPermision(Exception):
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue