mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
resolved conflict
This commit is contained in:
commit
60ee15a88b
29 changed files with 1274 additions and 256 deletions
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -35,6 +35,7 @@ body:
|
|||
label: Version
|
||||
description: What version are you running? Look to OpenPype Tray
|
||||
options:
|
||||
- 3.17.5-nightly.3
|
||||
- 3.17.5-nightly.2
|
||||
- 3.17.5-nightly.1
|
||||
- 3.17.4
|
||||
|
|
@ -134,7 +135,6 @@ body:
|
|||
- 3.15.1-nightly.5
|
||||
- 3.15.1-nightly.4
|
||||
- 3.15.1-nightly.3
|
||||
- 3.15.1-nightly.2
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class BlendLoader(plugin.AssetLoader):
|
|||
empties = [obj for obj in objects if obj.type == 'EMPTY']
|
||||
|
||||
for empty in empties:
|
||||
if empty.get(AVALON_PROPERTY):
|
||||
if empty.get(AVALON_PROPERTY) and empty.parent is None:
|
||||
return empty
|
||||
|
||||
return None
|
||||
|
|
@ -90,6 +90,7 @@ class BlendLoader(plugin.AssetLoader):
|
|||
members.append(data)
|
||||
|
||||
container = self._get_asset_container(data_to.objects)
|
||||
print(container)
|
||||
assert container, "No asset group found"
|
||||
|
||||
container.name = group_name
|
||||
|
|
@ -100,8 +101,11 @@ class BlendLoader(plugin.AssetLoader):
|
|||
|
||||
# Link all the container children to the collection
|
||||
for obj in container.children_recursive:
|
||||
print(obj)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
|
||||
print("")
|
||||
|
||||
# Remove the library from the blend file
|
||||
library = bpy.data.libraries.get(bpy.path.basename(libpath))
|
||||
bpy.data.libraries.remove(library)
|
||||
|
|
|
|||
|
|
@ -31,11 +31,12 @@ class CollectReview(pyblish.api.InstancePlugin):
|
|||
|
||||
focal_length = cameras[0].data.lens
|
||||
|
||||
# get isolate objects list from meshes instance members .
|
||||
# get isolate objects list from meshes instance members.
|
||||
types = {"MESH", "GPENCIL"}
|
||||
isolate_objects = [
|
||||
obj
|
||||
for obj in instance
|
||||
if isinstance(obj, bpy.types.Object) and obj.type == "MESH"
|
||||
if isinstance(obj, bpy.types.Object) and obj.type in types
|
||||
]
|
||||
|
||||
if not instance.data.get("remove"):
|
||||
|
|
|
|||
|
|
@ -11,20 +11,21 @@ import json
|
|||
import six
|
||||
|
||||
from openpype.lib import StringTemplate
|
||||
from openpype.client import get_asset_by_name
|
||||
from openpype.client import get_project, get_asset_by_name
|
||||
from openpype.settings import get_current_project_settings
|
||||
from openpype.pipeline import (
|
||||
Anatomy,
|
||||
get_current_project_name,
|
||||
get_current_asset_name,
|
||||
registered_host
|
||||
)
|
||||
from openpype.pipeline.context_tools import (
|
||||
get_current_context_template_data,
|
||||
get_current_project_asset
|
||||
registered_host,
|
||||
get_current_context,
|
||||
get_current_host_name,
|
||||
)
|
||||
from openpype.pipeline.create import CreateContext
|
||||
from openpype.pipeline.template_data import get_template_data
|
||||
from openpype.pipeline.context_tools import get_current_project_asset
|
||||
from openpype.widgets import popup
|
||||
from openpype.tools.utils.host_tools import get_tool_by_name
|
||||
from openpype.pipeline.create import CreateContext
|
||||
|
||||
import hou
|
||||
|
||||
|
|
@ -804,6 +805,45 @@ def get_camera_from_container(container):
|
|||
return cameras[0]
|
||||
|
||||
|
||||
def get_current_context_template_data_with_asset_data():
|
||||
"""
|
||||
TODOs:
|
||||
Support both 'assetData' and 'folderData' in future.
|
||||
"""
|
||||
|
||||
context = get_current_context()
|
||||
project_name = context["project_name"]
|
||||
asset_name = context["asset_name"]
|
||||
task_name = context["task_name"]
|
||||
host_name = get_current_host_name()
|
||||
|
||||
anatomy = Anatomy(project_name)
|
||||
project_doc = get_project(project_name)
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
|
||||
# get context specific vars
|
||||
asset_data = asset_doc["data"]
|
||||
|
||||
# compute `frameStartHandle` and `frameEndHandle`
|
||||
frame_start = asset_data.get("frameStart")
|
||||
frame_end = asset_data.get("frameEnd")
|
||||
handle_start = asset_data.get("handleStart")
|
||||
handle_end = asset_data.get("handleEnd")
|
||||
if frame_start is not None and handle_start is not None:
|
||||
asset_data["frameStartHandle"] = frame_start - handle_start
|
||||
|
||||
if frame_end is not None and handle_end is not None:
|
||||
asset_data["frameEndHandle"] = frame_end + handle_end
|
||||
|
||||
template_data = get_template_data(
|
||||
project_doc, asset_doc, task_name, host_name
|
||||
)
|
||||
template_data["root"] = anatomy.roots
|
||||
template_data["assetData"] = asset_data
|
||||
|
||||
return template_data
|
||||
|
||||
|
||||
def get_context_var_changes():
|
||||
"""get context var changes."""
|
||||
|
||||
|
|
@ -823,7 +863,7 @@ def get_context_var_changes():
|
|||
return houdini_vars_to_update
|
||||
|
||||
# Get Template data
|
||||
template_data = get_current_context_template_data()
|
||||
template_data = get_current_context_template_data_with_asset_data()
|
||||
|
||||
# Set Houdini Vars
|
||||
for item in houdini_vars:
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ from openpype.settings import get_project_settings
|
|||
from openpype.pipeline import get_current_project_name
|
||||
|
||||
from openpype.lib import StringTemplate
|
||||
from openpype.pipeline.context_tools import get_current_context_template_data
|
||||
|
||||
import hou
|
||||
|
||||
from .lib import get_current_context_template_data_with_asset_data
|
||||
|
||||
log = logging.getLogger("openpype.hosts.houdini.shelves")
|
||||
|
||||
|
||||
|
|
@ -23,29 +24,33 @@ def generate_shelves():
|
|||
# load configuration of houdini shelves
|
||||
project_name = get_current_project_name()
|
||||
project_settings = get_project_settings(project_name)
|
||||
shelves_set_config = project_settings["houdini"]["shelves"]
|
||||
shelves_configs = project_settings["houdini"]["shelves"]
|
||||
|
||||
if not shelves_set_config:
|
||||
if not shelves_configs:
|
||||
log.debug("No custom shelves found in project settings.")
|
||||
return
|
||||
|
||||
# Get Template data
|
||||
template_data = get_current_context_template_data()
|
||||
template_data = get_current_context_template_data_with_asset_data()
|
||||
|
||||
for config in shelves_configs:
|
||||
selected_option = config["options"]
|
||||
shelf_set_config = config[selected_option]
|
||||
|
||||
for shelf_set_config in shelves_set_config:
|
||||
shelf_set_filepath = shelf_set_config.get('shelf_set_source_path')
|
||||
shelf_set_os_filepath = shelf_set_filepath[current_os]
|
||||
if shelf_set_os_filepath:
|
||||
shelf_set_os_filepath = get_path_using_template_data(
|
||||
shelf_set_os_filepath, template_data
|
||||
)
|
||||
if not os.path.isfile(shelf_set_os_filepath):
|
||||
log.error("Shelf path doesn't exist - "
|
||||
"{}".format(shelf_set_os_filepath))
|
||||
continue
|
||||
if shelf_set_filepath:
|
||||
shelf_set_os_filepath = shelf_set_filepath[current_os]
|
||||
if shelf_set_os_filepath:
|
||||
shelf_set_os_filepath = get_path_using_template_data(
|
||||
shelf_set_os_filepath, template_data
|
||||
)
|
||||
if not os.path.isfile(shelf_set_os_filepath):
|
||||
log.error("Shelf path doesn't exist - "
|
||||
"{}".format(shelf_set_os_filepath))
|
||||
continue
|
||||
|
||||
hou.shelves.newShelfSet(file_path=shelf_set_os_filepath)
|
||||
continue
|
||||
hou.shelves.loadFile(shelf_set_os_filepath)
|
||||
continue
|
||||
|
||||
shelf_set_name = shelf_set_config.get('shelf_set_name')
|
||||
if not shelf_set_name:
|
||||
|
|
|
|||
|
|
@ -57,7 +57,17 @@ class ValidateFrameRange(pyblish.api.InstancePlugin):
|
|||
return
|
||||
|
||||
rop_node = hou.node(instance.data["instance_node"])
|
||||
if instance.data["frameStart"] > instance.data["frameEnd"]:
|
||||
frame_start = instance.data.get("frameStart")
|
||||
frame_end = instance.data.get("frameEnd")
|
||||
|
||||
if frame_start is None or frame_end is None:
|
||||
cls.log.debug(
|
||||
"Skipping frame range validation for "
|
||||
"instance without frame data: {}".format(rop_node.path())
|
||||
)
|
||||
return
|
||||
|
||||
if frame_start > frame_end:
|
||||
cls.log.info(
|
||||
"The ROP node render range is set to "
|
||||
"{0[frameStartHandle]} - {0[frameEndHandle]} "
|
||||
|
|
|
|||
|
|
@ -4,13 +4,11 @@ import os
|
|||
import pyblish.api
|
||||
|
||||
from pymxs import runtime as rt
|
||||
from openpype.pipeline import get_current_asset_name
|
||||
from openpype.pipeline.publish import KnownPublishError
|
||||
from openpype.hosts.max.api import colorspace
|
||||
from openpype.hosts.max.api.lib import get_max_version, get_current_renderer
|
||||
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
|
||||
from openpype.hosts.max.api.lib_renderproducts import RenderProducts
|
||||
from openpype.client import get_last_version_by_subset_name
|
||||
|
||||
|
||||
class CollectRender(pyblish.api.InstancePlugin):
|
||||
|
|
@ -28,7 +26,6 @@ class CollectRender(pyblish.api.InstancePlugin):
|
|||
current_file = os.path.join(folder, file)
|
||||
filepath = current_file.replace("\\", "/")
|
||||
context.data['currentFile'] = current_file
|
||||
asset = get_current_asset_name()
|
||||
|
||||
files_by_aov = RenderProducts().get_beauty(instance.name)
|
||||
aovs = RenderProducts().get_aovs(instance.name)
|
||||
|
|
@ -72,20 +69,6 @@ class CollectRender(pyblish.api.InstancePlugin):
|
|||
instance.data["files"].append(files_by_aov)
|
||||
|
||||
img_format = RenderProducts().image_format()
|
||||
project_name = context.data["projectName"]
|
||||
asset_doc = context.data["assetEntity"]
|
||||
asset_id = asset_doc["_id"]
|
||||
version_doc = get_last_version_by_subset_name(project_name,
|
||||
instance.name,
|
||||
asset_id)
|
||||
self.log.debug("version_doc: {0}".format(version_doc))
|
||||
|
||||
version_int = 1
|
||||
if version_doc:
|
||||
version_int += int(version_doc["name"])
|
||||
|
||||
self.log.debug(f"Setting {version_int} to context.")
|
||||
context.data["version"] = version_int
|
||||
# OCIO config not support in
|
||||
# most of the 3dsmax renderers
|
||||
# so this is currently hard coded
|
||||
|
|
@ -111,7 +94,7 @@ class CollectRender(pyblish.api.InstancePlugin):
|
|||
renderer = str(renderer_class).split(":")[0]
|
||||
# also need to get the render dir for conversion
|
||||
data = {
|
||||
"asset": asset,
|
||||
"asset": instance.data["asset"],
|
||||
"subset": str(instance.name),
|
||||
"publish": True,
|
||||
"maxversion": str(get_max_version()),
|
||||
|
|
@ -123,7 +106,6 @@ class CollectRender(pyblish.api.InstancePlugin):
|
|||
"plugin": "3dsmax",
|
||||
"frameStart": instance.data["frameStartHandle"],
|
||||
"frameEnd": instance.data["frameEndHandle"],
|
||||
"version": version_int,
|
||||
"farm": True
|
||||
}
|
||||
instance.data.update(data)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Updated as of 9 May 2022
|
||||
Updated as of 26 May 2023
|
||||
----------------------------
|
||||
In this package, you will find a brief introduction to the Scripting API for DaVinci Resolve Studio. Apart from this README.txt file, this package contains folders containing the basic import
|
||||
modules for scripting access (DaVinciResolve.py) and some representative examples.
|
||||
|
|
@ -19,7 +19,7 @@ DaVinci Resolve scripting requires one of the following to be installed (for all
|
|||
|
||||
Lua 5.1
|
||||
Python 2.7 64-bit
|
||||
Python 3.6 64-bit
|
||||
Python >= 3.6 64-bit
|
||||
|
||||
|
||||
Using a script
|
||||
|
|
@ -171,6 +171,10 @@ Project
|
|||
GetRenderResolutions(format, codec) --> [{Resolution}] # Returns list of resolutions applicable for the given render format (string) and render codec (string). Returns full list of resolutions if no argument is provided. Each element in the list is a dictionary with 2 keys "Width" and "Height".
|
||||
RefreshLUTList() --> Bool # Refreshes LUT List
|
||||
GetUniqueId() --> string # Returns a unique ID for the project item
|
||||
InsertAudioToCurrentTrackAtPlayhead(mediaPath, --> Bool # Inserts the media specified by mediaPath (string) with startOffsetInSamples (int) and durationInSamples (int) at the playhead on a selected track on the Fairlight page. Returns True if successful, otherwise False.
|
||||
startOffsetInSamples, durationInSamples)
|
||||
LoadBurnInPreset(presetName) --> Bool # Loads user defined data burn in preset for project when supplied presetName (string). Returns true if successful.
|
||||
ExportCurrentFrameAsStill(filePath) --> Bool # Exports current frame as still to supplied filePath. filePath must end in valid export file format. Returns True if succssful, False otherwise.
|
||||
|
||||
MediaStorage
|
||||
GetMountedVolumeList() --> [paths...] # Returns list of folder paths corresponding to mounted volumes displayed in Resolve’s Media Storage.
|
||||
|
|
@ -179,6 +183,7 @@ MediaStorage
|
|||
RevealInStorage(path) --> Bool # Expands and displays given file/folder path in Resolve’s Media Storage.
|
||||
AddItemListToMediaPool(item1, item2, ...) --> [clips...] # Adds specified file/folder paths from Media Storage into current Media Pool folder. Input is one or more file/folder paths. Returns a list of the MediaPoolItems created.
|
||||
AddItemListToMediaPool([items...]) --> [clips...] # Adds specified file/folder paths from Media Storage into current Media Pool folder. Input is an array of file/folder paths. Returns a list of the MediaPoolItems created.
|
||||
AddItemListToMediaPool([{itemInfo}, ...]) --> [clips...] # Adds list of itemInfos specified as dict of "media", "startFrame" (int), "endFrame" (int) from Media Storage into current Media Pool folder. Returns a list of the MediaPoolItems created.
|
||||
AddClipMattesToMediaPool(MediaPoolItem, [paths], stereoEye) --> Bool # Adds specified media files as mattes for the specified MediaPoolItem. StereoEye is an optional argument for specifying which eye to add the matte to for stereo clips ("left" or "right"). Returns True if successful.
|
||||
AddTimelineMattesToMediaPool([paths]) --> [MediaPoolItems] # Adds specified media files as timeline mattes in current media pool folder. Returns a list of created MediaPoolItems.
|
||||
|
||||
|
|
@ -189,20 +194,22 @@ MediaPool
|
|||
CreateEmptyTimeline(name) --> Timeline # Adds new timeline with given name.
|
||||
AppendToTimeline(clip1, clip2, ...) --> [TimelineItem] # Appends specified MediaPoolItem objects in the current timeline. Returns the list of appended timelineItems.
|
||||
AppendToTimeline([clips]) --> [TimelineItem] # Appends specified MediaPoolItem objects in the current timeline. Returns the list of appended timelineItems.
|
||||
AppendToTimeline([{clipInfo}, ...]) --> [TimelineItem] # Appends list of clipInfos specified as dict of "mediaPoolItem", "startFrame" (int), "endFrame" (int), (optional) "mediaType" (int; 1 - Video only, 2 - Audio only). Returns the list of appended timelineItems.
|
||||
AppendToTimeline([{clipInfo}, ...]) --> [TimelineItem] # Appends list of clipInfos specified as dict of "mediaPoolItem", "startFrame" (int), "endFrame" (int), (optional) "mediaType" (int; 1 - Video only, 2 - Audio only), "trackIndex" (int) and "recordFrame" (int). Returns the list of appended timelineItems.
|
||||
CreateTimelineFromClips(name, clip1, clip2,...) --> Timeline # Creates new timeline with specified name, and appends the specified MediaPoolItem objects.
|
||||
CreateTimelineFromClips(name, [clips]) --> Timeline # Creates new timeline with specified name, and appends the specified MediaPoolItem objects.
|
||||
CreateTimelineFromClips(name, [{clipInfo}]) --> Timeline # Creates new timeline with specified name, appending the list of clipInfos specified as a dict of "mediaPoolItem", "startFrame" (int), "endFrame" (int).
|
||||
ImportTimelineFromFile(filePath, {importOptions}) --> Timeline # Creates timeline based on parameters within given file and optional importOptions dict, with support for the keys:
|
||||
# "timelineName": string, specifies the name of the timeline to be created
|
||||
# "importSourceClips": Bool, specifies whether source clips should be imported, True by default
|
||||
CreateTimelineFromClips(name, [{clipInfo}]) --> Timeline # Creates new timeline with specified name, appending the list of clipInfos specified as a dict of "mediaPoolItem", "startFrame" (int), "endFrame" (int), "recordFrame" (int).
|
||||
ImportTimelineFromFile(filePath, {importOptions}) --> Timeline # Creates timeline based on parameters within given file (AAF/EDL/XML/FCPXML/DRT/ADL) and optional importOptions dict, with support for the keys:
|
||||
# "timelineName": string, specifies the name of the timeline to be created. Not valid for DRT import
|
||||
# "importSourceClips": Bool, specifies whether source clips should be imported, True by default. Not valid for DRT import
|
||||
# "sourceClipsPath": string, specifies a filesystem path to search for source clips if the media is inaccessible in their original path and if "importSourceClips" is True
|
||||
# "sourceClipsFolders": List of Media Pool folder objects to search for source clips if the media is not present in current folder and if "importSourceClips" is False
|
||||
# "sourceClipsFolders": List of Media Pool folder objects to search for source clips if the media is not present in current folder and if "importSourceClips" is False. Not valid for DRT import
|
||||
# "interlaceProcessing": Bool, specifies whether to enable interlace processing on the imported timeline being created. valid only for AAF import
|
||||
DeleteTimelines([timeline]) --> Bool # Deletes specified timelines in the media pool.
|
||||
GetCurrentFolder() --> Folder # Returns currently selected Folder.
|
||||
SetCurrentFolder(Folder) --> Bool # Sets current folder by given Folder.
|
||||
DeleteClips([clips]) --> Bool # Deletes specified clips or timeline mattes in the media pool
|
||||
ImportFolderFromFile(filePath, sourceClipsPath="") --> Bool # Returns true if import from given DRB filePath is successful, false otherwise
|
||||
# sourceClipsPath is a string that specifies a filesystem path to search for source clips if the media is inaccessible in their original path, empty by default
|
||||
DeleteFolders([subfolders]) --> Bool # Deletes specified subfolders in the media pool
|
||||
MoveClips([clips], targetFolder) --> Bool # Moves specified clips to target folder.
|
||||
MoveFolders([folders], targetFolder) --> Bool # Moves specified folders to target folder.
|
||||
|
|
@ -225,6 +232,7 @@ Folder
|
|||
GetSubFolderList() --> [folders...] # Returns a list of subfolders in the folder.
|
||||
GetIsFolderStale() --> bool # Returns true if folder is stale in collaboration mode, false otherwise
|
||||
GetUniqueId() --> string # Returns a unique ID for the media pool folder
|
||||
Export(filePath) --> bool # Returns true if export of DRB folder to filePath is successful, false otherwise
|
||||
|
||||
MediaPoolItem
|
||||
GetName() --> string # Returns the clip name.
|
||||
|
|
@ -257,6 +265,8 @@ MediaPoolItem
|
|||
UnlinkProxyMedia() --> Bool # Unlinks any proxy media associated with clip.
|
||||
ReplaceClip(filePath) --> Bool # Replaces the underlying asset and metadata of MediaPoolItem with the specified absolute clip path.
|
||||
GetUniqueId() --> string # Returns a unique ID for the media pool item
|
||||
TranscribeAudio() --> Bool # Transcribes audio of the MediaPoolItem. Returns True if successful; False otherwise
|
||||
ClearTranscription() --> Bool # Clears audio transcription of the MediaPoolItem. Returns True if successful; False otherwise.
|
||||
|
||||
Timeline
|
||||
GetName() --> string # Returns the timeline name.
|
||||
|
|
@ -266,6 +276,23 @@ Timeline
|
|||
SetStartTimecode(timecode) --> Bool # Set the start timecode of the timeline to the string 'timecode'. Returns true when the change is successful, false otherwise.
|
||||
GetStartTimecode() --> string # Returns the start timecode for the timeline.
|
||||
GetTrackCount(trackType) --> int # Returns the number of tracks for the given track type ("audio", "video" or "subtitle").
|
||||
AddTrack(trackType, optionalSubTrackType) --> Bool # Adds track of trackType ("video", "subtitle", "audio"). Second argument optionalSubTrackType is required for "audio"
|
||||
# optionalSubTrackType can be one of {"mono", "stereo", "5.1", "5.1film", "7.1", "7.1film", "adaptive1", ... , "adaptive24"}
|
||||
DeleteTrack(trackType, trackIndex) --> Bool # Deletes track of trackType ("video", "subtitle", "audio") and given trackIndex. 1 <= trackIndex <= GetTrackCount(trackType).
|
||||
SetTrackEnable(trackType, trackIndex, Bool) --> Bool # Enables/Disables track with given trackType and trackIndex
|
||||
# trackType is one of {"audio", "video", "subtitle"}
|
||||
# 1 <= trackIndex <= GetTrackCount(trackType).
|
||||
GetIsTrackEnabled(trackType, trackIndex) --> Bool # Returns True if track with given trackType and trackIndex is enabled and False otherwise.
|
||||
# trackType is one of {"audio", "video", "subtitle"}
|
||||
# 1 <= trackIndex <= GetTrackCount(trackType).
|
||||
SetTrackLock(trackType, trackIndex, Bool) --> Bool # Locks/Unlocks track with given trackType and trackIndex
|
||||
# trackType is one of {"audio", "video", "subtitle"}
|
||||
# 1 <= trackIndex <= GetTrackCount(trackType).
|
||||
GetIsTrackLocked(trackType, trackIndex) --> Bool # Returns True if track with given trackType and trackIndex is locked and False otherwise.
|
||||
# trackType is one of {"audio", "video", "subtitle"}
|
||||
# 1 <= trackIndex <= GetTrackCount(trackType).
|
||||
DeleteClips([timelineItems], Bool) --> Bool # Deletes specified TimelineItems from the timeline, performing ripple delete if the second argument is True. Second argument is optional (The default for this is False)
|
||||
SetClipsLinked([timelineItems], Bool) --> Bool # Links or unlinks the specified TimelineItems depending on second argument.
|
||||
GetItemListInTrack(trackType, index) --> [items...] # Returns a list of timeline items on that track (based on trackType and index). 1 <= index <= GetTrackCount(trackType).
|
||||
AddMarker(frameId, color, name, note, duration, --> Bool # Creates a new marker at given frameId position and with given marker information. 'customData' is optional and helps to attach user specific data to the marker.
|
||||
customData)
|
||||
|
|
@ -301,7 +328,7 @@ Timeline
|
|||
# "sourceClipsFolders": string, list of Media Pool folder objects to search for source clips if the media is not present in current folder
|
||||
|
||||
Export(fileName, exportType, exportSubtype) --> Bool # Exports timeline to 'fileName' as per input exportType & exportSubtype format.
|
||||
# Refer to section "Looking up timeline exports properties" for information on the parameters.
|
||||
# Refer to section "Looking up timeline export properties" for information on the parameters.
|
||||
GetSetting(settingName) --> string # Returns value of timeline setting (indicated by settingName : string). Check the section below for more information.
|
||||
SetSetting(settingName, settingValue) --> Bool # Sets timeline setting (indicated by settingName : string) to the value (settingValue : string). Check the section below for more information.
|
||||
InsertGeneratorIntoTimeline(generatorName) --> TimelineItem # Inserts a generator (indicated by generatorName : string) into the timeline.
|
||||
|
|
@ -313,6 +340,8 @@ Timeline
|
|||
GrabStill() --> galleryStill # Grabs still from the current video clip. Returns a GalleryStill object.
|
||||
GrabAllStills(stillFrameSource) --> [galleryStill] # Grabs stills from all the clips of the timeline at 'stillFrameSource' (1 - First frame, 2 - Middle frame). Returns the list of GalleryStill objects.
|
||||
GetUniqueId() --> string # Returns a unique ID for the timeline
|
||||
CreateSubtitlesFromAudio() --> Bool # Creates subtitles from audio for the timeline. Returns True on success, False otherwise.
|
||||
DetectSceneCuts() --> Bool # Detects and makes scene cuts along the timeline. Returns True if successful, False otherwise.
|
||||
|
||||
TimelineItem
|
||||
GetName() --> string # Returns the item name.
|
||||
|
|
@ -362,6 +391,7 @@ TimelineItem
|
|||
GetStereoLeftFloatingWindowParams() --> {keyframes...} # For the LEFT eye -> returns a dict (offset -> dict) of keyframe offsets and respective floating window params. Value at particular offset includes the left, right, top and bottom floating window values.
|
||||
GetStereoRightFloatingWindowParams() --> {keyframes...} # For the RIGHT eye -> returns a dict (offset -> dict) of keyframe offsets and respective floating window params. Value at particular offset includes the left, right, top and bottom floating window values.
|
||||
GetNumNodes() --> int # Returns the number of nodes in the current graph for the timeline item
|
||||
ApplyArriCdlLut() --> Bool # Applies ARRI CDL and LUT. Returns True if successful, False otherwise.
|
||||
SetLUT(nodeIndex, lutPath) --> Bool # Sets LUT on the node mapping the node index provided, 1 <= nodeIndex <= total number of nodes.
|
||||
# The lutPath can be an absolute path, or a relative path (based off custom LUT paths or the master LUT path).
|
||||
# The operation is successful for valid lut paths that Resolve has already discovered (see Project.RefreshLUTList).
|
||||
|
|
@ -376,8 +406,16 @@ TimelineItem
|
|||
SelectTakeByIndex(idx) --> Bool # Selects a take by index, 1 <= idx <= number of takes.
|
||||
FinalizeTake() --> Bool # Finalizes take selection.
|
||||
CopyGrades([tgtTimelineItems]) --> Bool # Copies the current grade to all the items in tgtTimelineItems list. Returns True on success and False if any error occurred.
|
||||
SetClipEnabled(Bool) --> Bool # Sets clip enabled based on argument.
|
||||
GetClipEnabled() --> Bool # Gets clip enabled status.
|
||||
UpdateSidecar() --> Bool # Updates sidecar file for BRAW clips or RMD file for R3D clips.
|
||||
GetUniqueId() --> string # Returns a unique ID for the timeline item
|
||||
LoadBurnInPreset(presetName) --> Bool # Loads user defined data burn in preset for clip when supplied presetName (string). Returns true if successful.
|
||||
GetNodeLabel(nodeIndex) --> string # Returns the label of the node at nodeIndex.
|
||||
CreateMagicMask(mode) --> Bool # Returns True if magic mask was created successfully, False otherwise. mode can "F" (forward), "B" (backward), or "BI" (bidirection)
|
||||
RegenerateMagicMask() --> Bool # Returns True if magic mask was regenerated successfully, False otherwise.
|
||||
Stabilize() --> Bool # Returns True if stabilization was successful, False otherwise
|
||||
SmartReframe() --> Bool # Performs Smart Reframe. Returns True if successful, False otherwise.
|
||||
|
||||
Gallery
|
||||
GetAlbumName(galleryStillAlbum) --> string # Returns the name of the GalleryStillAlbum object 'galleryStillAlbum'.
|
||||
|
|
@ -422,9 +460,11 @@ Invoke "Project:SetSetting", "Timeline:SetSetting" or "MediaPoolItem:SetClipProp
|
|||
ensure the success of the operation. You can troubleshoot the validity of keys and values by setting the desired result from the UI and checking property snapshots before and after the change.
|
||||
|
||||
The following Project properties have specifically enumerated values:
|
||||
"superScale" - the property value is an enumerated integer between 0 and 3 with these meanings: 0=Auto, 1=no scaling, and 2, 3 and 4 represent the Super Scale multipliers 2x, 3x and 4x.
|
||||
"superScale" - the property value is an enumerated integer between 0 and 4 with these meanings: 0=Auto, 1=no scaling, and 2, 3 and 4 represent the Super Scale multipliers 2x, 3x and 4x.
|
||||
for super scale multiplier '2x Enhanced', exactly 4 arguments must be passed as outlined below. If less than 4 arguments are passed, it will default to 2x.
|
||||
Affects:
|
||||
• x = Project:GetSetting('superScale') and Project:SetSetting('superScale', x)
|
||||
• for '2x Enhanced' --> Project:SetSetting('superScale', 2, sharpnessValue, noiseReductionValue), where sharpnessValue is a float in the range [0.0, 1.0] and noiseReductionValue is a float in the range [0.0, 1.0]
|
||||
|
||||
"timelineFrameRate" - the property value is one of the frame rates available to the user in project settings under "Timeline frame rate" option. Drop Frame can be configured for supported frame rates
|
||||
by appending the frame rate with "DF", e.g. "29.97 DF" will enable drop frame and "29.97" will disable drop frame
|
||||
|
|
@ -432,9 +472,11 @@ Affects:
|
|||
• x = Project:GetSetting('timelineFrameRate') and Project:SetSetting('timelineFrameRate', x)
|
||||
|
||||
The following Clip properties have specifically enumerated values:
|
||||
"superScale" - the property value is an enumerated integer between 1 and 3 with these meanings: 1=no scaling, and 2, 3 and 4 represent the Super Scale multipliers 2x, 3x and 4x.
|
||||
"Super Scale" - the property value is an enumerated integer between 1 and 4 with these meanings: 1=no scaling, and 2, 3 and 4 represent the Super Scale multipliers 2x, 3x and 4x.
|
||||
for super scale multiplier '2x Enhanced', exactly 4 arguments must be passed as outlined below. If less than 4 arguments are passed, it will default to 2x.
|
||||
Affects:
|
||||
• x = MediaPoolItem:GetClipProperty('Super Scale') and MediaPoolItem:SetClipProperty('Super Scale', x)
|
||||
• for '2x Enhanced' --> MediaPoolItem:SetClipProperty('Super Scale', 2, sharpnessValue, noiseReductionValue), where sharpnessValue is a float in the range [0.0, 1.0] and noiseReductionValue is a float in the range [0.0, 1.0]
|
||||
|
||||
|
||||
Looking up Render Settings
|
||||
|
|
@ -478,11 +520,6 @@ exportType can be one of the following constants:
|
|||
- resolve.EXPORT_DRT
|
||||
- resolve.EXPORT_EDL
|
||||
- resolve.EXPORT_FCP_7_XML
|
||||
- resolve.EXPORT_FCPXML_1_3
|
||||
- resolve.EXPORT_FCPXML_1_4
|
||||
- resolve.EXPORT_FCPXML_1_5
|
||||
- resolve.EXPORT_FCPXML_1_6
|
||||
- resolve.EXPORT_FCPXML_1_7
|
||||
- resolve.EXPORT_FCPXML_1_8
|
||||
- resolve.EXPORT_FCPXML_1_9
|
||||
- resolve.EXPORT_FCPXML_1_10
|
||||
|
|
@ -492,6 +529,8 @@ exportType can be one of the following constants:
|
|||
- resolve.EXPORT_TEXT_TAB
|
||||
- resolve.EXPORT_DOLBY_VISION_VER_2_9
|
||||
- resolve.EXPORT_DOLBY_VISION_VER_4_0
|
||||
- resolve.EXPORT_DOLBY_VISION_VER_5_1
|
||||
- resolve.EXPORT_OTIO
|
||||
exportSubtype can be one of the following enums:
|
||||
- resolve.EXPORT_NONE
|
||||
- resolve.EXPORT_AAF_NEW
|
||||
|
|
@ -504,6 +543,16 @@ When exportType is resolve.EXPORT_AAF, valid exportSubtype values are resolve.EX
|
|||
When exportType is resolve.EXPORT_EDL, valid exportSubtype values are resolve.EXPORT_CDL, resolve.EXPORT_SDL, resolve.EXPORT_MISSING_CLIPS and resolve.EXPORT_NONE.
|
||||
Note: Replace 'resolve.' when using the constants above, if a different Resolve class instance name is used.
|
||||
|
||||
Unsupported exportType types
|
||||
---------------------------------
|
||||
Starting with DaVinci Resolve 18.1, the following export types are not supported:
|
||||
- resolve.EXPORT_FCPXML_1_3
|
||||
- resolve.EXPORT_FCPXML_1_4
|
||||
- resolve.EXPORT_FCPXML_1_5
|
||||
- resolve.EXPORT_FCPXML_1_6
|
||||
- resolve.EXPORT_FCPXML_1_7
|
||||
|
||||
|
||||
Looking up Timeline item properties
|
||||
-----------------------------------
|
||||
This section covers additional notes for the function "TimelineItem:SetProperty" and "TimelineItem:GetProperty". These functions are used to get and set properties mentioned.
|
||||
|
|
@ -6,7 +6,10 @@ import contextlib
|
|||
from opentimelineio import opentime
|
||||
|
||||
from openpype.lib import Logger
|
||||
from openpype.pipeline.editorial import is_overlapping_otio_ranges
|
||||
from openpype.pipeline.editorial import (
|
||||
is_overlapping_otio_ranges,
|
||||
frames_to_timecode
|
||||
)
|
||||
|
||||
from ..otio import davinci_export as otio_export
|
||||
|
||||
|
|
@ -246,18 +249,22 @@ def get_media_pool_item(filepath, root: object = None) -> object:
|
|||
return None
|
||||
|
||||
|
||||
def create_timeline_item(media_pool_item: object,
|
||||
timeline: object = None,
|
||||
source_start: int = None,
|
||||
source_end: int = None) -> object:
|
||||
def create_timeline_item(
|
||||
media_pool_item: object,
|
||||
timeline: object = None,
|
||||
timeline_in: int = None,
|
||||
source_start: int = None,
|
||||
source_end: int = None,
|
||||
) -> object:
|
||||
"""
|
||||
Add media pool item to current or defined timeline.
|
||||
|
||||
Args:
|
||||
media_pool_item (resolve.MediaPoolItem): resolve's object
|
||||
timeline (resolve.Timeline)[optional]: resolve's object
|
||||
source_start (int)[optional]: media source input frame (sequence frame)
|
||||
source_end (int)[optional]: media source output frame (sequence frame)
|
||||
timeline (Optional[resolve.Timeline]): resolve's object
|
||||
timeline_in (Optional[int]): timeline input frame (sequence frame)
|
||||
source_start (Optional[int]): media source input frame (sequence frame)
|
||||
source_end (Optional[int]): media source output frame (sequence frame)
|
||||
|
||||
Returns:
|
||||
object: resolve.TimelineItem
|
||||
|
|
@ -269,16 +276,29 @@ def create_timeline_item(media_pool_item: object,
|
|||
clip_name = _clip_property("File Name")
|
||||
timeline = timeline or get_current_timeline()
|
||||
|
||||
# timing variables
|
||||
if all([timeline_in, source_start, source_end]):
|
||||
fps = timeline.GetSetting("timelineFrameRate")
|
||||
duration = source_end - source_start
|
||||
timecode_in = frames_to_timecode(timeline_in, fps)
|
||||
timecode_out = frames_to_timecode(timeline_in + duration, fps)
|
||||
else:
|
||||
timecode_in = None
|
||||
timecode_out = None
|
||||
|
||||
# if timeline was used then switch it to current timeline
|
||||
with maintain_current_timeline(timeline):
|
||||
# Add input mediaPoolItem to clip data
|
||||
clip_data = {"mediaPoolItem": media_pool_item}
|
||||
clip_data = {
|
||||
"mediaPoolItem": media_pool_item,
|
||||
}
|
||||
|
||||
# add source time range if input was given
|
||||
if source_start is not None:
|
||||
clip_data.update({"startFrame": source_start})
|
||||
if source_end is not None:
|
||||
clip_data.update({"endFrame": source_end})
|
||||
if source_start:
|
||||
clip_data["startFrame"] = source_start
|
||||
if source_end:
|
||||
clip_data["endFrame"] = source_end
|
||||
if timecode_in:
|
||||
clip_data["recordFrame"] = timecode_in
|
||||
|
||||
# add to timeline
|
||||
media_pool.AppendToTimeline([clip_data])
|
||||
|
|
@ -286,10 +306,15 @@ def create_timeline_item(media_pool_item: object,
|
|||
output_timeline_item = get_timeline_item(
|
||||
media_pool_item, timeline)
|
||||
|
||||
assert output_timeline_item, AssertionError(
|
||||
"Track Item with name `{}` doesn't exist on the timeline: `{}`".format(
|
||||
clip_name, timeline.GetName()
|
||||
))
|
||||
assert output_timeline_item, AssertionError((
|
||||
"Clip name '{}' was't created on the timeline: '{}' \n\n"
|
||||
"Please check if correct track position is activated, \n"
|
||||
"or if a clip is not already at the timeline in \n"
|
||||
"position: '{}' out: '{}'. \n\n"
|
||||
"Clip data: {}"
|
||||
).format(
|
||||
clip_name, timeline.GetName(), timecode_in, timecode_out, clip_data
|
||||
))
|
||||
return output_timeline_item
|
||||
|
||||
|
||||
|
|
@ -490,7 +515,7 @@ def imprint(timeline_item, data=None):
|
|||
|
||||
Arguments:
|
||||
timeline_item (hiero.core.TrackItem): hiero track item object
|
||||
data (dict): Any data which needst to be imprinted
|
||||
data (dict): Any data which needs to be imprinted
|
||||
|
||||
Examples:
|
||||
data = {
|
||||
|
|
|
|||
|
|
@ -306,11 +306,18 @@ class ClipLoader:
|
|||
self.active_project = lib.get_current_project()
|
||||
|
||||
# try to get value from options or evaluate key value for `handles`
|
||||
self.with_handles = options.get("handles") or bool(
|
||||
options.get("handles") is True)
|
||||
self.with_handles = options.get("handles") is True
|
||||
|
||||
# try to get value from options or evaluate key value for `load_to`
|
||||
self.new_timeline = options.get("newTimeline") or bool(
|
||||
"New timeline" in options.get("load_to", ""))
|
||||
self.new_timeline = (
|
||||
options.get("newTimeline") or
|
||||
options.get("load_to") == "New timeline"
|
||||
)
|
||||
# try to get value from options or evaluate key value for `load_how`
|
||||
self.sequential_load = (
|
||||
options.get("sequentially") or
|
||||
options.get("load_how") == "Sequentially in order"
|
||||
)
|
||||
|
||||
assert self._populate_data(), str(
|
||||
"Cannot Load selected data, look into database "
|
||||
|
|
@ -391,30 +398,70 @@ class ClipLoader:
|
|||
# create project bin for the media to be imported into
|
||||
self.active_bin = lib.create_bin(self.data["binPath"])
|
||||
|
||||
handle_start = self.data["versionData"].get("handleStart") or 0
|
||||
handle_end = self.data["versionData"].get("handleEnd") or 0
|
||||
|
||||
# create mediaItem in active project bin
|
||||
# create clip media
|
||||
media_pool_item = lib.create_media_pool_item(
|
||||
files,
|
||||
self.active_bin
|
||||
)
|
||||
_clip_property = media_pool_item.GetClipProperty
|
||||
|
||||
# get handles
|
||||
handle_start = self.data["versionData"].get("handleStart")
|
||||
handle_end = self.data["versionData"].get("handleEnd")
|
||||
if handle_start is None:
|
||||
handle_start = int(self.data["assetData"]["handleStart"])
|
||||
if handle_end is None:
|
||||
handle_end = int(self.data["assetData"]["handleEnd"])
|
||||
|
||||
# check frame duration from versionData or assetData
|
||||
frame_start = self.data["versionData"].get("frameStart")
|
||||
if frame_start is None:
|
||||
frame_start = self.data["assetData"]["frameStart"]
|
||||
|
||||
# check frame duration from versionData or assetData
|
||||
frame_end = self.data["versionData"].get("frameEnd")
|
||||
if frame_end is None:
|
||||
frame_end = self.data["assetData"]["frameEnd"]
|
||||
|
||||
db_frame_duration = int(frame_end) - int(frame_start) + 1
|
||||
|
||||
# get timeline in
|
||||
timeline_start = self.active_timeline.GetStartFrame()
|
||||
if self.sequential_load:
|
||||
# set timeline start frame
|
||||
timeline_in = int(timeline_start)
|
||||
else:
|
||||
# set timeline start frame + original clip in frame
|
||||
timeline_in = int(
|
||||
timeline_start + self.data["assetData"]["clipIn"])
|
||||
|
||||
source_in = int(_clip_property("Start"))
|
||||
source_out = int(_clip_property("End"))
|
||||
source_duration = int(_clip_property("Frames"))
|
||||
|
||||
if _clip_property("Type") == "Video":
|
||||
# check if source duration is shorter than db frame duration
|
||||
source_with_handles = True
|
||||
if source_duration < db_frame_duration:
|
||||
source_with_handles = False
|
||||
|
||||
# only exclude handles if source has no handles or
|
||||
# if user wants to load without handles
|
||||
if (
|
||||
not self.with_handles
|
||||
or not source_with_handles
|
||||
):
|
||||
source_in += handle_start
|
||||
source_out -= handle_end
|
||||
|
||||
# include handles
|
||||
if self.with_handles:
|
||||
source_in -= handle_start
|
||||
source_out += handle_end
|
||||
|
||||
# make track item from source in bin as item
|
||||
timeline_item = lib.create_timeline_item(
|
||||
media_pool_item, self.active_timeline, source_in, source_out)
|
||||
media_pool_item,
|
||||
self.active_timeline,
|
||||
timeline_in,
|
||||
source_in,
|
||||
source_out,
|
||||
)
|
||||
|
||||
print("Loading clips: `{}`".format(self.data["clip_name"]))
|
||||
return timeline_item
|
||||
|
|
@ -455,7 +502,7 @@ class TimelineItemLoader(LoaderPlugin):
|
|||
"""
|
||||
|
||||
options = [
|
||||
qargparse.Toggle(
|
||||
qargparse.Boolean(
|
||||
"handles",
|
||||
label="Include handles",
|
||||
default=0,
|
||||
|
|
@ -470,6 +517,16 @@ class TimelineItemLoader(LoaderPlugin):
|
|||
],
|
||||
default=0,
|
||||
help="Where do you want clips to be loaded?"
|
||||
),
|
||||
qargparse.Choice(
|
||||
"load_how",
|
||||
label="How to load clips",
|
||||
items=[
|
||||
"Original timing",
|
||||
"Sequentially in order"
|
||||
],
|
||||
default="Original timing",
|
||||
help="Would you like to place it at original timing?"
|
||||
)
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ from aiohttp_json_rpc.protocol import (
|
|||
)
|
||||
from aiohttp_json_rpc.exceptions import RpcError
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.lib import emit_event
|
||||
from openpype.hosts.tvpaint.tvpaint_plugin import get_plugin_files_path
|
||||
|
||||
|
|
@ -834,8 +835,12 @@ class BaseCommunicator:
|
|||
|
||||
|
||||
class QtCommunicator(BaseCommunicator):
|
||||
label = os.getenv("AVALON_LABEL")
|
||||
if not label:
|
||||
label = "AYON" if AYON_SERVER_ENABLED else "OpenPype"
|
||||
title = "{} Tools".format(label)
|
||||
menu_definitions = {
|
||||
"title": "OpenPype Tools",
|
||||
"title": title,
|
||||
"menu_items": [
|
||||
{
|
||||
"callback": "workfiles_tool",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import requests
|
|||
|
||||
import pyblish.api
|
||||
|
||||
from openpype.client import get_project, get_asset_by_name
|
||||
from openpype.client import get_asset_by_name
|
||||
from openpype.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost
|
||||
from openpype.hosts.tvpaint import TVPAINT_ROOT_DIR
|
||||
from openpype.settings import get_current_project_settings
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ class CollectWorkfileData(pyblish.api.ContextPlugin):
|
|||
"asset_name": context.data["asset"],
|
||||
"task_name": context.data["task"]
|
||||
}
|
||||
context.data["previous_context"] = current_context
|
||||
self.log.debug("Current context is: {}".format(current_context))
|
||||
|
||||
# Collect context from workfile metadata
|
||||
|
|
|
|||
|
|
@ -611,6 +611,12 @@ def get_openpype_username():
|
|||
settings and last option is to use `getpass.getuser()` which returns
|
||||
machine username.
|
||||
"""
|
||||
|
||||
if AYON_SERVER_ENABLED:
|
||||
import ayon_api
|
||||
|
||||
return ayon_api.get_user()["name"]
|
||||
|
||||
username = os.environ.get("OPENPYPE_USERNAME")
|
||||
if not username:
|
||||
local_settings = get_local_settings()
|
||||
|
|
|
|||
|
|
@ -25,10 +25,7 @@ from openpype.tests.lib import is_in_tests
|
|||
|
||||
from .publish.lib import filter_pyblish_plugins
|
||||
from .anatomy import Anatomy
|
||||
from .template_data import (
|
||||
get_template_data_with_names,
|
||||
get_template_data
|
||||
)
|
||||
from .template_data import get_template_data_with_names
|
||||
from .workfile import (
|
||||
get_workfile_template_key,
|
||||
get_custom_workfile_template_by_string_context,
|
||||
|
|
@ -483,6 +480,27 @@ def get_template_data_from_session(session=None, system_settings=None):
|
|||
)
|
||||
|
||||
|
||||
def get_current_context_template_data(system_settings=None):
|
||||
"""Prepare template data for current context.
|
||||
|
||||
Args:
|
||||
system_settings (Optional[Dict[str, Any]]): Prepared system settings.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any] Template data for current context.
|
||||
"""
|
||||
|
||||
context = get_current_context()
|
||||
project_name = context["project_name"]
|
||||
asset_name = context["asset_name"]
|
||||
task_name = context["task_name"]
|
||||
host_name = get_current_host_name()
|
||||
|
||||
return get_template_data_with_names(
|
||||
project_name, asset_name, task_name, host_name, system_settings
|
||||
)
|
||||
|
||||
|
||||
def get_workdir_from_session(session=None, template_key=None):
|
||||
"""Template data for template fill from session keys.
|
||||
|
||||
|
|
@ -661,70 +679,3 @@ def get_process_id():
|
|||
if _process_id is None:
|
||||
_process_id = str(uuid.uuid4())
|
||||
return _process_id
|
||||
|
||||
|
||||
def get_current_context_template_data():
|
||||
"""Template data for template fill from current context
|
||||
|
||||
Returns:
|
||||
Dict[str, Any] of the following tokens and their values
|
||||
Supported Tokens:
|
||||
- Regular Tokens
|
||||
- app
|
||||
- user
|
||||
- asset
|
||||
- parent
|
||||
- hierarchy
|
||||
- folder[name]
|
||||
- root[work, ...]
|
||||
- studio[code, name]
|
||||
- project[code, name]
|
||||
- task[type, name, short]
|
||||
|
||||
- Context Specific Tokens
|
||||
- assetData[frameStart]
|
||||
- assetData[frameEnd]
|
||||
- assetData[handleStart]
|
||||
- assetData[handleEnd]
|
||||
- assetData[frameStartHandle]
|
||||
- assetData[frameEndHandle]
|
||||
- assetData[resolutionHeight]
|
||||
- assetData[resolutionWidth]
|
||||
|
||||
"""
|
||||
|
||||
# pre-prepare get_template_data args
|
||||
current_context = get_current_context()
|
||||
project_name = current_context["project_name"]
|
||||
asset_name = current_context["asset_name"]
|
||||
anatomy = Anatomy(project_name)
|
||||
|
||||
# prepare get_template_data args
|
||||
project_doc = get_project(project_name)
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
task_name = current_context["task_name"]
|
||||
host_name = get_current_host_name()
|
||||
|
||||
# get regular template data
|
||||
template_data = get_template_data(
|
||||
project_doc, asset_doc, task_name, host_name
|
||||
)
|
||||
|
||||
template_data["root"] = anatomy.roots
|
||||
|
||||
# get context specific vars
|
||||
asset_data = asset_doc["data"].copy()
|
||||
|
||||
# compute `frameStartHandle` and `frameEndHandle`
|
||||
if "frameStart" in asset_data and "handleStart" in asset_data:
|
||||
asset_data["frameStartHandle"] = \
|
||||
asset_data["frameStart"] - asset_data["handleStart"]
|
||||
|
||||
if "frameEnd" in asset_data and "handleEnd" in asset_data:
|
||||
asset_data["frameEndHandle"] = \
|
||||
asset_data["frameEnd"] + asset_data["handleEnd"]
|
||||
|
||||
# add assetData
|
||||
template_data["assetData"] = asset_data
|
||||
|
||||
return template_data
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import pyblish.api
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.lib import get_openpype_username
|
||||
|
||||
|
||||
|
|
@ -7,7 +9,11 @@ class CollectCurrentUserPype(pyblish.api.ContextPlugin):
|
|||
|
||||
# Order must be after default pyblish-base CollectCurrentUser
|
||||
order = pyblish.api.CollectorOrder + 0.001
|
||||
label = "Collect Pype User"
|
||||
label = (
|
||||
"Collect AYON User"
|
||||
if AYON_SERVER_ENABLED
|
||||
else "Collect OpenPype User"
|
||||
)
|
||||
|
||||
def process(self, context):
|
||||
user = get_openpype_username()
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class CollectSceneVersion(pyblish.api.ContextPlugin):
|
|||
"hiero",
|
||||
"houdini",
|
||||
"maya",
|
||||
"max",
|
||||
"nuke",
|
||||
"photoshop",
|
||||
"resolve",
|
||||
|
|
|
|||
|
|
@ -344,13 +344,30 @@
|
|||
},
|
||||
"environment": {}
|
||||
},
|
||||
"11-0": {
|
||||
"use_python_2": true,
|
||||
"executables": {
|
||||
"windows": [
|
||||
"C:\\Program Files\\Nuke11.0v4\\Nuke11.0.exe"
|
||||
],
|
||||
"darwin": [],
|
||||
"linux": []
|
||||
},
|
||||
"arguments": {
|
||||
"windows": [],
|
||||
"darwin": [],
|
||||
"linux": []
|
||||
},
|
||||
"environment": {}
|
||||
},
|
||||
"__dynamic_keys_labels__": {
|
||||
"13-2": "13.2",
|
||||
"13-0": "13.0",
|
||||
"12-2": "12.2",
|
||||
"12-0": "12.0",
|
||||
"11-3": "11.3",
|
||||
"11-2": "11.2"
|
||||
"11-2": "11.2",
|
||||
"11-0": "11.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,70 +5,99 @@
|
|||
"is_group": true,
|
||||
"use_label_wrap": true,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
"type": "dict-conditional",
|
||||
"enum_key": "options",
|
||||
"enum_label": "Options",
|
||||
"enum_children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "shelf_set_name",
|
||||
"label": "Shelf Set Name"
|
||||
},
|
||||
{
|
||||
"type": "path",
|
||||
"key": "shelf_set_source_path",
|
||||
"label": "Shelf Set Path (optional)",
|
||||
"multipath": false,
|
||||
"multiplatform": true
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "shelf_definition",
|
||||
"label": "Shelves",
|
||||
"use_label_wrap": true,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "shelf_name",
|
||||
"label": "Shelf Name"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "tools_list",
|
||||
"label": "Tools",
|
||||
"use_label_wrap": true,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Name and Script Path are mandatory."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "label",
|
||||
"label": "Name"
|
||||
},
|
||||
{
|
||||
"type": "path",
|
||||
"key": "script",
|
||||
"label": "Script"
|
||||
},
|
||||
{
|
||||
"type": "path",
|
||||
"key": "icon",
|
||||
"label": "Icon"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "help",
|
||||
"label": "Help"
|
||||
}
|
||||
]
|
||||
|
||||
"key": "add_shelf_file",
|
||||
"label": "Add a .shelf file",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "add_shelf_file",
|
||||
"label": "Add a .shelf file",
|
||||
"children": [
|
||||
{
|
||||
"type": "path",
|
||||
"key": "shelf_set_source_path",
|
||||
"label": "Shelf Set Path",
|
||||
"multipath": false,
|
||||
"multiplatform": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "add_set_and_definitions",
|
||||
"label": "Add Shelf Set Name and Shelves Definitions",
|
||||
"children": [
|
||||
{
|
||||
"key": "add_set_and_definitions",
|
||||
"label": "Add Shelf Set Name and Shelves Definitions",
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "shelf_set_name",
|
||||
"label": "Shelf Set Name"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "shelf_definition",
|
||||
"label": "Shelves Definitions",
|
||||
"use_label_wrap": true,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "shelf_name",
|
||||
"label": "Shelf Name"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "tools_list",
|
||||
"label": "Tools",
|
||||
"use_label_wrap": true,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Name and Script Path are mandatory."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "label",
|
||||
"label": "Name"
|
||||
},
|
||||
{
|
||||
"type": "path",
|
||||
"key": "script",
|
||||
"label": "Script"
|
||||
},
|
||||
{
|
||||
"type": "path",
|
||||
"key": "icon",
|
||||
"label": "Icon"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "help",
|
||||
"label": "Help"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
from .window import (
|
||||
ContextDialog,
|
||||
main
|
||||
)
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
|
||||
if AYON_SERVER_ENABLED:
|
||||
from ._ayon_window import ContextDialog, main
|
||||
else:
|
||||
from ._openpype_window import ContextDialog, main
|
||||
|
||||
|
||||
__all__ = (
|
||||
"ContextDialog",
|
||||
"main"
|
||||
"main",
|
||||
)
|
||||
|
|
|
|||
799
openpype/tools/context_dialog/_ayon_window.py
Normal file
799
openpype/tools/context_dialog/_ayon_window.py
Normal file
|
|
@ -0,0 +1,799 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
import ayon_api
|
||||
from qtpy import QtWidgets, QtCore, QtGui
|
||||
|
||||
from openpype import style
|
||||
from openpype.lib.events import QueuedEventSystem
|
||||
from openpype.tools.ayon_utils.models import (
|
||||
ProjectsModel,
|
||||
HierarchyModel,
|
||||
)
|
||||
from openpype.tools.ayon_utils.widgets import (
|
||||
ProjectsCombobox,
|
||||
FoldersWidget,
|
||||
TasksWidget,
|
||||
)
|
||||
from openpype.tools.utils.lib import (
|
||||
center_window,
|
||||
get_openpype_qt_app,
|
||||
)
|
||||
|
||||
|
||||
class SelectionModel(object):
|
||||
"""Model handling selection changes.
|
||||
|
||||
Triggering events:
|
||||
- "selection.project.changed"
|
||||
- "selection.folder.changed"
|
||||
- "selection.task.changed"
|
||||
"""
|
||||
|
||||
event_source = "selection.model"
|
||||
|
||||
def __init__(self, controller):
|
||||
self._controller = controller
|
||||
|
||||
self._project_name = None
|
||||
self._folder_id = None
|
||||
self._task_id = None
|
||||
self._task_name = None
|
||||
|
||||
def get_selected_project_name(self):
|
||||
return self._project_name
|
||||
|
||||
def set_selected_project(self, project_name):
|
||||
self._project_name = project_name
|
||||
self._controller.emit_event(
|
||||
"selection.project.changed",
|
||||
{"project_name": project_name},
|
||||
self.event_source
|
||||
)
|
||||
|
||||
def get_selected_folder_id(self):
|
||||
return self._folder_id
|
||||
|
||||
def set_selected_folder(self, folder_id):
|
||||
if folder_id == self._folder_id:
|
||||
return
|
||||
self._folder_id = folder_id
|
||||
self._controller.emit_event(
|
||||
"selection.folder.changed",
|
||||
{
|
||||
"project_name": self._project_name,
|
||||
"folder_id": folder_id,
|
||||
},
|
||||
self.event_source
|
||||
)
|
||||
|
||||
def get_selected_task_name(self):
|
||||
return self._task_name
|
||||
|
||||
def get_selected_task_id(self):
|
||||
return self._task_id
|
||||
|
||||
def set_selected_task(self, task_id, task_name):
|
||||
if task_id == self._task_id:
|
||||
return
|
||||
|
||||
self._task_name = task_name
|
||||
self._task_id = task_id
|
||||
self._controller.emit_event(
|
||||
"selection.task.changed",
|
||||
{
|
||||
"project_name": self._project_name,
|
||||
"folder_id": self._folder_id,
|
||||
"task_name": task_name,
|
||||
"task_id": task_id,
|
||||
},
|
||||
self.event_source
|
||||
)
|
||||
|
||||
|
||||
class ExpectedSelection:
|
||||
def __init__(self, controller):
|
||||
self._project_name = None
|
||||
self._folder_id = None
|
||||
|
||||
self._project_selected = True
|
||||
self._folder_selected = True
|
||||
|
||||
self._controller = controller
|
||||
|
||||
def _emit_change(self):
|
||||
self._controller.emit_event(
|
||||
"expected_selection_changed",
|
||||
self.get_expected_selection_data(),
|
||||
)
|
||||
|
||||
def set_expected_selection(self, project_name, folder_id):
|
||||
self._project_name = project_name
|
||||
self._folder_id = folder_id
|
||||
|
||||
self._project_selected = False
|
||||
self._folder_selected = False
|
||||
self._emit_change()
|
||||
|
||||
def get_expected_selection_data(self):
|
||||
project_current = False
|
||||
folder_current = False
|
||||
if not self._project_selected:
|
||||
project_current = True
|
||||
elif not self._folder_selected:
|
||||
folder_current = True
|
||||
return {
|
||||
"project": {
|
||||
"name": self._project_name,
|
||||
"current": project_current,
|
||||
"selected": self._project_selected,
|
||||
},
|
||||
"folder": {
|
||||
"id": self._folder_id,
|
||||
"current": folder_current,
|
||||
"selected": self._folder_selected,
|
||||
},
|
||||
}
|
||||
|
||||
def is_expected_project_selected(self, project_name):
|
||||
return project_name == self._project_name and self._project_selected
|
||||
|
||||
def is_expected_folder_selected(self, folder_id):
|
||||
return folder_id == self._folder_id and self._folder_selected
|
||||
|
||||
def expected_project_selected(self, project_name):
|
||||
if project_name != self._project_name:
|
||||
return False
|
||||
self._project_selected = True
|
||||
self._emit_change()
|
||||
return True
|
||||
|
||||
def expected_folder_selected(self, folder_id):
|
||||
if folder_id != self._folder_id:
|
||||
return False
|
||||
self._folder_selected = True
|
||||
self._emit_change()
|
||||
return True
|
||||
|
||||
|
||||
class ContextDialogController:
|
||||
def __init__(self):
|
||||
self._event_system = None
|
||||
|
||||
self._projects_model = ProjectsModel(self)
|
||||
self._hierarchy_model = HierarchyModel(self)
|
||||
self._selection_model = SelectionModel(self)
|
||||
self._expected_selection = ExpectedSelection(self)
|
||||
|
||||
self._confirmed = False
|
||||
self._is_strict = False
|
||||
self._output_path = None
|
||||
|
||||
self._initial_project_name = None
|
||||
self._initial_folder_id = None
|
||||
self._initial_folder_label = None
|
||||
self._initial_project_found = True
|
||||
self._initial_folder_found = True
|
||||
self._initial_tasks_found = True
|
||||
|
||||
def reset(self):
|
||||
self._emit_event("controller.reset.started")
|
||||
|
||||
self._confirmed = False
|
||||
self._output_path = None
|
||||
|
||||
self._initial_project_name = None
|
||||
self._initial_folder_id = None
|
||||
self._initial_folder_label = None
|
||||
self._initial_project_found = True
|
||||
self._initial_folder_found = True
|
||||
self._initial_tasks_found = True
|
||||
|
||||
self._projects_model.reset()
|
||||
self._hierarchy_model.reset()
|
||||
|
||||
self._emit_event("controller.reset.finished")
|
||||
|
||||
def refresh(self):
|
||||
self._emit_event("controller.refresh.started")
|
||||
|
||||
self._projects_model.reset()
|
||||
self._hierarchy_model.reset()
|
||||
|
||||
self._emit_event("controller.refresh.finished")
|
||||
|
||||
# Event handling
|
||||
def emit_event(self, topic, data=None, source=None):
|
||||
"""Use implemented event system to trigger event."""
|
||||
|
||||
if data is None:
|
||||
data = {}
|
||||
self._get_event_system().emit(topic, data, source)
|
||||
|
||||
def register_event_callback(self, topic, callback):
|
||||
self._get_event_system().add_callback(topic, callback)
|
||||
|
||||
def set_output_json_path(self, output_path):
|
||||
self._output_path = output_path
|
||||
|
||||
def is_strict(self):
|
||||
return self._is_strict
|
||||
|
||||
def set_strict(self, enabled):
|
||||
if self._is_strict is enabled:
|
||||
return
|
||||
self._is_strict = enabled
|
||||
self._emit_event("strict.changed", {"strict": enabled})
|
||||
|
||||
# Data model functions
|
||||
def get_project_items(self, sender=None):
|
||||
return self._projects_model.get_project_items(sender)
|
||||
|
||||
def get_folder_items(self, project_name, sender=None):
|
||||
return self._hierarchy_model.get_folder_items(project_name, sender)
|
||||
|
||||
def get_task_items(self, project_name, folder_id, sender=None):
|
||||
return self._hierarchy_model.get_task_items(
|
||||
project_name, folder_id, sender
|
||||
)
|
||||
|
||||
# Expected selection helpers
|
||||
def set_expected_selection(self, project_name, folder_id):
|
||||
return self._expected_selection.set_expected_selection(
|
||||
project_name, folder_id
|
||||
)
|
||||
|
||||
def get_expected_selection_data(self):
|
||||
return self._expected_selection.get_expected_selection_data()
|
||||
|
||||
def expected_project_selected(self, project_name):
|
||||
self._expected_selection.expected_project_selected(project_name)
|
||||
|
||||
def expected_folder_selected(self, folder_id):
|
||||
self._expected_selection.expected_folder_selected(folder_id)
|
||||
|
||||
# Selection handling
|
||||
def get_selected_project_name(self):
|
||||
return self._selection_model.get_selected_project_name()
|
||||
|
||||
def set_selected_project(self, project_name):
|
||||
self._selection_model.set_selected_project(project_name)
|
||||
|
||||
def get_selected_folder_id(self):
|
||||
return self._selection_model.get_selected_folder_id()
|
||||
|
||||
def set_selected_folder(self, folder_id):
|
||||
self._selection_model.set_selected_folder(folder_id)
|
||||
|
||||
def get_selected_task_name(self):
|
||||
return self._selection_model.get_selected_task_name()
|
||||
|
||||
def get_selected_task_id(self):
|
||||
return self._selection_model.get_selected_task_id()
|
||||
|
||||
def set_selected_task(self, task_id, task_name):
|
||||
self._selection_model.set_selected_task(task_id, task_name)
|
||||
|
||||
def is_initial_context_valid(self):
|
||||
return self._initial_folder_found and self._initial_project_found
|
||||
|
||||
def set_initial_context(self, project_name=None, asset_name=None):
|
||||
result = self._prepare_initial_context(project_name, asset_name)
|
||||
|
||||
self._initial_project_name = project_name
|
||||
self._initial_folder_id = result["folder_id"]
|
||||
self._initial_folder_label = result["folder_label"]
|
||||
self._initial_project_found = result["project_found"]
|
||||
self._initial_folder_found = result["folder_found"]
|
||||
self._initial_tasks_found = result["tasks_found"]
|
||||
self._emit_event(
|
||||
"initial.context.changed",
|
||||
self.get_initial_context()
|
||||
)
|
||||
|
||||
def get_initial_context(self):
|
||||
return {
|
||||
"project_name": self._initial_project_name,
|
||||
"folder_id": self._initial_folder_id,
|
||||
"folder_label": self._initial_folder_label,
|
||||
"project_found": self._initial_project_found,
|
||||
"folder_found": self._initial_folder_found,
|
||||
"tasks_found": self._initial_tasks_found,
|
||||
"valid": (
|
||||
self._initial_project_found
|
||||
and self._initial_folder_found
|
||||
and self._initial_tasks_found
|
||||
)
|
||||
}
|
||||
|
||||
# Result of this tool
|
||||
def get_selected_context(self):
|
||||
project_name = None
|
||||
folder_id = None
|
||||
task_id = None
|
||||
task_name = None
|
||||
folder_path = None
|
||||
folder_name = None
|
||||
if self._confirmed:
|
||||
project_name = self.get_selected_project_name()
|
||||
folder_id = self.get_selected_folder_id()
|
||||
task_id = self.get_selected_task_id()
|
||||
task_name = self.get_selected_task_name()
|
||||
|
||||
folder_item = None
|
||||
if folder_id:
|
||||
folder_item = self._hierarchy_model.get_folder_item(
|
||||
project_name, folder_id)
|
||||
|
||||
if folder_item:
|
||||
folder_path = folder_item.path
|
||||
folder_name = folder_item.name
|
||||
return {
|
||||
"project": project_name,
|
||||
"project_name": project_name,
|
||||
"asset": folder_name,
|
||||
"folder_id": folder_id,
|
||||
"folder_path": folder_path,
|
||||
"task": task_name,
|
||||
"task_name": task_name,
|
||||
"task_id": task_id,
|
||||
"initial_context_valid": self.is_initial_context_valid(),
|
||||
}
|
||||
|
||||
def confirm_selection(self):
|
||||
self._confirmed = True
|
||||
|
||||
def store_output(self):
|
||||
if not self._output_path:
|
||||
return
|
||||
|
||||
dirpath = os.path.dirname(self._output_path)
|
||||
os.makedirs(dirpath, exist_ok=True)
|
||||
with open(self._output_path, "w") as stream:
|
||||
json.dump(self.get_selected_context(), stream, indent=4)
|
||||
|
||||
def _prepare_initial_context(self, project_name, asset_name):
|
||||
project_found = True
|
||||
output = {
|
||||
"project_found": project_found,
|
||||
"folder_id": None,
|
||||
"folder_label": None,
|
||||
"folder_found": True,
|
||||
"tasks_found": True,
|
||||
}
|
||||
if project_name is None:
|
||||
asset_name = None
|
||||
else:
|
||||
project = ayon_api.get_project(project_name)
|
||||
project_found = project is not None
|
||||
output["project_found"] = project_found
|
||||
if not project_found or not asset_name:
|
||||
return output
|
||||
|
||||
output["folder_label"] = asset_name
|
||||
|
||||
folder_id = None
|
||||
folder_found = False
|
||||
# First try to find by path
|
||||
folder = ayon_api.get_folder_by_path(project_name, asset_name)
|
||||
# Try to find by name if folder was not found by path
|
||||
# - prevent to query by name if 'asset_name' contains '/'
|
||||
if not folder and "/" not in asset_name:
|
||||
folder = next(
|
||||
ayon_api.get_folders(
|
||||
project_name, folder_names=[asset_name], fields=["id"]),
|
||||
None
|
||||
)
|
||||
|
||||
if folder:
|
||||
folder_id = folder["id"]
|
||||
folder_found = True
|
||||
|
||||
output["folder_id"] = folder_id
|
||||
output["folder_found"] = folder_found
|
||||
if not folder_found:
|
||||
return output
|
||||
|
||||
tasks = list(ayon_api.get_tasks(
|
||||
project_name, folder_ids=[folder_id], fields=["id"]
|
||||
))
|
||||
output["tasks_found"] = bool(tasks)
|
||||
return output
|
||||
|
||||
def _get_event_system(self):
|
||||
"""Inner event system for workfiles tool controller.
|
||||
|
||||
Is used for communication with UI. Event system is created on demand.
|
||||
|
||||
Returns:
|
||||
QueuedEventSystem: Event system which can trigger callbacks
|
||||
for topics.
|
||||
"""
|
||||
|
||||
if self._event_system is None:
|
||||
self._event_system = QueuedEventSystem()
|
||||
return self._event_system
|
||||
|
||||
def _emit_event(self, topic, data=None):
|
||||
self.emit_event(topic, data, "controller")
|
||||
|
||||
|
||||
class InvalidContextOverlay(QtWidgets.QFrame):
|
||||
confirmed = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent):
|
||||
super(InvalidContextOverlay, self).__init__(parent)
|
||||
self.setObjectName("OverlayFrame")
|
||||
|
||||
mid_widget = QtWidgets.QWidget(self)
|
||||
label_widget = QtWidgets.QLabel(
|
||||
"Requested context was not found...",
|
||||
mid_widget
|
||||
)
|
||||
|
||||
confirm_btn = QtWidgets.QPushButton("Close", mid_widget)
|
||||
|
||||
mid_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
mid_layout = QtWidgets.QVBoxLayout(mid_widget)
|
||||
mid_layout.setContentsMargins(0, 0, 0, 0)
|
||||
mid_layout.addWidget(label_widget, 0)
|
||||
mid_layout.addSpacing(30)
|
||||
mid_layout.addWidget(confirm_btn, 0)
|
||||
|
||||
main_layout = QtWidgets.QGridLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.addWidget(mid_widget, 1, 1)
|
||||
main_layout.setRowStretch(0, 1)
|
||||
main_layout.setRowStretch(1, 0)
|
||||
main_layout.setRowStretch(2, 1)
|
||||
main_layout.setColumnStretch(0, 1)
|
||||
main_layout.setColumnStretch(1, 0)
|
||||
main_layout.setColumnStretch(2, 1)
|
||||
|
||||
confirm_btn.clicked.connect(self.confirmed)
|
||||
|
||||
self._label_widget = label_widget
|
||||
self._confirm_btn = confirm_btn
|
||||
|
||||
def set_context(
|
||||
self,
|
||||
project_name,
|
||||
folder_label,
|
||||
project_found,
|
||||
folder_found,
|
||||
tasks_found,
|
||||
):
|
||||
lines = []
|
||||
if not project_found:
|
||||
lines.extend([
|
||||
"Requested project '{}' was not found...".format(
|
||||
project_name),
|
||||
])
|
||||
|
||||
elif not folder_found:
|
||||
lines.extend([
|
||||
"Requested folder was not found...",
|
||||
"",
|
||||
"Project: {}".format(project_name),
|
||||
"Folder: {}".format(folder_label),
|
||||
])
|
||||
elif not tasks_found:
|
||||
lines.extend([
|
||||
"Requested folder does not have any tasks...",
|
||||
"",
|
||||
"Project: {}".format(project_name),
|
||||
"Folder: {}".format(folder_label),
|
||||
])
|
||||
else:
|
||||
lines.append("Requested context was not found...")
|
||||
self._label_widget.setText("<br/>".join(lines))
|
||||
|
||||
|
||||
class ContextDialog(QtWidgets.QDialog):
|
||||
"""Dialog to select a context.
|
||||
|
||||
Context has 3 parts:
|
||||
- Project
|
||||
- Asset
|
||||
- Task
|
||||
|
||||
It is possible to predefine project and asset. In that case their widgets
|
||||
will have passed preselected values and will be disabled.
|
||||
"""
|
||||
def __init__(self, controller=None, parent=None):
|
||||
super(ContextDialog, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle("Select Context")
|
||||
self.setWindowIcon(QtGui.QIcon(style.app_icon_path()))
|
||||
|
||||
if controller is None:
|
||||
controller = ContextDialogController()
|
||||
|
||||
# Enable minimize and maximize for app
|
||||
window_flags = QtCore.Qt.Window
|
||||
if not parent:
|
||||
window_flags |= QtCore.Qt.WindowStaysOnTopHint
|
||||
self.setWindowFlags(window_flags)
|
||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
|
||||
# UI initialization
|
||||
main_splitter = QtWidgets.QSplitter(self)
|
||||
|
||||
# Left side widget contains project combobox and asset widget
|
||||
left_side_widget = QtWidgets.QWidget(main_splitter)
|
||||
|
||||
project_combobox = ProjectsCombobox(
|
||||
controller,
|
||||
parent=left_side_widget,
|
||||
handle_expected_selection=True
|
||||
)
|
||||
project_combobox.set_select_item_visible(True)
|
||||
|
||||
# Assets widget
|
||||
folders_widget = FoldersWidget(
|
||||
controller,
|
||||
parent=left_side_widget,
|
||||
handle_expected_selection=True
|
||||
)
|
||||
|
||||
left_side_layout = QtWidgets.QVBoxLayout(left_side_widget)
|
||||
left_side_layout.setContentsMargins(0, 0, 0, 0)
|
||||
left_side_layout.addWidget(project_combobox, 0)
|
||||
left_side_layout.addWidget(folders_widget, 1)
|
||||
|
||||
# Right side of window contains only tasks
|
||||
tasks_widget = TasksWidget(controller, parent=main_splitter)
|
||||
|
||||
# Add widgets to main splitter
|
||||
main_splitter.addWidget(left_side_widget)
|
||||
main_splitter.addWidget(tasks_widget)
|
||||
|
||||
# Set stretch of both sides
|
||||
main_splitter.setStretchFactor(0, 7)
|
||||
main_splitter.setStretchFactor(1, 3)
|
||||
|
||||
# Add confimation button to bottom right
|
||||
ok_btn = QtWidgets.QPushButton("OK", self)
|
||||
|
||||
buttons_layout = QtWidgets.QHBoxLayout()
|
||||
buttons_layout.setContentsMargins(0, 0, 0, 0)
|
||||
buttons_layout.addStretch(1)
|
||||
buttons_layout.addWidget(ok_btn, 0)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.addWidget(main_splitter, 1)
|
||||
main_layout.addLayout(buttons_layout, 0)
|
||||
|
||||
overlay_widget = InvalidContextOverlay(self)
|
||||
overlay_widget.setVisible(False)
|
||||
|
||||
ok_btn.clicked.connect(self._on_ok_click)
|
||||
project_combobox.refreshed.connect(self._on_projects_refresh)
|
||||
overlay_widget.confirmed.connect(self._on_overlay_confirm)
|
||||
|
||||
controller.register_event_callback(
|
||||
"selection.project.changed",
|
||||
self._on_project_selection_change
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"selection.folder.changed",
|
||||
self._on_folder_selection_change
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"selection.task.changed",
|
||||
self._on_task_selection_change
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"initial.context.changed",
|
||||
self._on_init_context_change
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"strict.changed",
|
||||
self._on_strict_changed
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"controller.reset.finished",
|
||||
self._on_controller_reset
|
||||
)
|
||||
controller.register_event_callback(
|
||||
"controller.refresh.finished",
|
||||
self._on_controller_refresh
|
||||
)
|
||||
|
||||
# Set stylehseet and resize window on first show
|
||||
self._first_show = True
|
||||
self._visible = False
|
||||
|
||||
self._controller = controller
|
||||
|
||||
self._project_combobox = project_combobox
|
||||
self._folders_widget = folders_widget
|
||||
self._tasks_widget = tasks_widget
|
||||
|
||||
self._ok_btn = ok_btn
|
||||
|
||||
self._overlay_widget = overlay_widget
|
||||
|
||||
self._apply_strict_changes(self.is_strict())
|
||||
|
||||
def is_strict(self):
|
||||
return self._controller.is_strict()
|
||||
|
||||
def showEvent(self, event):
|
||||
"""Override show event to do some callbacks."""
|
||||
super(ContextDialog, self).showEvent(event)
|
||||
self._visible = True
|
||||
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
# Set stylesheet and resize
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
self.resize(600, 700)
|
||||
center_window(self)
|
||||
self._controller.refresh()
|
||||
|
||||
initial_context = self._controller.get_initial_context()
|
||||
self._set_init_context(initial_context)
|
||||
self._overlay_widget.resize(self.size())
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(ContextDialog, self).resizeEvent(event)
|
||||
self._overlay_widget.resize(self.size())
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Ignore close event if is in strict state and context is not done."""
|
||||
if self.is_strict() and not self._ok_btn.isEnabled():
|
||||
# Allow to close window when initial context is not valid
|
||||
if self._controller.is_initial_context_valid():
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
if self.is_strict():
|
||||
self._confirm_selection()
|
||||
self._visible = False
|
||||
super(ContextDialog, self).closeEvent(event)
|
||||
|
||||
def set_strict(self, enabled):
|
||||
"""Change strictness of dialog."""
|
||||
|
||||
self._controller.set_strict(enabled)
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh all widget one by one.
|
||||
|
||||
When asset refresh is triggered we have to wait when is done so
|
||||
this method continues with `_on_asset_widget_refresh_finished`.
|
||||
"""
|
||||
|
||||
self._controller.reset()
|
||||
|
||||
def get_context(self):
|
||||
"""Result of dialog."""
|
||||
return self._controller.get_selected_context()
|
||||
|
||||
def set_context(self, project_name=None, asset_name=None):
|
||||
"""Set context which will be used and locked in dialog."""
|
||||
|
||||
self._controller.set_initial_context(project_name, asset_name)
|
||||
|
||||
def _on_projects_refresh(self):
|
||||
initial_context = self._controller.get_initial_context()
|
||||
self._controller.set_expected_selection(
|
||||
initial_context["project_name"],
|
||||
initial_context["folder_id"]
|
||||
)
|
||||
|
||||
def _on_overlay_confirm(self):
|
||||
self.close()
|
||||
|
||||
def _on_ok_click(self):
|
||||
# Store values to output
|
||||
self._confirm_selection()
|
||||
# Close dialog
|
||||
self.accept()
|
||||
|
||||
def _confirm_selection(self):
|
||||
self._controller.confirm_selection()
|
||||
|
||||
def _on_project_selection_change(self, event):
|
||||
self._on_selection_change(
|
||||
event["project_name"],
|
||||
)
|
||||
|
||||
def _on_folder_selection_change(self, event):
|
||||
self._on_selection_change(
|
||||
event["project_name"],
|
||||
event["folder_id"],
|
||||
)
|
||||
|
||||
def _on_task_selection_change(self, event):
|
||||
self._on_selection_change(
|
||||
event["project_name"],
|
||||
event["folder_id"],
|
||||
event["task_name"],
|
||||
)
|
||||
|
||||
def _on_selection_change(
|
||||
self, project_name, folder_id=None, task_name=None
|
||||
):
|
||||
self._validate_strict(project_name, folder_id, task_name)
|
||||
|
||||
def _on_init_context_change(self, event):
|
||||
self._set_init_context(event.data)
|
||||
if self._visible:
|
||||
self._controller.set_expected_selection(
|
||||
event["project_name"], event["folder_id"]
|
||||
)
|
||||
|
||||
def _set_init_context(self, init_context):
|
||||
project_name = init_context["project_name"]
|
||||
if not init_context["valid"]:
|
||||
self._overlay_widget.setVisible(True)
|
||||
self._overlay_widget.set_context(
|
||||
project_name,
|
||||
init_context["folder_label"],
|
||||
init_context["project_found"],
|
||||
init_context["folder_found"],
|
||||
init_context["tasks_found"]
|
||||
)
|
||||
return
|
||||
|
||||
self._overlay_widget.setVisible(False)
|
||||
if project_name:
|
||||
self._project_combobox.setEnabled(False)
|
||||
if init_context["folder_id"]:
|
||||
self._folders_widget.setEnabled(False)
|
||||
else:
|
||||
self._project_combobox.setEnabled(True)
|
||||
self._folders_widget.setEnabled(True)
|
||||
|
||||
def _on_strict_changed(self, event):
|
||||
self._apply_strict_changes(event["strict"])
|
||||
|
||||
def _on_controller_reset(self):
|
||||
self._apply_strict_changes(self.is_strict())
|
||||
self._project_combobox.refresh()
|
||||
|
||||
def _on_controller_refresh(self):
|
||||
self._project_combobox.refresh()
|
||||
|
||||
def _apply_strict_changes(self, is_strict):
|
||||
if not is_strict:
|
||||
if not self._ok_btn.isEnabled():
|
||||
self._ok_btn.setEnabled(True)
|
||||
return
|
||||
context = self._controller.get_selected_context()
|
||||
self._validate_strict(
|
||||
context["project_name"],
|
||||
context["folder_id"],
|
||||
context["task_name"]
|
||||
)
|
||||
|
||||
def _validate_strict(self, project_name, folder_id, task_name):
|
||||
if not self.is_strict():
|
||||
return
|
||||
|
||||
enabled = True
|
||||
if not project_name or not folder_id or not task_name:
|
||||
enabled = False
|
||||
self._ok_btn.setEnabled(enabled)
|
||||
|
||||
|
||||
def main(
|
||||
path_to_store,
|
||||
project_name=None,
|
||||
asset_name=None,
|
||||
strict=True
|
||||
):
|
||||
# Run Qt application
|
||||
app = get_openpype_qt_app()
|
||||
controller = ContextDialogController()
|
||||
controller.set_strict(strict)
|
||||
controller.set_initial_context(project_name, asset_name)
|
||||
controller.set_output_json_path(path_to_store)
|
||||
window = ContextDialog(controller=controller)
|
||||
window.show()
|
||||
app.exec_()
|
||||
controller.store_output()
|
||||
|
|
@ -22,7 +22,7 @@ class ContextDialog(QtWidgets.QDialog):
|
|||
|
||||
Context has 3 parts:
|
||||
- Project
|
||||
- Aseet
|
||||
- Asset
|
||||
- Task
|
||||
|
||||
It is possible to predefine project and asset. In that case their widgets
|
||||
|
|
@ -268,7 +268,7 @@ class ContextDialog(QtWidgets.QDialog):
|
|||
if self._set_context_asset:
|
||||
self._dbcon.Session["AVALON_ASSET"] = self._set_context_asset
|
||||
self._assets_widget.setEnabled(False)
|
||||
self._assets_widget.select_assets(self._set_context_asset)
|
||||
self._assets_widget.select_asset_by_name(self._set_context_asset)
|
||||
self._set_asset_to_tasks_widget()
|
||||
else:
|
||||
self._assets_widget.setEnabled(True)
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring Pype version."""
|
||||
__version__ = "3.17.5-nightly.2"
|
||||
__version__ = "3.17.5-nightly.3"
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class BlenderSettings(BaseSettingsModel):
|
|||
default_factory=BlenderImageIOModel,
|
||||
title="Color Management (ImageIO)"
|
||||
)
|
||||
render_settings: RenderSettingsModel = Field(
|
||||
RenderSettings: RenderSettingsModel = Field(
|
||||
default_factory=RenderSettingsModel, title="Render Settings")
|
||||
workfile_builder: TemplateWorkfileBaseOptions = Field(
|
||||
default_factory=TemplateWorkfileBaseOptions,
|
||||
|
|
@ -61,7 +61,7 @@ DEFAULT_VALUES = {
|
|||
},
|
||||
"set_frames_startup": True,
|
||||
"set_resolution_startup": True,
|
||||
"render_settings": DEFAULT_RENDER_SETTINGS,
|
||||
"RenderSettings": DEFAULT_RENDER_SETTINGS,
|
||||
"publish": DEFAULT_BLENDER_PUBLISH_SETTINGS,
|
||||
"workfile_builder": {
|
||||
"create_first_version": False,
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.3"
|
||||
__version__ = "0.1.4"
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ class ProcessSubmittedJobOnFarmModel(BaseSettingsModel):
|
|||
title="Reviewable products filter",
|
||||
)
|
||||
|
||||
@validator("aov_filter", "skip_integration_repre_list")
|
||||
@validator("aov_filter")
|
||||
def validate_unique_names(cls, value):
|
||||
ensure_unique_names(value)
|
||||
return value
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.2"
|
||||
__version__ = "0.1.3"
|
||||
|
|
|
|||
|
|
@ -22,16 +22,46 @@ class ShelfDefinitionModel(BaseSettingsModel):
|
|||
)
|
||||
|
||||
|
||||
class ShelvesModel(BaseSettingsModel):
|
||||
_layout = "expanded"
|
||||
shelf_set_name: str = Field("", title="Shelfs set name")
|
||||
|
||||
class AddShelfFileModel(BaseSettingsModel):
|
||||
shelf_set_source_path: MultiplatformPathModel = Field(
|
||||
default_factory=MultiplatformPathModel,
|
||||
title="Shelf Set Path (optional)"
|
||||
title="Shelf Set Path"
|
||||
)
|
||||
|
||||
|
||||
class AddSetAndDefinitionsModel(BaseSettingsModel):
|
||||
shelf_set_name: str = Field("", title="Shelf Set Name")
|
||||
shelf_definition: list[ShelfDefinitionModel] = Field(
|
||||
default_factory=list,
|
||||
title="Shelf Definitions"
|
||||
title="Shelves Definitions"
|
||||
)
|
||||
|
||||
|
||||
def shelves_enum_options():
|
||||
return [
|
||||
{
|
||||
"value": "add_shelf_file",
|
||||
"label": "Add a .shelf file"
|
||||
},
|
||||
{
|
||||
"value": "add_set_and_definitions",
|
||||
"label": "Add Shelf Set Name and Shelves Definitions"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
class ShelvesModel(BaseSettingsModel):
|
||||
options: str = Field(
|
||||
title="Options",
|
||||
description="Switch between shelves manager options",
|
||||
enum_resolver=shelves_enum_options,
|
||||
conditionalEnum=True
|
||||
)
|
||||
add_shelf_file: AddShelfFileModel = Field(
|
||||
title="Add a .shelf file",
|
||||
default_factory=AddShelfFileModel
|
||||
)
|
||||
add_set_and_definitions: AddSetAndDefinitionsModel = Field(
|
||||
title="Add Shelf Set Name and Shelves Definitions",
|
||||
default_factory=AddSetAndDefinitionsModel
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.2.6"
|
||||
__version__ = "0.2.7"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue