Merge branch 'develop' of github.com:pypeclub/OpenPype into feature/OP-3446_tray-publish-batch-mov

This commit is contained in:
Petr Kalis 2022-07-15 13:28:36 +02:00
commit c384c8b8ba
53 changed files with 1588 additions and 479 deletions

7
.gitmodules vendored Normal file
View file

@ -0,0 +1,7 @@
[submodule "vendor/powershell/BurntToast"]
path = vendor/powershell/BurntToast
url = https://github.com/Windos/BurntToast.git
[submodule "vendor/powershell/PSWriteColor"]
path = vendor/powershell/PSWriteColor
url = https://github.com/EvotecIT/PSWriteColor.git

View file

@ -1,8 +1,8 @@
# Changelog
## [3.12.1-nightly.4](https://github.com/pypeclub/OpenPype/tree/HEAD)
## [3.12.1](https://github.com/pypeclub/OpenPype/tree/3.12.1) (2022-07-13)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.12.0...HEAD)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.12.0...3.12.1)
### 📖 Documentation
@ -14,6 +14,9 @@
**🚀 Enhancements**
- TrayPublisher: Added more options for grouping of instances [\#3494](https://github.com/pypeclub/OpenPype/pull/3494)
- NewPublisher: Align creator attributes from top to bottom [\#3487](https://github.com/pypeclub/OpenPype/pull/3487)
- NewPublisher: Added ability to use label of instance [\#3484](https://github.com/pypeclub/OpenPype/pull/3484)
- General: Creator Plugins have access to project [\#3476](https://github.com/pypeclub/OpenPype/pull/3476)
- General: Better arguments order in creator init [\#3475](https://github.com/pypeclub/OpenPype/pull/3475)
- Ftrack: Trigger custom ftrack events on project creation and preparation [\#3465](https://github.com/pypeclub/OpenPype/pull/3465)
@ -21,10 +24,15 @@
- Blender: Bugfix - Set fps properly on open [\#3426](https://github.com/pypeclub/OpenPype/pull/3426)
- Hiero: Add custom scripts menu [\#3425](https://github.com/pypeclub/OpenPype/pull/3425)
- Blender: pre pyside install for all platforms [\#3400](https://github.com/pypeclub/OpenPype/pull/3400)
- Maya: Ability to set resolution for playblasts from asset, and override through review instance. [\#3360](https://github.com/pypeclub/OpenPype/pull/3360)
- Maya: Add additional playblast options to review Extractor. [\#3384](https://github.com/pypeclub/OpenPype/pull/3384)
**🐛 Bug fixes**
- TrayPublisher: Keep use instance label in list view [\#3493](https://github.com/pypeclub/OpenPype/pull/3493)
- General: Extract review use first frame of input sequence [\#3491](https://github.com/pypeclub/OpenPype/pull/3491)
- General: Fix Plist loading for application launch [\#3485](https://github.com/pypeclub/OpenPype/pull/3485)
- Nuke: Workfile tools open on start [\#3479](https://github.com/pypeclub/OpenPype/pull/3479)
- New Publisher: Disabled context change allows creation [\#3478](https://github.com/pypeclub/OpenPype/pull/3478)
- General: thumbnail extractor fix [\#3474](https://github.com/pypeclub/OpenPype/pull/3474)
- Kitsu: bugfix with sync-service ans publish plugins [\#3473](https://github.com/pypeclub/OpenPype/pull/3473)
- Flame: solved problem with multi-selected loading [\#3470](https://github.com/pypeclub/OpenPype/pull/3470)
@ -38,6 +46,7 @@
- Nuke: Slate frame is integrated [\#3427](https://github.com/pypeclub/OpenPype/pull/3427)
- Maya: Camera extra data - additional fix for \#3304 [\#3386](https://github.com/pypeclub/OpenPype/pull/3386)
- Maya: Handle excluding `model` family from frame range validator. [\#3370](https://github.com/pypeclub/OpenPype/pull/3370)
- Harmony: audio validator has wrong logic [\#3364](https://github.com/pypeclub/OpenPype/pull/3364)
**🔀 Refactored code**
@ -46,7 +55,9 @@
- General: Use query functions in global plugins [\#3459](https://github.com/pypeclub/OpenPype/pull/3459)
- Clockify: Use query functions in clockify actions [\#3458](https://github.com/pypeclub/OpenPype/pull/3458)
- General: Use query functions in rest api calls [\#3457](https://github.com/pypeclub/OpenPype/pull/3457)
- General: Use query functions in openpype lib functions [\#3454](https://github.com/pypeclub/OpenPype/pull/3454)
- General: Use query functions in load utils [\#3446](https://github.com/pypeclub/OpenPype/pull/3446)
- General: Move publish plugin and publish render abstractions [\#3442](https://github.com/pypeclub/OpenPype/pull/3442)
- General: Use Anatomy after move to pipeline [\#3436](https://github.com/pypeclub/OpenPype/pull/3436)
- General: Anatomy moved to pipeline [\#3435](https://github.com/pypeclub/OpenPype/pull/3435)
- Fusion: Use client query functions [\#3380](https://github.com/pypeclub/OpenPype/pull/3380)
@ -66,8 +77,6 @@
- Webserver: Added CORS middleware [\#3422](https://github.com/pypeclub/OpenPype/pull/3422)
- Attribute Defs UI: Files widget show what is allowed to drop in [\#3411](https://github.com/pypeclub/OpenPype/pull/3411)
- General: Add ability to change user value for templates [\#3366](https://github.com/pypeclub/OpenPype/pull/3366)
- Hosts: More options for in-host callbacks [\#3357](https://github.com/pypeclub/OpenPype/pull/3357)
- Multiverse: expose some settings to GUI [\#3350](https://github.com/pypeclub/OpenPype/pull/3350)
**🐛 Bug fixes**
@ -82,7 +91,6 @@
- Maya: vray device aspect ratio fix [\#3381](https://github.com/pypeclub/OpenPype/pull/3381)
- Flame: bunch of publishing issues [\#3377](https://github.com/pypeclub/OpenPype/pull/3377)
- Harmony: added unc path to zifile command in Harmony [\#3372](https://github.com/pypeclub/OpenPype/pull/3372)
- Standalone: settings improvements [\#3355](https://github.com/pypeclub/OpenPype/pull/3355)
**🔀 Refactored code**
@ -107,37 +115,20 @@
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.11.1-nightly.1...3.11.1)
**🆕 New features**
- Flame: custom export temp folder [\#3346](https://github.com/pypeclub/OpenPype/pull/3346)
- Nuke: removing third-party plugins [\#3344](https://github.com/pypeclub/OpenPype/pull/3344)
**🚀 Enhancements**
- Pyblish Pype: Hiding/Close issues [\#3367](https://github.com/pypeclub/OpenPype/pull/3367)
- Ftrack: Removed requirement of pypeclub role from default settings [\#3354](https://github.com/pypeclub/OpenPype/pull/3354)
- Kitsu: Prevent crash on missing frames information [\#3352](https://github.com/pypeclub/OpenPype/pull/3352)
**🐛 Bug fixes**
- Nuke: bake streams with slate on farm [\#3368](https://github.com/pypeclub/OpenPype/pull/3368)
- Harmony: audio validator has wrong logic [\#3364](https://github.com/pypeclub/OpenPype/pull/3364)
- Nuke: Fix missing variable in extract thumbnail [\#3363](https://github.com/pypeclub/OpenPype/pull/3363)
- Nuke: Fix precollect writes [\#3361](https://github.com/pypeclub/OpenPype/pull/3361)
- AE- fix validate\_scene\_settings and renderLocal [\#3358](https://github.com/pypeclub/OpenPype/pull/3358)
- deadline: fixing misidentification of revieables [\#3356](https://github.com/pypeclub/OpenPype/pull/3356)
- General: Create only one thumbnail per instance [\#3351](https://github.com/pypeclub/OpenPype/pull/3351)
- nuke: adding extract thumbnail settings 3.10 [\#3347](https://github.com/pypeclub/OpenPype/pull/3347)
- General: Fix last version function [\#3345](https://github.com/pypeclub/OpenPype/pull/3345)
## [3.11.0](https://github.com/pypeclub/OpenPype/tree/3.11.0) (2022-06-17)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.11.0-nightly.4...3.11.0)
**🐛 Bug fixes**
- General: Handle empty source key on instance [\#3342](https://github.com/pypeclub/OpenPype/pull/3342)
## [3.10.0](https://github.com/pypeclub/OpenPype/tree/3.10.0) (2022-05-26)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.10.0-nightly.6...3.10.0)

View file

@ -2522,12 +2522,30 @@ def load_capture_preset(data=None):
temp_options2['multiSampleEnable'] = False
temp_options2['multiSampleCount'] = preset[id][key]
if key == 'renderDepthOfField':
temp_options2['renderDepthOfField'] = preset[id][key]
if key == 'ssaoEnable':
if preset[id][key] is True:
temp_options2['ssaoEnable'] = True
else:
temp_options2['ssaoEnable'] = False
if key == 'ssaoSamples':
temp_options2['ssaoSamples'] = preset[id][key]
if key == 'ssaoAmount':
temp_options2['ssaoAmount'] = preset[id][key]
if key == 'ssaoRadius':
temp_options2['ssaoRadius'] = preset[id][key]
if key == 'hwFogDensity':
temp_options2['hwFogDensity'] = preset[id][key]
if key == 'ssaoFilterRadius':
temp_options2['ssaoFilterRadius'] = preset[id][key]
if key == 'alphaCut':
temp_options2['transparencyAlgorithm'] = 5
temp_options2['transparencyQuality'] = 1
@ -2535,6 +2553,48 @@ def load_capture_preset(data=None):
if key == 'headsUpDisplay':
temp_options['headsUpDisplay'] = True
if key == 'fogging':
temp_options['fogging'] = preset[id][key] or False
if key == 'hwFogStart':
temp_options2['hwFogStart'] = preset[id][key]
if key == 'hwFogEnd':
temp_options2['hwFogEnd'] = preset[id][key]
if key == 'hwFogAlpha':
temp_options2['hwFogAlpha'] = preset[id][key]
if key == 'hwFogFalloff':
temp_options2['hwFogFalloff'] = int(preset[id][key])
if key == 'hwFogColorR':
temp_options2['hwFogColorR'] = preset[id][key]
if key == 'hwFogColorG':
temp_options2['hwFogColorG'] = preset[id][key]
if key == 'hwFogColorB':
temp_options2['hwFogColorB'] = preset[id][key]
if key == 'motionBlurEnable':
if preset[id][key] is True:
temp_options2['motionBlurEnable'] = True
else:
temp_options2['motionBlurEnable'] = False
if key == 'motionBlurSampleCount':
temp_options2['motionBlurSampleCount'] = preset[id][key]
if key == 'motionBlurShutterOpenFraction':
temp_options2['motionBlurShutterOpenFraction'] = preset[id][key]
if key == 'lineAAEnable':
if preset[id][key] is True:
temp_options2['lineAAEnable'] = True
else:
temp_options2['lineAAEnable'] = False
else:
temp_options[str(key)] = preset[id][key]
@ -2544,7 +2604,24 @@ def load_capture_preset(data=None):
'gpuCacheDisplayFilter',
'multiSample',
'ssaoEnable',
'textureMaxResolution'
'ssaoSamples',
'ssaoAmount',
'ssaoFilterRadius',
'ssaoRadius',
'hwFogStart',
'hwFogEnd',
'hwFogAlpha',
'hwFogFalloff',
'hwFogColorR',
'hwFogColorG',
'hwFogColorB',
'hwFogDensity',
'textureMaxResolution',
'motionBlurEnable',
'motionBlurSampleCount',
'motionBlurShutterOpenFraction',
'lineAAEnable',
'renderDepthOfField'
]:
temp_options.pop(key, None)

View file

@ -54,20 +54,28 @@ class LoadClip(plugin.NukeLoader):
script_start = int(nuke.root()["first_frame"].value())
# option gui
defaults = {
"start_at_workfile": True
options_defaults = {
"start_at_workfile": True,
"add_retime": True
}
options = [
qargparse.Boolean(
"start_at_workfile",
help="Load at workfile start frame",
default=True
)
]
node_name_template = "{class_name}_{ext}"
@classmethod
def get_options(cls, *args):
return [
qargparse.Boolean(
"start_at_workfile",
help="Load at workfile start frame",
default=cls.options_defaults["start_at_workfile"]
),
qargparse.Boolean(
"add_retime",
help="Load with retime",
default=cls.options_defaults["add_retime"]
)
]
@classmethod
def get_representations(cls):
return (
@ -86,7 +94,10 @@ class LoadClip(plugin.NukeLoader):
file = self.fname.replace("\\", "/")
start_at_workfile = options.get(
"start_at_workfile", self.defaults["start_at_workfile"])
"start_at_workfile", self.options_defaults["start_at_workfile"])
add_retime = options.get(
"add_retime", self.options_defaults["add_retime"])
version = context['version']
version_data = version.get("data", {})
@ -151,7 +162,7 @@ class LoadClip(plugin.NukeLoader):
data_imprint = {}
for k in add_keys:
if k == 'version':
data_imprint.update({k: context["version"]['name']})
data_imprint[k] = context["version"]['name']
elif k == 'colorspace':
colorspace = repre["data"].get(k)
colorspace = colorspace or version_data.get(k)
@ -159,10 +170,13 @@ class LoadClip(plugin.NukeLoader):
if used_colorspace:
data_imprint["used_colorspace"] = used_colorspace
else:
data_imprint.update(
{k: context["version"]['data'].get(k, str(None))})
data_imprint[k] = context["version"]['data'].get(
k, str(None))
data_imprint.update({"objectName": read_name})
data_imprint["objectName"] = read_name
if add_retime and version_data.get("retime", None):
data_imprint["addRetime"] = True
read_node["tile_color"].setValue(int("0x4ecd25ff", 16))
@ -174,7 +188,7 @@ class LoadClip(plugin.NukeLoader):
loader=self.__class__.__name__,
data=data_imprint)
if version_data.get("retime", None):
if add_retime and version_data.get("retime", None):
self._make_retimes(read_node, version_data)
self.set_as_member(read_node)
@ -198,7 +212,12 @@ class LoadClip(plugin.NukeLoader):
read_node = nuke.toNode(container['objectName'])
file = get_representation_path(representation).replace("\\", "/")
start_at_workfile = bool("start at" in read_node['frame_mode'].value())
start_at_workfile = "start at" in read_node['frame_mode'].value()
add_retime = [
key for key in read_node.knobs().keys()
if "addRetime" in key
]
project_name = legacy_io.active_project()
version_doc = get_version_by_id(project_name, representation["parent"])
@ -286,7 +305,7 @@ class LoadClip(plugin.NukeLoader):
"updated to version: {}".format(version_doc.get("name"))
)
if version_data.get("retime", None):
if add_retime and version_data.get("retime", None):
self._make_retimes(read_node, version_data)
else:
self.clear_members(read_node)

View file

@ -1,20 +1,8 @@
from .pipeline import (
install,
ls,
set_project_name,
get_context_title,
get_context_data,
update_context_data,
TrayPublisherHost,
)
__all__ = (
"install",
"ls",
"set_project_name",
"get_context_title",
"get_context_data",
"update_context_data",
"TrayPublisherHost",
)

View file

@ -9,6 +9,8 @@ from openpype.pipeline import (
register_creator_plugin_path,
legacy_io,
)
from openpype.host import HostBase, INewPublisher
ROOT_DIR = os.path.dirname(os.path.dirname(
os.path.abspath(__file__)
@ -17,6 +19,35 @@ PUBLISH_PATH = os.path.join(ROOT_DIR, "plugins", "publish")
CREATE_PATH = os.path.join(ROOT_DIR, "plugins", "create")
class TrayPublisherHost(HostBase, INewPublisher):
name = "traypublisher"
def install(self):
os.environ["AVALON_APP"] = self.name
legacy_io.Session["AVALON_APP"] = self.name
pyblish.api.register_host("traypublisher")
pyblish.api.register_plugin_path(PUBLISH_PATH)
register_creator_plugin_path(CREATE_PATH)
def get_context_title(self):
return HostContext.get_project_name()
def get_context_data(self):
return HostContext.get_context_data()
def update_context_data(self, data, changes):
HostContext.save_context_data(data, changes)
def set_project_name(self, project_name):
# TODO Deregister project specific plugins and register new project
# plugins
os.environ["AVALON_PROJECT"] = project_name
legacy_io.Session["AVALON_PROJECT"] = project_name
legacy_io.install()
HostContext.set_project_name(project_name)
class HostContext:
_context_json_path = None
@ -150,32 +181,3 @@ def get_context_data():
def update_context_data(data, changes):
HostContext.save_context_data(data)
def get_context_title():
return HostContext.get_project_name()
def ls():
"""Probably will never return loaded containers."""
return []
def install():
"""This is called before a project is known.
Project is defined with 'set_project_name'.
"""
os.environ["AVALON_APP"] = "traypublisher"
pyblish.api.register_host("traypublisher")
pyblish.api.register_plugin_path(PUBLISH_PATH)
register_creator_plugin_path(CREATE_PATH)
def set_project_name(project_name):
# TODO Deregister project specific plugins and register new project plugins
os.environ["AVALON_PROJECT"] = project_name
legacy_io.Session["AVALON_PROJECT"] = project_name
legacy_io.install()
HostContext.set_project_name(project_name)

View file

@ -1,8 +1,8 @@
from openpype.lib.attribute_definitions import FileDef
from openpype.pipeline import (
Creator,
CreatedInstance
)
from openpype.lib import FileDef
from .pipeline import (
list_instances,
@ -12,6 +12,29 @@ from .pipeline import (
)
IMAGE_EXTENSIONS = [
".ani", ".anim", ".apng", ".art", ".bmp", ".bpg", ".bsave", ".cal",
".cin", ".cpc", ".cpt", ".dds", ".dpx", ".ecw", ".exr", ".fits",
".flic", ".flif", ".fpx", ".gif", ".hdri", ".hevc", ".icer",
".icns", ".ico", ".cur", ".ics", ".ilbm", ".jbig", ".jbig2",
".jng", ".jpeg", ".jpeg-ls", ".jpeg", ".2000", ".jpg", ".xr",
".jpeg", ".xt", ".jpeg-hdr", ".kra", ".mng", ".miff", ".nrrd",
".ora", ".pam", ".pbm", ".pgm", ".ppm", ".pnm", ".pcx", ".pgf",
".pictor", ".png", ".psb", ".psp", ".qtvr", ".ras",
".rgbe", ".logluv", ".tiff", ".sgi", ".tga", ".tiff", ".tiff/ep",
".tiff/it", ".ufo", ".ufp", ".wbmp", ".webp", ".xbm", ".xcf",
".xpm", ".xwd"
]
VIDEO_EXTENSIONS = [
".3g2", ".3gp", ".amv", ".asf", ".avi", ".drc", ".f4a", ".f4b",
".f4p", ".f4v", ".flv", ".gif", ".gifv", ".m2v", ".m4p", ".m4v",
".mkv", ".mng", ".mov", ".mp2", ".mp4", ".mpe", ".mpeg", ".mpg",
".mpv", ".mxf", ".nsv", ".ogg", ".ogv", ".qt", ".rm", ".rmvb",
".roq", ".svi", ".vob", ".webm", ".wmv", ".yuv"
]
REVIEW_EXTENSIONS = IMAGE_EXTENSIONS + VIDEO_EXTENSIONS
class TrayPublishCreator(Creator):
create_allow_context_change = True
host_name = "traypublisher"
@ -37,6 +60,21 @@ class TrayPublishCreator(Creator):
# Use same attributes as for instance attrobites
return self.get_instance_attr_defs()
def _store_new_instance(self, new_instance):
"""Tray publisher specific method to store instance.
Instance is stored into "workfile" of traypublisher and also add it
to CreateContext.
Args:
new_instance (CreatedInstance): Instance that should be stored.
"""
# Host implementation of storing metadata about instance
HostContext.add_instance(new_instance.data_to_store())
# Add instance to current context
self._add_instance_to_context(new_instance)
class SettingsCreator(TrayPublishCreator):
create_allow_context_change = True
@ -58,19 +96,27 @@ class SettingsCreator(TrayPublishCreator):
data["settings_creator"] = True
# Create new instance
new_instance = CreatedInstance(self.family, subset_name, data, self)
# Host implementation of storing metadata about instance
HostContext.add_instance(new_instance.data_to_store())
# Add instance to current context
self._add_instance_to_context(new_instance)
self._store_new_instance(new_instance)
def get_instance_attr_defs(self):
return [
FileDef(
"filepath",
"representation_files",
folders=False,
extensions=self.extensions,
allow_sequences=self.allow_sequences,
label="Filepath",
single_item=not self.allow_multiple_items,
label="Representations",
),
FileDef(
"reviewable",
folders=False,
extensions=REVIEW_EXTENSIONS,
allow_sequences=True,
single_item=True,
label="Reviewable representations",
extensions_label="Single reviewable item"
)
]
@ -92,6 +138,7 @@ class SettingsCreator(TrayPublishCreator):
"detailed_description": item_data["detailed_description"],
"extensions": item_data["extensions"],
"allow_sequences": item_data["allow_sequences"],
"allow_multiple_items": item_data["allow_multiple_items"],
"default_variants": item_data["default_variants"]
}
)

View file

@ -1,31 +0,0 @@
import pyblish.api
from openpype.lib import BoolDef
from openpype.pipeline import OpenPypePyblishPluginMixin
class CollectReviewFamily(
pyblish.api.InstancePlugin, OpenPypePyblishPluginMixin
):
"""Add review family."""
label = "Collect Review Family"
order = pyblish.api.CollectorOrder - 0.49
hosts = ["traypublisher"]
families = [
"image",
"render",
"plate",
"review"
]
def process(self, instance):
values = self.get_attr_values_from_data(instance.data)
if values.get("add_review_family"):
instance.data["families"].append("review")
@classmethod
def get_attribute_defs(cls):
return [
BoolDef("add_review_family", label="Review", default=True)
]

View file

@ -1,9 +1,31 @@
import os
import tempfile
import clique
import pyblish.api
class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin):
"""Collect data for instances created by settings creators."""
"""Collect data for instances created by settings creators.
Plugin create representations for simple instances based
on 'representation_files' attribute stored on instance data.
There is also possibility to have reviewable representation which can be
stored under 'reviewable' attribute stored on instance data. If there was
already created representation with the same files as 'revieable' containes
Representations can be marked for review and in that case is also added
'review' family to instance families. For review can be marked only one
representation so **first** representation that has extension available
in '_review_extensions' is used for review.
For instance 'source' is used path from last representation created
from 'representation_files'.
Set staging directory on instance. That is probably never used because
each created representation has it's own staging dir.
"""
label = "Collect Settings Simple Instances"
order = pyblish.api.CollectorOrder - 0.49
@ -14,37 +36,193 @@ class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin):
if not instance.data.get("settings_creator"):
return
if "families" not in instance.data:
instance.data["families"] = []
instance_label = instance.data["name"]
# Create instance's staging dir in temp
tmp_folder = tempfile.mkdtemp(prefix="traypublisher_")
instance.data["stagingDir"] = tmp_folder
instance.context.data["cleanupFullPaths"].append(tmp_folder)
if "representations" not in instance.data:
instance.data["representations"] = []
repres = instance.data["representations"]
self.log.debug((
"Created temp staging directory for instance {}. {}"
).format(instance_label, tmp_folder))
# Store filepaths for validation of their existence
source_filepaths = []
# Make sure there are no representations with same name
repre_names_counter = {}
# Store created names for logging
repre_names = []
# Store set of filepaths per each representation
representation_files_mapping = []
source = self._create_main_representations(
instance,
source_filepaths,
repre_names_counter,
repre_names,
representation_files_mapping
)
self._create_review_representation(
instance,
source_filepaths,
repre_names_counter,
repre_names,
representation_files_mapping
)
instance.data["source"] = source
instance.data["sourceFilepaths"] = list(set(source_filepaths))
self.log.debug(
(
"Created Simple Settings instance \"{}\""
" with {} representations: {}"
).format(
instance_label,
len(instance.data["representations"]),
", ".join(repre_names)
)
)
def _create_main_representations(
self,
instance,
source_filepaths,
repre_names_counter,
repre_names,
representation_files_mapping
):
creator_attributes = instance.data["creator_attributes"]
filepath_items = creator_attributes["representation_files"]
if not isinstance(filepath_items, list):
filepath_items = [filepath_items]
source = None
for filepath_item in filepath_items:
# Skip if filepath item does not have filenames
if not filepath_item["filenames"]:
continue
filepaths = {
os.path.join(filepath_item["directory"], filename)
for filename in filepath_item["filenames"]
}
source_filepaths.extend(filepaths)
source = self._calculate_source(filepaths)
representation = self._create_representation_data(
filepath_item, repre_names_counter, repre_names
)
instance.data["representations"].append(representation)
representation_files_mapping.append(
(filepaths, representation, source)
)
return source
def _create_review_representation(
self,
instance,
source_filepaths,
repre_names_counter,
repre_names,
representation_files_mapping
):
# Skip review representation creation if there are no representations
# created for "main" part
# - review representation must not be created in that case so
# validation can care about it
if not representation_files_mapping:
self.log.warning((
"There are missing source representations."
" Creation of review representation was skipped."
))
return
creator_attributes = instance.data["creator_attributes"]
filepath_item = creator_attributes["filepath"]
self.log.info(filepath_item)
filepaths = [
os.path.join(filepath_item["directory"], filename)
for filename in filepath_item["filenames"]
]
review_file_item = creator_attributes["reviewable"]
filenames = review_file_item.get("filenames")
if not filenames:
self.log.debug((
"Filepath for review is not defined."
" Skipping review representation creation."
))
return
instance.data["sourceFilepaths"] = filepaths
instance.data["stagingDir"] = filepath_item["directory"]
filepaths = {
os.path.join(review_file_item["directory"], filename)
for filename in filenames
}
source_filepaths.extend(filepaths)
# First try to find out representation with same filepaths
# so it's not needed to create new representation just for review
review_representation = None
# Review path (only for logging)
review_path = None
for item in representation_files_mapping:
_filepaths, representation, repre_path = item
if _filepaths == filepaths:
review_representation = representation
review_path = repre_path
break
if review_representation is None:
self.log.debug("Creating new review representation")
review_path = self._calculate_source(filepaths)
review_representation = self._create_representation_data(
review_file_item, repre_names_counter, repre_names
)
instance.data["representations"].append(review_representation)
if "review" not in instance.data["families"]:
instance.data["families"].append("review")
review_representation["tags"].append("review")
self.log.debug("Representation {} was marked for review. {}".format(
review_representation["name"], review_path
))
def _create_representation_data(
self, filepath_item, repre_names_counter, repre_names
):
"""Create new representation data based on file item.
Args:
filepath_item (Dict[str, Any]): Item with information about
representation paths.
repre_names_counter (Dict[str, int]): Store count of representation
names.
repre_names (List[str]): All used representation names. For
logging purposes.
Returns:
Dict: Prepared base representation data.
"""
filenames = filepath_item["filenames"]
_, ext = os.path.splitext(filenames[0])
ext = ext[1:]
if len(filenames) == 1:
filenames = filenames[0]
repres.append({
"ext": ext,
"name": ext,
repre_name = repre_ext = ext[1:]
if repre_name not in repre_names_counter:
repre_names_counter[repre_name] = 2
else:
counter = repre_names_counter[repre_name]
repre_names_counter[repre_name] += 1
repre_name = "{}_{}".format(repre_name, counter)
repre_names.append(repre_name)
return {
"ext": repre_ext,
"name": repre_name,
"stagingDir": filepath_item["directory"],
"files": filenames
})
"files": filenames,
"tags": []
}
self.log.debug("Created Simple Settings instance {}".format(
instance.data
))
def _calculate_source(self, filepaths):
cols, rems = clique.assemble(filepaths)
if cols:
source = cols[0].format("{head}{padding}{tail}")
elif rems:
source = rems[0]
return source

View file

@ -3,8 +3,17 @@ import pyblish.api
from openpype.pipeline import PublishValidationError
class ValidateWorkfilePath(pyblish.api.InstancePlugin):
"""Validate existence of workfile instance existence."""
class ValidateFilePath(pyblish.api.InstancePlugin):
"""Validate existence of source filepaths on instance.
Plugins looks into key 'sourceFilepaths' and validate if paths there
actually exist on disk.
Also validate if the key is filled but is empty. In that case also
crashes so do not fill the key if unfilled value should not cause error.
This is primarily created for Simple Creator instances.
"""
label = "Validate Workfile"
order = pyblish.api.ValidatorOrder - 0.49
@ -14,12 +23,28 @@ class ValidateWorkfilePath(pyblish.api.InstancePlugin):
def process(self, instance):
if "sourceFilepaths" not in instance.data:
self.log.info((
"Can't validate source filepaths existence."
"Skipped validation of source filepaths existence."
" Instance does not have collected 'sourceFilepaths'"
))
return
filepaths = instance.data.get("sourceFilepaths")
family = instance.data["family"]
label = instance.data["name"]
filepaths = instance.data["sourceFilepaths"]
if not filepaths:
raise PublishValidationError(
(
"Source filepaths of '{}' instance \"{}\" are not filled"
).format(family, label),
"File not filled",
(
"## Files were not filled"
"\nThis mean that you didn't enter any files into required"
" file input."
"\n- Please refresh publishing and check instance"
" <b>{}</b>"
).format(label)
)
not_found_files = [
filepath
@ -34,11 +59,7 @@ class ValidateWorkfilePath(pyblish.api.InstancePlugin):
raise PublishValidationError(
(
"Filepath of '{}' instance \"{}\" does not exist:\n{}"
).format(
instance.data["family"],
instance.data["name"],
joined_paths
),
).format(family, label, joined_paths),
"File not found",
(
"## Files were not found\nFiles\n{}"

View file

@ -14,6 +14,7 @@ class AbstractAttrDefMeta(ABCMeta):
Each object of `AbtractAttrDef` mus have defined 'key' attribute.
"""
def __call__(self, *args, **kwargs):
obj = super(AbstractAttrDefMeta, self).__call__(*args, **kwargs)
init_class = getattr(obj, "__init__class__", None)
@ -45,6 +46,7 @@ class AbtractAttrDef:
is_label_horizontal(bool): UI specific argument. Specify if label is
next to value input or ahead.
"""
is_value_def = True
def __init__(
@ -77,6 +79,7 @@ class AbtractAttrDef:
Convert passed value to a valid type. Use default if value can't be
converted.
"""
pass
@ -113,6 +116,7 @@ class UnknownDef(AbtractAttrDef):
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)
@ -204,6 +208,7 @@ class TextDef(AbtractAttrDef):
placeholder(str): UI placeholder for attribute.
default(str, None): Default value. Empty string used when not defined.
"""
def __init__(
self, key, multiline=None, regex=None, placeholder=None, default=None,
**kwargs
@ -531,14 +536,15 @@ class FileDef(AbtractAttrDef):
Args:
single_item(bool): Allow only single path item.
folders(bool): Allow folder paths.
extensions(list<str>): Allow files with extensions. Empty list will
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.
extensions_label(str): Custom label shown instead of extensions in UI.
default(str, List[str]): Default value.
"""
def __init__(
self, key, single_item=True, folders=None, extensions=None,
allow_sequences=True, default=None, **kwargs
allow_sequences=True, extensions_label=None, default=None, **kwargs
):
if folders is None and extensions is None:
folders = True
@ -578,6 +584,7 @@ class FileDef(AbtractAttrDef):
self.folders = folders
self.extensions = set(extensions)
self.allow_sequences = allow_sequences
self.extensions_label = extensions_label
super(FileDef, self).__init__(key, default=default, **kwargs)
def __eq__(self, other):

View file

@ -11,6 +11,10 @@ except Exception:
from openpype.lib.python_2_comp import WeakMethod
class MissingEventSystem(Exception):
pass
class EventCallback(object):
"""Callback registered to a topic.
@ -176,16 +180,20 @@ class Event(object):
topic (str): Identifier of event.
data (Any): Data specific for event. Dictionary is recommended.
source (str): Identifier of source.
event_system (EventSystem): Event system in which can be event
triggered.
"""
_data = {}
def __init__(self, topic, data=None, source=None):
def __init__(self, topic, data=None, source=None, event_system=None):
self._id = str(uuid4())
self._topic = topic
if data is None:
data = {}
self._data = data
self._source = source
self._event_system = event_system
def __getitem__(self, key):
return self._data[key]
@ -211,28 +219,118 @@ class Event(object):
def emit(self):
"""Emit event and trigger callbacks."""
StoredCallbacks.emit_event(self)
if self._event_system is None:
raise MissingEventSystem(
"Can't emit event {}. Does not have set event system.".format(
str(repr(self))
)
)
self._event_system.emit_event(self)
class StoredCallbacks:
_registered_callbacks = []
class EventSystem(object):
"""Encapsulate event handling into an object.
System wraps registered callbacks and triggered events into single object
so it is possible to create mutltiple independent systems that have their
topics and callbacks.
"""
def __init__(self):
self._registered_callbacks = []
def add_callback(self, topic, callback):
"""Register callback in event system.
Args:
topic (str): Topic for EventCallback.
callback (Callable): Function or method that will be called
when topic is triggered.
Returns:
EventCallback: Created callback object which can be used to
stop listening.
"""
@classmethod
def add_callback(cls, topic, callback):
callback = EventCallback(topic, callback)
cls._registered_callbacks.append(callback)
self._registered_callbacks.append(callback)
return callback
@classmethod
def emit_event(cls, event):
def create_event(self, topic, data, source):
"""Create new event which is bound to event system.
Args:
topic (str): Event topic.
data (dict): Data related to event.
source (str): Source of event.
Returns:
Event: Object of event.
"""
return Event(topic, data, source, self)
def emit(self, topic, data, source):
"""Create event based on passed data and emit it.
This is easiest way how to trigger event in an event system.
Args:
topic (str): Event topic.
data (dict): Data related to event.
source (str): Source of event.
Returns:
Event: Created and emitted event.
"""
event = self.create_event(topic, data, source)
event.emit()
return event
def emit_event(self, event):
"""Emit event object.
Args:
event (Event): Prepared event with topic and data.
"""
invalid_callbacks = []
for callback in cls._registered_callbacks:
for callback in self._registered_callbacks:
callback.process_event(event)
if not callback.is_ref_valid:
invalid_callbacks.append(callback)
for callback in invalid_callbacks:
cls._registered_callbacks.remove(callback)
self._registered_callbacks.remove(callback)
class GlobalEventSystem:
"""Event system living in global scope of process.
This is primarily used in host implementation to trigger events
related to DCC changes or changes of context in the host implementation.
"""
_global_event_system = None
@classmethod
def get_global_event_system(cls):
if cls._global_event_system is None:
cls._global_event_system = EventSystem()
return cls._global_event_system
@classmethod
def add_callback(cls, topic, callback):
event_system = cls.get_global_event_system()
return event_system.add_callback(topic, callback)
@classmethod
def emit(cls, topic, data, source):
event_system = cls.get_global_event_system()
return event_system.emit(topic, data, source)
def register_event_callback(topic, callback):
@ -249,7 +347,8 @@ def register_event_callback(topic, callback):
enable/disable listening to a topic or remove the callback from
the topic completely.
"""
return StoredCallbacks.add_callback(topic, callback)
return GlobalEventSystem.add_callback(topic, callback)
def emit_event(topic, data=None, source=None):
@ -263,6 +362,5 @@ def emit_event(topic, data=None, source=None):
Returns:
Event: Object of event that was emitted.
"""
event = Event(topic, data, source)
event.emit()
return event
return GlobalEventSystem.emit(topic, data, source)

View file

@ -84,6 +84,11 @@ class CreateProjectFolders(BaseAction):
create_project_folders(basic_paths, project_name)
self.create_ftrack_entities(basic_paths, project_entity)
self.trigger_event(
"openpype.project.structure.created",
{"project_name": project_name}
)
except Exception as exc:
self.log.warning("Creating of structure crashed.", exc_info=True)
session.rollback()

View file

@ -116,6 +116,7 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin):
"app_name": app_name,
"app_label": app_label,
"published_paths": "<br/>".join(sorted(published_paths)),
"source": instance.data.get("source", '')
}
comment = template.format(**format_data)
if not comment:

View file

@ -2,13 +2,13 @@ import os
import platform
from openpype.client import get_asset_by_name
from openpype.modules import OpenPypeModule
from openpype_interfaces import (
ITrayService,
ILaunchHookPaths
)
from openpype.lib.events import register_event_callback
from openpype.pipeline import AvalonMongoDB
from .exceptions import InvalidContextError
@ -197,22 +197,13 @@ class TimersManager(OpenPypeModule, ITrayService, ILaunchHookPaths):
" Project: \"{}\" Asset: \"{}\" Task: \"{}\""
).format(str(project_name), str(asset_name), str(task_name)))
dbconn = AvalonMongoDB()
dbconn.install()
dbconn.Session["AVALON_PROJECT"] = project_name
asset_doc = dbconn.find_one(
{
"type": "asset",
"name": asset_name
},
{
"data.tasks": True,
"data.parents": True
}
asset_doc = get_asset_by_name(
project_name,
asset_name,
fields=["_id", "name", "data.tasks", "data.parents"]
)
if not asset_doc:
dbconn.uninstall()
raise InvalidContextError((
"Asset \"{}\" not found in project \"{}\""
).format(asset_name, project_name))
@ -220,7 +211,6 @@ class TimersManager(OpenPypeModule, ITrayService, ILaunchHookPaths):
asset_data = asset_doc.get("data") or {}
asset_tasks = asset_data.get("tasks") or {}
if task_name not in asset_tasks:
dbconn.uninstall()
raise InvalidContextError((
"Task \"{}\" not found on asset \"{}\" in project \"{}\""
).format(task_name, asset_name, project_name))
@ -238,9 +228,10 @@ class TimersManager(OpenPypeModule, ITrayService, ILaunchHookPaths):
hierarchy_items = asset_data.get("parents") or []
hierarchy_items.append(asset_name)
dbconn.uninstall()
return {
"project_name": project_name,
"asset_id": str(asset_doc["_id"]),
"asset_name": asset_doc["name"],
"task_name": task_name,
"task_type": task_type,
"hierarchy": hierarchy_items

View file

@ -29,6 +29,7 @@ UpdateData = collections.namedtuple("UpdateData", ["instance", "changes"])
class ImmutableKeyError(TypeError):
"""Accessed key is immutable so does not allow changes or removements."""
def __init__(self, key, msg=None):
self.immutable_key = key
if not msg:
@ -40,6 +41,7 @@ class ImmutableKeyError(TypeError):
class HostMissRequiredMethod(Exception):
"""Host does not have implemented required functions for creation."""
def __init__(self, host, missing_methods):
self.missing_methods = missing_methods
self.host = host
@ -66,6 +68,7 @@ class InstanceMember:
TODO:
Implement and use!
"""
def __init__(self, instance, name):
self.instance = instance
@ -94,6 +97,7 @@ class AttributeValues:
values(dict): Values after possible conversion.
origin_data(dict): Values loaded from host before conversion.
"""
def __init__(self, attr_defs, values, origin_data=None):
from openpype.lib.attribute_definitions import UnknownDef
@ -174,6 +178,10 @@ class AttributeValues:
output = {}
for key in self._data:
output[key] = self[key]
for key, attr_def in self._attr_defs_by_key.items():
if key not in output:
output[key] = attr_def.default
return output
@staticmethod
@ -196,6 +204,7 @@ class CreatorAttributeValues(AttributeValues):
Args:
instance (CreatedInstance): Instance for which are values hold.
"""
def __init__(self, instance, *args, **kwargs):
self.instance = instance
super(CreatorAttributeValues, self).__init__(*args, **kwargs)
@ -211,6 +220,7 @@ class PublishAttributeValues(AttributeValues):
publish_attributes(PublishAttributes): Wrapper for multiple publish
attributes is used as parent object.
"""
def __init__(self, publish_attributes, *args, **kwargs):
self.publish_attributes = publish_attributes
super(PublishAttributeValues, self).__init__(*args, **kwargs)
@ -232,6 +242,7 @@ class PublishAttributes:
attr_plugins(list): List of publish plugins that may have defined
attribute definitions.
"""
def __init__(self, parent, origin_data, attr_plugins=None):
self.parent = parent
self._origin_data = copy.deepcopy(origin_data)
@ -270,6 +281,7 @@ class PublishAttributes:
key(str): Plugin name.
default: Default value if plugin was not found.
"""
if key not in self._data:
return default
@ -287,11 +299,13 @@ class PublishAttributes:
def plugin_names_order(self):
"""Plugin names order by their 'order' attribute."""
for name in self._plugin_names_order:
yield name
def data_to_store(self):
"""Convert attribute values to "data to store"."""
output = {}
for key, attr_value in self._data.items():
output[key] = attr_value.data_to_store()
@ -299,6 +313,7 @@ class PublishAttributes:
def changes(self):
"""Return changes per each key."""
changes = {}
for key, attr_val in self._data.items():
attr_changes = attr_val.changes()
@ -314,6 +329,7 @@ class PublishAttributes:
def set_publish_plugins(self, attr_plugins):
"""Set publish plugins attribute definitions."""
self._plugin_names_order = []
self._missing_plugins = []
self.attr_plugins = attr_plugins or []
@ -365,6 +381,7 @@ class CreatedInstance:
`openpype.pipeline.registered_host`.
new(bool): Is instance new.
"""
# Keys that can't be changed or removed from data after loading using
# creator.
# - 'creator_attributes' and 'publish_attributes' can change values of
@ -566,6 +583,7 @@ class CreatedInstance:
@property
def id(self):
"""Instance identifier."""
return self._data["instance_id"]
@property
@ -574,10 +592,12 @@ class CreatedInstance:
Access to data is needed to modify values.
"""
return self
def changes(self):
"""Calculate and return changes."""
changes = {}
new_keys = set()
for key, new_value in self._data.items():
@ -716,6 +736,7 @@ class CreateContext:
self.manual_creators = {}
self.publish_discover_result = None
self.publish_plugins_mismatch_targets = []
self.publish_plugins = []
self.plugins_with_defs = []
self._attr_plugins_by_family = {}
@ -838,6 +859,7 @@ class CreateContext:
discover_result = DiscoverResult()
plugins_with_defs = []
plugins_by_targets = []
plugins_mismatch_targets = []
if discover_publish_plugins:
discover_result = publish_plugins_discover()
publish_plugins = discover_result.plugins
@ -847,11 +869,19 @@ class CreateContext:
plugins_by_targets = pyblish.logic.plugins_by_targets(
publish_plugins, list(targets)
)
# Collect plugins that can have attribute definitions
for plugin in publish_plugins:
if OpenPypePyblishPluginMixin in inspect.getmro(plugin):
plugins_with_defs.append(plugin)
plugins_mismatch_targets = [
plugin
for plugin in publish_plugins
if plugin not in plugins_by_targets
]
self.publish_plugins_mismatch_targets = plugins_mismatch_targets
self.publish_discover_result = discover_result
self.publish_plugins = plugins_by_targets
self.plugins_with_defs = plugins_with_defs

View file

@ -102,6 +102,10 @@ class BaseCreator:
return self.create_context.project_name
@property
def host(self):
return self.create_context.host
def get_group_label(self):
"""Group label under which are instances grouped in UI.

View file

@ -51,6 +51,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
project_name = legacy_io.active_project()
self.fill_missing_asset_docs(context, project_name)
self.fill_instance_data_from_asset(context)
self.fill_latest_versions(context, project_name)
self.fill_anatomy_data(context)
@ -115,6 +116,23 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
"Not found asset documents with names \"{}\"."
).format(joined_asset_names))
def fill_instance_data_from_asset(self, context):
for instance in context:
asset_doc = instance.data.get("assetEntity")
if not asset_doc:
continue
asset_data = asset_doc["data"]
for key in (
"fps",
"frameStart",
"frameEnd",
"handleStart",
"handleEnd",
):
if key not in instance.data and key in asset_data:
instance.data[key] = asset_data[key]
def fill_latest_versions(self, context, project_name):
"""Try to find latest version for each instance's subset.

View file

@ -14,7 +14,7 @@ class CollectCleanupKeys(pyblish.api.ContextPlugin):
"""Prepare keys for 'ExplicitCleanUp' plugin."""
label = "Collect Cleanup Keys"
order = pyblish.api.CollectorOrder
order = pyblish.api.CollectorOrder - 0.5
def process(self, context):
context.data["cleanupFullPaths"] = []

View file

@ -47,12 +47,11 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin):
"label": subset,
"name": subset,
"family": in_data["family"],
"families": instance_families
"families": instance_families,
"representations": []
})
for key, value in in_data.items():
if key not in instance.data:
instance.data[key] = value
self.log.info("collected instance: {}".format(instance.data))
self.log.info("parsing data: {}".format(in_data))
instance.data["representations"] = list()

View file

@ -497,11 +497,29 @@
"override_viewport_options": true,
"displayLights": "default",
"textureMaxResolution": 1024,
"multiSample": 4,
"renderDepthOfField": true,
"shadows": true,
"textures": true,
"twoSidedLighting": true,
"ssaoEnable": true,
"lineAAEnable": true,
"multiSample": 8,
"ssaoEnable": false,
"ssaoAmount": 1,
"ssaoRadius": 16,
"ssaoFilterRadius": 16,
"ssaoSamples": 16,
"fogging": false,
"hwFogFalloff": "0",
"hwFogDensity": 0.0,
"hwFogStart": 0,
"hwFogEnd": 100,
"hwFogAlpha": 0,
"hwFogColorR": 1.0,
"hwFogColorG": 1.0,
"hwFogColorB": 1.0,
"motionBlurEnable": false,
"motionBlurSampleCount": 8,
"motionBlurShutterOpenFraction": 0.2,
"cameras": false,
"clipGhosts": false,
"controlVertices": false,

View file

@ -287,7 +287,11 @@
"LoadClip": {
"enabled": true,
"_representations": [],
"node_name_template": "{class_name}_{ext}"
"node_name_template": "{class_name}_{ext}",
"options_defaults": {
"start_at_workfile": true,
"add_retime": true
}
}
},
"workfile_builder": {

View file

@ -8,9 +8,10 @@
"default_variants": [
"Main"
],
"description": "Publish workfile backup",
"detailed_description": "",
"allow_sequences": true,
"description": "Backup of a working scene",
"detailed_description": "Workfiles are full scenes from any application that are directly edited by artists. They represent a state of work on a task at a given point and are usually not directly referenced into other scenes.",
"allow_sequences": false,
"allow_multiple_items": false,
"extensions": [
".ma",
".mb",
@ -30,6 +31,209 @@
".psb",
".aep"
]
},
{
"family": "model",
"identifier": "",
"label": "Model",
"icon": "fa.cubes",
"default_variants": [
"Main",
"Proxy",
"Sculpt"
],
"description": "Clean models",
"detailed_description": "Models should only contain geometry data, without any extras like cameras, locators or bones.\n\nKeep in mind that models published from tray publisher are not validated for correctness. ",
"allow_sequences": false,
"allow_multiple_items": true,
"extensions": [
".ma",
".mb",
".obj",
".abc",
".fbx",
".bgeo",
".bgeogz",
".bgeosc",
".usd",
".blend"
]
},
{
"family": "pointcache",
"identifier": "",
"label": "Pointcache",
"icon": "fa.gears",
"default_variants": [
"Main"
],
"description": "Geometry Caches",
"detailed_description": "Alembic or bgeo cache of animated data",
"allow_sequences": true,
"allow_multiple_items": true,
"extensions": [
".abc",
".bgeo",
".bgeogz",
".bgeosc"
]
},
{
"family": "plate",
"identifier": "",
"label": "Plate",
"icon": "mdi.camera-image",
"default_variants": [
"Main",
"BG",
"Animatic",
"Reference",
"Offline"
],
"description": "Footage Plates",
"detailed_description": "Any type of image seqeuence coming from outside of the studio. Usually camera footage, but could also be animatics used for reference.",
"allow_sequences": true,
"allow_multiple_items": true,
"extensions": [
".exr",
".png",
".dpx",
".jpg",
".tiff",
".tif",
".mov",
".mp4",
".avi"
]
},
{
"family": "render",
"identifier": "",
"label": "Render",
"icon": "mdi.folder-multiple-image",
"default_variants": [],
"description": "Rendered images or video",
"detailed_description": "Sequence or single file renders",
"allow_sequences": true,
"allow_multiple_items": true,
"extensions": [
".exr",
".png",
".dpx",
".jpg",
".jpeg",
".tiff",
".tif",
".mov",
".mp4",
".avi"
]
},
{
"family": "camera",
"identifier": "",
"label": "Camera",
"icon": "fa.video-camera",
"default_variants": [],
"description": "3d Camera",
"detailed_description": "Ideally this should be only camera itself with baked animation, however, it can technically also include helper geometry.",
"allow_sequences": false,
"allow_multiple_items": true,
"extensions": [
".abc",
".ma",
".hip",
".blend",
".fbx",
".usd"
]
},
{
"family": "image",
"identifier": "",
"label": "Image",
"icon": "fa.image",
"default_variants": [
"Reference",
"Texture",
"Concept",
"Background"
],
"description": "Single image",
"detailed_description": "Any image data can be published as image family. References, textures, concept art, matte paints. This is a fallback 2d family for everything that doesn't fit more specific family.",
"allow_sequences": false,
"allow_multiple_items": true,
"extensions": [
".exr",
".jpg",
".jpeg",
".dpx",
".bmp",
".tif",
".tiff",
".png",
".psb",
".psd"
]
},
{
"family": "vdb",
"identifier": "",
"label": "VDB Volumes",
"icon": "fa.cloud",
"default_variants": [],
"description": "Sparse volumetric data",
"detailed_description": "Hierarchical data structure for the efficient storage and manipulation of sparse volumetric data discretized on three-dimensional grids",
"allow_sequences": true,
"allow_multiple_items": true,
"extensions": [
".vdb"
]
},
{
"family": "matchmove",
"identifier": "",
"label": "Matchmove",
"icon": "fa.empire",
"default_variants": [
"Camera",
"Object",
"Mocap"
],
"description": "Matchmoving script",
"detailed_description": "Script exported from matchmoving application to be later processed into a tracked camera with additional data",
"allow_sequences": false,
"allow_multiple_items": true,
"extensions": []
},
{
"family": "rig",
"identifier": "",
"label": "Rig",
"icon": "fa.wheelchair",
"default_variants": [],
"description": "CG rig file",
"detailed_description": "CG rigged character or prop. Rig should be clean of any extra data and directly loadable into it's respective application\t",
"allow_sequences": false,
"allow_multiple_items": false,
"extensions": [
".ma",
".blend",
".hip",
".hda"
]
},
{
"family": "simpleUnrealTexture",
"identifier": "",
"label": "Simple UE texture",
"icon": "fa.image",
"default_variants": [],
"description": "Simple Unreal Engine texture",
"detailed_description": "Texture files with Unreal Engine naming conventions",
"allow_sequences": false,
"allow_multiple_items": true,
"extensions": []
}
],
"BatchMovCreator": {

View file

@ -822,7 +822,7 @@
},
{
"type": "label",
"label": "Template may contain formatting keys <b>intent</b>, <b>comment</b>, <b>host_name</b>, <b>app_name</b>, <b>app_label</b> and <b>published_paths</b>."
"label": "Template may contain formatting keys <b>intent</b>, <b>comment</b>, <b>host_name</b>, <b>app_name</b>, <b>app_label</b>, <b>published_paths</b> and <b>source</b>."
},
{
"type": "text",

View file

@ -67,6 +67,11 @@
"label": "Allow sequences",
"type": "boolean"
},
{
"key": "allow_multiple_items",
"label": "Allow multiple items",
"type": "boolean"
},
{
"type": "list",
"key": "extensions",

View file

@ -202,12 +202,15 @@
"decimal": 0
},
{
"type": "number",
"key": "multiSample",
"label": "Anti Aliasing Samples",
"decimal": 0,
"minimum": 0,
"maximum": 32
"type": "splitter"
},
{
"type":"boolean",
"key": "renderDepthOfField",
"label": "Depth of Field"
},
{
"type": "splitter"
},
{
"type": "boolean",
@ -224,11 +227,145 @@
"key": "twoSidedLighting",
"label": "Two Sided Lighting"
},
{
"type": "splitter"
},
{
"type": "boolean",
"key": "lineAAEnable",
"label": "Enable Anti-Aliasing"
},
{
"type": "number",
"key": "multiSample",
"label": "Anti Aliasing Samples",
"decimal": 0,
"minimum": 0,
"maximum": 32
},
{
"type": "splitter"
},
{
"type": "boolean",
"key": "ssaoEnable",
"label": "Screen Space Ambient Occlusion"
},
{
"type": "number",
"key": "ssaoAmount",
"label": "SSAO Amount"
},
{
"type": "number",
"key": "ssaoRadius",
"label": "SSAO Radius"
},
{
"type": "number",
"key": "ssaoFilterRadius",
"label": "SSAO Filter Radius",
"decimal": 0,
"minimum": 1,
"maximum": 32
},
{
"type": "number",
"key": "ssaoSamples",
"label": "SSAO Samples",
"decimal": 0,
"minimum": 8,
"maximum": 32
},
{
"type": "splitter"
},
{
"type": "boolean",
"key": "fogging",
"label": "Enable Hardware Fog"
},
{
"type": "enum",
"key": "hwFogFalloff",
"label": "Hardware Falloff",
"enum_items": [
{ "0": "Linear"},
{ "1": "Exponential"},
{ "2": "Exponential Squared"}
]
},
{
"type": "number",
"key": "hwFogDensity",
"label": "Fog Density",
"decimal": 2,
"minimum": 0,
"maximum": 1
},
{
"type": "number",
"key": "hwFogStart",
"label": "Fog Start"
},
{
"type": "number",
"key": "hwFogEnd",
"label": "Fog End"
},
{
"type": "number",
"key": "hwFogAlpha",
"label": "Fog Alpha"
},
{
"type": "number",
"key": "hwFogColorR",
"label": "Fog Color R",
"decimal": 2,
"minimum": 0,
"maximum": 1
},
{
"type": "number",
"key": "hwFogColorG",
"label": "Fog Color G",
"decimal": 2,
"minimum": 0,
"maximum": 1
},
{
"type": "number",
"key": "hwFogColorB",
"label": "Fog Color B",
"decimal": 2,
"minimum": 0,
"maximum": 1
},
{
"type": "splitter"
},
{
"type": "boolean",
"key": "motionBlurEnable",
"label": "Enable Motion Blur"
},
{
"type": "number",
"key": "motionBlurSampleCount",
"label": "Motion Blur Sample Count",
"decimal": 0,
"minimum": 8,
"maximum": 32
},
{
"type": "number",
"key": "motionBlurShutterOpenFraction",
"label": "Shutter Open Fraction",
"decimal": 3,
"minimum": 0.01,
"maximum": 32
},
{
"type": "splitter"
},

View file

@ -11,10 +11,52 @@
{
"key": "LoadImage",
"label": "Image Loader"
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "LoadClip",
"label": "Clip Loader",
"checkbox_key": "enabled",
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
},
{
"key": "LoadClip",
"label": "Clip Loader"
"type": "list",
"key": "_representations",
"label": "Representations",
"object_type": "text"
},
{
"type": "text",
"key": "node_name_template",
"label": "Node name template"
},
{
"type": "splitter"
},
{
"type": "dict",
"collapsible": false,
"key": "options_defaults",
"label": "Loader option defaults",
"children": [
{
"type": "boolean",
"key": "start_at_workfile",
"label": "Start at worfile beggining"
},
{
"type": "boolean",
"key": "add_retime",
"label": "Add retime"
}
]
}
]
}

View file

@ -154,15 +154,20 @@ class PublishReport:
self._all_instances_by_id = {}
self._current_context = None
def reset(self, context, publish_discover_result=None):
def reset(self, context, create_context):
"""Reset report and clear all data."""
self._publish_discover_result = publish_discover_result
self._publish_discover_result = create_context.publish_discover_result
self._plugin_data = []
self._plugin_data_with_plugin = []
self._current_plugin_data = {}
self._all_instances_by_id = {}
self._current_context = context
for plugin in create_context.publish_plugins_mismatch_targets:
plugin_data = self._add_plugin_data_item(plugin)
plugin_data["skipped"] = True
def add_plugin_iter(self, plugin, context):
"""Add report about single iteration of plugin."""
for instance in context:
@ -205,6 +210,7 @@ class PublishReport:
"name": plugin.__name__,
"label": label,
"order": plugin.order,
"targets": list(plugin.targets),
"instances_data": [],
"actions_data": [],
"skipped": False,
@ -569,6 +575,8 @@ class PublisherController:
# Stop publishing
self.stop_publish()
self.save_changes()
# Reset avalon context
self.create_context.reset_avalon_context()
@ -777,10 +785,7 @@ class PublisherController:
# - pop the key after first collector using it would be safest option?
self._publish_context.data["create_context"] = self.create_context
self._publish_report.reset(
self._publish_context,
self.create_context.publish_discover_result
)
self._publish_report.reset(self._publish_context, self.create_context)
self._publish_validation_errors = []
self._publish_current_plugin_validation_errors = None
self._publish_error = None

View file

@ -83,10 +83,8 @@ class PublishReport:
logs = []
plugins_items_by_id = {}
plugins_id_order = []
for plugin_data in data["plugins_data"]:
item = PluginItem(plugin_data)
plugins_id_order.append(item.id)
plugins_items_by_id[item.id] = item
for instance_data_item in plugin_data["instances_data"]:
instance_id = instance_data_item["id"]
@ -95,6 +93,14 @@ class PublishReport:
copy.deepcopy(log_item_data), item.id, instance_id
)
logs.append(log_item)
sorted_plugins = sorted(
plugins_items_by_id.values(),
key=lambda item: item.order
)
plugins_id_order = [
plugin_item.id
for plugin_item in sorted_plugins
]
logs_by_instance_id = collections.defaultdict(list)
for log_item in logs:

View file

@ -1,3 +1,4 @@
from math import ceil
from Qt import QtWidgets, QtCore, QtGui
from openpype.widgets.nice_checkbox import NiceCheckbox
@ -137,13 +138,75 @@ class PluginLoadReportWidget(QtWidgets.QWidget):
self._model.set_report(report)
class ZoomPlainText(QtWidgets.QPlainTextEdit):
def __init__(self, *args, **kwargs):
super(ZoomPlainText, self).__init__(*args, **kwargs)
anim_timer = QtCore.QTimer()
anim_timer.setInterval(20)
anim_timer.timeout.connect(self._scaling_callback)
self._anim_timer = anim_timer
self._zoom_enabled = False
self._scheduled_scalings = 0
self._point_size = None
def wheelEvent(self, event):
if not self._zoom_enabled:
super(ZoomPlainText, self).wheelEvent(event)
return
degrees = float(event.delta()) / 8
steps = int(ceil(degrees / 5))
self._scheduled_scalings += steps
if (self._scheduled_scalings * steps < 0):
self._scheduled_scalings = steps
self._anim_timer.start()
def _scaling_callback(self):
if self._scheduled_scalings == 0:
self._anim_timer.stop()
return
factor = 1.0 + (self._scheduled_scalings / 300)
font = self.font()
if self._point_size is None:
self._point_size = font.pointSizeF()
self._point_size *= factor
if self._point_size < 1:
self._point_size = 1.0
font.setPointSizeF(self._point_size)
# Using 'self.setFont(font)' would not be propagated when stylesheets
# are applied on this widget
self.setStyleSheet("font-size: {}pt".format(font.pointSize()))
if self._scheduled_scalings > 0:
self._scheduled_scalings -= 1
else:
self._scheduled_scalings += 1
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Control:
self._zoom_enabled = True
super(ZoomPlainText, self).keyPressEvent(event)
def keyReleaseEvent(self, event):
if event.key() == QtCore.Qt.Key_Control:
self._zoom_enabled = False
super(ZoomPlainText, self).keyReleaseEvent(event)
class DetailsWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(DetailsWidget, self).__init__(parent)
output_widget = QtWidgets.QPlainTextEdit(self)
output_widget.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
output_widget = ZoomPlainText(self)
output_widget.setObjectName("PublishLogConsole")
output_widget.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)

View file

@ -854,6 +854,9 @@ class ProjectWidget(SettingsCategoryWidget):
project_list_widget.version_change_requested.connect(
self._on_source_version_change
)
project_list_widget.extract_to_file_requested.connect(
self._on_extract_to_file
)
self.project_list_widget = project_list_widget

View file

@ -1008,6 +1008,7 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel):
class ProjectListWidget(QtWidgets.QWidget):
project_changed = QtCore.Signal()
version_change_requested = QtCore.Signal(str)
extract_to_file_requested = QtCore.Signal()
def __init__(self, parent, only_active=False):
self._parent = parent
@ -1099,7 +1100,12 @@ class ProjectListWidget(QtWidgets.QWidget):
self.version_change_requested
)
submenu.addAction(action)
extract_action = QtWidgets.QAction("Extract to file", menu)
extract_action.triggered.connect(self.extract_to_file_requested)
menu.addMenu(submenu)
menu.addAction(extract_action)
menu.exec_(QtGui.QCursor.pos())
def on_item_clicked(self, new_index):

View file

@ -12,9 +12,7 @@ from openpype.pipeline import (
install_host,
AvalonMongoDB,
)
from openpype.hosts.traypublisher import (
api as traypublisher
)
from openpype.hosts.traypublisher.api import TrayPublisherHost
from openpype.tools.publisher import PublisherWindow
from openpype.tools.utils.constants import PROJECT_NAME_ROLE
from openpype.tools.utils.models import (
@ -111,9 +109,13 @@ class StandaloneOverlayWidget(QtWidgets.QFrame):
if project_name:
self._set_project(project_name)
@property
def host(self):
return self._publisher_window.controller.host
def _set_project(self, project_name):
self._project_name = project_name
traypublisher.set_project_name(project_name)
self.host.set_project_name(project_name)
self.setVisible(False)
self.project_selected.emit(project_name)
@ -190,7 +192,8 @@ class TrayPublishWindow(PublisherWindow):
def main():
install_host(traypublisher)
host = TrayPublisherHost()
install_host(host)
app = QtWidgets.QApplication([])
window = TrayPublishWindow()
window.show()

View file

@ -380,7 +380,8 @@ Viewport2Options = {
"transparencyAlgorithm": 1,
"transparencyQuality": 0.33,
"useMaximumHardwareLights": True,
"vertexAnimationCache": 0
"vertexAnimationCache": 0,
"renderDepthOfField": 0
}

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
__version__ = "3.12.1-nightly.4"
__version__ = "3.12.1"

View file

@ -1,6 +1,7 @@
import os
import collections
import uuid
import json
from Qt import QtWidgets, QtCore, QtGui
@ -26,6 +27,27 @@ IS_SEQUENCE_ROLE = QtCore.Qt.UserRole + 7
EXT_ROLE = QtCore.Qt.UserRole + 8
def convert_bytes_to_json(bytes_value):
if isinstance(bytes_value, QtCore.QByteArray):
# Raw data are already QByteArray and we don't have to load them
encoded_data = bytes_value
else:
encoded_data = QtCore.QByteArray.fromRawData(bytes_value)
stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.ReadOnly)
text = stream.readQString()
try:
return json.loads(text)
except Exception:
return None
def convert_data_to_bytes(data):
bytes_value = QtCore.QByteArray()
stream = QtCore.QDataStream(bytes_value, QtCore.QIODevice.WriteOnly)
stream.writeQString(json.dumps(data))
return bytes_value
class SupportLabel(QtWidgets.QLabel):
pass
@ -33,7 +55,7 @@ class SupportLabel(QtWidgets.QLabel):
class DropEmpty(QtWidgets.QWidget):
_empty_extensions = "Any file"
def __init__(self, single_item, allow_sequences, parent):
def __init__(self, single_item, allow_sequences, extensions_label, parent):
super(DropEmpty, self).__init__(parent)
drop_label_widget = QtWidgets.QLabel("Drag & Drop files here", self)
@ -61,7 +83,19 @@ class DropEmpty(QtWidgets.QWidget):
widget.setAlignment(QtCore.Qt.AlignCenter)
widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
update_size_timer = QtCore.QTimer()
update_size_timer.setInterval(10)
update_size_timer.setSingleShot(True)
update_size_timer.timeout.connect(self._on_update_size_timer)
self._update_size_timer = update_size_timer
if extensions_label and not extensions_label.startswith(" "):
extensions_label = " " + extensions_label
self._single_item = single_item
self._extensions_label = extensions_label
self._allow_sequences = allow_sequences
self._allowed_extensions = set()
self._allow_folders = None
@ -114,22 +148,51 @@ class DropEmpty(QtWidgets.QWidget):
items_label = "Single "
if len(allowed_items) == 1:
allowed_items_label = allowed_items[0]
extensions_label = allowed_items[0]
elif len(allowed_items) == 2:
allowed_items_label = " or ".join(allowed_items)
extensions_label = " or ".join(allowed_items)
else:
last_item = allowed_items.pop(-1)
new_last_item = " or ".join(last_item, allowed_items.pop(-1))
allowed_items.append(new_last_item)
allowed_items_label = ", ".join(allowed_items)
extensions_label = ", ".join(allowed_items)
allowed_items_label = extensions_label
items_label += allowed_items_label
label_tooltip = None
if self._allowed_extensions:
items_label += " of\n{}".format(
", ".join(sorted(self._allowed_extensions))
)
if self._extensions_label:
label_tooltip = items_label
items_label = self._extensions_label
if self._items_label_widget.text() == items_label:
return
self._items_label_widget.setToolTip(label_tooltip)
self._items_label_widget.setText(items_label)
self._update_size_timer.start()
def resizeEvent(self, event):
super(DropEmpty, self).resizeEvent(event)
self._update_size_timer.start()
def _on_update_size_timer(self):
"""Recalculate height of label with extensions.
Dynamic QLabel with word wrap does not handle properly it's sizeHint
calculations on show. This way it is recalculated. It is good practice
to trigger this method with small offset using '_update_size_timer'.
"""
width = self._items_label_widget.width()
height = self._items_label_widget.heightForWidth(width)
self._items_label_widget.setMinimumHeight(height)
self._items_label_widget.updateGeometry()
def paintEvent(self, event):
super(DropEmpty, self).paintEvent(event)
@ -162,6 +225,7 @@ class FilesModel(QtGui.QStandardItemModel):
def __init__(self, single_item, allow_sequences):
super(FilesModel, self).__init__()
self._id = str(uuid.uuid4())
self._single_item = single_item
self._multivalue = False
self._allow_sequences = allow_sequences
@ -171,6 +235,10 @@ class FilesModel(QtGui.QStandardItemModel):
self._filenames_by_dirpath = collections.defaultdict(set)
self._items_by_dirpath = collections.defaultdict(list)
@property
def id(self):
return self._id
def set_multivalue(self, multivalue):
"""Disable filtering."""
@ -245,6 +313,66 @@ class FilesModel(QtGui.QStandardItemModel):
return item_id, item
def mimeData(self, indexes):
item_ids = [
index.data(ITEM_ID_ROLE)
for index in indexes
]
item_ids_data = convert_data_to_bytes(item_ids)
mime_data = super(FilesModel, self).mimeData(indexes)
mime_data.setData("files_widget/internal_move", item_ids_data)
file_items = []
for item_id in item_ids:
file_item = self.get_file_item_by_id(item_id)
if file_item:
file_items.append(file_item.to_dict())
full_item_data = convert_data_to_bytes({
"items": file_items,
"id": self._id
})
mime_data.setData("files_widget/full_data", full_item_data)
return mime_data
def dropMimeData(self, mime_data, action, row, col, index):
item_ids = convert_bytes_to_json(
mime_data.data("files_widget/internal_move")
)
if item_ids is None:
return False
# Find matching item after which will be items moved
# - store item before moved items are removed
root = self.invisibleRootItem()
if row >= 0:
src_item = self.item(row)
else:
src_item_id = index.data(ITEM_ID_ROLE)
src_item = self._items_by_id.get(src_item_id)
# Take out items that should be moved
items = []
for item_id in item_ids:
item = self._items_by_id.get(item_id)
if item:
self.takeRow(item.row())
items.append(item)
# Skip if there are not items that can be moved
if not items:
return False
# Calculate row where items should be inserted
if src_item:
src_row = src_item.row()
else:
src_row = root.rowCount()
root.insertRow(src_row, items)
return True
class FilesProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
@ -428,6 +556,9 @@ class FilesView(QtWidgets.QListView):
QtWidgets.QAbstractItemView.ExtendedSelection
)
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.setDragDropMode(self.InternalMove)
remove_btn = InViewButton(self)
pix_enabled = paint_image_with_color(
@ -529,11 +660,13 @@ class FilesView(QtWidgets.QListView):
class FilesWidget(QtWidgets.QFrame):
value_changed = QtCore.Signal()
def __init__(self, single_item, allow_sequences, parent):
def __init__(self, single_item, allow_sequences, extensions_label, parent):
super(FilesWidget, self).__init__(parent)
self.setAcceptDrops(True)
empty_widget = DropEmpty(single_item, allow_sequences, self)
empty_widget = DropEmpty(
single_item, allow_sequences, extensions_label, self
)
files_model = FilesModel(single_item, allow_sequences)
files_proxy_model = FilesProxyModel()
@ -553,6 +686,7 @@ class FilesWidget(QtWidgets.QFrame):
files_view.context_menu_requested.connect(
self._on_context_menu_requested
)
self._in_set_value = False
self._single_item = single_item
self._multivalue = False
@ -637,8 +771,6 @@ class FilesWidget(QtWidgets.QFrame):
)
self._widgets_by_id[item_id] = widget
self._files_proxy_model.sort(0)
if not self._in_set_value:
self.value_changed.emit()
@ -743,12 +875,22 @@ class FilesWidget(QtWidgets.QFrame):
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
full_data_value = mime_data.data("files_widget/full_data")
if self._handle_full_data_drag(full_data_value):
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
def dragLeaveEvent(self, event):
event.accept()
def dropEvent(self, event):
if self._multivalue:
return
mime_data = event.mimeData()
if not self._multivalue and mime_data.hasUrls():
if mime_data.hasUrls():
event.accept()
# event.setDropAction(QtCore.Qt.CopyAction)
filepaths = []
for url in mime_data.urls():
filepath = url.toLocalFile()
@ -759,7 +901,58 @@ class FilesWidget(QtWidgets.QFrame):
filepaths = self._files_proxy_model.filter_valid_files(filepaths)
if filepaths:
self._add_filepaths(filepaths)
event.accept()
if self._handle_full_data_drop(
mime_data.data("files_widget/full_data")
):
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
super(FilesWidget, self).dropEvent(event)
def _handle_full_data_drag(self, value):
if value is None:
return False
full_data = convert_bytes_to_json(value)
if full_data is None:
return False
if full_data["id"] == self._files_model.id:
return False
return True
def _handle_full_data_drop(self, value):
if value is None:
return False
full_data = convert_bytes_to_json(value)
if full_data is None:
return False
if full_data["id"] == self._files_model.id:
return False
for item in full_data["items"]:
filepaths = [
os.path.join(item["directory"], filename)
for filename in item["filenames"]
]
filepaths = self._files_proxy_model.filter_valid_files(filepaths)
if filepaths:
self._add_filepaths(filepaths)
if self._copy_modifiers_enabled():
return False
return True
def _copy_modifiers_enabled(self):
if (
QtWidgets.QApplication.keyboardModifiers()
& QtCore.Qt.ControlModifier
):
return True
return False
def _add_filepaths(self, filepaths):
self._files_model.add_filepaths(filepaths)

View file

@ -443,7 +443,10 @@ class UnknownAttrWidget(_BaseAttrDefWidget):
class FileAttrWidget(_BaseAttrDefWidget):
def _ui_init(self):
input_widget = FilesWidget(
self.attr_def.single_item, self.attr_def.allow_sequences, self
self.attr_def.single_item,
self.attr_def.allow_sequences,
self.attr_def.extensions_label,
self
)
if self.attr_def.tooltip:

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "OpenPype"
version = "3.12.1-nightly.4" # OpenPype
version = "3.12.1" # OpenPype
description = "Open VFX and Animation pipeline with support."
authors = ["OpenPype Team <info@openpype.io>"]
license = "MIT License"

View file

@ -28,6 +28,13 @@ if($arguments -eq "--no-submodule-update") {
$disable_submodule_update=$true
}
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
function Start-Progress {
param([ScriptBlock]$code)
$scroll = "/-\|/-\|"
@ -72,14 +79,18 @@ function Exit-WithCode($exitcode) {
function Show-PSWarning() {
if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Host "!!! " -NoNewline -ForegroundColor Red
Write-Host "You are using old version of PowerShell. $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)"
Write-Host "Please update to at least 7.0 - " -NoNewline -ForegroundColor Gray
Write-Host "https://github.com/PowerShell/PowerShell/releases" -ForegroundColor White
Write-Color -Text "!!! ", "You are using old version of PowerShell - ", "$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)" -Color Red, Yellow, White
Write-Color -Text " Please update to at least 7.0 - ", "https://github.com/PowerShell/PowerShell/releases" -Color Yellow, White
Exit-WithCode 1
}
}
function Install-Poetry() {
Write-Color -Text ">>> ", "Installing Poetry ... " -Color Green, Gray
$env:POETRY_HOME="$openpype_root\.poetry"
(Invoke-WebRequest -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py -UseBasicParsing).Content | python -
}
$art = @"
. . .. . ..
@ -103,10 +114,6 @@ Write-Host $art -ForegroundColor DarkGreen
# Enable if PS 7.x is needed.
# Show-PSWarning
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
$env:_INSIDE_OPENPYPE_TOOL = "1"
if (-not (Test-Path 'env:POETRY_HOME')) {
@ -119,8 +126,7 @@ $version_file = Get-Content -Path "$($openpype_root)\openpype\version.py"
$result = [regex]::Matches($version_file, '__version__ = "(?<version>\d+\.\d+.\d+.*)"')
$openpype_version = $result[0].Groups['version'].Value
if (-not $openpype_version) {
Write-Host "!!! " -ForegroundColor yellow -NoNewline
Write-Host "Cannot determine OpenPype version."
Write-Color -Text "!!! ", "Cannot determine OpenPype version." -Color Yellow, Gray
Exit-WithCode 1
}
@ -129,75 +135,60 @@ if (-not (Test-Path -PathType Container -Path "$($openpype_root)\build")) {
New-Item -ItemType Directory -Force -Path "$($openpype_root)\build"
}
Write-Host "--- " -NoNewline -ForegroundColor yellow
Write-Host "Cleaning build directory ..."
Write-Color -Text "--- ", "Cleaning build directory ..." -Color Yellow, Gray
try {
Remove-Item -Recurse -Force "$($openpype_root)\build\*"
}
catch {
Write-Host "!!! " -NoNewline -ForegroundColor Red
Write-Host "Cannot clean build directory, possibly because process is using it."
Write-Host $_.Exception.Message
Write-Color -Text "!!! ", "Cannot clean build directory, possibly because process is using it." -Color Red, Gray
Write-Color -Text $_.Exception.Message -Color Red
Exit-WithCode 1
}
if (-not $disable_submodule_update) {
Write-Host ">>> " -NoNewLine -ForegroundColor green
Write-Host "Making sure submodules are up-to-date ..."
git submodule update --init --recursive
Write-Color -Text ">>> ", "Making sure submodules are up-to-date ..." -Color Green, Gray
& git submodule update --init --recursive
} else {
Write-Host "*** " -NoNewLine -ForegroundColor yellow
Write-Host "Not updating submodules ..."
Write-Color -Text "*** ", "Not updating submodules ..." -Color Green, Gray
}
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "OpenPype [ " -NoNewline -ForegroundColor white
Write-host $openpype_version -NoNewline -ForegroundColor green
Write-Host " ]" -ForegroundColor white
Write-Color -Text ">>> ", "OpenPype [ ", $openpype_version, " ]" -Color Green, White, Cyan, White
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Reading Poetry ... " -NoNewline
Write-Color -Text ">>> ", "Reading Poetry ... " -Color Green, Gray -NoNewline
if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) {
Write-Host "NOT FOUND" -ForegroundColor Yellow
Write-Host "*** " -NoNewline -ForegroundColor Yellow
Write-Host "We need to install Poetry create virtual env first ..."
Write-Color -Text "NOT FOUND" -Color Yellow
Write-Color -Text "*** ", "We need to install Poetry create virtual env first ..." -Color Yellow, Gray
& "$openpype_root\tools\create_env.ps1"
} else {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
}
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Cleaning cache files ... " -NoNewline
Write-Color -Text ">>> ", "Cleaning cache files ... " -Color Green, Gray -NoNewline
Get-ChildItem $openpype_root -Filter "*.pyc" -Force -Recurse | Where-Object { $_.FullName -inotmatch 'build' } | Remove-Item -Force
Get-ChildItem $openpype_root -Filter "*.pyo" -Force -Recurse | Where-Object { $_.FullName -inotmatch 'build' } | Remove-Item -Force
Get-ChildItem $openpype_root -Filter "__pycache__" -Force -Recurse | Where-Object { $_.FullName -inotmatch 'build' } | Remove-Item -Force -Recurse
Write-Host "OK" -ForegroundColor green
Write-Color -Text "OK" -Color green
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Building OpenPype ..."
Write-Color -Text ">>> ", "Building OpenPype ..." -Color Green, White
$startTime = [int][double]::Parse((Get-Date -UFormat %s))
$out = & "$($env:POETRY_HOME)\bin\poetry" run python setup.py build 2>&1
Set-Content -Path "$($openpype_root)\build\build.log" -Value $out
if ($LASTEXITCODE -ne 0)
{
Write-Host "------------------------------------------" -ForegroundColor Red
Write-Color -Text "------------------------------------------" -Color Red
Get-Content "$($openpype_root)\build\build.log"
Write-Host "------------------------------------------" -ForegroundColor Red
Write-Host "!!! " -NoNewLine -ForegroundColor Red
Write-Host "Build failed. Check the log: " -NoNewline
Write-Host ".\build\build.log" -ForegroundColor Yellow
Write-Color -Text "------------------------------------------" -Color Yellow
Write-Color -Text "!!! ", "Build failed. Check the log: ", ".\build\build.log" -Color Red, Yellow, White
Exit-WithCode $LASTEXITCODE
}
Set-Content -Path "$($openpype_root)\build\build.log" -Value $out
& "$($env:POETRY_HOME)\bin\poetry" run python "$($openpype_root)\tools\build_dependencies.py"
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "restoring current directory"
Write-Color -Text ">>> ", "Restoring current directory" -Color Green, Gray
Set-Location -Path $current_dir
$endTime = [int][double]::Parse((Get-Date -UFormat %s))
Write-Host "*** " -NoNewline -ForegroundColor Cyan
Write-Host "All done in $($endTime - $startTime) secs. You will find OpenPype and build log in " -NoNewLine
Write-Host "'.\build'" -NoNewline -ForegroundColor Green
Write-Host " directory."
New-BurntToastNotification -AppLogo "$openpype_root/openpype/resources/icons/openpype_icon.png" -Text "OpenPype build complete!", "All done in $($endTime - $startTime) secs. You will find OpenPype and build log in build directory."
Write-Color -Text "*** ", "All done in ", $($endTime - $startTime), " secs. You will find OpenPype and build log in ", "'.\build'", " directory." -Color Green, Gray, White, Gray, White, Gray

View file

@ -11,6 +11,12 @@
PS> .\build_win_installer.ps1
#>
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
function Start-Progress {
param([ScriptBlock]$code)
@ -44,7 +50,6 @@ function Start-Progress {
#>
}
function Exit-WithCode($exitcode) {
# Only exit this host process if it's a child of another PowerShell parent process...
$parentPID = (Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$PID" | Select-Object -Property ParentProcessId).ParentProcessId
@ -56,10 +61,8 @@ function Exit-WithCode($exitcode) {
function Show-PSWarning() {
if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Host "!!! " -NoNewline -ForegroundColor Red
Write-Host "You are using old version of PowerShell. $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)"
Write-Host "Please update to at least 7.0 - " -NoNewline -ForegroundColor Gray
Write-Host "https://github.com/PowerShell/PowerShell/releases" -ForegroundColor White
Write-Color -Text "!!! ", "You are using old version of PowerShell - ", "$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)" -Color Red, Yellow, White
Write-Color -Text " Please update to at least 7.0 - ", "https://github.com/PowerShell/PowerShell/releases" -Color Yellow, White
Exit-WithCode 1
}
}
@ -87,9 +90,6 @@ Write-Host $art -ForegroundColor DarkGreen
# Enable if PS 7.x is needed.
# Show-PSWarning
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
Set-Location -Path $openpype_root
@ -97,16 +97,15 @@ $version_file = Get-Content -Path "$($openpype_root)\openpype\version.py"
$result = [regex]::Matches($version_file, '__version__ = "(?<version>\d+\.\d+.\d+.*)"')
$openpype_version = $result[0].Groups['version'].Value
if (-not $openpype_version) {
Write-Host "!!! " -ForegroundColor yellow -NoNewline
Write-Host "Cannot determine OpenPype version."
Write-Color -Text "!!! ", "Cannot determine OpenPype version." -Color Yellow, Gray
Exit-WithCode 1
}
$env:BUILD_VERSION = $openpype_version
iscc
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Detecting host Python ... " -NoNewline
Write-Color ">>> ", "Detecting host Python ... " -Color Green, White -NoNewline
$python = "python"
if (Get-Command "pyenv" -ErrorAction SilentlyContinue) {
$pyenv_python = & pyenv which python
@ -115,7 +114,7 @@ if (Get-Command "pyenv" -ErrorAction SilentlyContinue) {
}
}
if (-not (Get-Command $python -ErrorAction SilentlyContinue)) {
Write-Host "!!! Python not detected" -ForegroundColor red
Write-Color "!!! ", "Python not detected" -Color Red, Yellow
Set-Location -Path $current_dir
Exit-WithCode 1
}
@ -128,7 +127,7 @@ $p = & $python -c $version_command
$env:PYTHON_VERSION = $p
$m = $p -match '(\d+)\.(\d+)'
if(-not $m) {
Write-Host "!!! Cannot determine version" -ForegroundColor red
Write-Color "!!! ", "Cannot determine version" -Color Red, Yellow
Set-Location -Path $current_dir
Exit-WithCode 1
}
@ -145,8 +144,7 @@ if (($matches[1] -lt 3) -or ($matches[2] -lt 7)) {
Write-Host "OK [ $p ]" -ForegroundColor green
}
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Creating OpenPype installer ... " -ForegroundColor white
Write-Color -Text ">>> ", "Creating OpenPype installer ... " -Color Green, White
$build_dir_command = @"
import sys
@ -155,24 +153,25 @@ print('exe.{}-{}'.format(get_platform(), sys.version[0:3]))
"@
$build_dir = & $python -c $build_dir_command
Write-Host "Build directory ... ${build_dir}" -ForegroundColor white
Write-Color -Text "--- ", "Build directory ", "${build_dir}" -Color Green, Gray, White
$env:BUILD_DIR = $build_dir
if (Get-Command iscc -errorAction SilentlyContinue -ErrorVariable ProcessError)
{
iscc "$openpype_root\inno_setup.iss"
}else {
Write-Host "!!! Cannot find Inno Setup command" -ForegroundColor red
Write-Host "!!! You can download it at https://jrsoftware.org/" -ForegroundColor red
if (-not (Get-Command iscc -errorAction SilentlyContinue -ErrorVariable ProcessError)) {
Write-Color -Text "!!! ", "Cannot find Inno Setup command" -Color Red, Yellow
Write-Color "!!! You can download it at https://jrsoftware.org/" -ForegroundColor red
Exit-WithCode 1
}
& iscc "$openpype_root\inno_setup.iss"
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "restoring current directory"
if ($LASTEXITCODE -ne 0) {
Write-Color -Text "!!! ", "Creating installer failed." -Color Red, Yellow
Exit-WithCode 1
}
Write-Color -Text ">>> ", "Restoring current directory" -Color Green, Gray
Set-Location -Path $current_dir
Write-Host "*** " -NoNewline -ForegroundColor Cyan
Write-Host "All done. You will find OpenPype installer in " -NoNewLine
Write-Host "'.\build'" -NoNewline -ForegroundColor Green
Write-Host " directory."
New-BurntToastNotification -AppLogo "$openpype_root/openpype/resources/icons/openpype_icon.png" -Text "OpenPype build complete!", "All done. You will find You will find OpenPype installer in '.\build' directory."
Write-Color -Text "*** ", "All done. You will find OpenPype installer in ", "'.\build'", " directory." -Color Green, Gray, White, Gray

View file

@ -24,6 +24,15 @@ if($arguments -eq "--verbose") {
$poetry_verbosity="-vvv"
}
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
& git submodule update --init --recursive
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
function Exit-WithCode($exitcode) {
# Only exit this host process if it's a child of another PowerShell parent process...
$parentPID = (Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$PID" | Select-Object -Property ParentProcessId).ParentProcessId
@ -36,30 +45,26 @@ function Exit-WithCode($exitcode) {
function Show-PSWarning() {
if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Host "!!! " -NoNewline -ForegroundColor Red
Write-Host "You are using old version of PowerShell. $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)"
Write-Host "Please update to at least 7.0 - " -NoNewline -ForegroundColor Gray
Write-Host "https://github.com/PowerShell/PowerShell/releases" -ForegroundColor White
Write-Color -Text "!!! ", "You are using old version of PowerShell - ", "$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)" -Color Red, Yellow, White
Write-Color -Text " Please update to at least 7.0 - ", "https://github.com/PowerShell/PowerShell/releases" -Color Yellow, White
Exit-WithCode 1
}
}
function Install-Poetry() {
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Installing Poetry ... "
Write-Color -Text ">>> ", "Installing Poetry ... " -Color Green, Gray
$python = "python"
if (Get-Command "pyenv" -ErrorAction SilentlyContinue) {
if (-not (Test-Path -PathType Leaf -Path "$($openpype_root)\.python-version")) {
$result = & pyenv global
if ($result -eq "no global version configured") {
Write-Host "!!! " -NoNewline -ForegroundColor Red
Write-Host "Using pyenv but having no local or global version of Python set."
Write-Color -Text "!!! ", "Using pyenv but having no local or global version of Python set." -Color Red, Yellow
Exit-WithCode 1
}
}
$python = & pyenv which python
}
$env:POETRY_HOME="$openpype_root\.poetry"
@ -68,8 +73,7 @@ function Install-Poetry() {
function Test-Python() {
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Detecting host Python ... " -NoNewline
Write-Color -Text ">>> ", "Detecting host Python ... " -Color Green, Gray -NoNewline
$python = "python"
if (Get-Command "pyenv" -ErrorAction SilentlyContinue) {
$pyenv_python = & pyenv which python
@ -97,22 +101,17 @@ print('{0}.{1}'.format(sys.version_info[0], sys.version_info[1]))
}
# We are supporting python 3.7 only
if (($matches[1] -lt 3) -or ($matches[2] -lt 7)) {
Write-Host "FAILED Version [ $p ] is old and unsupported" -ForegroundColor red
Write-Color -Text "FAILED ", "Version ", "[", $p ,"]", "is old and unsupported" -Color Red, Yellow, Cyan, White, Cyan, Yellow
Set-Location -Path $current_dir
Exit-WithCode 1
} elseif (($matches[1] -eq 3) -and ($matches[2] -gt 7)) {
Write-Host "WARNING Version [ $p ] is unsupported, use at your own risk." -ForegroundColor yellow
Write-Host "*** " -NoNewline -ForegroundColor yellow
Write-Host "OpenPype supports only Python 3.7" -ForegroundColor white
Write-Color -Text "WARNING Version ", "[", $p, "]", " is unsupported, use at your own risk." -Color Yellow, Cyan, White, Cyan, Yellow
Write-Color -Text "*** ", "OpenPype supports only Python 3.7" -Color Yellow, White
} else {
Write-Host "OK [ $p ]" -ForegroundColor green
Write-Color "OK ", "[", $p, "]" -Color Green, Cyan, White, Cyan
}
}
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
if (-not (Test-Path 'env:POETRY_HOME')) {
$env:POETRY_HOME = "$openpype_root\.poetry"
}
@ -150,41 +149,38 @@ $version_file = Get-Content -Path "$($openpype_root)\openpype\version.py"
$result = [regex]::Matches($version_file, '__version__ = "(?<version>\d+\.\d+.\d+.*)"')
$openpype_version = $result[0].Groups['version'].Value
if (-not $openpype_version) {
Write-Host "!!! " -ForegroundColor yellow -NoNewline
Write-Host "Cannot determine OpenPype version."
Write-Color -Text "!!! ", "Cannot determine OpenPype version." -Color Red, Yellow
Set-Location -Path $current_dir
Exit-WithCode 1
}
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Found OpenPype version " -NoNewline
Write-Host "[ $($openpype_version) ]" -ForegroundColor Green
Write-Color -Text ">>> ", "Found OpenPype version ", "[ ", $($openpype_version), " ]" -Color Green, Gray, Cyan, White, Cyan
Test-Python
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Reading Poetry ... " -NoNewline
Write-Color -Text ">>> ", "Reading Poetry ... " -Color Green, Gray -NoNewline
if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) {
Write-Host "NOT FOUND" -ForegroundColor Yellow
Write-Color -Text "NOT FOUND" -Color Yellow
Install-Poetry
Write-Host "INSTALLED" -ForegroundColor Cyan
Write-Color -Text "INSTALLED" -Color Cyan
} else {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
}
if (-not (Test-Path -PathType Leaf -Path "$($openpype_root)\poetry.lock")) {
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Installing virtual environment and creating lock."
Write-Color -Text ">>> ", "Installing virtual environment and creating lock." -Color Green, Gray
} else {
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Installing virtual environment from lock."
Write-Color -Text ">>> ", "Installing virtual environment from lock." -Color Green, Gray
}
$startTime = [int][double]::Parse((Get-Date -UFormat %s))
& "$env:POETRY_HOME\bin\poetry" install --no-root $poetry_verbosity --ansi
if ($LASTEXITCODE -ne 0) {
Write-Host "!!! " -ForegroundColor yellow -NoNewline
Write-Host "Poetry command failed."
Write-Color -Text "!!! ", "Poetry command failed." -Color Red, Yellow
Set-Location -Path $current_dir
Exit-WithCode 1
}
$endTime = [int][double]::Parse((Get-Date -UFormat %s))
Set-Location -Path $current_dir
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Virtual environment created."
New-BurntToastNotification -AppLogo "$openpype_root/openpype/resources/icons/openpype_icon.png" -Text "OpenPype", "Virtual environment created.", "All done in $($endTime - $startTime) secs."
Write-Color -Text ">>> ", "Virtual environment created." -Color Green, White

View file

@ -19,6 +19,13 @@ PS> .\create_zip.ps1 --path C:\OpenPype
#>
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
function Exit-WithCode($exitcode) {
# Only exit this host process if it's a child of another PowerShell parent process...
$parentPID = (Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$PID" | Select-Object -Property ParentProcessId).ParentProcessId
@ -31,18 +38,12 @@ function Exit-WithCode($exitcode) {
function Show-PSWarning() {
if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Host "!!! " -NoNewline -ForegroundColor Red
Write-Host "You are using old version of PowerShell. $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)"
Write-Host "Please update to at least 7.0 - " -NoNewline -ForegroundColor Gray
Write-Host "https://github.com/PowerShell/PowerShell/releases" -ForegroundColor White
Write-Color -Text "!!! ", "You are using old version of PowerShell - ", "$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)" -Color Red, Yellow, White
Write-Color -Text " Please update to at least 7.0 - ", "https://github.com/PowerShell/PowerShell/releases" -Color Yellow, White
Exit-WithCode 1
}
}
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
$env:_INSIDE_OPENPYPE_TOOL = "1"
if (-not (Test-Path 'env:POETRY_HOME')) {
@ -78,31 +79,25 @@ $version_file = Get-Content -Path "$($openpype_root)\openpype\version.py"
$result = [regex]::Matches($version_file, '__version__ = "(?<version>\d+\.\d+.\d+.*)"')
$openpype_version = $result[0].Groups['version'].Value
if (-not $openpype_version) {
Write-Host "!!! " -ForegroundColor yellow -NoNewline
Write-Host "Cannot determine OpenPype version."
Write-Color -Text "!!! ", "Cannot determine OpenPype version." -Color Yellow, Gray
Exit-WithCode 1
}
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Reading Poetry ... " -NoNewline
Write-Color -Text ">>> ", "Reading Poetry ... " -Color Green, Gray -NoNewline
if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) {
Write-Host "NOT FOUND" -ForegroundColor Yellow
Write-Host "*** " -NoNewline -ForegroundColor Yellow
Write-Host "We need to install Poetry create virtual env first ..."
Write-Color -Text "NOT FOUND" -Color Yellow
Write-Color -Text "*** ", "We need to install Poetry create virtual env first ..." -Color Yellow, Gray
& "$openpype_root\tools\create_env.ps1"
} else {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
}
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Cleaning cache files ... " -NoNewline
Write-Color -Text ">>> ", "Cleaning cache files ... " -Color Green, Gray -NoNewline
Get-ChildItem $openpype_root -Filter "__pycache__" -Force -Recurse| Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\\.venv' )} | Remove-Item -Force -Recurse
Get-ChildItem $openpype_root -Filter "*.pyc" -Force -Recurse | Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\\.venv' )} | Remove-Item -Force
Get-ChildItem $openpype_root -Filter "*.pyo" -Force -Recurse | Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\\.venv' )} | Remove-Item -Force
Write-Host "OK" -ForegroundColor green
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Generating zip from current sources ..."
Write-Color -Text ">>> ", "Generating zip from current sources ..." -Color Green, Gray
$env:PYTHONPATH="$($openpype_root);$($env:PYTHONPATH)"
$env:OPENPYPE_ROOT="$($openpype_root)"
& "$($env:POETRY_HOME)\bin\poetry" run python "$($openpype_root)\tools\create_zip.py" $ARGS

View file

@ -15,6 +15,9 @@ $current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
$env:_INSIDE_OPENPYPE_TOOL = "1"
if (-not (Test-Path 'env:POETRY_HOME')) {
@ -23,16 +26,16 @@ if (-not (Test-Path 'env:POETRY_HOME')) {
Set-Location -Path $openpype_root
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Reading Poetry ... " -NoNewline
Write-Color -Text ">>> ", "Reading Poetry ... " -Color Green, Gray -NoNewline
if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) {
Write-Host "NOT FOUND" -ForegroundColor Yellow
Write-Host "*** " -NoNewline -ForegroundColor Yellow
Write-Host "We need to install Poetry create virtual env first ..."
Write-Color -Text "NOT FOUND" -Color Yellow
Write-Color -Text "*** ", "We need to install Poetry create virtual env first ..." -Color Yellow, Gray
& "$openpype_root\tools\create_env.ps1"
} else {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
}
$startTime = [int][double]::Parse((Get-Date -UFormat %s))
& "$($env:POETRY_HOME)\bin\poetry" run python "$($openpype_root)\tools\fetch_thirdparty_libs.py"
$endTime = [int][double]::Parse((Get-Date -UFormat %s))
Set-Location -Path $current_dir
New-BurntToastNotification -AppLogo "$openpype_root/openpype/resources/icons/openpype_icon.png" -Text "OpenPype", "Dependencies downloaded", "All done in $($endTime - $startTime) secs."

View file

@ -44,27 +44,30 @@ $art = @"
"@
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
Write-Host $art -ForegroundColor DarkGreen
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Reading Poetry ... " -NoNewline
Write-Color -Text ">>> ", "Reading Poetry ... " -Color Green, Gray -NoNewline
if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) {
Write-Host "NOT FOUND" -ForegroundColor Yellow
Write-Host "*** " -NoNewline -ForegroundColor Yellow
Write-Host "We need to install Poetry create virtual env first ..."
& "$openpype_root\tools\create_env.ps1"
Write-Color -Text "NOT FOUND" -Color Yellow
Install-Poetry
Write-Color -Text "INSTALLED" -Color Cyan
} else {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
}
Write-Host "This will not overwrite existing source rst files, only scan and add new."
Write-Color -Text "... ", "This will not overwrite existing source rst files, only scan and add new." -Color Yellow, Gray
Set-Location -Path $openpype_root
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Running apidoc ..."
Write-Color -Text ">>> ", "Running apidoc ..." -Color Green, Gray
& "$env:POETRY_HOME\bin\poetry" run sphinx-apidoc -M -e -d 10 --ext-intersphinx --ext-todo --ext-coverage --ext-viewcode -o "$($openpype_root)\docs\source" igniter
& "$env:POETRY_HOME\bin\poetry" run sphinx-apidoc.exe -M -e -d 10 --ext-intersphinx --ext-todo --ext-coverage --ext-viewcode -o "$($openpype_root)\docs\source" openpype vendor, openpype\vendor
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Building html ..."
Write-Color -Text ">>> ", "Building html ..." -Color Green, Gray
& "$env:POETRY_HOME\bin\poetry" run python "$($openpype_root)\setup.py" build_sphinx
Set-Location -Path $current_dir

View file

@ -11,6 +11,13 @@ PS> .\run_mongo.ps1
#>
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
$art = @"
. . .. . ..
@ -43,8 +50,7 @@ function Exit-WithCode($exitcode) {
function Find-Mongo ($preferred_version) {
$defaultPath = "C:\Program Files\MongoDB\Server"
Write-Host ">>> " -NoNewLine -ForegroundColor Green
Write-Host "Detecting MongoDB ... " -NoNewline
Write-Color -Text ">>> ", "Detecting MongoDB ... " -Color Geen, Gray -NoNewline
if (-not (Get-Command "mongod" -ErrorAction SilentlyContinue)) {
if(Test-Path "$($defaultPath)\*\bin\mongod.exe" -PathType Leaf) {
# we have mongo server installed on standard Windows location
@ -52,17 +58,14 @@ function Find-Mongo ($preferred_version) {
# $preferred_version.
$mongoVersions = Get-ChildItem -Directory 'C:\Program Files\MongoDB\Server' | Sort-Object -Property {$_.Name -as [int]}
if(Test-Path "$($mongoVersions[-1])\bin\mongod.exe" -PathType Leaf) {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
$use_version = $mongoVersions[-1]
foreach ($v in $mongoVersions) {
Write-Host " - found [ " -NoNewline
Write-Host $v -NoNewLine -ForegroundColor Cyan
Write-Host " ]" -NoNewLine
Write-Color -Text " - found [ ", $v, " ]" - Color Cyan, White, Cyan -NoNewLine
$version = Split-Path $v -Leaf
if ($preferred_version -eq $version) {
Write-Host " *" -ForegroundColor Green
Write-Color -Text " *" -Color Green
$use_version = $v
} else {
Write-Host ""
@ -71,27 +74,20 @@ function Find-Mongo ($preferred_version) {
$env:PATH = "$($env:PATH);$($use_version)\bin\"
Write-Host " - auto-added from [ " -NoNewline
Write-Host "$($use_version)\bin\mongod.exe" -NoNewLine -ForegroundColor Cyan
Write-Host " ]"
Write-Color -Text " - auto-added from [ ", "$($use_version)\bin\mongod.exe", " ]" -Color Cyan, White, Cyan
return "$($use_version)\bin\mongod.exe"
} else {
Write-Host "FAILED " -NoNewLine -ForegroundColor Red
Write-Host "MongoDB not detected" -ForegroundColor Yellow
Write-Host "Tried to find it on standard location " -NoNewline -ForegroundColor Gray
Write-Host " [ " -NoNewline -ForegroundColor Cyan
Write-Host "$($mongoVersions[-1])\bin\mongod.exe" -NoNewline -ForegroundColor White
Write-Host " ] " -NoNewLine -ForegroundColor Cyan
Write-Host "but failed." -ForegroundColor Gray
Write-Color -Text "FAILED " -Color Red -NoNewLine
Write-Color -Text "MongoDB not detected" -Color Yellow
Write-Color -Text "Tried to find it on standard location ", "[ ", "$($mongoVersions[-1])\bin\mongod.exe", " ]", " but failed." -Color Gray, Cyan, White, Cyan, Gray -NoNewline
Exit-WithCode 1
}
} else {
Write-Host "FAILED " -NoNewLine -ForegroundColor Red
Write-Host "MongoDB not detected in PATH" -ForegroundColor Yellow
Write-Color -Text "FAILED ", "MongoDB not detected in PATH" -Color Red, Yellow
Exit-WithCode 1
}
} else {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
return Get-Command "mongod" -ErrorAction SilentlyContinue
}
<#
@ -104,9 +100,6 @@ function Find-Mongo ($preferred_version) {
#>
}
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# mongodb port
$port = 2707
@ -116,15 +109,7 @@ $dbpath = (Get-Item $openpype_root).parent.FullName + "\mongo_db_data"
$preferred_version = "5.0"
$mongoPath = Find-Mongo $preferred_version
Write-Host ">>> " -NoNewLine -ForegroundColor Green
Write-Host "Using DB path: " -NoNewLine
Write-Host " [ " -NoNewline -ForegroundColor Cyan
Write-Host "$($dbpath)" -NoNewline -ForegroundColor White
Write-Host " ] "-ForegroundColor Cyan
Write-Host ">>> " -NoNewLine -ForegroundColor Green
Write-Host "Port: " -NoNewLine
Write-Host " [ " -NoNewline -ForegroundColor Cyan
Write-Host "$($port)" -NoNewline -ForegroundColor White
Write-Host " ] " -ForegroundColor Cyan
Start-Process -FilePath $mongopath "--dbpath $($dbpath) --port $($port)" -PassThru | Out-Null
Write-Color -Text ">>> ", "Using DB path: ", "[ ", "$($dbpath)", " ]" -Color Green, Gray, Cyan, White, Cyan
Write-Color -Text ">>> ", "Port: ", "[ ", "$($port)", " ]", -Color Green, Gray, Cyan, White, Cyan
Start-Process -FilePath $mongopath "--dbpath $($dbpath) --port $($port)" -PassThru | Out-Null

View file

@ -35,6 +35,9 @@ $current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
$env:_INSIDE_OPENPYPE_TOOL = "1"
# make sure Poetry is in PATH
@ -45,15 +48,13 @@ $env:PATH = "$($env:PATH);$($env:POETRY_HOME)\bin"
Set-Location -Path $openpype_root
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Reading Poetry ... " -NoNewline
Write-Color -Text ">>> ", "Reading Poetry ... " -Color Green, Gray -NoNewline
if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) {
Write-Host "NOT FOUND" -ForegroundColor Yellow
Write-Host "*** " -NoNewline -ForegroundColor Yellow
Write-Host "We need to install Poetry create virtual env first ..."
& "$openpype_root\tools\create_env.ps1"
Write-Color -Text "NOT FOUND" -Color Yellow
Install-Poetry
Write-Color -Text "INSTALLED" -Color Cyan
} else {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
}
& "$env:POETRY_HOME\bin\poetry" run python "$($openpype_root)\start.py" projectmanager

View file

@ -15,6 +15,9 @@ $current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
$env:_INSIDE_OPENPYPE_TOOL = "1"
# make sure Poetry is in PATH
@ -25,15 +28,13 @@ $env:PATH = "$($env:PATH);$($env:POETRY_HOME)\bin"
Set-Location -Path $openpype_root
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Reading Poetry ... " -NoNewline
Write-Color -Text ">>> ", "Reading Poetry ... " -Color Green, Gray -NoNewline
if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) {
Write-Host "NOT FOUND" -ForegroundColor Yellow
Write-Host "*** " -NoNewline -ForegroundColor Yellow
Write-Host "We need to install Poetry create virtual env first ..."
& "$openpype_root\tools\create_env.ps1"
Write-Color -Text "NOT FOUND" -Color Yellow
Install-Poetry
Write-Color -Text "INSTALLED" -Color Cyan
} else {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
}
& "$env:POETRY_HOME\bin\poetry" run python "$($openpype_root)\start.py" settings --dev

View file

@ -11,6 +11,13 @@ PS> .\run_test.ps1
#>
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
function Exit-WithCode($exitcode) {
# Only exit this host process if it's a child of another PowerShell parent process...
$parentPID = (Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$PID" | Select-Object -Property ParentProcessId).ParentProcessId
@ -22,10 +29,8 @@ function Exit-WithCode($exitcode) {
function Show-PSWarning() {
if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Host "!!! " -NoNewline -ForegroundColor Red
Write-Host "You are using old version of PowerShell. $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)"
Write-Host "Please update to at least 7.0 - " -NoNewline -ForegroundColor Gray
Write-Host "https://github.com/PowerShell/PowerShell/releases" -ForegroundColor White
Write-Color -Text "!!! ", "You are using old version of PowerShell - ", "$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)" -Color Red, Yellow, White
Write-Color -Text " Please update to at least 7.0 - ", "https://github.com/PowerShell/PowerShell/releases" -Color Yellow, White
Exit-WithCode 1
}
}
@ -53,10 +58,6 @@ Write-Host $art -ForegroundColor DarkGreen
# Enable if PS 7.x is needed.
# Show-PSWarning
$current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
$env:_INSIDE_OPENPYPE_TOOL = "1"
if (-not (Test-Path 'env:POETRY_HOME')) {
@ -69,46 +70,32 @@ $version_file = Get-Content -Path "$($openpype_root)\openpype\version.py"
$result = [regex]::Matches($version_file, '__version__ = "(?<version>\d+\.\d+.\d+.*)"')
$openpype_version = $result[0].Groups['version'].Value
if (-not $openpype_version) {
Write-Host "!!! " -ForegroundColor yellow -NoNewline
Write-Host "Cannot determine OpenPype version."
Write-Color -Text "!!! ", "Cannot determine OpenPype version." -Color Yellow, Gray
Exit-WithCode 1
}
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "OpenPype [ " -NoNewline -ForegroundColor white
Write-host $openpype_version -NoNewline -ForegroundColor green
Write-Host " ] ..." -ForegroundColor white
Write-Color -Text ">>> ", "OpenPype [ ", $openpype_version, " ]" -Color Green, White, Cyan, White
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Reading Poetry ... " -NoNewline
Write-Color -Text ">>> ", "Reading Poetry ... " -Color Green, Gray -NoNewline
if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) {
Write-Host "NOT FOUND" -ForegroundColor Yellow
Write-Host "*** " -NoNewline -ForegroundColor Yellow
Write-Host "We need to install Poetry create virtual env first ..."
Write-Color -Text "NOT FOUND" -Color Yellow
Write-Color -Text "*** ", "We need to install Poetry create virtual env first ..." -Color Yellow, Gray
& "$openpype_root\tools\create_env.ps1"
} else {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
}
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Cleaning cache files ... " -NoNewline
Write-Color -Text ">>> ", "Cleaning cache files ... " -Color Green, Gray -NoNewline
Get-ChildItem $openpype_root -Filter "*.pyc" -Force -Recurse | Where-Object { $_.FullName -inotmatch 'build' } | Remove-Item -Force
Get-ChildItem $openpype_root -Filter "*.pyo" -Force -Recurse | Where-Object { $_.FullName -inotmatch 'build' } | Remove-Item -Force
Get-ChildItem $openpype_root -Filter "__pycache__" -Force -Recurse | Where-Object { $_.FullName -inotmatch 'build' } | Remove-Item -Force -Recurse
Write-Host "OK" -ForegroundColor green
Write-Color -Text "OK" -Color green
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "Testing OpenPype ..."
Write-Color -Text ">>> ", "Testing OpenPype ..." -Color Green, White
$original_pythonpath = $env:PYTHONPATH
$env:PYTHONPATH="$($openpype_root);$($env:PYTHONPATH)"
& "$env:POETRY_HOME\bin\poetry" run pytest -x --capture=sys --print -W ignore::DeprecationWarning "$($openpype_root)/tests"
$env:PYTHONPATH = $original_pythonpath
Write-Host ">>> " -NoNewline -ForegroundColor green
Write-Host "restoring current directory"
Write-Color -Text ">>> ", "Restoring current directory" -Color Green, Gray
Set-Location -Path $current_dir

View file

@ -14,6 +14,9 @@ $current_dir = Get-Location
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$openpype_root = (Get-Item $script_dir).parent.FullName
# Install PSWriteColor to support colorized output to terminal
$env:PSModulePath = $env:PSModulePath + ";$($openpype_root)\vendor\powershell"
$env:_INSIDE_OPENPYPE_TOOL = "1"
# make sure Poetry is in PATH
@ -24,15 +27,13 @@ $env:PATH = "$($env:PATH);$($env:POETRY_HOME)\bin"
Set-Location -Path $openpype_root
Write-Host ">>> " -NoNewline -ForegroundColor Green
Write-Host "Reading Poetry ... " -NoNewline
Write-Color -Text ">>> ", "Reading Poetry ... " -Color Green, Gray -NoNewline
if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) {
Write-Host "NOT FOUND" -ForegroundColor Yellow
Write-Host "*** " -NoNewline -ForegroundColor Yellow
Write-Host "We need to install Poetry create virtual env first ..."
Write-Color -Text "NOT FOUND" -Color Yellow
Write-Color -Text "*** ", "We need to install Poetry create virtual env first ..." -Color Yellow, Gray
& "$openpype_root\tools\create_env.ps1"
} else {
Write-Host "OK" -ForegroundColor Green
Write-Color -Text "OK" -Color Green
}
& "$($env:POETRY_HOME)\bin\poetry" run python "$($openpype_root)\start.py" tray --debug

1
vendor/powershell/BurntToast vendored Submodule

@ -0,0 +1 @@
Subproject commit ae0acdd870a2fd8d9f0d147de22dc36d6c5e399e

1
vendor/powershell/PSWriteColor vendored Submodule

@ -0,0 +1 @@
Subproject commit 12eda384ebd7a7954e15855e312215c009c97114