mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
[Automated] Merged develop into main
This commit is contained in:
commit
722654c2bd
37 changed files with 896 additions and 181 deletions
|
|
@ -202,13 +202,10 @@ def reload_pipeline(*args):
|
|||
avalon.api.uninstall()
|
||||
|
||||
for module in (
|
||||
"avalon.io",
|
||||
"avalon.lib",
|
||||
"avalon.pipeline",
|
||||
"avalon.tools.creator.app",
|
||||
"avalon.tools.manager.app",
|
||||
"avalon.api",
|
||||
"avalon.tools",
|
||||
"avalon.io",
|
||||
"avalon.lib",
|
||||
"avalon.pipeline",
|
||||
"avalon.api",
|
||||
):
|
||||
module = importlib.import_module(module)
|
||||
importlib.reload(module)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ import logging
|
|||
|
||||
# Pipeline imports
|
||||
import avalon.api
|
||||
from avalon import io, pipeline
|
||||
from avalon import io
|
||||
|
||||
from openpype.lib import version_up
|
||||
from openpype.hosts.fusion import api
|
||||
from openpype.hosts.fusion.api import lib
|
||||
from openpype.lib.avalon_context import get_workdir_from_session
|
||||
|
||||
log = logging.getLogger("Update Slap Comp")
|
||||
|
||||
|
|
@ -44,16 +45,6 @@ def _format_version_folder(folder):
|
|||
return version_folder
|
||||
|
||||
|
||||
def _get_work_folder(session):
|
||||
"""Convenience function to get the work folder path of the current asset"""
|
||||
|
||||
# Get new filename, create path based on asset and work template
|
||||
template_work = self._project["config"]["template"]["work"]
|
||||
work_path = pipeline._format_work_template(template_work, session)
|
||||
|
||||
return os.path.normpath(work_path)
|
||||
|
||||
|
||||
def _get_fusion_instance():
|
||||
fusion = getattr(sys.modules["__main__"], "fusion", None)
|
||||
if fusion is None:
|
||||
|
|
@ -72,7 +63,7 @@ def _format_filepath(session):
|
|||
asset = session["AVALON_ASSET"]
|
||||
|
||||
# Save updated slap comp
|
||||
work_path = _get_work_folder(session)
|
||||
work_path = get_workdir_from_session(session)
|
||||
walk_to_dir = os.path.join(work_path, "scenes", "slapcomp")
|
||||
slapcomp_dir = os.path.abspath(walk_to_dir)
|
||||
|
||||
|
|
@ -112,7 +103,7 @@ def _update_savers(comp, session):
|
|||
None
|
||||
"""
|
||||
|
||||
new_work = _get_work_folder(session)
|
||||
new_work = get_workdir_from_session(session)
|
||||
renders = os.path.join(new_work, "renders")
|
||||
version_folder = _format_version_folder(renders)
|
||||
renders_version = os.path.join(renders, version_folder)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ import logging
|
|||
from Qt import QtWidgets, QtCore
|
||||
|
||||
import avalon.api
|
||||
from avalon import io, pipeline
|
||||
from avalon import io
|
||||
from avalon.vendor import qtawesome as qta
|
||||
|
||||
from openpype import style
|
||||
from openpype.hosts.fusion import api
|
||||
from openpype.lib.avalon_context import get_workdir_from_session
|
||||
|
||||
log = logging.getLogger("Fusion Switch Shot")
|
||||
|
||||
|
|
@ -123,7 +124,7 @@ class App(QtWidgets.QWidget):
|
|||
|
||||
def _on_open_from_dir(self):
|
||||
|
||||
start_dir = self._get_context_directory()
|
||||
start_dir = get_workdir_from_session()
|
||||
comp_file, _ = QtWidgets.QFileDialog.getOpenFileName(
|
||||
self, "Choose comp", start_dir)
|
||||
|
||||
|
|
@ -157,17 +158,6 @@ class App(QtWidgets.QWidget):
|
|||
import colorbleed.scripts.fusion_switch_shot as switch_shot
|
||||
switch_shot.switch(asset_name=asset, filepath=file_name, new=True)
|
||||
|
||||
def _get_context_directory(self):
|
||||
|
||||
project = io.find_one({"type": "project",
|
||||
"name": avalon.api.Session["AVALON_PROJECT"]},
|
||||
projection={"config": True})
|
||||
|
||||
template = project["config"]["template"]["work"]
|
||||
dir = pipeline._format_work_template(template, avalon.api.Session)
|
||||
|
||||
return dir
|
||||
|
||||
def collect_slap_comps(self, directory):
|
||||
items = glob.glob("{}/*.comp".format(directory))
|
||||
return items
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@ def zip_and_move(source, destination):
|
|||
log.debug(f"Saved '{source}' to '{destination}'")
|
||||
|
||||
|
||||
def show(module_name):
|
||||
def show(tool_name):
|
||||
"""Call show on "module_name".
|
||||
|
||||
This allows to make a QApplication ahead of time and always "exec_" to
|
||||
|
|
@ -375,13 +375,6 @@ def show(module_name):
|
|||
# requests to be received properly.
|
||||
time.sleep(1)
|
||||
|
||||
# Get tool name from module name
|
||||
# TODO this is for backwards compatibility not sure if `TB_sceneOpened.js`
|
||||
# is automatically updated.
|
||||
# Previous javascript sent 'module_name' which contained whole tool import
|
||||
# string e.g. "avalon.tools.workfiles" now it should be only "workfiles"
|
||||
tool_name = module_name.split(".")[-1]
|
||||
|
||||
kwargs = {}
|
||||
if tool_name == "loader":
|
||||
kwargs["use_context"] = True
|
||||
|
|
|
|||
|
|
@ -37,17 +37,17 @@ class ToolWindows:
|
|||
|
||||
|
||||
def edit_shader_definitions():
|
||||
from avalon.tools import lib
|
||||
from Qt import QtWidgets
|
||||
from openpype.hosts.maya.api.shader_definition_editor import (
|
||||
ShaderDefinitionsEditor
|
||||
)
|
||||
from openpype.tools.utils import qt_app_context
|
||||
|
||||
top_level_widgets = QtWidgets.QApplication.topLevelWidgets()
|
||||
main_window = next(widget for widget in top_level_widgets
|
||||
if widget.objectName() == "MayaWindow")
|
||||
|
||||
with lib.application():
|
||||
with qt_app_context():
|
||||
window = ToolWindows.get_window("shader_definition_editor")
|
||||
if not window:
|
||||
window = ShaderDefinitionsEditor(parent=main_window)
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ def install():
|
|||
return
|
||||
|
||||
def deferred():
|
||||
from avalon.tools import publish
|
||||
pyblish_icon = host_tools.get_pyblish_icon()
|
||||
parent_widget = get_main_window()
|
||||
cmds.menu(
|
||||
MENU_NAME,
|
||||
|
|
@ -80,7 +80,7 @@ def install():
|
|||
command=lambda *args: host_tools.show_publish(
|
||||
parent=parent_widget
|
||||
),
|
||||
image=publish.ICON
|
||||
image=pyblish_icon
|
||||
)
|
||||
|
||||
cmds.menuItem(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import maya.cmds as cmds
|
||||
import maya.cmds as cmds # noqa
|
||||
from avalon import api
|
||||
from openpype.api import get_project_settings
|
||||
from openpype.hosts.maya.api.lib import (
|
||||
|
|
@ -42,20 +43,20 @@ class VRaySceneLoader(api.Loader):
|
|||
with maintained_selection():
|
||||
cmds.namespace(addNamespace=namespace)
|
||||
with namespaced(namespace, new=False):
|
||||
nodes, group_node = self.create_vray_scene(name,
|
||||
filename=self.fname)
|
||||
nodes, root_node = self.create_vray_scene(name,
|
||||
filename=self.fname)
|
||||
|
||||
self[:] = nodes
|
||||
if not nodes:
|
||||
return
|
||||
|
||||
# colour the group node
|
||||
presets = get_project_settings(os.environ['AVALON_PROJECT'])
|
||||
colors = presets['maya']['load']['colors']
|
||||
settings = get_project_settings(os.environ['AVALON_PROJECT'])
|
||||
colors = settings['maya']['load']['colors']
|
||||
c = colors.get(family)
|
||||
if c is not None:
|
||||
cmds.setAttr("{0}.useOutlinerColor".format(group_node), 1)
|
||||
cmds.setAttr("{0}.outlinerColor".format(group_node),
|
||||
cmds.setAttr("{0}.useOutlinerColor".format(root_node), 1)
|
||||
cmds.setAttr("{0}.outlinerColor".format(root_node),
|
||||
(float(c[0])/255),
|
||||
(float(c[1])/255),
|
||||
(float(c[2])/255)
|
||||
|
|
@ -123,17 +124,21 @@ class VRaySceneLoader(api.Loader):
|
|||
mesh_node_name = "VRayScene_{}".format(name)
|
||||
|
||||
trans = cmds.createNode(
|
||||
"transform", name="{}".format(mesh_node_name))
|
||||
mesh = cmds.createNode(
|
||||
"mesh", name="{}_Shape".format(mesh_node_name), parent=trans)
|
||||
"transform", name=mesh_node_name)
|
||||
vray_scene = cmds.createNode(
|
||||
"VRayScene", name="{}_VRSCN".format(mesh_node_name), parent=trans)
|
||||
mesh = cmds.createNode(
|
||||
"mesh", name="{}_Shape".format(mesh_node_name), parent=trans)
|
||||
|
||||
cmds.connectAttr(
|
||||
"{}.outMesh".format(vray_scene), "{}.inMesh".format(mesh))
|
||||
|
||||
cmds.setAttr("{}.FilePath".format(vray_scene), filename, type="string")
|
||||
|
||||
# Lock the shape nodes so the user cannot delete these
|
||||
cmds.lockNode(mesh, lock=True)
|
||||
cmds.lockNode(vray_scene, lock=True)
|
||||
|
||||
# Create important connections
|
||||
cmds.connectAttr("time1.outTime",
|
||||
"{0}.inputTime".format(trans))
|
||||
|
|
@ -141,11 +146,9 @@ class VRaySceneLoader(api.Loader):
|
|||
# Connect mesh to initialShadingGroup
|
||||
cmds.sets([mesh], forceElement="initialShadingGroup")
|
||||
|
||||
group_node = cmds.group(empty=True, name="{}_GRP".format(name))
|
||||
cmds.parent(trans, group_node)
|
||||
nodes = [trans, vray_scene, mesh, group_node]
|
||||
nodes = [trans, vray_scene, mesh]
|
||||
|
||||
# Fix: Force refresh so the mesh shows correctly after creation
|
||||
cmds.refresh()
|
||||
|
||||
return nodes, group_node
|
||||
return nodes, trans
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
import six
|
||||
import platform
|
||||
import contextlib
|
||||
|
|
@ -679,10 +678,10 @@ def get_render_path(node):
|
|||
}
|
||||
|
||||
nuke_imageio_writes = get_created_node_imageio_setting(**data_preset)
|
||||
host_name = os.environ.get("AVALON_APP")
|
||||
|
||||
application = lib.get_application(os.environ["AVALON_APP_NAME"])
|
||||
data.update({
|
||||
"application": application,
|
||||
"app": host_name,
|
||||
"nuke_imageio_writes": nuke_imageio_writes
|
||||
})
|
||||
|
||||
|
|
@ -805,18 +804,14 @@ def create_write_node(name, data, input=None, prenodes=None,
|
|||
'''
|
||||
|
||||
imageio_writes = get_created_node_imageio_setting(**data)
|
||||
app_manager = ApplicationManager()
|
||||
app_name = os.environ.get("AVALON_APP_NAME")
|
||||
if app_name:
|
||||
app = app_manager.applications.get(app_name)
|
||||
|
||||
for knob in imageio_writes["knobs"]:
|
||||
if knob["name"] == "file_type":
|
||||
representation = knob["value"]
|
||||
|
||||
host_name = os.environ.get("AVALON_APP")
|
||||
try:
|
||||
data.update({
|
||||
"app": app.host_name,
|
||||
"app": host_name,
|
||||
"imageio_writes": imageio_writes,
|
||||
"representation": representation,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -446,6 +446,8 @@ class ExporterReviewMov(ExporterReview):
|
|||
return path
|
||||
|
||||
def generate_mov(self, farm=False, **kwargs):
|
||||
reformat_node_add = kwargs["reformat_node_add"]
|
||||
reformat_node_config = kwargs["reformat_node_config"]
|
||||
bake_viewer_process = kwargs["bake_viewer_process"]
|
||||
bake_viewer_input_process_node = kwargs[
|
||||
"bake_viewer_input_process"]
|
||||
|
|
@ -483,6 +485,30 @@ class ExporterReviewMov(ExporterReview):
|
|||
self.previous_node = r_node
|
||||
self.log.debug("Read... `{}`".format(self._temp_nodes[subset]))
|
||||
|
||||
# add reformat node
|
||||
if reformat_node_add:
|
||||
# append reformated tag
|
||||
add_tags.append("reformated")
|
||||
|
||||
rf_node = nuke.createNode("Reformat")
|
||||
for kn_conf in reformat_node_config:
|
||||
_type = kn_conf["type"]
|
||||
k_name = str(kn_conf["name"])
|
||||
k_value = kn_conf["value"]
|
||||
|
||||
# to remove unicode as nuke doesn't like it
|
||||
if _type == "string":
|
||||
k_value = str(kn_conf["value"])
|
||||
|
||||
rf_node[k_name].setValue(k_value)
|
||||
|
||||
# connect
|
||||
rf_node.setInput(0, self.previous_node)
|
||||
self._temp_nodes[subset].append(rf_node)
|
||||
self.previous_node = rf_node
|
||||
self.log.debug(
|
||||
"Reformat... `{}`".format(self._temp_nodes[subset]))
|
||||
|
||||
# only create colorspace baking if toggled on
|
||||
if bake_viewer_process:
|
||||
if bake_viewer_input_process_node:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import re
|
||||
import pyblish.api
|
||||
import openpype
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
|
|
@ -25,6 +26,7 @@ class ExtractReviewDataMov(openpype.api.Extractor):
|
|||
def process(self, instance):
|
||||
families = instance.data["families"]
|
||||
task_type = instance.context.data["taskType"]
|
||||
subset = instance.data["subset"]
|
||||
self.log.info("Creating staging dir...")
|
||||
|
||||
if "representations" not in instance.data:
|
||||
|
|
@ -46,6 +48,7 @@ class ExtractReviewDataMov(openpype.api.Extractor):
|
|||
for o_name, o_data in self.outputs.items():
|
||||
f_families = o_data["filter"]["families"]
|
||||
f_task_types = o_data["filter"]["task_types"]
|
||||
f_subsets = o_data["filter"]["sebsets"]
|
||||
|
||||
# test if family found in context
|
||||
test_families = any([
|
||||
|
|
@ -69,11 +72,25 @@ class ExtractReviewDataMov(openpype.api.Extractor):
|
|||
bool(not f_task_types)
|
||||
])
|
||||
|
||||
# test subsets from filter
|
||||
test_subsets = any([
|
||||
# check if any of subset filter inputs
|
||||
# converted to regex patern is not found in subset
|
||||
# we keep strict case sensitivity
|
||||
bool(next((
|
||||
s for s in f_subsets
|
||||
if re.search(re.compile(s), subset)
|
||||
), None)),
|
||||
# but if no subsets were set then make this acuntable too
|
||||
bool(not f_subsets)
|
||||
])
|
||||
|
||||
# we need all filters to be positive for this
|
||||
# preset to be activated
|
||||
test_all = all([
|
||||
test_families,
|
||||
test_task_types
|
||||
test_task_types,
|
||||
test_subsets
|
||||
])
|
||||
|
||||
# if it is not positive then skip this preset
|
||||
|
|
@ -120,6 +137,13 @@ class ExtractReviewDataMov(openpype.api.Extractor):
|
|||
if generated_repres:
|
||||
# assign to representations
|
||||
instance.data["representations"] += generated_repres
|
||||
else:
|
||||
instance.data["families"].remove("review")
|
||||
self.log.info((
|
||||
"Removing `review` from families. "
|
||||
"Not available baking profile."
|
||||
))
|
||||
self.log.debug(instance.data["families"])
|
||||
|
||||
self.log.debug(
|
||||
"_ representations: {}".format(
|
||||
|
|
|
|||
|
|
@ -81,14 +81,10 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
parsed_subset = instance.data["subset"].replace(
|
||||
instance.data["family"], '')
|
||||
|
||||
fill_pairs = {
|
||||
explicit_data = {
|
||||
"subset": parsed_subset
|
||||
}
|
||||
|
||||
fill_pairs = prepare_template_data(fill_pairs)
|
||||
workfile_subset = format_template_with_optional_keys(
|
||||
fill_pairs, self.workfile_subset_template)
|
||||
|
||||
processed_instance = False
|
||||
for repre in instance.data["representations"]:
|
||||
ext = repre["ext"].replace('.', '')
|
||||
|
|
@ -102,6 +98,21 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
if ext in self.main_workfile_extensions or \
|
||||
ext in self.other_workfile_extensions:
|
||||
|
||||
formatting_data = self._get_parsed_groups(
|
||||
repre_file,
|
||||
self.input_naming_patterns["workfile"],
|
||||
self.input_naming_groups["workfile"],
|
||||
self.color_space
|
||||
)
|
||||
self.log.info("Parsed groups from workfile "
|
||||
"name '{}': {}".format(repre_file,
|
||||
formatting_data))
|
||||
|
||||
formatting_data.update(explicit_data)
|
||||
fill_pairs = prepare_template_data(formatting_data)
|
||||
workfile_subset = format_template_with_optional_keys(
|
||||
fill_pairs, self.workfile_subset_template)
|
||||
|
||||
asset_build = self._get_asset_build(
|
||||
repre_file,
|
||||
self.input_naming_patterns["workfile"],
|
||||
|
|
@ -148,11 +159,23 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
resource_files[workfile_subset].append(item)
|
||||
|
||||
if ext in self.texture_extensions:
|
||||
formatting_data = self._get_parsed_groups(
|
||||
repre_file,
|
||||
self.input_naming_patterns["textures"],
|
||||
self.input_naming_groups["textures"],
|
||||
self.color_space
|
||||
)
|
||||
|
||||
self.log.info("Parsed groups from texture "
|
||||
"name '{}': {}".format(repre_file,
|
||||
formatting_data))
|
||||
|
||||
c_space = self._get_color_space(
|
||||
repre_file,
|
||||
self.color_space
|
||||
)
|
||||
|
||||
# optional value
|
||||
channel = self._get_channel_name(
|
||||
repre_file,
|
||||
self.input_naming_patterns["textures"],
|
||||
|
|
@ -160,6 +183,7 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
self.color_space
|
||||
)
|
||||
|
||||
# optional value
|
||||
shader = self._get_shader_name(
|
||||
repre_file,
|
||||
self.input_naming_patterns["textures"],
|
||||
|
|
@ -167,13 +191,15 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
self.color_space
|
||||
)
|
||||
|
||||
formatting_data = {
|
||||
explicit_data = {
|
||||
"color_space": c_space or '', # None throws exception
|
||||
"channel": channel or '',
|
||||
"shader": shader or '',
|
||||
"subset": parsed_subset or ''
|
||||
}
|
||||
|
||||
formatting_data.update(explicit_data)
|
||||
|
||||
fill_pairs = prepare_template_data(formatting_data)
|
||||
subset = format_template_with_optional_keys(
|
||||
fill_pairs, self.texture_subset_template)
|
||||
|
|
@ -243,6 +269,13 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
for asset_build, version, subset, family in asset_builds:
|
||||
if not main_version:
|
||||
main_version = version
|
||||
|
||||
try:
|
||||
version_int = int(version or main_version or 1)
|
||||
except ValueError:
|
||||
self.log.error("Parsed version {} is not "
|
||||
"an number".format(version))
|
||||
|
||||
new_instance = context.create_instance(subset)
|
||||
new_instance.data.update(
|
||||
{
|
||||
|
|
@ -251,7 +284,7 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
"label": subset,
|
||||
"name": subset,
|
||||
"family": family,
|
||||
"version": int(version or main_version or 1),
|
||||
"version": version_int,
|
||||
"asset_build": asset_build # remove in validator
|
||||
}
|
||||
)
|
||||
|
|
@ -320,13 +353,14 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
"""
|
||||
asset_name = "NOT_AVAIL"
|
||||
|
||||
return self._parse(name, input_naming_patterns, input_naming_groups,
|
||||
color_spaces, 'asset') or asset_name
|
||||
return (self._parse_key(name, input_naming_patterns,
|
||||
input_naming_groups, color_spaces, 'asset') or
|
||||
asset_name)
|
||||
|
||||
def _get_version(self, name, input_naming_patterns, input_naming_groups,
|
||||
color_spaces):
|
||||
found = self._parse(name, input_naming_patterns, input_naming_groups,
|
||||
color_spaces, 'version')
|
||||
found = self._parse_key(name, input_naming_patterns,
|
||||
input_naming_groups, color_spaces, 'version')
|
||||
|
||||
if found:
|
||||
return found.replace('v', '')
|
||||
|
|
@ -336,8 +370,8 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
def _get_udim(self, name, input_naming_patterns, input_naming_groups,
|
||||
color_spaces):
|
||||
"""Parses from 'name' udim value."""
|
||||
found = self._parse(name, input_naming_patterns, input_naming_groups,
|
||||
color_spaces, 'udim')
|
||||
found = self._parse_key(name, input_naming_patterns,
|
||||
input_naming_groups, color_spaces, 'udim')
|
||||
if found:
|
||||
return found
|
||||
|
||||
|
|
@ -375,12 +409,15 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
Unknown format of channel name and color spaces >> cs are known
|
||||
list - 'color_space' used as a placeholder
|
||||
"""
|
||||
found = self._parse(name, input_naming_patterns, input_naming_groups,
|
||||
color_spaces, 'shader')
|
||||
if found:
|
||||
return found
|
||||
found = None
|
||||
try:
|
||||
found = self._parse_key(name, input_naming_patterns,
|
||||
input_naming_groups, color_spaces,
|
||||
'shader')
|
||||
except ValueError:
|
||||
self.log.warning("Didn't find shader in {}".format(name))
|
||||
|
||||
self.log.warning("Didn't find shader in {}".format(name))
|
||||
return found
|
||||
|
||||
def _get_channel_name(self, name, input_naming_patterns,
|
||||
input_naming_groups, color_spaces):
|
||||
|
|
@ -389,15 +426,18 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
Unknown format of channel name and color spaces >> cs are known
|
||||
list - 'color_space' used as a placeholder
|
||||
"""
|
||||
found = self._parse(name, input_naming_patterns, input_naming_groups,
|
||||
color_spaces, 'channel')
|
||||
if found:
|
||||
return found
|
||||
found = None
|
||||
try:
|
||||
found = self._parse_key(name, input_naming_patterns,
|
||||
input_naming_groups, color_spaces,
|
||||
'channel')
|
||||
except ValueError:
|
||||
self.log.warning("Didn't find channel in {}".format(name))
|
||||
|
||||
self.log.warning("Didn't find channel in {}".format(name))
|
||||
return found
|
||||
|
||||
def _parse(self, name, input_naming_patterns, input_naming_groups,
|
||||
color_spaces, key):
|
||||
def _parse_key(self, name, input_naming_patterns, input_naming_groups,
|
||||
color_spaces, key):
|
||||
"""Universal way to parse 'name' with configurable regex groups.
|
||||
|
||||
Args:
|
||||
|
|
@ -411,23 +451,47 @@ class CollectTextures(pyblish.api.ContextPlugin):
|
|||
Raises:
|
||||
ValueError - if broken 'input_naming_groups'
|
||||
"""
|
||||
parsed_groups = self._get_parsed_groups(name,
|
||||
input_naming_patterns,
|
||||
input_naming_groups,
|
||||
color_spaces)
|
||||
|
||||
try:
|
||||
parsed_value = parsed_groups[key]
|
||||
return parsed_value
|
||||
except (IndexError, KeyError):
|
||||
msg = ("'Textures group positions' must " +
|
||||
"have '{}' key".format(key))
|
||||
raise ValueError(msg)
|
||||
|
||||
def _get_parsed_groups(self, name, input_naming_patterns,
|
||||
input_naming_groups, color_spaces):
|
||||
"""Universal way to parse 'name' with configurable regex groups.
|
||||
|
||||
Args:
|
||||
name (str): workfile name or texture name
|
||||
input_naming_patterns (list):
|
||||
[workfile_pattern] or [texture_pattern]
|
||||
input_naming_groups (list)
|
||||
ordinal position of regex groups matching to input_naming..
|
||||
color_spaces (list) - predefined color spaces
|
||||
|
||||
Returns:
|
||||
(dict) {group_name:parsed_value}
|
||||
"""
|
||||
for input_pattern in input_naming_patterns:
|
||||
for cs in color_spaces:
|
||||
pattern = input_pattern.replace('{color_space}', cs)
|
||||
regex_result = re.findall(pattern, name)
|
||||
if regex_result:
|
||||
idx = list(input_naming_groups).index(key)
|
||||
if idx < 0:
|
||||
msg = "input_naming_groups must " +\
|
||||
"have '{}' key".format(key)
|
||||
raise ValueError(msg)
|
||||
if len(regex_result[0]) == len(input_naming_groups):
|
||||
return dict(zip(input_naming_groups, regex_result[0]))
|
||||
else:
|
||||
self.log.warning("No of parsed groups doesn't match "
|
||||
"no of group labels")
|
||||
|
||||
try:
|
||||
parsed_value = regex_result[0][idx]
|
||||
return parsed_value
|
||||
except IndexError:
|
||||
self.log.warning("Wrong index, probably "
|
||||
"wrong name {}".format(name))
|
||||
raise ValueError("Name '{}' cannot be parsed by any "
|
||||
"'{}' patterns".format(name, input_naming_patterns))
|
||||
|
||||
def _update_representations(self, upd_representations):
|
||||
"""Frames dont have sense for textures, add collected udims instead."""
|
||||
|
|
|
|||
|
|
@ -644,6 +644,166 @@ def get_workdir(
|
|||
)
|
||||
|
||||
|
||||
def template_data_from_session(session=None):
|
||||
""" Return dictionary with template from session keys.
|
||||
|
||||
Args:
|
||||
session (dict, Optional): The Session to use. If not provided use the
|
||||
currently active global Session.
|
||||
Returns:
|
||||
dict: All available data from session.
|
||||
"""
|
||||
from avalon import io
|
||||
import avalon.api
|
||||
|
||||
if session is None:
|
||||
session = avalon.api.Session
|
||||
|
||||
project_name = session["AVALON_PROJECT"]
|
||||
project_doc = io._database[project_name].find_one({"type": "project"})
|
||||
asset_doc = io._database[project_name].find_one({
|
||||
"type": "asset",
|
||||
"name": session["AVALON_ASSET"]
|
||||
})
|
||||
task_name = session["AVALON_TASK"]
|
||||
host_name = session["AVALON_APP"]
|
||||
return get_workdir_data(project_doc, asset_doc, task_name, host_name)
|
||||
|
||||
|
||||
def compute_session_changes(
|
||||
session, task=None, asset=None, app=None, template_key=None
|
||||
):
|
||||
"""Compute the changes for a Session object on asset, task or app switch
|
||||
|
||||
This does *NOT* update the Session object, but returns the changes
|
||||
required for a valid update of the Session.
|
||||
|
||||
Args:
|
||||
session (dict): The initial session to compute changes to.
|
||||
This is required for computing the full Work Directory, as that
|
||||
also depends on the values that haven't changed.
|
||||
task (str, Optional): Name of task to switch to.
|
||||
asset (str or dict, Optional): Name of asset to switch to.
|
||||
You can also directly provide the Asset dictionary as returned
|
||||
from the database to avoid an additional query. (optimization)
|
||||
app (str, Optional): Name of app to switch to.
|
||||
|
||||
Returns:
|
||||
dict: The required changes in the Session dictionary.
|
||||
|
||||
"""
|
||||
changes = dict()
|
||||
|
||||
# If no changes, return directly
|
||||
if not any([task, asset, app]):
|
||||
return changes
|
||||
|
||||
# Get asset document and asset
|
||||
asset_document = None
|
||||
asset_tasks = None
|
||||
if isinstance(asset, dict):
|
||||
# Assume asset database document
|
||||
asset_document = asset
|
||||
asset_tasks = asset_document.get("data", {}).get("tasks")
|
||||
asset = asset["name"]
|
||||
|
||||
if not asset_document or not asset_tasks:
|
||||
from avalon import io
|
||||
|
||||
# Assume asset name
|
||||
asset_document = io.find_one(
|
||||
{
|
||||
"name": asset,
|
||||
"type": "asset"
|
||||
},
|
||||
{"data.tasks": True}
|
||||
)
|
||||
assert asset_document, "Asset must exist"
|
||||
|
||||
# Detect any changes compared session
|
||||
mapping = {
|
||||
"AVALON_ASSET": asset,
|
||||
"AVALON_TASK": task,
|
||||
"AVALON_APP": app,
|
||||
}
|
||||
changes = {
|
||||
key: value
|
||||
for key, value in mapping.items()
|
||||
if value and value != session.get(key)
|
||||
}
|
||||
if not changes:
|
||||
return changes
|
||||
|
||||
# Compute work directory (with the temporary changed session so far)
|
||||
_session = session.copy()
|
||||
_session.update(changes)
|
||||
|
||||
changes["AVALON_WORKDIR"] = get_workdir_from_session(_session)
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
def get_workdir_from_session(session=None, template_key=None):
|
||||
import avalon.api
|
||||
|
||||
if session is None:
|
||||
session = avalon.api.Session
|
||||
project_name = session["AVALON_PROJECT"]
|
||||
host_name = session["AVALON_APP"]
|
||||
anatomy = Anatomy(project_name)
|
||||
template_data = template_data_from_session(session)
|
||||
anatomy_filled = anatomy.format(template_data)
|
||||
|
||||
if not template_key:
|
||||
task_type = template_data["task"]["type"]
|
||||
template_key = get_workfile_template_key(
|
||||
task_type,
|
||||
host_name,
|
||||
project_name=project_name
|
||||
)
|
||||
return anatomy_filled[template_key]["folder"]
|
||||
|
||||
|
||||
def update_current_task(task=None, asset=None, app=None, template_key=None):
|
||||
"""Update active Session to a new task work area.
|
||||
|
||||
This updates the live Session to a different `asset`, `task` or `app`.
|
||||
|
||||
Args:
|
||||
task (str): The task to set.
|
||||
asset (str): The asset to set.
|
||||
app (str): The app to set.
|
||||
|
||||
Returns:
|
||||
dict: The changed key, values in the current Session.
|
||||
|
||||
"""
|
||||
import avalon.api
|
||||
from avalon.pipeline import emit
|
||||
|
||||
changes = compute_session_changes(
|
||||
avalon.api.Session,
|
||||
task=task,
|
||||
asset=asset,
|
||||
app=app,
|
||||
template_key=template_key
|
||||
)
|
||||
|
||||
# Update the Session and environments. Pop from environments all keys with
|
||||
# value set to None.
|
||||
for key, value in changes.items():
|
||||
avalon.api.Session[key] = value
|
||||
if value is None:
|
||||
os.environ.pop(key, None)
|
||||
else:
|
||||
os.environ[key] = value
|
||||
|
||||
# Emit session change
|
||||
emit("taskChanged", changes.copy())
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
@with_avalon
|
||||
def get_workfile_doc(asset_id, task_name, filename, dbcon=None):
|
||||
"""Return workfile document for entered context.
|
||||
|
|
@ -952,7 +1112,7 @@ class BuildWorkfile:
|
|||
Returns:
|
||||
(dict): preset per entered task name
|
||||
"""
|
||||
host_name = avalon.api.registered_host().__name__.rsplit(".", 1)[-1]
|
||||
host_name = os.environ["AVALON_APP"]
|
||||
project_settings = get_project_settings(
|
||||
avalon.io.Session["AVALON_PROJECT"]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import tempfile
|
||||
import time
|
||||
from datetime import datetime
|
||||
import subprocess
|
||||
import json
|
||||
import platform
|
||||
import uuid
|
||||
from Deadline.Scripting import RepositoryUtils, FileUtils
|
||||
|
||||
|
||||
|
|
@ -36,9 +37,11 @@ def inject_openpype_environment(deadlinePlugin):
|
|||
print("--- OpenPype executable: {}".format(openpype_app))
|
||||
|
||||
# tempfile.TemporaryFile cannot be used because of locking
|
||||
export_url = os.path.join(tempfile.gettempdir(),
|
||||
time.strftime('%Y%m%d%H%M%S'),
|
||||
'env.json') # add HHMMSS + delete later
|
||||
temp_file_name = "{}_{}.json".format(
|
||||
datetime.utcnow().strftime('%Y%m%d%H%M%S%f'),
|
||||
str(uuid.uuid1())
|
||||
)
|
||||
export_url = os.path.join(tempfile.gettempdir(), temp_file_name)
|
||||
print(">>> Temporary path: {}".format(export_url))
|
||||
|
||||
args = [
|
||||
|
|
|
|||
|
|
@ -20,11 +20,16 @@ from openpype_modules.ftrack.lib import (
|
|||
query_custom_attributes,
|
||||
CUST_ATTR_ID_KEY,
|
||||
CUST_ATTR_AUTO_SYNC,
|
||||
FPS_KEYS,
|
||||
|
||||
avalon_sync,
|
||||
|
||||
BaseEvent
|
||||
)
|
||||
from openpype_modules.ftrack.lib.avalon_sync import (
|
||||
convert_to_fps,
|
||||
InvalidFpsValue
|
||||
)
|
||||
from openpype.lib import CURRENT_DOC_SCHEMAS
|
||||
|
||||
|
||||
|
|
@ -1149,12 +1154,31 @@ class SyncToAvalonEvent(BaseEvent):
|
|||
"description": ftrack_ent["description"]
|
||||
}
|
||||
}
|
||||
invalid_fps_items = []
|
||||
cust_attrs = self.get_cust_attr_values(ftrack_ent)
|
||||
for key, val in cust_attrs.items():
|
||||
if key.startswith("avalon_"):
|
||||
continue
|
||||
|
||||
if key in FPS_KEYS:
|
||||
try:
|
||||
val = convert_to_fps(val)
|
||||
except InvalidFpsValue:
|
||||
invalid_fps_items.append((ftrack_ent["id"], val))
|
||||
continue
|
||||
|
||||
final_entity["data"][key] = val
|
||||
|
||||
if invalid_fps_items:
|
||||
fps_msg = (
|
||||
"These entities have invalid fps value in custom attributes"
|
||||
)
|
||||
items = []
|
||||
for entity_id, value in invalid_fps_items:
|
||||
ent_path = self.get_ent_path(entity_id)
|
||||
items.append("{} - \"{}\"".format(ent_path, value))
|
||||
self.report_items["error"][fps_msg] = items
|
||||
|
||||
_mongo_id_str = cust_attrs.get(CUST_ATTR_ID_KEY)
|
||||
if _mongo_id_str:
|
||||
try:
|
||||
|
|
@ -2155,11 +2179,19 @@ class SyncToAvalonEvent(BaseEvent):
|
|||
)
|
||||
|
||||
convert_types_by_id[attr_id] = convert_type
|
||||
default_value = attr["default"]
|
||||
if key in FPS_KEYS:
|
||||
try:
|
||||
default_value = convert_to_fps(default_value)
|
||||
except InvalidFpsValue:
|
||||
pass
|
||||
|
||||
entities_dict[ftrack_project_id]["hier_attrs"][key] = (
|
||||
attr["default"]
|
||||
)
|
||||
|
||||
# PREPARE DATA BEFORE THIS
|
||||
invalid_fps_items = []
|
||||
avalon_hier = []
|
||||
for item in values:
|
||||
value = item["value"]
|
||||
|
|
@ -2173,8 +2205,25 @@ class SyncToAvalonEvent(BaseEvent):
|
|||
|
||||
if convert_type:
|
||||
value = convert_type(value)
|
||||
|
||||
if key in FPS_KEYS:
|
||||
try:
|
||||
value = convert_to_fps(value)
|
||||
except InvalidFpsValue:
|
||||
invalid_fps_items.append((entity_id, value))
|
||||
continue
|
||||
entities_dict[entity_id]["hier_attrs"][key] = value
|
||||
|
||||
if invalid_fps_items:
|
||||
fps_msg = (
|
||||
"These entities have invalid fps value in custom attributes"
|
||||
)
|
||||
items = []
|
||||
for entity_id, value in invalid_fps_items:
|
||||
ent_path = self.get_ent_path(entity_id)
|
||||
items.append("{} - \"{}\"".format(ent_path, value))
|
||||
self.report_items["error"][fps_msg] = items
|
||||
|
||||
# Get dictionary with not None hierarchical values to pull to childs
|
||||
project_values = {}
|
||||
for key, value in (
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from openpype_modules.ftrack.lib import (
|
|||
CUST_ATTR_TOOLS,
|
||||
CUST_ATTR_APPLICATIONS,
|
||||
CUST_ATTR_INTENT,
|
||||
FPS_KEYS,
|
||||
|
||||
default_custom_attributes_definition,
|
||||
app_definitions_from_app_manager,
|
||||
|
|
@ -519,20 +520,28 @@ class CustomAttributes(BaseAction):
|
|||
self.show_message(event, msg)
|
||||
|
||||
def process_attribute(self, data):
|
||||
existing_attrs = self.session.query(
|
||||
"CustomAttributeConfiguration"
|
||||
).all()
|
||||
existing_attrs = self.session.query((
|
||||
"select is_hierarchical, key, type, entity_type, object_type_id"
|
||||
" from CustomAttributeConfiguration"
|
||||
)).all()
|
||||
matching = []
|
||||
is_hierarchical = data.get("is_hierarchical", False)
|
||||
for attr in existing_attrs:
|
||||
if (
|
||||
attr["key"] != data["key"] or
|
||||
attr["type"]["name"] != data["type"]["name"]
|
||||
is_hierarchical != attr["is_hierarchical"]
|
||||
or attr["key"] != data["key"]
|
||||
):
|
||||
continue
|
||||
|
||||
if data.get("is_hierarchical") is True:
|
||||
if attr["is_hierarchical"] is True:
|
||||
matching.append(attr)
|
||||
if attr["type"]["name"] != data["type"]["name"]:
|
||||
if data["key"] in FPS_KEYS and attr["type"]["name"] == "text":
|
||||
self.log.info("Kept 'fps' as text custom attribute.")
|
||||
return
|
||||
continue
|
||||
|
||||
if is_hierarchical:
|
||||
matching.append(attr)
|
||||
|
||||
elif "object_type_id" in data:
|
||||
if (
|
||||
attr["entity_type"] == data["entity_type"] and
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ from .constants import (
|
|||
CUST_ATTR_GROUP,
|
||||
CUST_ATTR_TOOLS,
|
||||
CUST_ATTR_APPLICATIONS,
|
||||
CUST_ATTR_INTENT
|
||||
CUST_ATTR_INTENT,
|
||||
FPS_KEYS
|
||||
)
|
||||
from .settings import (
|
||||
get_ftrack_event_mongo_info
|
||||
|
|
@ -30,6 +31,8 @@ __all__ = (
|
|||
"CUST_ATTR_GROUP",
|
||||
"CUST_ATTR_TOOLS",
|
||||
"CUST_ATTR_APPLICATIONS",
|
||||
"CUST_ATTR_INTENT",
|
||||
"FPS_KEYS",
|
||||
|
||||
"get_ftrack_event_mongo_info",
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ import re
|
|||
import json
|
||||
import collections
|
||||
import copy
|
||||
import numbers
|
||||
|
||||
import six
|
||||
|
||||
from avalon.api import AvalonMongoDB
|
||||
|
||||
|
|
@ -14,7 +17,7 @@ from openpype.api import (
|
|||
)
|
||||
from openpype.lib import ApplicationManager
|
||||
|
||||
from .constants import CUST_ATTR_ID_KEY
|
||||
from .constants import CUST_ATTR_ID_KEY, FPS_KEYS
|
||||
from .custom_attributes import get_openpype_attr, query_custom_attributes
|
||||
|
||||
from bson.objectid import ObjectId
|
||||
|
|
@ -33,6 +36,106 @@ CURRENT_DOC_SCHEMAS = {
|
|||
}
|
||||
|
||||
|
||||
class InvalidFpsValue(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def is_string_number(value):
|
||||
"""Can string value be converted to number (float)."""
|
||||
if not isinstance(value, six.string_types):
|
||||
raise TypeError("Expected {} got {}".format(
|
||||
", ".join(str(t) for t in six.string_types), str(type(value))
|
||||
))
|
||||
if value == ".":
|
||||
return False
|
||||
|
||||
if value.startswith("."):
|
||||
value = "0" + value
|
||||
elif value.endswith("."):
|
||||
value = value + "0"
|
||||
|
||||
if re.match(r"^\d+(\.\d+)?$", value) is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def convert_to_fps(source_value):
|
||||
"""Convert value into fps value.
|
||||
|
||||
Non string values are kept untouched. String is tried to convert.
|
||||
Valid values:
|
||||
"1000"
|
||||
"1000.05"
|
||||
"1000,05"
|
||||
",05"
|
||||
".05"
|
||||
"1000,"
|
||||
"1000."
|
||||
"1000/1000"
|
||||
"1000.05/1000"
|
||||
"1000/1000.05"
|
||||
"1000.05/1000.05"
|
||||
"1000,05/1000"
|
||||
"1000/1000,05"
|
||||
"1000,05/1000,05"
|
||||
|
||||
Invalid values:
|
||||
"/"
|
||||
"/1000"
|
||||
"1000/"
|
||||
","
|
||||
"."
|
||||
...any other string
|
||||
|
||||
Returns:
|
||||
float: Converted value.
|
||||
|
||||
Raises:
|
||||
InvalidFpsValue: When value can't be converted to float.
|
||||
"""
|
||||
if not isinstance(source_value, six.string_types):
|
||||
if isinstance(source_value, numbers.Number):
|
||||
return float(source_value)
|
||||
return source_value
|
||||
|
||||
value = source_value.strip().replace(",", ".")
|
||||
if not value:
|
||||
raise InvalidFpsValue("Got empty value")
|
||||
|
||||
subs = value.split("/")
|
||||
if len(subs) == 1:
|
||||
str_value = subs[0]
|
||||
if not is_string_number(str_value):
|
||||
raise InvalidFpsValue(
|
||||
"Value \"{}\" can't be converted to number.".format(value)
|
||||
)
|
||||
return float(str_value)
|
||||
|
||||
elif len(subs) == 2:
|
||||
divident, divisor = subs
|
||||
if not divident or not is_string_number(divident):
|
||||
raise InvalidFpsValue(
|
||||
"Divident value \"{}\" can't be converted to number".format(
|
||||
divident
|
||||
)
|
||||
)
|
||||
|
||||
if not divisor or not is_string_number(divisor):
|
||||
raise InvalidFpsValue(
|
||||
"Divisor value \"{}\" can't be converted to number".format(
|
||||
divident
|
||||
)
|
||||
)
|
||||
divisor_float = float(divisor)
|
||||
if divisor_float == 0.0:
|
||||
raise InvalidFpsValue("Can't divide by zero")
|
||||
return float(divident) / divisor_float
|
||||
|
||||
raise InvalidFpsValue(
|
||||
"Value can't be converted to number \"{}\"".format(source_value)
|
||||
)
|
||||
|
||||
|
||||
def create_chunks(iterable, chunk_size=None):
|
||||
"""Separate iterable into multiple chunks by size.
|
||||
|
||||
|
|
@ -980,6 +1083,7 @@ class SyncEntitiesFactory:
|
|||
sync_ids
|
||||
)
|
||||
|
||||
invalid_fps_items = []
|
||||
for item in items:
|
||||
entity_id = item["entity_id"]
|
||||
attr_id = item["configuration_id"]
|
||||
|
|
@ -992,8 +1096,24 @@ class SyncEntitiesFactory:
|
|||
value = item["value"]
|
||||
if convert_type:
|
||||
value = convert_type(value)
|
||||
|
||||
if key in FPS_KEYS:
|
||||
try:
|
||||
value = convert_to_fps(value)
|
||||
except InvalidFpsValue:
|
||||
invalid_fps_items.append((entity_id, value))
|
||||
self.entities_dict[entity_id][store_key][key] = value
|
||||
|
||||
if invalid_fps_items:
|
||||
fps_msg = (
|
||||
"These entities have invalid fps value in custom attributes"
|
||||
)
|
||||
items = []
|
||||
for entity_id, value in invalid_fps_items:
|
||||
ent_path = self.get_ent_path(entity_id)
|
||||
items.append("{} - \"{}\"".format(ent_path, value))
|
||||
self.report_items["error"][fps_msg] = items
|
||||
|
||||
# process hierarchical attributes
|
||||
self.set_hierarchical_attribute(
|
||||
hier_attrs, sync_ids, cust_attr_type_name_by_id
|
||||
|
|
@ -1026,8 +1146,15 @@ class SyncEntitiesFactory:
|
|||
if key.startswith("avalon_"):
|
||||
store_key = "avalon_attrs"
|
||||
|
||||
default_value = attr["default"]
|
||||
if key in FPS_KEYS:
|
||||
try:
|
||||
default_value = convert_to_fps(default_value)
|
||||
except InvalidFpsValue:
|
||||
pass
|
||||
|
||||
self.entities_dict[self.ft_project_id][store_key][key] = (
|
||||
attr["default"]
|
||||
default_value
|
||||
)
|
||||
|
||||
# Add attribute ids to entities dictionary
|
||||
|
|
@ -1069,6 +1196,7 @@ class SyncEntitiesFactory:
|
|||
True
|
||||
)
|
||||
|
||||
invalid_fps_items = []
|
||||
avalon_hier = []
|
||||
for item in items:
|
||||
value = item["value"]
|
||||
|
|
@ -1088,6 +1216,13 @@ class SyncEntitiesFactory:
|
|||
|
||||
entity_id = item["entity_id"]
|
||||
key = attribute_key_by_id[attr_id]
|
||||
if key in FPS_KEYS:
|
||||
try:
|
||||
value = convert_to_fps(value)
|
||||
except InvalidFpsValue:
|
||||
invalid_fps_items.append((entity_id, value))
|
||||
continue
|
||||
|
||||
if key.startswith("avalon_"):
|
||||
store_key = "avalon_attrs"
|
||||
avalon_hier.append(key)
|
||||
|
|
@ -1095,6 +1230,16 @@ class SyncEntitiesFactory:
|
|||
store_key = "hier_attrs"
|
||||
self.entities_dict[entity_id][store_key][key] = value
|
||||
|
||||
if invalid_fps_items:
|
||||
fps_msg = (
|
||||
"These entities have invalid fps value in custom attributes"
|
||||
)
|
||||
items = []
|
||||
for entity_id, value in invalid_fps_items:
|
||||
ent_path = self.get_ent_path(entity_id)
|
||||
items.append("{} - \"{}\"".format(ent_path, value))
|
||||
self.report_items["error"][fps_msg] = items
|
||||
|
||||
# Get dictionary with not None hierarchical values to pull to childs
|
||||
top_id = self.ft_project_id
|
||||
project_values = {}
|
||||
|
|
|
|||
|
|
@ -12,3 +12,9 @@ CUST_ATTR_APPLICATIONS = "applications"
|
|||
CUST_ATTR_TOOLS = "tools_env"
|
||||
# Intent custom attribute name
|
||||
CUST_ATTR_INTENT = "intent"
|
||||
|
||||
FPS_KEYS = {
|
||||
"fps",
|
||||
# For development purposes
|
||||
"fps_string"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -399,15 +399,6 @@ class CreatedInstance:
|
|||
self._data["active"] = data.get("active", True)
|
||||
self._data["creator_identifier"] = creator.identifier
|
||||
|
||||
# QUESTION handle version of instance here or in creator?
|
||||
version = None
|
||||
if not new:
|
||||
version = data.get("version")
|
||||
|
||||
if version is None:
|
||||
version = 1
|
||||
self._data["version"] = version
|
||||
|
||||
# Pop from source data all keys that are defined in `_data` before
|
||||
# this moment and through their values away
|
||||
# - they should be the same and if are not then should not change
|
||||
|
|
|
|||
|
|
@ -34,7 +34,12 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin):
|
|||
self.log.info("subset {}".format(instance.data['subset']))
|
||||
|
||||
# skip crypto passes.
|
||||
if 'crypto' in instance.data['subset']:
|
||||
# TODO: This is just a quick fix and has its own side-effects - it is
|
||||
# affecting every subset name with `crypto` in its name.
|
||||
# This must be solved properly, maybe using tags on
|
||||
# representation that can be determined much earlier and
|
||||
# with better precision.
|
||||
if 'crypto' in instance.data['subset'].lower():
|
||||
self.log.info("Skipping crypto passes.")
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -1171,6 +1171,9 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
self.log.debug("input_width: `{}`".format(input_width))
|
||||
self.log.debug("input_height: `{}`".format(input_height))
|
||||
|
||||
reformat_in_baking = bool("reformated" in new_repre["tags"])
|
||||
self.log.debug("reformat_in_baking: `{}`".format(reformat_in_baking))
|
||||
|
||||
# Use instance resolution if output definition has not set it.
|
||||
if output_width is None or output_height is None:
|
||||
output_width = temp_data["resolution_width"]
|
||||
|
|
@ -1182,6 +1185,17 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
output_width = input_width
|
||||
output_height = input_height
|
||||
|
||||
if reformat_in_baking:
|
||||
self.log.debug((
|
||||
"Using resolution from input. It is already "
|
||||
"reformated from baking process"
|
||||
))
|
||||
output_width = input_width
|
||||
output_height = input_height
|
||||
pixel_aspect = 1
|
||||
new_repre["resolutionWidth"] = input_width
|
||||
new_repre["resolutionHeight"] = input_height
|
||||
|
||||
output_width = int(output_width)
|
||||
output_height = int(output_height)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,15 @@ import sys
|
|||
import logging
|
||||
|
||||
# Pipeline imports
|
||||
from avalon import api, io, pipeline
|
||||
from avalon import api, io
|
||||
import avalon.fusion
|
||||
|
||||
# Config imports
|
||||
import openpype.lib as pype
|
||||
import openpype.hosts.fusion.lib as fusion_lib
|
||||
|
||||
from openpype.lib.avalon_context import get_workdir_from_session
|
||||
|
||||
log = logging.getLogger("Update Slap Comp")
|
||||
|
||||
self = sys.modules[__name__]
|
||||
|
|
@ -44,16 +46,6 @@ def _format_version_folder(folder):
|
|||
return version_folder
|
||||
|
||||
|
||||
def _get_work_folder(session):
|
||||
"""Convenience function to get the work folder path of the current asset"""
|
||||
|
||||
# Get new filename, create path based on asset and work template
|
||||
template_work = self._project["config"]["template"]["work"]
|
||||
work_path = pipeline._format_work_template(template_work, session)
|
||||
|
||||
return os.path.normpath(work_path)
|
||||
|
||||
|
||||
def _get_fusion_instance():
|
||||
fusion = getattr(sys.modules["__main__"], "fusion", None)
|
||||
if fusion is None:
|
||||
|
|
@ -72,7 +64,7 @@ def _format_filepath(session):
|
|||
asset = session["AVALON_ASSET"]
|
||||
|
||||
# Save updated slap comp
|
||||
work_path = _get_work_folder(session)
|
||||
work_path = get_workdir_from_session(session)
|
||||
walk_to_dir = os.path.join(work_path, "scenes", "slapcomp")
|
||||
slapcomp_dir = os.path.abspath(walk_to_dir)
|
||||
|
||||
|
|
@ -103,7 +95,7 @@ def _update_savers(comp, session):
|
|||
None
|
||||
"""
|
||||
|
||||
new_work = _get_work_folder(session)
|
||||
new_work = get_workdir_from_session(session)
|
||||
renders = os.path.join(new_work, "renders")
|
||||
version_folder = _format_version_folder(renders)
|
||||
renders_version = os.path.join(renders, version_folder)
|
||||
|
|
|
|||
|
|
@ -589,6 +589,12 @@
|
|||
12,
|
||||
255
|
||||
],
|
||||
"vrayscene_layer": [
|
||||
255,
|
||||
150,
|
||||
12,
|
||||
255
|
||||
],
|
||||
"yeticache": [
|
||||
99,
|
||||
206,
|
||||
|
|
|
|||
|
|
@ -116,13 +116,42 @@
|
|||
"baking": {
|
||||
"filter": {
|
||||
"task_types": [],
|
||||
"families": []
|
||||
"families": [],
|
||||
"sebsets": []
|
||||
},
|
||||
"extension": "mov",
|
||||
"viewer_process_override": "",
|
||||
"bake_viewer_process": true,
|
||||
"bake_viewer_input_process": true,
|
||||
"add_tags": []
|
||||
"add_tags": [],
|
||||
"reformat_node_add": false,
|
||||
"reformat_node_config": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "type",
|
||||
"value": "to format"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "format",
|
||||
"value": "HD_1080"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "filter",
|
||||
"value": "Lanczos6"
|
||||
},
|
||||
{
|
||||
"type": "bool",
|
||||
"name": "black_outside",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"type": "bool",
|
||||
"name": "pbb",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -584,8 +584,9 @@ class DictConditionalEntity(ItemEntity):
|
|||
|
||||
self.enum_entity.update_default_value(enum_value)
|
||||
for children_by_key in self.non_gui_children.values():
|
||||
value_copy = copy.deepcopy(value)
|
||||
for key, child_obj in children_by_key.items():
|
||||
child_value = value.get(key, NOT_SET)
|
||||
child_value = value_copy.get(key, NOT_SET)
|
||||
child_obj.update_default_value(child_value)
|
||||
|
||||
def update_studio_value(self, value):
|
||||
|
|
@ -620,8 +621,9 @@ class DictConditionalEntity(ItemEntity):
|
|||
|
||||
self.enum_entity.update_studio_value(enum_value)
|
||||
for children_by_key in self.non_gui_children.values():
|
||||
value_copy = copy.deepcopy(value)
|
||||
for key, child_obj in children_by_key.items():
|
||||
child_value = value.get(key, NOT_SET)
|
||||
child_value = value_copy.get(key, NOT_SET)
|
||||
child_obj.update_studio_value(child_value)
|
||||
|
||||
def update_project_value(self, value):
|
||||
|
|
@ -656,8 +658,9 @@ class DictConditionalEntity(ItemEntity):
|
|||
|
||||
self.enum_entity.update_project_value(enum_value)
|
||||
for children_by_key in self.non_gui_children.values():
|
||||
value_copy = copy.deepcopy(value)
|
||||
for key, child_obj in children_by_key.items():
|
||||
child_value = value.get(key, NOT_SET)
|
||||
child_value = value_copy.get(key, NOT_SET)
|
||||
child_obj.update_project_value(child_value)
|
||||
|
||||
def _discard_changes(self, on_change_trigger):
|
||||
|
|
|
|||
|
|
@ -75,6 +75,11 @@
|
|||
"label": "Vray Proxy:",
|
||||
"key": "vrayproxy"
|
||||
},
|
||||
{
|
||||
"type": "color",
|
||||
"label": "Vray Scene:",
|
||||
"key": "vrayscene_layer"
|
||||
},
|
||||
{
|
||||
"type": "color",
|
||||
"label": "Yeti Cache:",
|
||||
|
|
|
|||
|
|
@ -195,6 +195,12 @@
|
|||
"label": "Families",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"key": "sebsets",
|
||||
"label": "Subsets",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -226,6 +232,121 @@
|
|||
"label": "Add additional tags to representations",
|
||||
"type": "list",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "reformat_node_add",
|
||||
"label": "Add Reformat Node",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"type": "collapsible-wrap",
|
||||
"label": "Reformat Node Knobs",
|
||||
"collapsible": true,
|
||||
"collapsed": false,
|
||||
"children": [
|
||||
{
|
||||
"type": "list",
|
||||
"key": "reformat_node_config",
|
||||
"object_type": {
|
||||
"type": "dict-conditional",
|
||||
"enum_key": "type",
|
||||
"enum_label": "Type",
|
||||
"enum_children": [
|
||||
{
|
||||
"key": "string",
|
||||
"label": "String",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "name",
|
||||
"label": "Name"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "value",
|
||||
"label": "Value"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "bool",
|
||||
"label": "Boolean",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "name",
|
||||
"label": "Name"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "value",
|
||||
"label": "Value"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "number",
|
||||
"label": "Number",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "name",
|
||||
"label": "Name"
|
||||
},
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "value",
|
||||
"label": "Value",
|
||||
"object_types": [
|
||||
{
|
||||
"type": "number",
|
||||
"key": "number",
|
||||
"default": 1,
|
||||
"decimal": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "list_numbers",
|
||||
"label": "2 Numbers",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "name",
|
||||
"label": "Name"
|
||||
},
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "value",
|
||||
"label": "Value",
|
||||
"object_types": [
|
||||
{
|
||||
"type": "number",
|
||||
"key": "x",
|
||||
"default": 1,
|
||||
"decimal": 4
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "y",
|
||||
"default": 1,
|
||||
"decimal": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ from subprocess import Popen
|
|||
|
||||
import ftrack_api
|
||||
from Qt import QtWidgets, QtCore
|
||||
from openpype import style
|
||||
from openpype.api import get_current_project_settings
|
||||
from openpype.lib.avalon_context import update_current_task
|
||||
from openpype.tools.utils.lib import qt_app_context
|
||||
from avalon import io, api, style, schema
|
||||
from avalon import io, api, schema
|
||||
from . import widget, model
|
||||
|
||||
module = sys.modules[__name__]
|
||||
|
|
@ -463,12 +465,12 @@ class Window(QtWidgets.QDialog):
|
|||
return
|
||||
task_name = task_model.itemData(index)[0]
|
||||
try:
|
||||
api.update_current_task(task=task_name, asset=asset_name)
|
||||
update_current_task(task=task_name, asset=asset_name)
|
||||
self.open_app()
|
||||
|
||||
finally:
|
||||
if origin_task is not None and origin_asset is not None:
|
||||
api.update_current_task(
|
||||
update_current_task(
|
||||
task=origin_task, asset=origin_asset
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ from collections import defaultdict
|
|||
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
# TODO: expose this better in avalon core
|
||||
from avalon.tools import lib
|
||||
from avalon.tools.models import TreeModel
|
||||
from openpype.tools.utils.models import TreeModel
|
||||
from openpype.tools.utils.lib import (
|
||||
preserve_expanded_rows,
|
||||
preserve_selection,
|
||||
)
|
||||
|
||||
from .models import (
|
||||
AssetModel,
|
||||
|
|
@ -88,8 +90,8 @@ class AssetOutliner(QtWidgets.QWidget):
|
|||
"""Add all items from the current scene"""
|
||||
|
||||
items = []
|
||||
with lib.preserve_expanded_rows(self.view):
|
||||
with lib.preserve_selection(self.view):
|
||||
with preserve_expanded_rows(self.view):
|
||||
with preserve_selection(self.view):
|
||||
self.clear()
|
||||
nodes = commands.get_all_asset_nodes()
|
||||
items = commands.create_items_from_nodes(nodes)
|
||||
|
|
@ -100,8 +102,8 @@ class AssetOutliner(QtWidgets.QWidget):
|
|||
def get_selected_assets(self):
|
||||
"""Add all selected items from the current scene"""
|
||||
|
||||
with lib.preserve_expanded_rows(self.view):
|
||||
with lib.preserve_selection(self.view):
|
||||
with preserve_expanded_rows(self.view):
|
||||
with preserve_selection(self.view):
|
||||
self.clear()
|
||||
nodes = commands.get_selected_nodes()
|
||||
items = commands.create_items_from_nodes(nodes)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from avalon import api, io, style, schema
|
|||
from avalon.vendor import qtawesome
|
||||
|
||||
from avalon.lib import HeroVersionType
|
||||
from avalon.tools.models import TreeModel, Item
|
||||
from openpype.tools.utils.models import TreeModel, Item
|
||||
|
||||
from .lib import (
|
||||
get_site_icons,
|
||||
|
|
|
|||
|
|
@ -7,9 +7,13 @@ from Qt import QtWidgets, QtCore
|
|||
from avalon import io, api, style
|
||||
from avalon.vendor import qtawesome
|
||||
from avalon.lib import HeroVersionType
|
||||
from avalon.tools import lib as tools_lib
|
||||
|
||||
from openpype.modules import ModulesManager
|
||||
from openpype.tools.utils.lib import (
|
||||
get_progress_for_repre,
|
||||
iter_model_rows,
|
||||
format_version
|
||||
)
|
||||
|
||||
from .switch_dialog import SwitchAssetDialog
|
||||
from .model import InventoryModel
|
||||
|
|
@ -20,12 +24,12 @@ DEFAULT_COLOR = "#fb9c15"
|
|||
log = logging.getLogger("SceneInventory")
|
||||
|
||||
|
||||
class SceneInvetoryView(QtWidgets.QTreeView):
|
||||
class SceneInventoryView(QtWidgets.QTreeView):
|
||||
data_changed = QtCore.Signal()
|
||||
hierarchy_view_changed = QtCore.Signal(bool)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(SceneInvetoryView, self).__init__(parent=parent)
|
||||
super(SceneInventoryView, self).__init__(parent=parent)
|
||||
|
||||
# view settings
|
||||
self.setIndentation(12)
|
||||
|
|
@ -373,7 +377,7 @@ class SceneInvetoryView(QtWidgets.QTreeView):
|
|||
if not repre_doc:
|
||||
continue
|
||||
|
||||
progress = tools_lib.get_progress_for_repre(
|
||||
progress = get_progress_for_repre(
|
||||
repre_doc,
|
||||
active_site,
|
||||
remote_site
|
||||
|
|
@ -544,7 +548,7 @@ class SceneInvetoryView(QtWidgets.QTreeView):
|
|||
"toggle": selection_model.Toggle,
|
||||
}[options.get("mode", "select")]
|
||||
|
||||
for item in tools_lib.iter_model_rows(model, 0):
|
||||
for item in iter_model_rows(model, 0):
|
||||
item = item.data(InventoryModel.ItemRole)
|
||||
if item.get("isGroupNode"):
|
||||
continue
|
||||
|
|
@ -704,7 +708,7 @@ class SceneInvetoryView(QtWidgets.QTreeView):
|
|||
labels = []
|
||||
for version in all_versions:
|
||||
is_hero = version["type"] == "hero_version"
|
||||
label = tools_lib.format_version(version["name"], is_hero)
|
||||
label = format_version(version["name"], is_hero)
|
||||
labels.append(label)
|
||||
versions_by_label[label] = version["name"]
|
||||
|
||||
|
|
@ -792,3 +796,40 @@ class SceneInvetoryView(QtWidgets.QTreeView):
|
|||
).format(version_str)
|
||||
dialog.setText(msg)
|
||||
dialog.exec_()
|
||||
|
||||
def update_all(self):
|
||||
"""Update all items that are currently 'outdated' in the view"""
|
||||
# Get the source model through the proxy model
|
||||
model = self.model().sourceModel()
|
||||
|
||||
# Get all items from outdated groups
|
||||
outdated_items = []
|
||||
for index in iter_model_rows(model,
|
||||
column=0,
|
||||
include_root=False):
|
||||
item = index.data(model.ItemRole)
|
||||
|
||||
if not item.get("isGroupNode"):
|
||||
continue
|
||||
|
||||
# Only the group nodes contain the "highest_version" data and as
|
||||
# such we find only the groups and take its children.
|
||||
if not model.outdated(item):
|
||||
continue
|
||||
|
||||
# Collect all children which we want to update
|
||||
children = item.children()
|
||||
outdated_items.extend(children)
|
||||
|
||||
if not outdated_items:
|
||||
log.info("Nothing to update.")
|
||||
return
|
||||
|
||||
# Trigger update to latest
|
||||
for item in outdated_items:
|
||||
try:
|
||||
api.update(item, -1)
|
||||
except AssertionError:
|
||||
self._show_version_error_dialog(None, [item])
|
||||
log.warning("Update failed", exc_info=True)
|
||||
self.data_changed.emit()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ from .model import (
|
|||
InventoryModel,
|
||||
FilterProxyModel
|
||||
)
|
||||
from .view import SceneInvetoryView
|
||||
from .view import SceneInventoryView
|
||||
|
||||
|
||||
module = sys.modules[__name__]
|
||||
|
|
@ -54,14 +54,21 @@ class SceneInventoryWindow(QtWidgets.QDialog):
|
|||
outdated_only_checkbox.setToolTip("Show outdated files only")
|
||||
outdated_only_checkbox.setChecked(False)
|
||||
|
||||
icon = qtawesome.icon("fa.arrow-up", color="white")
|
||||
update_all_button = QtWidgets.QPushButton(self)
|
||||
update_all_button.setToolTip("Update all outdated to latest version")
|
||||
update_all_button.setIcon(icon)
|
||||
|
||||
icon = qtawesome.icon("fa.refresh", color="white")
|
||||
refresh_button = QtWidgets.QPushButton(self)
|
||||
update_all_button.setToolTip("Refresh")
|
||||
refresh_button.setIcon(icon)
|
||||
|
||||
control_layout = QtWidgets.QHBoxLayout()
|
||||
control_layout.addWidget(filter_label)
|
||||
control_layout.addWidget(text_filter)
|
||||
control_layout.addWidget(outdated_only_checkbox)
|
||||
control_layout.addWidget(update_all_button)
|
||||
control_layout.addWidget(refresh_button)
|
||||
|
||||
# endregion control
|
||||
|
|
@ -73,7 +80,7 @@ class SceneInventoryWindow(QtWidgets.QDialog):
|
|||
proxy.setDynamicSortFilter(True)
|
||||
proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||
|
||||
view = SceneInvetoryView(self)
|
||||
view = SceneInventoryView(self)
|
||||
view.setModel(proxy)
|
||||
|
||||
# set some nice default widths for the view
|
||||
|
|
@ -98,11 +105,13 @@ class SceneInventoryWindow(QtWidgets.QDialog):
|
|||
self._on_outdated_state_change
|
||||
)
|
||||
view.hierarchy_view_changed.connect(
|
||||
self._on_hiearchy_view_change
|
||||
self._on_hierarchy_view_change
|
||||
)
|
||||
view.data_changed.connect(self.refresh)
|
||||
refresh_button.clicked.connect(self.refresh)
|
||||
update_all_button.clicked.connect(self._on_update_all)
|
||||
|
||||
self._update_all_button = update_all_button
|
||||
self._outdated_only_checkbox = outdated_only_checkbox
|
||||
self._view = view
|
||||
self._model = model
|
||||
|
|
@ -146,7 +155,7 @@ class SceneInventoryWindow(QtWidgets.QDialog):
|
|||
kwargs["selected"] = self._view._selected
|
||||
self._model.refresh(**kwargs)
|
||||
|
||||
def _on_hiearchy_view_change(self, enabled):
|
||||
def _on_hierarchy_view_change(self, enabled):
|
||||
self._proxy.set_hierarchy_view(enabled)
|
||||
self._model.set_hierarchy_view(enabled)
|
||||
|
||||
|
|
@ -158,6 +167,9 @@ class SceneInventoryWindow(QtWidgets.QDialog):
|
|||
self._outdated_only_checkbox.isChecked()
|
||||
)
|
||||
|
||||
def _on_update_all(self):
|
||||
self._view.update_all()
|
||||
|
||||
|
||||
def show(root=None, debug=False, parent=None, items=None):
|
||||
"""Display Scene Inventory GUI
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import sys
|
|||
|
||||
import openpype
|
||||
import pyblish.api
|
||||
from openpype.tools.utils.host_tools import show_publish
|
||||
|
||||
|
||||
def main(env):
|
||||
from avalon.tools import publish
|
||||
# Registers pype's Global pyblish plugins
|
||||
openpype.install()
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ def main(env):
|
|||
continue
|
||||
pyblish.api.register_plugin_path(path)
|
||||
|
||||
return publish.show()
|
||||
return show_publish()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from .lib import (
|
|||
get_warning_pixmap,
|
||||
set_style_property,
|
||||
DynamicQThread,
|
||||
qt_app_context,
|
||||
)
|
||||
|
||||
from .models import (
|
||||
|
|
@ -39,6 +40,7 @@ __all__ = (
|
|||
"get_warning_pixmap",
|
||||
"set_style_property",
|
||||
"DynamicQThread",
|
||||
"qt_app_context",
|
||||
|
||||
"RecursiveSortFilterProxyModel",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
It is possible to create `HostToolsHelper` in host implementation or
|
||||
use singleton approach with global functions (using helper anyway).
|
||||
"""
|
||||
|
||||
import os
|
||||
import avalon.api
|
||||
import pyblish.api
|
||||
from .lib import qt_app_context
|
||||
|
||||
|
||||
|
|
@ -196,10 +197,29 @@ class HostToolsHelper:
|
|||
library_loader_tool.refresh()
|
||||
|
||||
def show_publish(self, parent=None):
|
||||
"""Publish UI."""
|
||||
from avalon.tools import publish
|
||||
"""Try showing the most desirable publish GUI
|
||||
|
||||
publish.show(parent)
|
||||
This function cycles through the currently registered
|
||||
graphical user interfaces, if any, and presents it to
|
||||
the user.
|
||||
"""
|
||||
|
||||
pyblish_show = self._discover_pyblish_gui()
|
||||
return pyblish_show(parent)
|
||||
|
||||
def _discover_pyblish_gui(self):
|
||||
"""Return the most desirable of the currently registered GUIs"""
|
||||
# Prefer last registered
|
||||
guis = list(reversed(pyblish.api.registered_guis()))
|
||||
for gui in guis:
|
||||
try:
|
||||
gui = __import__(gui).show
|
||||
except (ImportError, AttributeError):
|
||||
continue
|
||||
else:
|
||||
return gui
|
||||
|
||||
raise ImportError("No Pyblish GUI found")
|
||||
|
||||
def get_look_assigner_tool(self, parent):
|
||||
"""Create, cache and return look assigner tool window."""
|
||||
|
|
@ -394,3 +414,11 @@ def show_publish(parent=None):
|
|||
|
||||
def show_experimental_tools_dialog(parent=None):
|
||||
_SingletonPoint.show_tool_by_name("experimental_tools", parent)
|
||||
|
||||
|
||||
def get_pyblish_icon():
|
||||
pyblish_dir = os.path.abspath(os.path.dirname(pyblish.api.__file__))
|
||||
icon_path = os.path.join(pyblish_dir, "icons", "logo-32x32.svg")
|
||||
if os.path.exists(icon_path):
|
||||
return icon_path
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ from openpype.lib import (
|
|||
create_workdir_extra_folders,
|
||||
get_system_general_anatomy_data
|
||||
)
|
||||
from openpype.lib.avalon_context import (
|
||||
update_current_task,
|
||||
compute_session_changes
|
||||
)
|
||||
from .model import FilesModel
|
||||
from .view import FilesView
|
||||
|
||||
|
|
@ -667,7 +671,7 @@ class FilesWidget(QtWidgets.QWidget):
|
|||
session["AVALON_APP"],
|
||||
project_name=session["AVALON_PROJECT"]
|
||||
)
|
||||
changes = pipeline.compute_session_changes(
|
||||
changes = compute_session_changes(
|
||||
session,
|
||||
asset=self._get_asset_doc(),
|
||||
task=self._task_name,
|
||||
|
|
@ -681,7 +685,7 @@ class FilesWidget(QtWidgets.QWidget):
|
|||
"""Enter the asset and task session currently selected"""
|
||||
|
||||
session = api.Session.copy()
|
||||
changes = pipeline.compute_session_changes(
|
||||
changes = compute_session_changes(
|
||||
session,
|
||||
asset=self._get_asset_doc(),
|
||||
task=self._task_name,
|
||||
|
|
@ -692,7 +696,7 @@ class FilesWidget(QtWidgets.QWidget):
|
|||
# to avoid any unwanted Task Changed callbacks to be triggered.
|
||||
return
|
||||
|
||||
api.update_current_task(
|
||||
update_current_task(
|
||||
asset=self._get_asset_doc(),
|
||||
task=self._task_name,
|
||||
template_key=self.template_key
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import os
|
||||
import logging
|
||||
|
||||
from Qt import QtCore, QtGui
|
||||
from Qt import QtCore
|
||||
|
||||
from avalon import style
|
||||
from avalon.vendor import qtawesome
|
||||
from avalon.tools.models import TreeModel, Item
|
||||
from openpype.tools.utils.models import TreeModel, Item
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue