mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-02 08:54:53 +01:00
Merge remote-tracking branch 'origin/develop' into feature/PYPE-316-muster-render-support
This commit is contained in:
commit
aca96d0d97
17 changed files with 501 additions and 168 deletions
|
|
@ -46,8 +46,6 @@ from .lib import (
|
|||
get_data_hierarchical_attr
|
||||
)
|
||||
|
||||
from .widgets.message_window import message
|
||||
|
||||
__all__ = [
|
||||
# plugin classes
|
||||
"Extractor",
|
||||
|
|
@ -89,7 +87,4 @@ __all__ = [
|
|||
"Colorspace",
|
||||
"Dataflow",
|
||||
|
||||
# QtWidgets
|
||||
"message"
|
||||
|
||||
]
|
||||
|
|
|
|||
|
|
@ -85,6 +85,12 @@ class DeleteAsset(BaseAction):
|
|||
'type': 'asset',
|
||||
'name': entity['name']
|
||||
})
|
||||
|
||||
if av_entity is None:
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'Didn\'t found assets in avalon'
|
||||
}
|
||||
|
||||
asset_label = {
|
||||
'type': 'label',
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ from pypeapp import Logger
|
|||
|
||||
log = Logger().get_logger(__name__, "nuke")
|
||||
|
||||
# log = api.Logger.getLogger(__name__, "nuke")
|
||||
|
||||
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
|
||||
|
||||
PARENT_DIR = os.path.dirname(__file__)
|
||||
|
|
@ -38,9 +36,8 @@ LOAD_PATH = os.path.join(PLUGINS_DIR, "nuke", "load")
|
|||
CREATE_PATH = os.path.join(PLUGINS_DIR, "nuke", "create")
|
||||
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "nuke", "inventory")
|
||||
|
||||
self = sys.modules[__name__]
|
||||
self.nLogger = None
|
||||
|
||||
# registering pyblish gui regarding settings in presets
|
||||
if os.getenv("PYBLISH_GUI", None):
|
||||
pyblish.register_gui(os.getenv("PYBLISH_GUI", None))
|
||||
|
||||
|
|
@ -66,6 +63,7 @@ class NukeHandler(logging.Handler):
|
|||
"fatal",
|
||||
"error"
|
||||
]:
|
||||
msg = self.format(record)
|
||||
nuke.message(msg)
|
||||
|
||||
|
||||
|
|
@ -77,9 +75,6 @@ if nuke_handler.get_name() \
|
|||
logging.getLogger().addHandler(nuke_handler)
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
if not self.nLogger:
|
||||
self.nLogger = Logger
|
||||
|
||||
|
||||
def reload_config():
|
||||
"""Attempt to reload pipeline at run-time.
|
||||
|
|
@ -157,7 +152,7 @@ def uninstall():
|
|||
|
||||
def on_pyblish_instance_toggled(instance, old_value, new_value):
|
||||
"""Toggle node passthrough states on instance toggles."""
|
||||
self.log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
|
||||
log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
|
||||
instance, old_value, new_value))
|
||||
|
||||
from avalon.nuke import (
|
||||
|
|
|
|||
|
|
@ -29,6 +29,53 @@ def onScriptLoad():
|
|||
nuke.tcl('load movWriter')
|
||||
|
||||
|
||||
def checkInventoryVersions():
|
||||
"""
|
||||
Actiual version idetifier of Loaded containers
|
||||
|
||||
Any time this function is run it will check all nodes and filter only Loader nodes for its version. It will get all versions from database
|
||||
and check if the node is having actual version. If not then it will color it to red.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# get all Loader nodes by avalon attribute metadata
|
||||
for each in nuke.allNodes():
|
||||
if each.Class() == 'Read':
|
||||
container = avalon.nuke.parse_container(each)
|
||||
|
||||
if container:
|
||||
node = container["_tool"]
|
||||
avalon_knob_data = get_avalon_knob_data(node)
|
||||
|
||||
# get representation from io
|
||||
representation = io.find_one({
|
||||
"type": "representation",
|
||||
"_id": io.ObjectId(avalon_knob_data["representation"])
|
||||
})
|
||||
|
||||
# Get start frame from version data
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
# check the available version and do match
|
||||
# change color of node if not max verion
|
||||
if version.get("name") not in [max_version]:
|
||||
node["tile_color"].setValue(int("0xd84f20ff", 16))
|
||||
else:
|
||||
node["tile_color"].setValue(int("0x4ecd25ff", 16))
|
||||
|
||||
|
||||
def writes_version_sync():
|
||||
try:
|
||||
rootVersion = pype.get_version_from_path(nuke.root().name())
|
||||
|
|
|
|||
170
pype/plugins/nuke/load/load_script_precomp.py
Normal file
170
pype/plugins/nuke/load/load_script_precomp.py
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
from avalon import api, style, io
|
||||
from pype.nuke.lib import get_avalon_knob_data
|
||||
import nuke
|
||||
import os
|
||||
from pype.api import Logger
|
||||
log = Logger().get_logger(__name__, "nuke")
|
||||
|
||||
|
||||
|
||||
class LinkAsGroup(api.Loader):
|
||||
"""Copy the published file to be pasted at the desired location"""
|
||||
|
||||
representations = ["nk"]
|
||||
families = ["*"]
|
||||
|
||||
label = "Load Precomp"
|
||||
order = 10
|
||||
icon = "file"
|
||||
color = style.colors.dark
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
from avalon.nuke import containerise
|
||||
# for k, v in context.items():
|
||||
# log.info("key: `{}`, value: {}\n".format(k, v))
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("startFrame", None)
|
||||
last = version_data.get("endFrame", None)
|
||||
|
||||
# Fallback to asset name when namespace is None
|
||||
if namespace is None:
|
||||
namespace = context['asset']['name']
|
||||
|
||||
file = self.fname.replace("\\", "/")
|
||||
self.log.info("file: {}\n".format(self.fname))
|
||||
|
||||
precomp_name = context["representation"]["context"]["subset"]
|
||||
|
||||
# Set global in point to start frame (if in version.data)
|
||||
start = context["version"]["data"].get("startFrame", None)
|
||||
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["startFrame", "endFrame", "handles",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {
|
||||
"start_frame": start,
|
||||
"fstart": first,
|
||||
"fend": last,
|
||||
"version": vname
|
||||
}
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: context["version"]['data'][k]})
|
||||
data_imprint.update({"objectName": precomp_name})
|
||||
|
||||
# group context is set to precomp, so back up one level.
|
||||
nuke.endGroup()
|
||||
|
||||
# P = nuke.nodes.LiveGroup("file {}".format(file))
|
||||
P = nuke.createNode(
|
||||
"Precomp",
|
||||
"file {}".format(file))
|
||||
|
||||
# Set colorspace defined in version data
|
||||
colorspace = context["version"]["data"].get("colorspace", None)
|
||||
self.log.info("colorspace: {}\n".format(colorspace))
|
||||
|
||||
|
||||
# ['version', 'file', 'reading', 'output', 'useOutput']
|
||||
|
||||
P["name"].setValue("{}_{}".format(name, namespace))
|
||||
P["useOutput"].setValue(True)
|
||||
|
||||
with P:
|
||||
# iterate trough all nodes in group node and find pype writes
|
||||
writes = [n.name() for n in nuke.allNodes()
|
||||
if n.Class() == "Write"
|
||||
if get_avalon_knob_data(n)]
|
||||
|
||||
# create panel for selecting output
|
||||
panel_choices = " ".join(writes)
|
||||
panel_label = "Select write node for output"
|
||||
p = nuke.Panel("Select Write Node")
|
||||
p.addEnumerationPulldown(
|
||||
panel_label, panel_choices)
|
||||
p.show()
|
||||
P["output"].setValue(p.value(panel_label))
|
||||
|
||||
P["tile_color"].setValue(0xff0ff0ff)
|
||||
|
||||
return containerise(
|
||||
node=P,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
|
||||
node = nuke.toNode(container['objectName'])
|
||||
|
||||
root = api.get_representation_path(representation).replace("\\","/")
|
||||
|
||||
# Get start frame from version data
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
updated_dict = {}
|
||||
updated_dict.update({
|
||||
"representation": str(representation["_id"]),
|
||||
"endFrame": version["data"].get("endFrame"),
|
||||
"version": version.get("name"),
|
||||
"colorspace": version["data"].get("colorspace"),
|
||||
"source": version["data"].get("source"),
|
||||
"handles": version["data"].get("handles"),
|
||||
"fps": version["data"].get("fps"),
|
||||
"author": version["data"].get("author"),
|
||||
"outputDir": version["data"].get("outputDir"),
|
||||
})
|
||||
|
||||
# Update the imprinted representation
|
||||
update_container(
|
||||
node,
|
||||
updated_dict
|
||||
)
|
||||
|
||||
node["file"].setValue(root)
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
node["tile_color"].setValue(int("0xd84f20ff", 16))
|
||||
else:
|
||||
node["tile_color"].setValue(int("0xff0ff0ff", 16))
|
||||
|
||||
log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
|
|
@ -128,11 +128,15 @@ class LoadSequence(api.Loader):
|
|||
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["startFrame", "endFrame", "handles",
|
||||
"source", "colorspace", "author", "fps"]
|
||||
"source", "colorspace", "author", "fps", "version"]
|
||||
|
||||
data_imprint = {}
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: context["version"]['data'][k]})
|
||||
if k is 'version':
|
||||
data_imprint.update({k: context["version"]['name']})
|
||||
else:
|
||||
data_imprint.update({k: context["version"]['data'][k]})
|
||||
|
||||
data_imprint.update({"objectName": read_name})
|
||||
|
||||
r["tile_color"].setValue(int("0x4ecd25ff", 16))
|
||||
|
|
|
|||
|
|
@ -55,10 +55,10 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin):
|
|||
instance = context.create_instance(subset)
|
||||
|
||||
instance.data.update({
|
||||
"subset": family + subset,
|
||||
"subset": subset,
|
||||
"asset": asset_name,
|
||||
"label": family + subset,
|
||||
"name": family + subset,
|
||||
"label": subset,
|
||||
"name": subset,
|
||||
"family": family,
|
||||
"families": [family, 'ftrack'],
|
||||
})
|
||||
|
|
@ -74,10 +74,9 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin):
|
|||
collections, remainder = clique.assemble(component['files'])
|
||||
if collections:
|
||||
self.log.debug(collections)
|
||||
range = collections[0].format('{range}')
|
||||
instance.data['startFrame'] = range.split('-')[0]
|
||||
instance.data['endFrame'] = range.split('-')[1]
|
||||
|
||||
instance.data['startFrame'] = component['startFrame']
|
||||
instance.data['endFrame'] = component['endFrame']
|
||||
instance.data['frameRate'] = component['frameRate']
|
||||
|
||||
instance.data["files"].append(component)
|
||||
instance.data["representations"].append(component)
|
||||
|
|
|
|||
|
|
@ -57,19 +57,19 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
|
|||
"name": "thumbnail" # Default component name is "main".
|
||||
}
|
||||
elif comp['preview']:
|
||||
if not instance.data.get('startFrameReview'):
|
||||
instance.data['startFrameReview'] = instance.data['startFrame']
|
||||
if not instance.data.get('endFrameReview'):
|
||||
instance.data['endFrameReview'] = instance.data['endFrame']
|
||||
if not comp.get('startFrameReview'):
|
||||
comp['startFrameReview'] = comp['startFrame']
|
||||
if not comp.get('endFrameReview'):
|
||||
comp['endFrameReview'] = instance.data['endFrame']
|
||||
location = ft_session.query(
|
||||
'Location where name is "ftrack.server"').one()
|
||||
component_data = {
|
||||
# Default component name is "main".
|
||||
"name": "ftrackreview-mp4",
|
||||
"metadata": {'ftr_meta': json.dumps({
|
||||
'frameIn': int(instance.data['startFrameReview']),
|
||||
'frameOut': int(instance.data['endFrameReview']),
|
||||
'frameRate': 25.0})}
|
||||
'frameIn': int(comp['startFrameReview']),
|
||||
'frameOut': int(comp['endFrameReview']),
|
||||
'frameRate': float(comp['frameRate')]})}
|
||||
}
|
||||
else:
|
||||
component_data = {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from pyblish import api as pyblish
|
|||
from pypeapp import Logger
|
||||
from .. import api
|
||||
|
||||
from ..widgets.message_window import message
|
||||
|
||||
import requests
|
||||
|
||||
log = Logger().get_logger(__name__, "premiere")
|
||||
|
|
@ -42,7 +44,7 @@ def request_aport(url_path, data={}):
|
|||
return req
|
||||
|
||||
except Exception as e:
|
||||
api.message(title="Premiere Aport Server",
|
||||
message(title="Premiere Aport Server",
|
||||
message="Before you can run Premiere, start Aport Server. \n Error: {}".format(
|
||||
e),
|
||||
level="critical")
|
||||
|
|
@ -99,7 +101,7 @@ def install():
|
|||
|
||||
# synchronize extensions
|
||||
extensions_sync()
|
||||
api.message(title="pyblish_paths", message=str(reg_paths), level="info")
|
||||
message(title="pyblish_paths", message=str(reg_paths), level="info")
|
||||
|
||||
|
||||
def uninstall():
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ class Window(QtWidgets.QDialog):
|
|||
initialized = False
|
||||
WIDTH = 1100
|
||||
HEIGHT = 500
|
||||
NOT_SELECTED = '< Nothing is selected >'
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(Window, self).__init__(parent=parent)
|
||||
|
|
@ -40,19 +39,9 @@ class Window(QtWidgets.QDialog):
|
|||
# Validators
|
||||
self.valid_parent = False
|
||||
|
||||
# statusbar - added under asset_widget
|
||||
label_message = QtWidgets.QLabel()
|
||||
label_message.setFixedHeight(20)
|
||||
|
||||
# assets widget
|
||||
widget_assets_wrap = QtWidgets.QWidget()
|
||||
widget_assets_wrap.setContentsMargins(0, 0, 0, 0)
|
||||
widget_assets = AssetWidget(self)
|
||||
|
||||
layout_assets = QtWidgets.QVBoxLayout(widget_assets_wrap)
|
||||
layout_assets.addWidget(widget_assets)
|
||||
layout_assets.addWidget(label_message)
|
||||
|
||||
# family widget
|
||||
widget_family = FamilyWidget(self)
|
||||
|
||||
|
|
@ -67,10 +56,10 @@ class Window(QtWidgets.QDialog):
|
|||
QtWidgets.QSizePolicy.Expanding
|
||||
)
|
||||
body.setOrientation(QtCore.Qt.Horizontal)
|
||||
body.addWidget(widget_assets_wrap)
|
||||
body.addWidget(widget_assets)
|
||||
body.addWidget(widget_family)
|
||||
body.addWidget(widget_components)
|
||||
body.setStretchFactor(body.indexOf(widget_assets_wrap), 2)
|
||||
body.setStretchFactor(body.indexOf(widget_assets), 2)
|
||||
body.setStretchFactor(body.indexOf(widget_family), 3)
|
||||
body.setStretchFactor(body.indexOf(widget_components), 5)
|
||||
|
||||
|
|
@ -82,13 +71,10 @@ class Window(QtWidgets.QDialog):
|
|||
# signals
|
||||
widget_assets.selection_changed.connect(self.on_asset_changed)
|
||||
|
||||
self.label_message = label_message
|
||||
self.widget_assets = widget_assets
|
||||
self.widget_family = widget_family
|
||||
self.widget_components = widget_components
|
||||
|
||||
self.echo("Connected to Database")
|
||||
|
||||
# on start
|
||||
self.on_start()
|
||||
|
||||
|
|
@ -131,22 +117,6 @@ class Window(QtWidgets.QDialog):
|
|||
parents.append(parent['name'])
|
||||
return parents
|
||||
|
||||
def echo(self, message):
|
||||
''' Shows message in label that disappear in 5s
|
||||
:param message: Message that will be displayed
|
||||
:type message: str
|
||||
'''
|
||||
self.label_message.setText(str(message))
|
||||
def clear_text():
|
||||
''' Helps prevent crash if this Window object
|
||||
is deleted before 5s passed
|
||||
'''
|
||||
try:
|
||||
self.label_message.set_text("")
|
||||
except:
|
||||
pass
|
||||
QtCore.QTimer.singleShot(5000, lambda: clear_text())
|
||||
|
||||
def on_asset_changed(self):
|
||||
'''Callback on asset selection changed
|
||||
|
||||
|
|
@ -160,7 +130,7 @@ class Window(QtWidgets.QDialog):
|
|||
self.widget_family.change_asset(asset['name'])
|
||||
else:
|
||||
self.valid_parent = False
|
||||
self.widget_family.change_asset(self.NOT_SELECTED)
|
||||
self.widget_family.change_asset(None)
|
||||
self.widget_family.on_data_changed()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
|
|
|
|||
|
|
@ -20,15 +20,14 @@ from .model_tree_view_deselectable import DeselectableTreeView
|
|||
|
||||
from .widget_asset_view import AssetView
|
||||
from .widget_asset import AssetWidget
|
||||
|
||||
from .widget_family_desc import FamilyDescriptionWidget
|
||||
from .widget_family import FamilyWidget
|
||||
|
||||
from .widget_drop_empty import DropEmpty
|
||||
from .widget_component_item import ComponentItem
|
||||
from .widget_components_list import ComponentsList
|
||||
|
||||
from .widget_drop_frame import DropDataFrame
|
||||
|
||||
from .widget_components import ComponentsWidget
|
||||
|
||||
from.widget_shadow import ShadowWidget
|
||||
from .widget_shadow import ShadowWidget
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ class TasksTemplateModel(TreeModel):
|
|||
|
||||
COLUMNS = ["Tasks"]
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, selectable=True):
|
||||
super(TasksTemplateModel, self).__init__()
|
||||
self.selectable = False
|
||||
self._icons = {
|
||||
"__default__": awesome.icon("fa.folder-o",
|
||||
color=style.colors.default)
|
||||
}
|
||||
self.selectable = selectable
|
||||
self.icon = awesome.icon(
|
||||
'fa.calendar-check-o',
|
||||
color=style.colors.default
|
||||
)
|
||||
|
||||
def set_tasks(self, tasks):
|
||||
"""Set assets to track by their database id
|
||||
|
|
@ -32,13 +32,11 @@ class TasksTemplateModel(TreeModel):
|
|||
|
||||
self.beginResetModel()
|
||||
|
||||
icon = self._icons["__default__"]
|
||||
for task in tasks:
|
||||
node = Node({
|
||||
"Tasks": task,
|
||||
"icon": icon
|
||||
"icon": self.icon
|
||||
})
|
||||
|
||||
self.add_child(node)
|
||||
|
||||
self.endResetModel()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import contextlib
|
|||
from . import QtWidgets, QtCore
|
||||
from . import RecursiveSortFilterProxyModel, AssetModel, AssetView
|
||||
from . import awesome, style
|
||||
from . import TasksTemplateModel, DeselectableTreeView
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def preserve_expanded_rows(tree_view,
|
||||
|
|
@ -128,7 +130,7 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
|
||||
self.parent_widget = parent
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(4)
|
||||
|
||||
|
|
@ -163,12 +165,31 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
layout.addLayout(header)
|
||||
layout.addWidget(view)
|
||||
|
||||
# tasks
|
||||
task_view = DeselectableTreeView()
|
||||
task_view.setIndentation(0)
|
||||
task_view.setHeaderHidden(True)
|
||||
task_view.setVisible(False)
|
||||
|
||||
task_model = TasksTemplateModel()
|
||||
task_view.setModel(task_model)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(4)
|
||||
main_layout.addLayout(layout, 80)
|
||||
main_layout.addWidget(task_view, 20)
|
||||
|
||||
# Signals/Slots
|
||||
selection = view.selectionModel()
|
||||
selection.selectionChanged.connect(self.selection_changed)
|
||||
selection.currentChanged.connect(self.current_changed)
|
||||
refresh.clicked.connect(self.refresh)
|
||||
|
||||
self.selection_changed.connect(self._refresh_tasks)
|
||||
|
||||
self.task_view = task_view
|
||||
self.task_model = task_model
|
||||
self.refreshButton = refresh
|
||||
self.model = model
|
||||
self.proxy = proxy
|
||||
|
|
@ -181,10 +202,17 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
def collect_data(self):
|
||||
project = self.db.find_one({'type': 'project'})
|
||||
asset = self.db.find_one({'_id': self.get_active_asset()})
|
||||
|
||||
try:
|
||||
index = self.task_view.selectedIndexes()[0]
|
||||
task = self.task_model.itemData(index)[0]
|
||||
except Exception:
|
||||
task = None
|
||||
data = {
|
||||
'project': project['name'],
|
||||
'asset': asset['name'],
|
||||
'parents': self.get_parents(asset)
|
||||
'parents': self.get_parents(asset),
|
||||
'task': task
|
||||
}
|
||||
return data
|
||||
|
||||
|
|
@ -223,6 +251,18 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
def refresh(self):
|
||||
self._refresh_model()
|
||||
|
||||
def _refresh_tasks(self):
|
||||
tasks = []
|
||||
selected = self.get_selected_assets()
|
||||
if len(selected) == 1:
|
||||
asset = self.db.find_one({
|
||||
"_id": selected[0], "type": "asset"
|
||||
})
|
||||
if asset:
|
||||
tasks = asset.get('data', {}).get('tasks', [])
|
||||
self.task_model.set_tasks(tasks)
|
||||
self.task_view.setVisible(len(tasks)>0)
|
||||
|
||||
def get_active_asset(self):
|
||||
"""Return the asset id the current asset."""
|
||||
current = self.view.currentIndex()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ class ComponentItem(QtWidgets.QFrame):
|
|||
C_HOVER = '#ffffff'
|
||||
C_ACTIVE = '#4BB543'
|
||||
C_ACTIVE_HOVER = '#4BF543'
|
||||
|
||||
signal_remove = QtCore.Signal(object)
|
||||
signal_thumbnail = QtCore.Signal(object)
|
||||
signal_preview = QtCore.Signal(object)
|
||||
|
|
@ -283,12 +284,28 @@ class ComponentItem(QtWidgets.QFrame):
|
|||
self.preview.change_checked(hover)
|
||||
|
||||
def collect_data(self):
|
||||
in_files = self.in_data['files']
|
||||
staging_dir = os.path.dirname(in_files[0])
|
||||
|
||||
files = [os.path.basename(file) for file in in_files]
|
||||
if len(files) == 1:
|
||||
files = files[0]
|
||||
|
||||
data = {
|
||||
'ext': self.in_data['ext'],
|
||||
'label': self.name.text(),
|
||||
'representation': self.input_repre.text(),
|
||||
'files': self.in_data['files'],
|
||||
'name': self.input_repre.text(),
|
||||
'stagingDir': staging_dir,
|
||||
'files': files,
|
||||
'thumbnail': self.is_thumbnail(),
|
||||
'preview': self.is_preview()
|
||||
}
|
||||
|
||||
if ('startFrame' in self.in_data and 'endFrame' in self.in_data):
|
||||
data['startFrame'] = self.in_data['startFrame']
|
||||
data['endFrame'] = self.in_data['endFrame']
|
||||
|
||||
if 'frameRate' in self.in_data:
|
||||
data['frameRate'] = self.in_data['frameRate']
|
||||
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
import re
|
||||
import json
|
||||
import clique
|
||||
import subprocess
|
||||
from pypeapp import config
|
||||
|
|
@ -49,7 +50,7 @@ class DropDataFrame(QtWidgets.QFrame):
|
|||
else:
|
||||
# If path is in clipboard as string
|
||||
try:
|
||||
path = ent.text()
|
||||
path = os.path.normpath(ent.text())
|
||||
if os.path.exists(path):
|
||||
paths.append(path)
|
||||
else:
|
||||
|
|
@ -170,6 +171,13 @@ class DropDataFrame(QtWidgets.QFrame):
|
|||
repr_name = file_ext.replace('.', '')
|
||||
range = collection.format('{ranges}')
|
||||
|
||||
# TODO: ranges must not be with missing frames!!!
|
||||
# - this is goal implementation:
|
||||
# startFrame, endFrame = range.split('-')
|
||||
rngs = range.split(',')
|
||||
startFrame = rngs[0].split('-')[0]
|
||||
endFrame = rngs[-1].split('-')[-1]
|
||||
|
||||
actions = []
|
||||
|
||||
data = {
|
||||
|
|
@ -177,39 +185,15 @@ class DropDataFrame(QtWidgets.QFrame):
|
|||
'name': file_base,
|
||||
'ext': file_ext,
|
||||
'file_info': range,
|
||||
'startFrame': startFrame,
|
||||
'endFrame': endFrame,
|
||||
'representation': repr_name,
|
||||
'folder_path': folder_path,
|
||||
'is_sequence': True,
|
||||
'actions': actions
|
||||
}
|
||||
self._process_data(data)
|
||||
|
||||
def _get_ranges(self, indexes):
|
||||
if len(indexes) == 1:
|
||||
return str(indexes[0])
|
||||
ranges = []
|
||||
first = None
|
||||
last = None
|
||||
for index in indexes:
|
||||
if first is None:
|
||||
first = index
|
||||
last = index
|
||||
elif (last+1) == index:
|
||||
last = index
|
||||
else:
|
||||
if first == last:
|
||||
range = str(first)
|
||||
else:
|
||||
range = '{}-{}'.format(first, last)
|
||||
ranges.append(range)
|
||||
first = index
|
||||
last = index
|
||||
if first == last:
|
||||
range = str(first)
|
||||
else:
|
||||
range = '{}-{}'.format(first, last)
|
||||
ranges.append(range)
|
||||
return ', '.join(ranges)
|
||||
self._process_data(data)
|
||||
|
||||
def _process_remainder(self, remainder):
|
||||
filename = os.path.basename(remainder)
|
||||
|
|
@ -232,39 +216,75 @@ class DropDataFrame(QtWidgets.QFrame):
|
|||
'is_sequence': False,
|
||||
'actions': actions
|
||||
}
|
||||
data['file_info'] = self.get_file_info(data)
|
||||
|
||||
self._process_data(data)
|
||||
|
||||
def get_file_info(self, data):
|
||||
output = None
|
||||
if data['ext'] == '.mov':
|
||||
try:
|
||||
# ffProbe must be in PATH
|
||||
filepath = data['files'][0]
|
||||
args = ['ffprobe', '-show_streams', filepath]
|
||||
p = subprocess.Popen(
|
||||
args,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=True
|
||||
)
|
||||
datalines=[]
|
||||
for line in iter(p.stdout.readline, b''):
|
||||
line = line.decode("utf-8").replace('\r\n', '')
|
||||
datalines.append(line)
|
||||
def load_data_with_probe(self, filepath):
|
||||
args = [
|
||||
'ffprobe',
|
||||
'-v', 'quiet',
|
||||
'-print_format', 'json',
|
||||
'-show_format',
|
||||
'-show_streams', filepath
|
||||
]
|
||||
ffprobe_p = subprocess.Popen(
|
||||
args,
|
||||
stdout=subprocess.PIPE,
|
||||
shell=True
|
||||
)
|
||||
ffprobe_output = ffprobe_p.communicate()[0]
|
||||
if ffprobe_p.returncode != 0:
|
||||
raise RuntimeError(
|
||||
'Failed on ffprobe: check if ffprobe path is set in PATH env'
|
||||
)
|
||||
return json.loads(ffprobe_output)['streams'][0]
|
||||
|
||||
def get_file_data(self, data):
|
||||
filepath = data['files'][0]
|
||||
ext = data['ext']
|
||||
output = {}
|
||||
probe_data = self.load_data_with_probe(filepath)
|
||||
|
||||
if (
|
||||
ext in self.presets['extensions']['image_file'] or
|
||||
ext in self.presets['extensions']['video_file']
|
||||
):
|
||||
if 'frameRate' not in data:
|
||||
# default value
|
||||
frameRate = 25
|
||||
frameRate_string = probe_data.get('r_frame_rate')
|
||||
if frameRate_string:
|
||||
frameRate = int(frameRate_string.split('/')[0])
|
||||
|
||||
output['frameRate'] = frameRate
|
||||
|
||||
if 'startFrame' not in data or 'endFrame' not in data:
|
||||
startFrame = endFrame = 1
|
||||
endFrame_string = probe_data.get('nb_frames')
|
||||
|
||||
if endFrame_string:
|
||||
endFrame = int(endFrame_string)
|
||||
|
||||
output['startFrame'] = startFrame
|
||||
output['endFrame'] = endFrame
|
||||
|
||||
file_info = None
|
||||
if 'file_info' in data:
|
||||
file_info = data['file_info']
|
||||
elif ext in ['.mov']:
|
||||
file_info = probe_data.get('codec_name')
|
||||
|
||||
output['file_info'] = file_info
|
||||
|
||||
find_value = 'codec_name'
|
||||
for line in datalines:
|
||||
if line.startswith(find_value):
|
||||
output = line.replace(find_value + '=', '')
|
||||
break
|
||||
except Exception as e:
|
||||
pass
|
||||
return output
|
||||
|
||||
def _process_data(self, data):
|
||||
ext = data['ext']
|
||||
# load file data info
|
||||
file_data = self.get_file_data(data)
|
||||
for key, value in file_data.items():
|
||||
data[key] = value
|
||||
|
||||
icon = 'default'
|
||||
for ico, exts in self.presets['extensions'].items():
|
||||
if ext in exts:
|
||||
|
|
|
|||
|
|
@ -17,12 +17,14 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
data = dict()
|
||||
_jobs = dict()
|
||||
Separator = "---separator---"
|
||||
NOT_SELECTED = '< Nothing is selected >'
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
# Store internal states in here
|
||||
self.state = {"valid": False}
|
||||
self.parent_widget = parent
|
||||
self.asset_name = self.NOT_SELECTED
|
||||
|
||||
body = QtWidgets.QWidget()
|
||||
lists = QtWidgets.QWidget()
|
||||
|
|
@ -30,12 +32,10 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
container = QtWidgets.QWidget()
|
||||
|
||||
list_families = QtWidgets.QListWidget()
|
||||
input_asset = QtWidgets.QLineEdit()
|
||||
input_asset.setEnabled(False)
|
||||
input_asset.setStyleSheet("color: #BBBBBB;")
|
||||
|
||||
input_subset = QtWidgets.QLineEdit()
|
||||
input_result = QtWidgets.QLineEdit()
|
||||
input_result.setStyleSheet("color: gray;")
|
||||
input_result.setStyleSheet("color: #BBBBBB;")
|
||||
input_result.setEnabled(False)
|
||||
|
||||
# region Menu for default subset names
|
||||
|
|
@ -51,6 +51,20 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
name_layout.addWidget(btn_subset)
|
||||
name_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# version
|
||||
version_spinbox = QtWidgets.QSpinBox()
|
||||
version_spinbox.setMinimum(1)
|
||||
version_spinbox.setMaximum(9999)
|
||||
version_spinbox.setEnabled(False)
|
||||
version_spinbox.setStyleSheet("color: #BBBBBB;")
|
||||
|
||||
version_checkbox = QtWidgets.QCheckBox("Next Available Version")
|
||||
version_checkbox.setCheckState(QtCore.Qt.CheckState(2))
|
||||
|
||||
version_layout = QtWidgets.QHBoxLayout()
|
||||
version_layout.addWidget(version_spinbox)
|
||||
version_layout.addWidget(version_checkbox)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(container)
|
||||
|
||||
header = FamilyDescriptionWidget(self)
|
||||
|
|
@ -58,11 +72,11 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
|
||||
layout.addWidget(QtWidgets.QLabel("Family"))
|
||||
layout.addWidget(list_families)
|
||||
layout.addWidget(QtWidgets.QLabel("Asset"))
|
||||
layout.addWidget(input_asset)
|
||||
layout.addWidget(QtWidgets.QLabel("Subset"))
|
||||
layout.addLayout(name_layout)
|
||||
layout.addWidget(input_result)
|
||||
layout.addWidget(QtWidgets.QLabel("Version"))
|
||||
layout.addLayout(version_layout)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
options = QtWidgets.QWidget()
|
||||
|
|
@ -75,6 +89,7 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(body)
|
||||
|
||||
layout.addWidget(lists)
|
||||
layout.addWidget(options, 0, QtCore.Qt.AlignLeft)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
|
@ -83,9 +98,9 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
layout.addWidget(body)
|
||||
|
||||
input_subset.textChanged.connect(self.on_data_changed)
|
||||
input_asset.textChanged.connect(self.on_data_changed)
|
||||
list_families.currentItemChanged.connect(self.on_selection_changed)
|
||||
list_families.currentItemChanged.connect(header.set_item)
|
||||
version_checkbox.stateChanged.connect(self.on_version_refresh)
|
||||
|
||||
self.stateChanged.connect(self._on_state_changed)
|
||||
|
||||
|
|
@ -93,8 +108,9 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
self.menu_subset = menu_subset
|
||||
self.btn_subset = btn_subset
|
||||
self.list_families = list_families
|
||||
self.input_asset = input_asset
|
||||
self.input_result = input_result
|
||||
self.version_checkbox = version_checkbox
|
||||
self.version_spinbox = version_spinbox
|
||||
|
||||
self.refresh()
|
||||
|
||||
|
|
@ -103,7 +119,8 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
family = plugin.family.rsplit(".", 1)[-1]
|
||||
data = {
|
||||
'family': family,
|
||||
'subset': self.input_subset.text()
|
||||
'subset': self.input_result.text(),
|
||||
'version': self.version_spinbox.value()
|
||||
}
|
||||
return data
|
||||
|
||||
|
|
@ -112,7 +129,10 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
return self.parent_widget.db
|
||||
|
||||
def change_asset(self, name):
|
||||
self.input_asset.setText(name)
|
||||
if name is None:
|
||||
name = self.NOT_SELECTED
|
||||
self.asset_name = name
|
||||
self.on_data_changed()
|
||||
|
||||
def _on_state_changed(self, state):
|
||||
self.state['valid'] = state
|
||||
|
|
@ -153,22 +173,37 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
self.input_subset.setText(action.text())
|
||||
|
||||
def _on_data_changed(self):
|
||||
item = self.list_families.currentItem()
|
||||
asset_name = self.asset_name
|
||||
subset_name = self.input_subset.text()
|
||||
asset_name = self.input_asset.text()
|
||||
item = self.list_families.currentItem()
|
||||
|
||||
# Get the assets from the database which match with the name
|
||||
assets_db = self.db.find(filter={"type": "asset"}, projection={"name": 1})
|
||||
assets = [asset for asset in assets_db if asset_name in asset["name"]]
|
||||
if item is None:
|
||||
return
|
||||
if assets:
|
||||
# Get plugin and family
|
||||
plugin = item.data(PluginRole)
|
||||
if plugin is None:
|
||||
return
|
||||
family = plugin.family.rsplit(".", 1)[-1]
|
||||
|
||||
assets = None
|
||||
if asset_name != self.NOT_SELECTED:
|
||||
# Get the assets from the database which match with the name
|
||||
assets_db = self.db.find(
|
||||
filter={"type": "asset"},
|
||||
projection={"name": 1}
|
||||
)
|
||||
assets = [
|
||||
asset for asset in assets_db if asset_name in asset["name"]
|
||||
]
|
||||
|
||||
# Get plugin and family
|
||||
plugin = item.data(PluginRole)
|
||||
if plugin is None:
|
||||
return
|
||||
|
||||
family = plugin.family.rsplit(".", 1)[-1]
|
||||
|
||||
# Update the result
|
||||
if subset_name:
|
||||
subset_name = subset_name[0].upper() + subset_name[1:]
|
||||
self.input_result.setText("{}{}".format(family, subset_name))
|
||||
|
||||
if assets:
|
||||
# Get all subsets of the current asset
|
||||
asset_ids = [asset["_id"] for asset in assets]
|
||||
subsets = self.db.find(filter={"type": "subset",
|
||||
|
|
@ -191,28 +226,62 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
|
||||
self._build_menu(defaults)
|
||||
|
||||
# Update the result
|
||||
if subset_name:
|
||||
subset_name = subset_name[0].upper() + subset_name[1:]
|
||||
self.input_result.setText("{}{}".format(family, subset_name))
|
||||
|
||||
item.setData(ExistsRole, True)
|
||||
self.echo("Ready ..")
|
||||
else:
|
||||
self._build_menu([])
|
||||
item.setData(ExistsRole, False)
|
||||
if asset_name != self.parent_widget.NOT_SELECTED:
|
||||
self.echo("'%s' not found .." % asset_name)
|
||||
if asset_name != self.NOT_SELECTED:
|
||||
# TODO add logging into standalone_publish
|
||||
print("'%s' not found .." % asset_name)
|
||||
|
||||
self.on_version_refresh()
|
||||
|
||||
# Update the valid state
|
||||
valid = (
|
||||
asset_name != self.NOT_SELECTED and
|
||||
subset_name.strip() != "" and
|
||||
asset_name.strip() != "" and
|
||||
item.data(QtCore.Qt.ItemIsEnabled) and
|
||||
item.data(ExistsRole)
|
||||
)
|
||||
self.stateChanged.emit(valid)
|
||||
|
||||
def on_version_refresh(self):
|
||||
auto_version = self.version_checkbox.isChecked()
|
||||
self.version_spinbox.setEnabled(not auto_version)
|
||||
if not auto_version:
|
||||
return
|
||||
|
||||
asset_name = self.asset_name
|
||||
subset_name = self.input_result.text()
|
||||
version = 1
|
||||
|
||||
if (
|
||||
asset_name != self.NOT_SELECTED and
|
||||
subset_name.strip() != ''
|
||||
):
|
||||
asset = self.db.find_one({
|
||||
'type': 'asset',
|
||||
'name': asset_name
|
||||
})
|
||||
subset = self.db.find_one({
|
||||
'type': 'subset',
|
||||
'parent': asset['_id'],
|
||||
'name': subset_name
|
||||
})
|
||||
if subset:
|
||||
versions = self.db.find({
|
||||
'type': 'version',
|
||||
'parent': subset['_id']
|
||||
})
|
||||
if versions:
|
||||
versions = sorted(
|
||||
[v for v in versions],
|
||||
key=lambda ver: ver['name']
|
||||
)
|
||||
version = int(versions[-1]['name']) + 1
|
||||
|
||||
self.version_spinbox.setValue(version)
|
||||
|
||||
def on_data_changed(self, *args):
|
||||
|
||||
# Set invalid state until it's reconfirmed to be valid by the
|
||||
|
|
@ -270,10 +339,6 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
|
||||
self.list_families.setCurrentItem(self.list_families.item(0))
|
||||
|
||||
def echo(self, message):
|
||||
if hasattr(self.parent_widget, 'echo'):
|
||||
self.parent_widget.echo(message)
|
||||
|
||||
def schedule(self, func, time, channel="default"):
|
||||
try:
|
||||
self._jobs[channel].stop()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
|
||||
from pype.nuke.lib import writes_version_sync, onScriptLoad
|
||||
from pype.nuke.lib import (
|
||||
writes_version_sync,
|
||||
onScriptLoad,
|
||||
checkInventoryVersions
|
||||
)
|
||||
|
||||
import nuke
|
||||
from pypeapp import Logger
|
||||
|
||||
|
|
@ -8,5 +13,6 @@ log = Logger().get_logger(__name__, "nuke")
|
|||
|
||||
nuke.addOnScriptSave(writes_version_sync)
|
||||
nuke.addOnScriptSave(onScriptLoad)
|
||||
nuke.addOnScriptSave(checkInventoryVersions)
|
||||
|
||||
log.info('Automatic syncing of write file knob to script version')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue