Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #2556 from pypeclub/feature/OP-2429_Publisher-Prep…
Browse files Browse the repository at this point in the history
…arations-before-standalone-publisher
  • Loading branch information
mkolar authored Feb 7, 2022
2 parents 4b3834e + 3160199 commit ecae9f6
Show file tree
Hide file tree
Showing 33 changed files with 1,790 additions and 411 deletions.
2 changes: 1 addition & 1 deletion openpype/hosts/testhost/plugins/create/auto_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class MyAutoCreator(AutoCreator):
identifier = "workfile"
family = "workfile"

def get_attribute_defs(self):
def get_instance_attr_defs(self):
output = [
lib.NumberDef("number_key", label="Number")
]
Expand Down
24 changes: 21 additions & 3 deletions openpype/hosts/testhost/plugins/create/test_creator_1.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from openpype import resources
from openpype.hosts.testhost.api import pipeline
from openpype.pipeline import (
Expand All @@ -13,6 +14,8 @@ class TestCreatorOne(Creator):
family = "test"
description = "Testing creator of testhost"

create_allow_context_change = False

def get_icon(self):
return resources.get_openpype_splash_filepath()

Expand All @@ -33,7 +36,10 @@ def remove_instances(self, instances):
for instance in instances:
self._remove_instance_from_context(instance)

def create(self, subset_name, data, options=None):
def create(self, subset_name, data, pre_create_data):
print("Data that can be used in create:\n{}".format(
json.dumps(pre_create_data, indent=4)
))
new_instance = CreatedInstance(self.family, subset_name, data, self)
pipeline.HostContext.add_instance(new_instance.data_to_store())
self.log.info(new_instance.data)
Expand All @@ -46,9 +52,21 @@ def get_default_variants(self):
"different_variant"
]

def get_attribute_defs(self):
def get_instance_attr_defs(self):
output = [
lib.NumberDef("number_key", label="Number"),
]
return output

def get_pre_create_attr_defs(self):
output = [
lib.NumberDef("number_key", label="Number")
lib.BoolDef("use_selection", label="Use selection"),
lib.UISeparatorDef(),
lib.UILabelDef("Testing label"),
lib.FileDef("filepath", folders=True, label="Filepath"),
lib.FileDef(
"filepath_2", multipath=True, folders=True, label="Filepath 2"
)
]
return output

Expand Down
4 changes: 2 additions & 2 deletions openpype/hosts/testhost/plugins/create/test_creator_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class TestCreatorTwo(Creator):
def get_icon(self):
return "cube"

def create(self, subset_name, data, options=None):
def create(self, subset_name, data, pre_create_data):
new_instance = CreatedInstance(self.family, subset_name, data, self)
pipeline.HostContext.add_instance(new_instance.data_to_store())
self.log.info(new_instance.data)
Expand All @@ -38,7 +38,7 @@ def remove_instances(self, instances):
for instance in instances:
self._remove_instance_from_context(instance)

def get_attribute_defs(self):
def get_instance_attr_defs(self):
output = [
lib.NumberDef("number_key"),
lib.TextDef("text_key")
Expand Down
2 changes: 1 addition & 1 deletion openpype/hosts/testhost/plugins/publish/collect_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class CollectContextDataTestHost(
hosts = ["testhost"]

@classmethod
def get_attribute_defs(cls):
def get_instance_attr_defs(cls):
return [
attribute_definitions.BoolDef(
"test_bool",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class CollectInstanceOneTestHost(
hosts = ["testhost"]

@classmethod
def get_attribute_defs(cls):
def get_instance_attr_defs(cls):
return [
attribute_definitions.NumberDef(
"version",
Expand Down
5 changes: 2 additions & 3 deletions openpype/pipeline/create/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def __init__(self, attr_defs, values, origin_data=None):
attr_defs_by_key = {
attr_def.key: attr_def
for attr_def in attr_defs
if attr_def.is_value_def
}
for key, value in values.items():
if key not in attr_defs_by_key:
Expand Down Expand Up @@ -306,8 +307,6 @@ def set_publish_plugins(self, attr_plugins):
self._plugin_names_order = []
self._missing_plugins = []
self.attr_plugins = attr_plugins or []
if not attr_plugins:
return

origin_data = self._origin_data
data = self._data
Expand Down Expand Up @@ -420,7 +419,7 @@ def __init__(
# Stored creator specific attribute values
# {key: value}
creator_values = copy.deepcopy(orig_creator_attributes)
creator_attr_defs = creator.get_attribute_defs()
creator_attr_defs = creator.get_instance_attr_defs()

self._data["creator_attributes"] = CreatorAttributeValues(
self, creator_attr_defs, creator_values, orig_creator_attributes
Expand Down
28 changes: 24 additions & 4 deletions openpype/pipeline/create/creator_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def _remove_instance_from_context(self, instance):
self.create_context.creator_removed_instance(instance)

@abstractmethod
def create(self, options=None):
def create(self):
"""Create new instance.
Replacement of `process` method from avalon implementation.
Expand Down Expand Up @@ -163,7 +163,7 @@ def get_subset_name(
dynamic_data=dynamic_data
)

def get_attribute_defs(self):
def get_instance_attr_defs(self):
"""Plugin attribute definitions.
Attribute definitions of plugin that hold data about created instance
Expand Down Expand Up @@ -199,15 +199,22 @@ class Creator(BaseCreator):
# - may not be used if `get_detail_description` is overriden
detailed_description = None

# It does make sense to change context on creation
# - in some cases it may confuse artists because it would not be used
# e.g. for buld creators
create_allow_context_change = True

@abstractmethod
def create(self, subset_name, instance_data, options=None):
def create(self, subset_name, instance_data, pre_create_data):
"""Create new instance and store it.
Ideally should be stored to workfile using host implementation.
Args:
subset_name(str): Subset name of created instance.
instance_data(dict):
instance_data(dict): Base data for instance.
pre_create_data(dict): Data based on pre creation attributes.
Those may affect how creator works.
"""

# instance = CreatedInstance(
Expand Down Expand Up @@ -258,6 +265,19 @@ def get_default_variant(self):

return None

def get_pre_create_attr_defs(self):
"""Plugin attribute definitions needed for creation.
Attribute definitions of plugin that define how creation will work.
Values of these definitions are passed to `create` method.
NOTE:
Convert method should be implemented which should care about updating
keys/values when plugin attributes change.
Returns:
list<AbtractAttrDef>: Attribute definitions that can be tweaked for
created instance.
"""
return []


class AutoCreator(BaseCreator):
"""Creator which is automatically triggered without user interaction.
Expand Down
16 changes: 14 additions & 2 deletions openpype/pipeline/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@

from .attribute_definitions import (
AbtractAttrDef,

UIDef,
UISeparatorDef,
UILabelDef,

UnknownDef,
NumberDef,
TextDef,
EnumDef,
BoolDef
BoolDef,
FileDef,
)


Expand All @@ -18,9 +24,15 @@
"BeforeWorkfileSave",

"AbtractAttrDef",

"UIDef",
"UISeparatorDef",
"UILabelDef",

"UnknownDef",
"NumberDef",
"TextDef",
"EnumDef",
"BoolDef"
"BoolDef",
"FileDef",
)
130 changes: 128 additions & 2 deletions openpype/pipeline/lib/attribute_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,21 @@ class AbtractAttrDef:
key(str): Under which key will be attribute value stored.
label(str): Attribute label.
tooltip(str): Attribute tooltip.
is_label_horizontal(bool): UI specific argument. Specify if label is
next to value input or ahead.
"""
is_value_def = True

def __init__(self, key, default, label=None, tooltip=None):
def __init__(
self, key, default, label=None, tooltip=None, is_label_horizontal=None
):
if is_label_horizontal is None:
is_label_horizontal = True
self.key = key
self.label = label
self.tooltip = tooltip
self.default = default
self.is_label_horizontal = is_label_horizontal
self._id = uuid.uuid4()

self.__init__class__ = AbtractAttrDef
Expand All @@ -68,8 +76,39 @@ def convert_value(self, value):
pass


# -----------------------------------------
# UI attribute definitoins won't hold value
# -----------------------------------------

class UIDef(AbtractAttrDef):
is_value_def = False

def __init__(self, key=None, default=None, *args, **kwargs):
super(UIDef, self).__init__(key, default, *args, **kwargs)

def convert_value(self, value):
return value


class UISeparatorDef(UIDef):
pass


class UILabelDef(UIDef):
def __init__(self, label):
super(UILabelDef, self).__init__(label=label)


# ---------------------------------------
# Attribute defintioins should hold value
# ---------------------------------------

class UnknownDef(AbtractAttrDef):
"""Definition is not known because definition is not available."""
"""Definition is not known because definition is not available.
This attribute can be used to keep existing data unchanged but does not
have known definition of type.
"""
def __init__(self, key, default=None, **kwargs):
kwargs["default"] = default
super(UnknownDef, self).__init__(key, **kwargs)
Expand Down Expand Up @@ -261,3 +300,90 @@ def convert_value(self, value):
if isinstance(value, bool):
return value
return self.default


class FileDef(AbtractAttrDef):
"""File definition.
It is possible to define filters of allowed file extensions and if supports
folders.
Args:
multipath(bool): Allow multiple path.
folders(bool): Allow folder paths.
extensions(list<str>): Allow files with extensions. Empty list will
allow all extensions and None will disable files completely.
default(str, list<str>): Defautl value.
"""

def __init__(
self, key, multipath=False, folders=None, extensions=None,
default=None, **kwargs
):
if folders is None and extensions is None:
folders = True
extensions = []

if default is None:
if multipath:
default = []
else:
default = ""
else:
if multipath:
if not isinstance(default, (tuple, list, set)):
raise TypeError((
"'default' argument must be 'list', 'tuple' or 'set'"
", not '{}'"
).format(type(default)))

else:
if not isinstance(default, six.string_types):
raise TypeError((
"'default' argument must be 'str' not '{}'"
).format(type(default)))
default = default.strip()

# Change horizontal label
is_label_horizontal = kwargs.get("is_label_horizontal")
if is_label_horizontal is None:
is_label_horizontal = True
if multipath:
is_label_horizontal = False
kwargs["is_label_horizontal"] = is_label_horizontal

self.multipath = multipath
self.folders = folders
self.extensions = extensions
super(FileDef, self).__init__(key, default=default, **kwargs)

def __eq__(self, other):
if not super(FileDef, self).__eq__(other):
return False

return (
self.multipath == other.multipath
and self.folders == other.folders
and self.extensions == other.extensions
)

def convert_value(self, value):
if isinstance(value, six.string_types):
if self.multipath:
value = [value.strip()]
else:
value = value.strip()
return value

if isinstance(value, (tuple, list, set)):
_value = []
for item in value:
if isinstance(item, six.string_types):
_value.append(item.strip())

if self.multipath:
return _value

if not _value:
return self.default
return _value[0].strip()

return str(value).strip()
2 changes: 1 addition & 1 deletion openpype/style/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
},
"nice-checkbox": {
"bg-checked": "#56a06f",
"bg-unchecked": "#434b56",
"bg-unchecked": "#21252B",
"bg-checker": "#D3D8DE",
"bg-checker-hover": "#F0F2F5"
},
Expand Down
Loading

0 comments on commit ecae9f6

Please sign in to comment.