resolved conflict

This commit is contained in:
Kayla Man 2023-11-06 14:17:44 +08:00
commit 60ee15a88b
29 changed files with 1274 additions and 256 deletions

View file

@ -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

View file

@ -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)

View file

@ -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"):

View file

@ -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:

View file

@ -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:

View file

@ -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]} "

View file

@ -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)

View file

@ -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 Resolves Media Storage.
@ -179,6 +183,7 @@ MediaStorage
RevealInStorage(path) --> Bool # Expands and displays given file/folder path in Resolves 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.

View file

@ -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 = {

View file

@ -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?"
)
]

View file

@ -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",

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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

View file

@ -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()

View file

@ -24,6 +24,7 @@ class CollectSceneVersion(pyblish.api.ContextPlugin):
"hiero",
"houdini",
"maya",
"max",
"nuke",
"photoshop",
"resolve",

View file

@ -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"
}
}
},

View file

@ -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"
}
]
}
}
]
}
}
]
}
]
}
]
}

View file

@ -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",
)

View 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()

View file

@ -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)

View file

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

View file

@ -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,

View file

@ -1 +1 @@
__version__ = "0.1.3"
__version__ = "0.1.4"

View file

@ -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

View file

@ -1 +1 @@
__version__ = "0.1.2"
__version__ = "0.1.3"

View file

@ -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
)

View file

@ -1 +1 @@
__version__ = "0.2.6"
__version__ = "0.2.7"