diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py
index 368a70f395..718c4b574c 100644
--- a/openpype/hosts/flame/hooks/pre_flame_setup.py
+++ b/openpype/hosts/flame/hooks/pre_flame_setup.py
@@ -2,6 +2,7 @@ import os
import json
import tempfile
import contextlib
+import socket
from openpype.lib import (
PreLaunchHook, get_openpype_username)
from openpype.hosts import flame as opflame
@@ -32,6 +33,7 @@ class FlamePrelaunch(PreLaunchHook):
"""Hook entry method."""
project_doc = self.data["project_doc"]
user_name = get_openpype_username()
+ hostname = socket.gethostname() # not returning wiretap host name
self.log.debug("Collected user \"{}\"".format(user_name))
self.log.info(pformat(project_doc))
@@ -53,11 +55,12 @@ class FlamePrelaunch(PreLaunchHook):
"FieldDominance": "PROGRESSIVE"
}
+
data_to_script = {
# from settings
- "host_name": "localhost",
- "volume_name": "stonefs",
- "group_name": "staff",
+ "host_name": os.getenv("FLAME_WIRETAP_HOSTNAME") or hostname,
+ "volume_name": os.getenv("FLAME_WIRETAP_VOLUME"),
+ "group_name": os.getenv("FLAME_WIRETAP_GROUP"),
"color_policy": "ACES 1.1",
# from project
diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_thumbnails_jpg.xml b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_thumbnails_jpg.xml
new file mode 100644
index 0000000000..fa43ceece7
--- /dev/null
+++ b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_thumbnails_jpg.xml
@@ -0,0 +1,58 @@
+
+
+ sequence
+ Creates a 8-bit Jpeg file per segment.
+
+ NONE
+
+ <name>
+ True
+ True
+
+ image
+ FX
+ NoChange
+ False
+ 10
+
+ True
+ False
+
+ audio
+ FX
+ FlattenTracks
+ True
+ 10
+
+
+
+
+ 4
+ 1
+ 2
+
+
diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_video_h264.xml b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_video_h264.xml
new file mode 100644
index 0000000000..3ca185b8b4
--- /dev/null
+++ b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_video_h264.xml
@@ -0,0 +1,72 @@
+
+
+ sequence
+ Create MOV H264 files per segment with thumbnail
+
+ NONE
+
+ <name>
+ True
+ True
+
+ movie
+ FX
+ FlattenTracks
+ True
+ 5
+
+ True
+ False
+
+ audio
+ Original
+ NoChange
+ True
+ 5
+
+
+
+ QuickTime
+ <segment name>
+ 0
+ PCS_709
+ None
+ Autodesk
+ Flame
+ 2021
+
+
+
+ 4
+ 1
+ 2
+
+
\ No newline at end of file
diff --git a/openpype/hosts/hiero/otio/__init__.py b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/__init__.py
similarity index 100%
rename from openpype/hosts/hiero/otio/__init__.py
rename to openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/__init__.py
diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/app_utils.py b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/app_utils.py
new file mode 100644
index 0000000000..b255d8d3f5
--- /dev/null
+++ b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/app_utils.py
@@ -0,0 +1,162 @@
+import os
+import io
+import ConfigParser as CP
+from xml.etree import ElementTree as ET
+from contextlib import contextmanager
+
+PLUGIN_DIR = os.path.dirname(os.path.dirname(__file__))
+EXPORT_PRESETS_DIR = os.path.join(PLUGIN_DIR, "export_preset")
+
+CONFIG_DIR = os.path.join(os.path.expanduser(
+ "~/.openpype"), "openpype_flame_to_ftrack")
+
+
+@contextmanager
+def make_temp_dir():
+ import tempfile
+
+ try:
+ dirpath = tempfile.mkdtemp()
+
+ yield dirpath
+
+ except IOError as _error:
+ raise IOError("Not able to create temp dir file: {}".format(_error))
+
+ finally:
+ pass
+
+
+@contextmanager
+def get_config(section=None):
+ cfg_file_path = os.path.join(CONFIG_DIR, "settings.ini")
+
+ # create config dir
+ if not os.path.exists(CONFIG_DIR):
+ print("making dirs at: `{}`".format(CONFIG_DIR))
+ os.makedirs(CONFIG_DIR, mode=0o777)
+
+ # write default data to settings.ini
+ if not os.path.exists(cfg_file_path):
+ default_cfg = cfg_default()
+ config = CP.RawConfigParser()
+ config.readfp(io.BytesIO(default_cfg))
+ with open(cfg_file_path, 'wb') as cfg_file:
+ config.write(cfg_file)
+
+ try:
+ config = CP.RawConfigParser()
+ config.read(cfg_file_path)
+ if section:
+ _cfg_data = {
+ k: v
+ for s in config.sections()
+ for k, v in config.items(s)
+ if s == section
+ }
+ else:
+ _cfg_data = {s: dict(config.items(s)) for s in config.sections()}
+
+ yield _cfg_data
+
+ except IOError as _error:
+ raise IOError('Not able to read settings.ini file: {}'.format(_error))
+
+ finally:
+ pass
+
+
+def set_config(cfg_data, section=None):
+ cfg_file_path = os.path.join(CONFIG_DIR, "settings.ini")
+
+ config = CP.RawConfigParser()
+ config.read(cfg_file_path)
+
+ try:
+ if not section:
+ for section in cfg_data:
+ for key, value in cfg_data[section].items():
+ config.set(section, key, value)
+ else:
+ for key, value in cfg_data.items():
+ config.set(section, key, value)
+
+ with open(cfg_file_path, 'wb') as cfg_file:
+ config.write(cfg_file)
+
+ except IOError as _error:
+ raise IOError('Not able to write settings.ini file: {}'.format(_error))
+
+
+def cfg_default():
+ return """
+[main]
+workfile_start_frame = 1001
+shot_handles = 0
+shot_name_template = {sequence}_{shot}
+hierarchy_template = shots[Folder]/{sequence}[Sequence]
+create_task_type = Compositing
+"""
+
+
+def configure_preset(file_path, data):
+ split_fp = os.path.splitext(file_path)
+ new_file_path = split_fp[0] + "_tmp" + split_fp[-1]
+ with open(file_path, "r") as datafile:
+ tree = ET.parse(datafile)
+ for key, value in data.items():
+ for element in tree.findall(".//{}".format(key)):
+ print(element)
+ element.text = str(value)
+ tree.write(new_file_path)
+
+ return new_file_path
+
+
+def export_thumbnail(sequence, tempdir_path, data):
+ import flame
+ export_preset = os.path.join(
+ EXPORT_PRESETS_DIR,
+ "openpype_seg_thumbnails_jpg.xml"
+ )
+ new_path = configure_preset(export_preset, data)
+ poster_frame_exporter = flame.PyExporter()
+ poster_frame_exporter.foreground = True
+ poster_frame_exporter.export(sequence, new_path, tempdir_path)
+
+
+def export_video(sequence, tempdir_path, data):
+ import flame
+ export_preset = os.path.join(
+ EXPORT_PRESETS_DIR,
+ "openpype_seg_video_h264.xml"
+ )
+ new_path = configure_preset(export_preset, data)
+ poster_frame_exporter = flame.PyExporter()
+ poster_frame_exporter.foreground = True
+ poster_frame_exporter.export(sequence, new_path, tempdir_path)
+
+
+def timecode_to_frames(timecode, framerate):
+ def _seconds(value):
+ if isinstance(value, str):
+ _zip_ft = zip((3600, 60, 1, 1 / framerate), value.split(':'))
+ return sum(f * float(t) for f, t in _zip_ft)
+ elif isinstance(value, (int, float)):
+ return value / framerate
+ return 0
+
+ def _frames(seconds):
+ return seconds * framerate
+
+ def tc_to_frames(_timecode, start=None):
+ return _frames(_seconds(_timecode) - _seconds(start))
+
+ if '+' in timecode:
+ timecode = timecode.replace('+', ':')
+ elif '#' in timecode:
+ timecode = timecode.replace('#', ':')
+
+ frames = int(round(tc_to_frames(timecode, start='00:00:00:00')))
+
+ return frames
diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/ftrack_lib.py b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/ftrack_lib.py
new file mode 100644
index 0000000000..26b197ee1d
--- /dev/null
+++ b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/ftrack_lib.py
@@ -0,0 +1,448 @@
+import os
+import sys
+import six
+import re
+import json
+
+import app_utils
+
+# Fill following constants or set them via environment variable
+FTRACK_MODULE_PATH = None
+FTRACK_API_KEY = None
+FTRACK_API_USER = None
+FTRACK_SERVER = None
+
+
+def import_ftrack_api():
+ try:
+ import ftrack_api
+ return ftrack_api
+ except ImportError:
+ import sys
+ ftrk_m_p = FTRACK_MODULE_PATH or os.getenv("FTRACK_MODULE_PATH")
+ sys.path.append(ftrk_m_p)
+ import ftrack_api
+ return ftrack_api
+
+
+def get_ftrack_session():
+ import os
+ ftrack_api = import_ftrack_api()
+
+ # fill your own credentials
+ url = FTRACK_SERVER or os.getenv("FTRACK_SERVER") or ""
+ user = FTRACK_API_USER or os.getenv("FTRACK_API_USER") or ""
+ api = FTRACK_API_KEY or os.getenv("FTRACK_API_KEY") or ""
+
+ first_validation = True
+ if not user:
+ print('- Ftrack Username is not set')
+ first_validation = False
+ if not api:
+ print('- Ftrack API key is not set')
+ first_validation = False
+ if not first_validation:
+ return False
+
+ try:
+ return ftrack_api.Session(
+ server_url=url,
+ api_user=user,
+ api_key=api
+ )
+ except Exception as _e:
+ print("Can't log into Ftrack with used credentials: {}".format(_e))
+ ftrack_cred = {
+ 'Ftrack server': str(url),
+ 'Username': str(user),
+ 'API key': str(api),
+ }
+
+ item_lens = [len(key) + 1 for key in ftrack_cred]
+ justify_len = max(*item_lens)
+ for key, value in ftrack_cred.items():
+ print('{} {}'.format((key + ':').ljust(justify_len, ' '), value))
+ return False
+
+
+def get_project_task_types(project_entity):
+ tasks = {}
+ proj_template = project_entity['project_schema']
+ temp_task_types = proj_template['_task_type_schema']['types']
+
+ for type in temp_task_types:
+ if type['name'] not in tasks:
+ tasks[type['name']] = type
+
+ return tasks
+
+
+class FtrackComponentCreator:
+ default_location = "ftrack.server"
+ ftrack_locations = {}
+ thumbnails = []
+ videos = []
+ temp_dir = None
+
+ def __init__(self, session):
+ self.session = session
+ self._get_ftrack_location()
+
+ def generate_temp_data(self, selection, change_preset_data):
+ with app_utils.make_temp_dir() as tempdir_path:
+ for seq in selection:
+ app_utils.export_thumbnail(
+ seq, tempdir_path, change_preset_data)
+ app_utils.export_video(seq, tempdir_path, change_preset_data)
+
+ return tempdir_path
+
+ def collect_generated_data(self, tempdir_path):
+ temp_files = os.listdir(tempdir_path)
+ self.thumbnails = [f for f in temp_files if "jpg" in f]
+ self.videos = [f for f in temp_files if "mov" in f]
+ self.temp_dir = tempdir_path
+
+ def get_thumb_path(self, shot_name):
+ # get component files
+ thumb_f = next((f for f in self.thumbnails if shot_name in f), None)
+ return os.path.join(self.temp_dir, thumb_f)
+
+ def get_video_path(self, shot_name):
+ # get component files
+ video_f = next((f for f in self.videos if shot_name in f), None)
+ return os.path.join(self.temp_dir, video_f)
+
+ def close(self):
+ self.ftrack_locations = {}
+ self.session = None
+
+ def create_comonent(self, shot_entity, data, assetversion_entity=None):
+ self.shot_entity = shot_entity
+ location = self._get_ftrack_location()
+
+ file_path = data["file_path"]
+
+ # get extension
+ file = os.path.basename(file_path)
+ _n, ext = os.path.splitext(file)
+
+ name = "ftrackreview-mp4" if "mov" in ext else "thumbnail"
+
+ component_data = {
+ "name": name,
+ "file_path": file_path,
+ "file_type": ext,
+ "location": location
+ }
+
+ if name == "ftrackreview-mp4":
+ duration = data["duration"]
+ handles = data["handles"]
+ fps = data["fps"]
+ component_data["metadata"] = {
+ 'ftr_meta': json.dumps({
+ 'frameIn': int(0),
+ 'frameOut': int(duration + (handles * 2)),
+ 'frameRate': float(fps)
+ })
+ }
+ if not assetversion_entity:
+ # get assettype entity from session
+ assettype_entity = self._get_assettype({"short": "reference"})
+
+ # get or create asset entity from session
+ asset_entity = self._get_asset({
+ "name": "plateReference",
+ "type": assettype_entity,
+ "parent": self.shot_entity
+ })
+
+ # get or create assetversion entity from session
+ assetversion_entity = self._get_assetversion({
+ "version": 0,
+ "asset": asset_entity
+ })
+
+ # get or create component entity
+ self._set_component(component_data, {
+ "name": name,
+ "version": assetversion_entity,
+ })
+
+ return assetversion_entity
+
+ def _overwrite_members(self, entity, data):
+ origin_location = self._get_ftrack_location("ftrack.origin")
+ location = data.pop("location")
+
+ self._remove_component_from_location(entity, location)
+
+ entity["file_type"] = data["file_type"]
+
+ try:
+ origin_location.add_component(
+ entity, data["file_path"]
+ )
+ # Add components to location.
+ location.add_component(
+ entity, origin_location, recursive=True)
+ except Exception as __e:
+ print("Error: {}".format(__e))
+ self._remove_component_from_location(entity, origin_location)
+ origin_location.add_component(
+ entity, data["file_path"]
+ )
+ # Add components to location.
+ location.add_component(
+ entity, origin_location, recursive=True)
+
+ def _remove_component_from_location(self, entity, location):
+ print(location)
+ # Removing existing members from location
+ components = list(entity.get("members", []))
+ components += [entity]
+ for component in components:
+ for loc in component.get("component_locations", []):
+ if location["id"] == loc["location_id"]:
+ print("<< Removing component: {}".format(component))
+ location.remove_component(
+ component, recursive=False
+ )
+
+ # Deleting existing members on component entity
+ for member in entity.get("members", []):
+ self.session.delete(member)
+ print("<< Deleting member: {}".format(member))
+ del(member)
+
+ self._commit()
+
+ # Reset members in memory
+ if "members" in entity.keys():
+ entity["members"] = []
+
+ def _get_assettype(self, data):
+ return self.session.query(
+ self._query("AssetType", data)).first()
+
+ def _set_component(self, comp_data, base_data):
+ component_metadata = comp_data.pop("metadata", {})
+
+ component_entity = self.session.query(
+ self._query("Component", base_data)
+ ).first()
+
+ if component_entity:
+ # overwrite existing members in component enity
+ # - get data for member from `ftrack.origin` location
+ self._overwrite_members(component_entity, comp_data)
+
+ # Adding metadata
+ existing_component_metadata = component_entity["metadata"]
+ existing_component_metadata.update(component_metadata)
+ component_entity["metadata"] = existing_component_metadata
+ return
+
+ assetversion_entity = base_data["version"]
+ location = comp_data.pop("location")
+
+ component_entity = assetversion_entity.create_component(
+ comp_data["file_path"],
+ data=comp_data,
+ location=location
+ )
+
+ # Adding metadata
+ existing_component_metadata = component_entity["metadata"]
+ existing_component_metadata.update(component_metadata)
+ component_entity["metadata"] = existing_component_metadata
+
+ if comp_data["name"] == "thumbnail":
+ self.shot_entity["thumbnail_id"] = component_entity["id"]
+ assetversion_entity["thumbnail_id"] = component_entity["id"]
+
+ self._commit()
+
+ def _get_asset(self, data):
+ # first find already created
+ asset_entity = self.session.query(
+ self._query("Asset", data)
+ ).first()
+
+ if asset_entity:
+ return asset_entity
+
+ asset_entity = self.session.create("Asset", data)
+
+ # _commit if created
+ self._commit()
+
+ return asset_entity
+
+ def _get_assetversion(self, data):
+ assetversion_entity = self.session.query(
+ self._query("AssetVersion", data)
+ ).first()
+
+ if assetversion_entity:
+ return assetversion_entity
+
+ assetversion_entity = self.session.create("AssetVersion", data)
+
+ # _commit if created
+ self._commit()
+
+ return assetversion_entity
+
+ def _commit(self):
+ try:
+ self.session.commit()
+ except Exception:
+ tp, value, tb = sys.exc_info()
+ # self.session.rollback()
+ # self.session._configure_locations()
+ six.reraise(tp, value, tb)
+
+ def _get_ftrack_location(self, name=None):
+ name = name or self.default_location
+
+ if name in self.ftrack_locations:
+ return self.ftrack_locations[name]
+
+ location = self.session.query(
+ 'Location where name is "{}"'.format(name)
+ ).one()
+ self.ftrack_locations[name] = location
+ return location
+
+ def _query(self, entitytype, data):
+ """ Generate a query expression from data supplied.
+
+ If a value is not a string, we'll add the id of the entity to the
+ query.
+
+ Args:
+ entitytype (str): The type of entity to query.
+ data (dict): The data to identify the entity.
+ exclusions (list): All keys to exclude from the query.
+
+ Returns:
+ str: String query to use with "session.query"
+ """
+ queries = []
+ if sys.version_info[0] < 3:
+ for key, value in data.items():
+ if not isinstance(value, (str, int)):
+ print("value: {}".format(value))
+ if "id" in value.keys():
+ queries.append(
+ "{0}.id is \"{1}\"".format(key, value["id"])
+ )
+ else:
+ queries.append("{0} is \"{1}\"".format(key, value))
+ else:
+ for key, value in data.items():
+ if not isinstance(value, (str, int)):
+ print("value: {}".format(value))
+ if "id" in value.keys():
+ queries.append(
+ "{0}.id is \"{1}\"".format(key, value["id"])
+ )
+ else:
+ queries.append("{0} is \"{1}\"".format(key, value))
+
+ query = (
+ "select id from " + entitytype + " where " + " and ".join(queries)
+ )
+ print(query)
+ return query
+
+
+class FtrackEntityOperator:
+ def __init__(self, session, project_entity):
+ self.session = session
+ self.project_entity = project_entity
+
+ def commit(self):
+ try:
+ self.session.commit()
+ except Exception:
+ tp, value, tb = sys.exc_info()
+ self.session.rollback()
+ self.session._configure_locations()
+ six.reraise(tp, value, tb)
+
+ def create_ftrack_entity(self, session, type, name, parent=None):
+ parent = parent or self.project_entity
+ entity = session.create(type, {
+ 'name': name,
+ 'parent': parent
+ })
+ try:
+ session.commit()
+ except Exception:
+ tp, value, tb = sys.exc_info()
+ session.rollback()
+ session._configure_locations()
+ six.reraise(tp, value, tb)
+ return entity
+
+ def get_ftrack_entity(self, session, type, name, parent):
+ query = '{} where name is "{}" and project_id is "{}"'.format(
+ type, name, self.project_entity["id"])
+
+ try:
+ entity = session.query(query).one()
+ except Exception:
+ entity = None
+
+ # if entity doesnt exist then create one
+ if not entity:
+ entity = self.create_ftrack_entity(
+ session,
+ type,
+ name,
+ parent
+ )
+
+ return entity
+
+ def create_parents(self, template):
+ parents = []
+ t_split = template.split("/")
+ replace_patern = re.compile(r"(\[.*\])")
+ type_patern = re.compile(r"\[(.*)\]")
+
+ for t_s in t_split:
+ match_type = type_patern.findall(t_s)
+ if not match_type:
+ raise Exception((
+ "Missing correct type flag in : {}"
+ "/n Example: name[Type]").format(
+ t_s)
+ )
+ new_name = re.sub(replace_patern, "", t_s)
+ f_type = match_type.pop()
+
+ parents.append((new_name, f_type))
+
+ return parents
+
+ def create_task(self, task_type, task_types, parent):
+ existing_task = [
+ child for child in parent['children']
+ if child.entity_type.lower() == 'task'
+ if child['name'].lower() in task_type.lower()
+ ]
+
+ if existing_task:
+ return existing_task.pop()
+
+ task = self.session.create('Task', {
+ "name": task_type.lower(),
+ "parent": parent
+ })
+ task["type"] = task_types[task_type]
+
+ return task
diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/panel_app.py b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/panel_app.py
new file mode 100644
index 0000000000..9e39147776
--- /dev/null
+++ b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/panel_app.py
@@ -0,0 +1,524 @@
+from PySide2 import QtWidgets, QtCore
+
+import uiwidgets
+import app_utils
+import ftrack_lib
+
+
+def clear_inner_modules():
+ import sys
+
+ if "ftrack_lib" in sys.modules.keys():
+ del sys.modules["ftrack_lib"]
+ print("Ftrack Lib module removed from sys.modules")
+
+ if "app_utils" in sys.modules.keys():
+ del sys.modules["app_utils"]
+ print("app_utils module removed from sys.modules")
+
+ if "uiwidgets" in sys.modules.keys():
+ del sys.modules["uiwidgets"]
+ print("uiwidgets module removed from sys.modules")
+
+
+class MainWindow(QtWidgets.QWidget):
+
+ def __init__(self, klass, *args, **kwargs):
+ super(MainWindow, self).__init__(*args, **kwargs)
+ self.panel_class = klass
+
+ def closeEvent(self, event):
+ # clear all temp data
+ print("Removing temp data")
+ self.panel_class.clear_temp_data()
+ self.panel_class.close()
+ clear_inner_modules()
+ # now the panel can be closed
+ event.accept()
+
+
+class FlameToFtrackPanel(object):
+ session = None
+ temp_data_dir = None
+ processed_components = []
+ project_entity = None
+ task_types = {}
+ all_task_types = {}
+
+ # TreeWidget
+ columns = {
+ "Sequence name": {
+ "columnWidth": 200,
+ "order": 0
+ },
+ "Shot name": {
+ "columnWidth": 200,
+ "order": 1
+ },
+ "Clip duration": {
+ "columnWidth": 100,
+ "order": 2
+ },
+ "Shot description": {
+ "columnWidth": 500,
+ "order": 3
+ },
+ "Task description": {
+ "columnWidth": 500,
+ "order": 4
+ },
+ }
+
+ def __init__(self, selection):
+ print(selection)
+
+ self.session = ftrack_lib.get_ftrack_session()
+ self.selection = selection
+ self.window = MainWindow(self)
+
+ # creating ui
+ self.window.setMinimumSize(1500, 600)
+ self.window.setWindowTitle('Sequence Shots to Ftrack')
+ self.window.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
+ self.window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
+ self.window.setFocusPolicy(QtCore.Qt.StrongFocus)
+ self.window.setStyleSheet('background-color: #313131')
+
+ self._create_project_widget()
+ self._create_tree_widget()
+ self._set_sequence_params()
+ self._generate_widgets()
+ self._generate_layouts()
+ self._timeline_info()
+ self._fix_resolution()
+
+ self.window.show()
+
+ def _generate_widgets(self):
+ with app_utils.get_config("main") as cfg_data:
+ cfg_d = cfg_data
+
+ self._create_task_type_widget(cfg_d)
+
+ # input fields
+ self.shot_name_label = uiwidgets.FlameLabel(
+ 'Shot name template', 'normal', self.window)
+ self.shot_name_template_input = uiwidgets.FlameLineEdit(
+ cfg_d["shot_name_template"], self.window)
+
+ self.hierarchy_label = uiwidgets.FlameLabel(
+ 'Parents template', 'normal', self.window)
+ self.hierarchy_template_input = uiwidgets.FlameLineEdit(
+ cfg_d["hierarchy_template"], self.window)
+
+ self.start_frame_label = uiwidgets.FlameLabel(
+ 'Workfile start frame', 'normal', self.window)
+ self.start_frame_input = uiwidgets.FlameLineEdit(
+ cfg_d["workfile_start_frame"], self.window)
+
+ self.handles_label = uiwidgets.FlameLabel(
+ 'Shot handles', 'normal', self.window)
+ self.handles_input = uiwidgets.FlameLineEdit(
+ cfg_d["shot_handles"], self.window)
+
+ self.width_label = uiwidgets.FlameLabel(
+ 'Sequence width', 'normal', self.window)
+ self.width_input = uiwidgets.FlameLineEdit(
+ str(self.seq_width), self.window)
+
+ self.height_label = uiwidgets.FlameLabel(
+ 'Sequence height', 'normal', self.window)
+ self.height_input = uiwidgets.FlameLineEdit(
+ str(self.seq_height), self.window)
+
+ self.pixel_aspect_label = uiwidgets.FlameLabel(
+ 'Pixel aspect ratio', 'normal', self.window)
+ self.pixel_aspect_input = uiwidgets.FlameLineEdit(
+ str(1.00), self.window)
+
+ self.fps_label = uiwidgets.FlameLabel(
+ 'Frame rate', 'normal', self.window)
+ self.fps_input = uiwidgets.FlameLineEdit(
+ str(self.fps), self.window)
+
+ # Button
+ self.select_all_btn = uiwidgets.FlameButton(
+ 'Select All', self.select_all, self.window)
+
+ self.remove_temp_data_btn = uiwidgets.FlameButton(
+ 'Remove temp data', self.clear_temp_data, self.window)
+
+ self.ftrack_send_btn = uiwidgets.FlameButton(
+ 'Send to Ftrack', self._send_to_ftrack, self.window)
+
+ def _generate_layouts(self):
+ # left props
+ v_shift = 0
+ prop_layout_l = QtWidgets.QGridLayout()
+ prop_layout_l.setHorizontalSpacing(30)
+ if self.project_selector_enabled:
+ prop_layout_l.addWidget(self.project_select_label, v_shift, 0)
+ prop_layout_l.addWidget(self.project_select_input, v_shift, 1)
+ v_shift += 1
+ prop_layout_l.addWidget(self.shot_name_label, (v_shift + 0), 0)
+ prop_layout_l.addWidget(
+ self.shot_name_template_input, (v_shift + 0), 1)
+ prop_layout_l.addWidget(self.hierarchy_label, (v_shift + 1), 0)
+ prop_layout_l.addWidget(
+ self.hierarchy_template_input, (v_shift + 1), 1)
+ prop_layout_l.addWidget(self.start_frame_label, (v_shift + 2), 0)
+ prop_layout_l.addWidget(self.start_frame_input, (v_shift + 2), 1)
+ prop_layout_l.addWidget(self.handles_label, (v_shift + 3), 0)
+ prop_layout_l.addWidget(self.handles_input, (v_shift + 3), 1)
+ prop_layout_l.addWidget(self.task_type_label, (v_shift + 4), 0)
+ prop_layout_l.addWidget(
+ self.task_type_input, (v_shift + 4), 1)
+
+ # right props
+ prop_widget_r = QtWidgets.QWidget(self.window)
+ prop_layout_r = QtWidgets.QGridLayout(prop_widget_r)
+ prop_layout_r.setHorizontalSpacing(30)
+ prop_layout_r.setAlignment(
+ QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
+ prop_layout_r.setContentsMargins(0, 0, 0, 0)
+ prop_layout_r.addWidget(self.width_label, 1, 0)
+ prop_layout_r.addWidget(self.width_input, 1, 1)
+ prop_layout_r.addWidget(self.height_label, 2, 0)
+ prop_layout_r.addWidget(self.height_input, 2, 1)
+ prop_layout_r.addWidget(self.pixel_aspect_label, 3, 0)
+ prop_layout_r.addWidget(self.pixel_aspect_input, 3, 1)
+ prop_layout_r.addWidget(self.fps_label, 4, 0)
+ prop_layout_r.addWidget(self.fps_input, 4, 1)
+
+ # prop layout
+ prop_main_layout = QtWidgets.QHBoxLayout()
+ prop_main_layout.addLayout(prop_layout_l, 1)
+ prop_main_layout.addSpacing(20)
+ prop_main_layout.addWidget(prop_widget_r, 1)
+
+ # buttons layout
+ hbox = QtWidgets.QHBoxLayout()
+ hbox.addWidget(self.remove_temp_data_btn)
+ hbox.addWidget(self.select_all_btn)
+ hbox.addWidget(self.ftrack_send_btn)
+
+ # put all layouts together
+ main_frame = QtWidgets.QVBoxLayout(self.window)
+ main_frame.setMargin(20)
+ main_frame.addLayout(prop_main_layout)
+ main_frame.addWidget(self.tree)
+ main_frame.addLayout(hbox)
+
+ def _set_sequence_params(self):
+ for select in self.selection:
+ self.seq_height = select.height
+ self.seq_width = select.width
+ self.fps = float(str(select.frame_rate)[:-4])
+ break
+
+ def _create_task_type_widget(self, cfg_d):
+ print(self.project_entity)
+ self.task_types = ftrack_lib.get_project_task_types(
+ self.project_entity)
+
+ self.task_type_label = uiwidgets.FlameLabel(
+ 'Create Task (type)', 'normal', self.window)
+ self.task_type_input = uiwidgets.FlamePushButtonMenu(
+ cfg_d["create_task_type"], self.task_types.keys(), self.window)
+
+ def _create_project_widget(self):
+ import flame
+ # get project name from flame current project
+ self.project_name = flame.project.current_project.name
+
+ # get project from ftrack -
+ # ftrack project name has to be the same as flame project!
+ query = 'Project where full_name is "{}"'.format(self.project_name)
+
+ # globally used variables
+ self.project_entity = self.session.query(query).first()
+
+ self.project_selector_enabled = bool(not self.project_entity)
+
+ if self.project_selector_enabled:
+ self.all_projects = self.session.query(
+ "Project where status is active").all()
+ self.project_entity = self.all_projects[0]
+ project_names = [p["full_name"] for p in self.all_projects]
+ self.all_task_types = {
+ p["full_name"]: ftrack_lib.get_project_task_types(p).keys()
+ for p in self.all_projects
+ }
+ self.project_select_label = uiwidgets.FlameLabel(
+ 'Select Ftrack project', 'normal', self.window)
+ self.project_select_input = uiwidgets.FlamePushButtonMenu(
+ self.project_entity["full_name"], project_names, self.window)
+ self.project_select_input.selection_changed.connect(
+ self._on_project_changed)
+
+ def _create_tree_widget(self):
+ ordered_column_labels = self.columns.keys()
+ for _name, _value in self.columns.items():
+ ordered_column_labels.pop(_value["order"])
+ ordered_column_labels.insert(_value["order"], _name)
+
+ self.tree = uiwidgets.FlameTreeWidget(
+ ordered_column_labels, self.window)
+
+ # Allow multiple items in tree to be selected
+ self.tree.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
+
+ # Set tree column width
+ for _name, _val in self.columns.items():
+ self.tree.setColumnWidth(
+ _val["order"],
+ _val["columnWidth"]
+ )
+
+ # Prevent weird characters when shrinking tree columns
+ self.tree.setTextElideMode(QtCore.Qt.ElideNone)
+
+ def _resolve_project_entity(self):
+ if self.project_selector_enabled:
+ selected_project_name = self.project_select_input.text()
+ self.project_entity = next(
+ (p for p in self.all_projects
+ if p["full_name"] in selected_project_name),
+ None
+ )
+
+ def _save_ui_state_to_cfg(self):
+ _cfg_data_back = {
+ "shot_name_template": self.shot_name_template_input.text(),
+ "workfile_start_frame": self.start_frame_input.text(),
+ "shot_handles": self.handles_input.text(),
+ "hierarchy_template": self.hierarchy_template_input.text(),
+ "create_task_type": self.task_type_input.text()
+ }
+
+ # add cfg data back to settings.ini
+ app_utils.set_config(_cfg_data_back, "main")
+
+ def _send_to_ftrack(self):
+ # resolve active project and add it to self.project_entity
+ self._resolve_project_entity()
+ self._save_ui_state_to_cfg()
+
+ # get hanldes from gui input
+ handles = self.handles_input.text()
+
+ # get frame start from gui input
+ frame_start = int(self.start_frame_input.text())
+
+ # get task type from gui input
+ task_type = self.task_type_input.text()
+
+ # get resolution from gui inputs
+ fps = self.fps_input.text()
+
+ entity_operator = ftrack_lib.FtrackEntityOperator(
+ self.session, self.project_entity)
+ component_creator = ftrack_lib.FtrackComponentCreator(self.session)
+
+ if not self.temp_data_dir:
+ self.window.hide()
+ self.temp_data_dir = component_creator.generate_temp_data(
+ self.selection,
+ {
+ "nbHandles": handles
+ }
+ )
+ self.window.show()
+
+ # collect generated files to list data for farther use
+ component_creator.collect_generated_data(self.temp_data_dir)
+
+ # Get all selected items from treewidget
+ for item in self.tree.selectedItems():
+ # frame ranges
+ frame_duration = int(item.text(2))
+ frame_end = frame_start + frame_duration
+
+ # description
+ shot_description = item.text(3)
+ task_description = item.text(4)
+
+ # other
+ sequence_name = item.text(0)
+ shot_name = item.text(1)
+
+ thumb_fp = component_creator.get_thumb_path(shot_name)
+ video_fp = component_creator.get_video_path(shot_name)
+
+ print("processed comps: {}".format(self.processed_components))
+ print("processed thumb_fp: {}".format(thumb_fp))
+
+ processed = False
+ if thumb_fp not in self.processed_components:
+ self.processed_components.append(thumb_fp)
+ else:
+ processed = True
+
+ print("processed: {}".format(processed))
+
+ # populate full shot info
+ shot_attributes = {
+ "sequence": sequence_name,
+ "shot": shot_name,
+ "task": task_type
+ }
+
+ # format shot name template
+ _shot_name = self.shot_name_template_input.text().format(
+ **shot_attributes)
+
+ # format hierarchy template
+ _hierarchy_text = self.hierarchy_template_input.text().format(
+ **shot_attributes)
+ print(_hierarchy_text)
+
+ # solve parents
+ parents = entity_operator.create_parents(_hierarchy_text)
+ print(parents)
+
+ # obtain shot parents entities
+ _parent = None
+ for _name, _type in parents:
+ p_entity = entity_operator.get_ftrack_entity(
+ self.session,
+ _type,
+ _name,
+ _parent
+ )
+ print(p_entity)
+ _parent = p_entity
+
+ # obtain shot ftrack entity
+ f_s_entity = entity_operator.get_ftrack_entity(
+ self.session,
+ "Shot",
+ _shot_name,
+ _parent
+ )
+ print("Shot entity is: {}".format(f_s_entity))
+
+ if not processed:
+ # first create thumbnail and get version entity
+ assetversion_entity = component_creator.create_comonent(
+ f_s_entity, {
+ "file_path": thumb_fp
+ }
+ )
+
+ # secondly add video to version entity
+ component_creator.create_comonent(
+ f_s_entity, {
+ "file_path": video_fp,
+ "duration": frame_duration,
+ "handles": int(handles),
+ "fps": float(fps)
+ }, assetversion_entity
+ )
+
+ # create custom attributtes
+ custom_attrs = {
+ "frameStart": frame_start,
+ "frameEnd": frame_end,
+ "handleStart": int(handles),
+ "handleEnd": int(handles),
+ "resolutionWidth": int(self.width_input.text()),
+ "resolutionHeight": int(self.height_input.text()),
+ "pixelAspect": float(self.pixel_aspect_input.text()),
+ "fps": float(fps)
+ }
+
+ # update custom attributes on shot entity
+ for key in custom_attrs:
+ f_s_entity['custom_attributes'][key] = custom_attrs[key]
+
+ task_entity = entity_operator.create_task(
+ task_type, self.task_types, f_s_entity)
+
+ # Create notes.
+ user = self.session.query(
+ "User where username is \"{}\"".format(self.session.api_user)
+ ).first()
+
+ f_s_entity.create_note(shot_description, author=user)
+
+ if task_description:
+ task_entity.create_note(task_description, user)
+
+ entity_operator.commit()
+
+ component_creator.close()
+
+ def _fix_resolution(self):
+ # Center window in linux
+ resolution = QtWidgets.QDesktopWidget().screenGeometry()
+ self.window.move(
+ (resolution.width() / 2) - (self.window.frameSize().width() / 2),
+ (resolution.height() / 2) - (self.window.frameSize().height() / 2))
+
+ def _on_project_changed(self):
+ task_types = self.all_task_types[self.project_name]
+ self.task_type_input.set_menu_options(task_types)
+
+ def _timeline_info(self):
+ # identificar as informacoes dos segmentos na timeline
+ for sequence in self.selection:
+ frame_rate = float(str(sequence.frame_rate)[:-4])
+ for ver in sequence.versions:
+ for tracks in ver.tracks:
+ for segment in tracks.segments:
+ print(segment.attributes)
+ if str(segment.name)[1:-1] == "":
+ continue
+ # get clip frame duration
+ record_duration = str(segment.record_duration)[1:-1]
+ clip_duration = app_utils.timecode_to_frames(
+ record_duration, frame_rate)
+
+ # populate shot source metadata
+ shot_description = ""
+ for attr in ["tape_name", "source_name", "head",
+ "tail", "file_path"]:
+ if not hasattr(segment, attr):
+ continue
+ _value = getattr(segment, attr)
+ _label = attr.replace("_", " ").capitalize()
+ row = "{}: {}\n".format(_label, _value)
+ shot_description += row
+
+ # Add timeline segment to tree
+ QtWidgets.QTreeWidgetItem(self.tree, [
+ str(sequence.name)[1:-1], # seq
+ str(segment.name)[1:-1], # shot
+ str(clip_duration), # clip duration
+ shot_description, # shot description
+ str(segment.comment)[1:-1] # task description
+ ]).setFlags(
+ QtCore.Qt.ItemIsEditable
+ | QtCore.Qt.ItemIsEnabled
+ | QtCore.Qt.ItemIsSelectable
+ )
+
+ # Select top item in tree
+ self.tree.setCurrentItem(self.tree.topLevelItem(0))
+
+ def select_all(self, ):
+ self.tree.selectAll()
+
+ def clear_temp_data(self):
+ import shutil
+
+ self.processed_components = []
+
+ if self.temp_data_dir:
+ shutil.rmtree(self.temp_data_dir)
+ self.temp_data_dir = None
+ print("All Temp data were destroied ...")
+
+ def close(self):
+ self._save_ui_state_to_cfg()
+ self.session.close()
diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/uiwidgets.py b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/uiwidgets.py
new file mode 100644
index 0000000000..0d4807a4ea
--- /dev/null
+++ b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/uiwidgets.py
@@ -0,0 +1,212 @@
+from PySide2 import QtWidgets, QtCore
+
+
+class FlameLabel(QtWidgets.QLabel):
+ """
+ Custom Qt Flame Label Widget
+
+ For different label looks set label_type as:
+ 'normal', 'background', or 'outline'
+
+ To use:
+
+ label = FlameLabel('Label Name', 'normal', window)
+ """
+
+ def __init__(self, label_name, label_type, parent_window, *args, **kwargs):
+ super(FlameLabel, self).__init__(*args, **kwargs)
+
+ self.setText(label_name)
+ self.setParent(parent_window)
+ self.setMinimumSize(130, 28)
+ self.setMaximumHeight(28)
+ self.setFocusPolicy(QtCore.Qt.NoFocus)
+
+ # Set label stylesheet based on label_type
+
+ if label_type == 'normal':
+ self.setStyleSheet(
+ 'QLabel {color: #9a9a9a; border-bottom: 1px inset #282828; font: 14px "Discreet"}' # noqa
+ 'QLabel:disabled {color: #6a6a6a}'
+ )
+ elif label_type == 'background':
+ self.setAlignment(QtCore.Qt.AlignCenter)
+ self.setStyleSheet(
+ 'color: #9a9a9a; background-color: #393939; font: 14px "Discreet"' # noqa
+ )
+ elif label_type == 'outline':
+ self.setAlignment(QtCore.Qt.AlignCenter)
+ self.setStyleSheet(
+ 'color: #9a9a9a; background-color: #212121; border: 1px solid #404040; font: 14px "Discreet"' # noqa
+ )
+
+
+class FlameLineEdit(QtWidgets.QLineEdit):
+ """
+ Custom Qt Flame Line Edit Widget
+
+ Main window should include this:
+ window.setFocusPolicy(QtCore.Qt.StrongFocus)
+
+ To use:
+
+ line_edit = FlameLineEdit('Some text here', window)
+ """
+
+ def __init__(self, text, parent_window, *args, **kwargs):
+ super(FlameLineEdit, self).__init__(*args, **kwargs)
+
+ self.setText(text)
+ self.setParent(parent_window)
+ self.setMinimumHeight(28)
+ self.setMinimumWidth(110)
+ self.setStyleSheet(
+ 'QLineEdit {color: #9a9a9a; background-color: #373e47; selection-color: #262626; selection-background-color: #b8b1a7; font: 14px "Discreet"}' # noqa
+ 'QLineEdit:focus {background-color: #474e58}' # noqa
+ 'QLineEdit:disabled {color: #6a6a6a; background-color: #373737}'
+ )
+
+
+class FlameTreeWidget(QtWidgets.QTreeWidget):
+ """
+ Custom Qt Flame Tree Widget
+
+ To use:
+
+ tree_headers = ['Header1', 'Header2', 'Header3', 'Header4']
+ tree = FlameTreeWidget(tree_headers, window)
+ """
+
+ def __init__(self, tree_headers, parent_window, *args, **kwargs):
+ super(FlameTreeWidget, self).__init__(*args, **kwargs)
+
+ self.setMinimumWidth(1000)
+ self.setMinimumHeight(300)
+ self.setSortingEnabled(True)
+ self.sortByColumn(0, QtCore.Qt.AscendingOrder)
+ self.setAlternatingRowColors(True)
+ self.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.setStyleSheet(
+ 'QTreeWidget {color: #9a9a9a; background-color: #2a2a2a; alternate-background-color: #2d2d2d; font: 14px "Discreet"}' # noqa
+ 'QTreeWidget::item:selected {color: #d9d9d9; background-color: #474747; border: 1px solid #111111}' # noqa
+ 'QHeaderView {color: #9a9a9a; background-color: #393939; font: 14px "Discreet"}' # noqa
+ 'QTreeWidget::item:selected {selection-background-color: #111111}'
+ 'QMenu {color: #9a9a9a; background-color: #24303d; font: 14px "Discreet"}' # noqa
+ 'QMenu::item:selected {color: #d9d9d9; background-color: #3a4551}'
+ )
+ self.verticalScrollBar().setStyleSheet('color: #818181')
+ self.horizontalScrollBar().setStyleSheet('color: #818181')
+ self.setHeaderLabels(tree_headers)
+
+
+class FlameButton(QtWidgets.QPushButton):
+ """
+ Custom Qt Flame Button Widget
+
+ To use:
+
+ button = FlameButton('Button Name', do_this_when_pressed, window)
+ """
+
+ def __init__(self, button_name, do_when_pressed, parent_window,
+ *args, **kwargs):
+ super(FlameButton, self).__init__(*args, **kwargs)
+
+ self.setText(button_name)
+ self.setParent(parent_window)
+ self.setMinimumSize(QtCore.QSize(110, 28))
+ self.setMaximumSize(QtCore.QSize(110, 28))
+ self.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.clicked.connect(do_when_pressed)
+ self.setStyleSheet(
+ 'QPushButton {color: #9a9a9a; background-color: #424142; border-top: 1px inset #555555; border-bottom: 1px inset black; font: 14px "Discreet"}' # noqa
+ 'QPushButton:pressed {color: #d9d9d9; background-color: #4f4f4f; border-top: 1px inset #666666; font: italic}' # noqa
+ 'QPushButton:disabled {color: #747474; background-color: #353535; border-top: 1px solid #444444; border-bottom: 1px solid #242424}' # noqa
+ )
+
+
+class FlamePushButton(QtWidgets.QPushButton):
+ """
+ Custom Qt Flame Push Button Widget
+
+ To use:
+
+ pushbutton = FlamePushButton(' Button Name', True_or_False, window)
+ """
+
+ def __init__(self, button_name, button_checked, parent_window,
+ *args, **kwargs):
+ super(FlamePushButton, self).__init__(*args, **kwargs)
+
+ self.setText(button_name)
+ self.setParent(parent_window)
+ self.setCheckable(True)
+ self.setChecked(button_checked)
+ self.setMinimumSize(155, 28)
+ self.setMaximumSize(155, 28)
+ self.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.setStyleSheet(
+ 'QPushButton {color: #9a9a9a; background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: .93 #424142, stop: .94 #2e3b48); text-align: left; border-top: 1px inset #555555; border-bottom: 1px inset black; font: 14px "Discreet"}' # noqa
+ 'QPushButton:checked {color: #d9d9d9; background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: .93 #4f4f4f, stop: .94 #5a7fb4); font: italic; border: 1px inset black; border-bottom: 1px inset #404040; border-right: 1px inset #404040}' # noqa
+ 'QPushButton:disabled {color: #6a6a6a; background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: .93 #383838, stop: .94 #353535); font: light; border-top: 1px solid #575757; border-bottom: 1px solid #242424; border-right: 1px solid #353535; border-left: 1px solid #353535}' # noqa
+ 'QToolTip {color: black; background-color: #ffffde; border: black solid 1px}' # noqa
+ )
+
+
+class FlamePushButtonMenu(QtWidgets.QPushButton):
+ """
+ Custom Qt Flame Menu Push Button Widget
+
+ To use:
+
+ push_button_menu_options = ['Item 1', 'Item 2', 'Item 3', 'Item 4']
+ menu_push_button = FlamePushButtonMenu('push_button_name',
+ push_button_menu_options, window)
+
+ or
+
+ push_button_menu_options = ['Item 1', 'Item 2', 'Item 3', 'Item 4']
+ menu_push_button = FlamePushButtonMenu(push_button_menu_options[0],
+ push_button_menu_options, window)
+ """
+ selection_changed = QtCore.Signal(str)
+
+ def __init__(self, button_name, menu_options, parent_window,
+ *args, **kwargs):
+ super(FlamePushButtonMenu, self).__init__(*args, **kwargs)
+
+ self.setParent(parent_window)
+ self.setMinimumHeight(28)
+ self.setMinimumWidth(110)
+ self.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.setStyleSheet(
+ 'QPushButton {color: #9a9a9a; background-color: #24303d; font: 14px "Discreet"}' # noqa
+ 'QPushButton:disabled {color: #747474; background-color: #353535; border-top: 1px solid #444444; border-bottom: 1px solid #242424}' # noqa
+ )
+
+ pushbutton_menu = QtWidgets.QMenu(parent_window)
+ pushbutton_menu.setFocusPolicy(QtCore.Qt.NoFocus)
+ pushbutton_menu.setStyleSheet(
+ 'QMenu {color: #9a9a9a; background-color:#24303d; font: 14px "Discreet"}' # noqa
+ 'QMenu::item:selected {color: #d9d9d9; background-color: #3a4551}'
+ )
+
+ self._pushbutton_menu = pushbutton_menu
+ self.setMenu(pushbutton_menu)
+ self.set_menu_options(menu_options, button_name)
+
+ def set_menu_options(self, menu_options, current_option=None):
+ self._pushbutton_menu.clear()
+ current_option = current_option or menu_options[0]
+
+ for option in menu_options:
+ action = self._pushbutton_menu.addAction(option)
+ action.triggered.connect(self._on_action_trigger)
+
+ if current_option is not None:
+ self.setText(current_option)
+
+ def _on_action_trigger(self):
+ action = self.sender()
+ self.setText(action.text())
+ self.selection_changed.emit(action.text())
diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/openpype_flame_to_ftrack.py b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/openpype_flame_to_ftrack.py
new file mode 100644
index 0000000000..688b8b6ae3
--- /dev/null
+++ b/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/openpype_flame_to_ftrack.py
@@ -0,0 +1,38 @@
+from __future__ import print_function
+
+import os
+import sys
+
+SCRIPT_DIR = os.path.dirname(__file__)
+PACKAGE_DIR = os.path.join(SCRIPT_DIR, "modules")
+sys.path.append(PACKAGE_DIR)
+
+
+def flame_panel_executor(selection):
+ if "panel_app" in sys.modules.keys():
+ print("panel_app module is already loaded")
+ del sys.modules["panel_app"]
+ print("panel_app module removed from sys.modules")
+
+ import panel_app
+ panel_app.FlameToFtrackPanel(selection)
+
+
+def scope_sequence(selection):
+ import flame
+ return any(isinstance(item, flame.PySequence) for item in selection)
+
+
+def get_media_panel_custom_ui_actions():
+ return [
+ {
+ "name": "OpenPype: Ftrack",
+ "actions": [
+ {
+ "name": "Create Shots",
+ "isVisible": scope_sequence,
+ "execute": flame_panel_executor
+ }
+ ]
+ }
+ ]
diff --git a/openpype/hosts/hiero/__init__.py b/openpype/hosts/hiero/__init__.py
index 1781f808e2..15bd10fdb0 100644
--- a/openpype/hosts/hiero/__init__.py
+++ b/openpype/hosts/hiero/__init__.py
@@ -6,7 +6,7 @@ def add_implementation_envs(env, _app):
# Add requirements to HIERO_PLUGIN_PATH
pype_root = os.environ["OPENPYPE_REPOS_ROOT"]
new_hiero_paths = [
- os.path.join(pype_root, "openpype", "hosts", "hiero", "startup")
+ os.path.join(pype_root, "openpype", "hosts", "hiero", "api", "startup")
]
old_hiero_path = env.get("HIERO_PLUGIN_PATH") or ""
for path in old_hiero_path.split(os.pathsep):
diff --git a/openpype/hosts/hiero/api/__init__.py b/openpype/hosts/hiero/api/__init__.py
index 8d0105ae5f..f3c32b268c 100644
--- a/openpype/hosts/hiero/api/__init__.py
+++ b/openpype/hosts/hiero/api/__init__.py
@@ -23,6 +23,7 @@ from .pipeline import (
from .lib import (
pype_tag_name,
+ flatten,
get_track_items,
get_current_project,
get_current_sequence,
@@ -75,6 +76,7 @@ __all__ = [
# Lib functions
"pype_tag_name",
+ "flatten",
"get_track_items",
"get_current_project",
"get_current_sequence",
diff --git a/openpype/hosts/hiero/api/lib.py b/openpype/hosts/hiero/api/lib.py
index 21b65e5c96..9a22d8cf27 100644
--- a/openpype/hosts/hiero/api/lib.py
+++ b/openpype/hosts/hiero/api/lib.py
@@ -4,6 +4,7 @@ Host specific functions where host api is connected
import os
import re
import sys
+import platform
import ast
import shutil
import hiero
@@ -12,7 +13,6 @@ import avalon.api as avalon
import avalon.io
from openpype.api import (Logger, Anatomy, get_anatomy_settings)
from . import tags
-from compiler.ast import flatten
try:
from PySide.QtCore import QFile, QTextStream
@@ -38,6 +38,14 @@ self.default_bin_name = "openpypeBin"
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
+def flatten(_list):
+ for item in _list:
+ if isinstance(item, (list, tuple)):
+ for sub_item in flatten(item):
+ yield sub_item
+ else:
+ yield item
+
def get_current_project(remove_untitled=False):
projects = flatten(hiero.core.projects())
if not remove_untitled:
@@ -250,7 +258,7 @@ def set_track_item_pype_tag(track_item, data=None):
Returns:
hiero.core.Tag
"""
- data = data or dict()
+ data = data or {}
# basic Tag's attribute
tag_data = {
@@ -284,7 +292,7 @@ def get_track_item_pype_data(track_item):
Returns:
dict: data found on pype tag
"""
- data = dict()
+ data = {}
# get pype data tag from track item
tag = get_track_item_pype_tag(track_item)
@@ -299,8 +307,20 @@ def get_track_item_pype_data(track_item):
try:
# capture exceptions which are related to strings only
- value = ast.literal_eval(v)
- except (ValueError, SyntaxError):
+ if re.match(r"^[\d]+$", v):
+ value = int(v)
+ elif re.match(r"^True$", v):
+ value = True
+ elif re.match(r"^False$", v):
+ value = False
+ elif re.match(r"^None$", v):
+ value = None
+ elif re.match(r"^[\w\d_]+$", v):
+ value = v
+ else:
+ value = ast.literal_eval(v)
+ except (ValueError, SyntaxError) as msg:
+ log.warning(msg)
value = v
data.update({key: value})
@@ -729,9 +749,14 @@ def get_selected_track_items(sequence=None):
def set_selected_track_items(track_items_list, sequence=None):
_sequence = sequence or get_current_sequence()
+ # make sure only trackItems are in list selection
+ only_track_items = [
+ i for i in track_items_list
+ if isinstance(i, hiero.core.TrackItem)]
+
# Getting selection
timeline_editor = hiero.ui.getTimelineEditor(_sequence)
- return timeline_editor.setSelection(track_items_list)
+ return timeline_editor.setSelection(only_track_items)
def _read_doc_from_path(path):
@@ -759,6 +784,13 @@ def _set_hrox_project_knobs(doc, **knobs):
# set attributes to Project Tag
proj_elem = doc.documentElement().firstChildElement("Project")
for k, v in knobs.items():
+ if "ocioconfigpath" in k:
+ paths_to_format = v[platform.system().lower()]
+ for _path in paths_to_format:
+ v = _path.format(**os.environ)
+ if not os.path.exists(v):
+ continue
+ log.debug("Project colorspace knob `{}` was set to `{}`".format(k, v))
if isinstance(v, dict):
continue
proj_elem.setAttribute(str(k), v)
diff --git a/openpype/hosts/hiero/api/otio/__init__.py b/openpype/hosts/hiero/api/otio/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/openpype/hosts/hiero/otio/hiero_export.py b/openpype/hosts/hiero/api/otio/hiero_export.py
similarity index 98%
rename from openpype/hosts/hiero/otio/hiero_export.py
rename to openpype/hosts/hiero/api/otio/hiero_export.py
index af4322e3d9..abf510403e 100644
--- a/openpype/hosts/hiero/otio/hiero_export.py
+++ b/openpype/hosts/hiero/api/otio/hiero_export.py
@@ -5,7 +5,6 @@ import os
import re
import sys
import ast
-from compiler.ast import flatten
import opentimelineio as otio
from . import utils
import hiero.core
@@ -29,6 +28,15 @@ self.timeline = None
self.include_tags = True
+def flatten(_list):
+ for item in _list:
+ if isinstance(item, (list, tuple)):
+ for sub_item in flatten(item):
+ yield sub_item
+ else:
+ yield item
+
+
def get_current_hiero_project(remove_untitled=False):
projects = flatten(hiero.core.projects())
if not remove_untitled:
@@ -74,13 +82,11 @@ def create_time_effects(otio_clip, track_item):
otio_effect = otio.schema.LinearTimeWarp()
otio_effect.name = "Speed"
otio_effect.time_scalar = speed
- otio_effect.metadata = {}
# freeze frame effect
if speed == 0.:
otio_effect = otio.schema.FreezeFrame()
otio_effect.name = "FreezeFrame"
- otio_effect.metadata = {}
if otio_effect:
# add otio effect to clip effects
diff --git a/openpype/hosts/hiero/otio/hiero_import.py b/openpype/hosts/hiero/api/otio/hiero_import.py
similarity index 100%
rename from openpype/hosts/hiero/otio/hiero_import.py
rename to openpype/hosts/hiero/api/otio/hiero_import.py
diff --git a/openpype/hosts/hiero/otio/utils.py b/openpype/hosts/hiero/api/otio/utils.py
similarity index 100%
rename from openpype/hosts/hiero/otio/utils.py
rename to openpype/hosts/hiero/api/otio/utils.py
diff --git a/openpype/hosts/hiero/api/plugin.py b/openpype/hosts/hiero/api/plugin.py
index 75d1c1b18f..2bbb1df8c1 100644
--- a/openpype/hosts/hiero/api/plugin.py
+++ b/openpype/hosts/hiero/api/plugin.py
@@ -191,7 +191,7 @@ class CreatorWidget(QtWidgets.QDialog):
content_layout = content_layout or self.content_layout[-1]
# fix order of process by defined order value
- ordered_keys = data.keys()
+ ordered_keys = list(data.keys())
for k, v in data.items():
try:
# try removing a key from index which should
diff --git a/openpype/hosts/hiero/startup/HieroPlayer/PlayerPresets.hrox b/openpype/hosts/hiero/api/startup/HieroPlayer/PlayerPresets.hrox
similarity index 100%
rename from openpype/hosts/hiero/startup/HieroPlayer/PlayerPresets.hrox
rename to openpype/hosts/hiero/api/startup/HieroPlayer/PlayerPresets.hrox
diff --git a/openpype/hosts/hiero/startup/Icons/1_add_handles_end.png b/openpype/hosts/hiero/api/startup/Icons/1_add_handles_end.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/1_add_handles_end.png
rename to openpype/hosts/hiero/api/startup/Icons/1_add_handles_end.png
diff --git a/openpype/hosts/hiero/startup/Icons/2_add_handles.png b/openpype/hosts/hiero/api/startup/Icons/2_add_handles.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/2_add_handles.png
rename to openpype/hosts/hiero/api/startup/Icons/2_add_handles.png
diff --git a/openpype/hosts/hiero/startup/Icons/3D.png b/openpype/hosts/hiero/api/startup/Icons/3D.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/3D.png
rename to openpype/hosts/hiero/api/startup/Icons/3D.png
diff --git a/openpype/hosts/hiero/startup/Icons/3_add_handles_start.png b/openpype/hosts/hiero/api/startup/Icons/3_add_handles_start.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/3_add_handles_start.png
rename to openpype/hosts/hiero/api/startup/Icons/3_add_handles_start.png
diff --git a/openpype/hosts/hiero/startup/Icons/4_2D.png b/openpype/hosts/hiero/api/startup/Icons/4_2D.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/4_2D.png
rename to openpype/hosts/hiero/api/startup/Icons/4_2D.png
diff --git a/openpype/hosts/hiero/startup/Icons/edit.png b/openpype/hosts/hiero/api/startup/Icons/edit.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/edit.png
rename to openpype/hosts/hiero/api/startup/Icons/edit.png
diff --git a/openpype/hosts/hiero/startup/Icons/fusion.png b/openpype/hosts/hiero/api/startup/Icons/fusion.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/fusion.png
rename to openpype/hosts/hiero/api/startup/Icons/fusion.png
diff --git a/openpype/hosts/hiero/startup/Icons/hierarchy.png b/openpype/hosts/hiero/api/startup/Icons/hierarchy.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/hierarchy.png
rename to openpype/hosts/hiero/api/startup/Icons/hierarchy.png
diff --git a/openpype/hosts/hiero/startup/Icons/houdini.png b/openpype/hosts/hiero/api/startup/Icons/houdini.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/houdini.png
rename to openpype/hosts/hiero/api/startup/Icons/houdini.png
diff --git a/openpype/hosts/hiero/startup/Icons/layers.psd b/openpype/hosts/hiero/api/startup/Icons/layers.psd
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/layers.psd
rename to openpype/hosts/hiero/api/startup/Icons/layers.psd
diff --git a/openpype/hosts/hiero/startup/Icons/lense.png b/openpype/hosts/hiero/api/startup/Icons/lense.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/lense.png
rename to openpype/hosts/hiero/api/startup/Icons/lense.png
diff --git a/openpype/hosts/hiero/startup/Icons/lense1.png b/openpype/hosts/hiero/api/startup/Icons/lense1.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/lense1.png
rename to openpype/hosts/hiero/api/startup/Icons/lense1.png
diff --git a/openpype/hosts/hiero/startup/Icons/maya.png b/openpype/hosts/hiero/api/startup/Icons/maya.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/maya.png
rename to openpype/hosts/hiero/api/startup/Icons/maya.png
diff --git a/openpype/hosts/hiero/startup/Icons/nuke.png b/openpype/hosts/hiero/api/startup/Icons/nuke.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/nuke.png
rename to openpype/hosts/hiero/api/startup/Icons/nuke.png
diff --git a/openpype/hosts/hiero/startup/Icons/pype_icon.png b/openpype/hosts/hiero/api/startup/Icons/pype_icon.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/pype_icon.png
rename to openpype/hosts/hiero/api/startup/Icons/pype_icon.png
diff --git a/openpype/hosts/hiero/startup/Icons/resolution.png b/openpype/hosts/hiero/api/startup/Icons/resolution.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/resolution.png
rename to openpype/hosts/hiero/api/startup/Icons/resolution.png
diff --git a/openpype/hosts/hiero/startup/Icons/resolution.psd b/openpype/hosts/hiero/api/startup/Icons/resolution.psd
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/resolution.psd
rename to openpype/hosts/hiero/api/startup/Icons/resolution.psd
diff --git a/openpype/hosts/hiero/startup/Icons/retiming.png b/openpype/hosts/hiero/api/startup/Icons/retiming.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/retiming.png
rename to openpype/hosts/hiero/api/startup/Icons/retiming.png
diff --git a/openpype/hosts/hiero/startup/Icons/retiming.psd b/openpype/hosts/hiero/api/startup/Icons/retiming.psd
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/retiming.psd
rename to openpype/hosts/hiero/api/startup/Icons/retiming.psd
diff --git a/openpype/hosts/hiero/startup/Icons/review.png b/openpype/hosts/hiero/api/startup/Icons/review.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/review.png
rename to openpype/hosts/hiero/api/startup/Icons/review.png
diff --git a/openpype/hosts/hiero/startup/Icons/review.psd b/openpype/hosts/hiero/api/startup/Icons/review.psd
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/review.psd
rename to openpype/hosts/hiero/api/startup/Icons/review.psd
diff --git a/openpype/hosts/hiero/startup/Icons/volume.png b/openpype/hosts/hiero/api/startup/Icons/volume.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/volume.png
rename to openpype/hosts/hiero/api/startup/Icons/volume.png
diff --git a/openpype/hosts/hiero/startup/Icons/z_layer_bg.png b/openpype/hosts/hiero/api/startup/Icons/z_layer_bg.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/z_layer_bg.png
rename to openpype/hosts/hiero/api/startup/Icons/z_layer_bg.png
diff --git a/openpype/hosts/hiero/startup/Icons/z_layer_fg.png b/openpype/hosts/hiero/api/startup/Icons/z_layer_fg.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/z_layer_fg.png
rename to openpype/hosts/hiero/api/startup/Icons/z_layer_fg.png
diff --git a/openpype/hosts/hiero/startup/Icons/z_layer_main.png b/openpype/hosts/hiero/api/startup/Icons/z_layer_main.png
similarity index 100%
rename from openpype/hosts/hiero/startup/Icons/z_layer_main.png
rename to openpype/hosts/hiero/api/startup/Icons/z_layer_main.png
diff --git a/openpype/hosts/hiero/startup/Python/Startup/SpreadsheetExport.py b/openpype/hosts/hiero/api/startup/Python/Startup/SpreadsheetExport.py
similarity index 86%
rename from openpype/hosts/hiero/startup/Python/Startup/SpreadsheetExport.py
rename to openpype/hosts/hiero/api/startup/Python/Startup/SpreadsheetExport.py
index 3adea8051c..9c919e7cb4 100644
--- a/openpype/hosts/hiero/startup/Python/Startup/SpreadsheetExport.py
+++ b/openpype/hosts/hiero/api/startup/Python/Startup/SpreadsheetExport.py
@@ -18,7 +18,7 @@ except:
### Magic Widget Finding Methods - This stuff crawls all the PySide widgets, looking for an answer
def findWidget(w):
global foundryWidgets
- if 'Foundry' in w.metaObject().className():
+ if "Foundry" in w.metaObject().className():
foundryWidgets += [w]
for c in w.children():
@@ -49,7 +49,7 @@ def activeSpreadsheetTreeView():
Does some PySide widget Magic to detect the Active Spreadsheet TreeView.
"""
spreadsheetViews = getFoundryWidgetsWithClassName(
- filter='SpreadsheetTreeView')
+ filter="SpreadsheetTreeView")
for spreadSheet in spreadsheetViews:
if spreadSheet.hasFocus():
activeSpreadSheet = spreadSheet
@@ -77,23 +77,23 @@ class SpreadsheetExportCSVAction(QAction):
spreadsheetTreeView = activeSpreadsheetTreeView()
if not spreadsheetTreeView:
- return 'Unable to detect the active TreeView.'
+ return "Unable to detect the active TreeView."
seq = hiero.ui.activeView().sequence()
if not seq:
- print 'Unable to detect the active Sequence from the activeView.'
+ print("Unable to detect the active Sequence from the activeView.")
return
# The data model of the QTreeView
model = spreadsheetTreeView.model()
- csvSavePath = os.path.join(QDir.homePath(), 'Desktop',
- seq.name() + '.csv')
+ csvSavePath = os.path.join(QDir.homePath(), "Desktop",
+ seq.name() + ".csv")
savePath, filter = QFileDialog.getSaveFileName(
None,
caption="Export Spreadsheet to .CSV as...",
dir=csvSavePath,
filter="*.csv")
- print 'Saving To: ' + str(savePath)
+ print("Saving To: {}".format(savePath))
# Saving was cancelled...
if len(savePath) == 0:
@@ -101,12 +101,12 @@ class SpreadsheetExportCSVAction(QAction):
# Get the Visible Header Columns from the QTreeView
- #csvHeader = ['Event', 'Status', 'Shot Name', 'Reel', 'Track', 'Speed', 'Src In', 'Src Out','Src Duration', 'Dst In', 'Dst Out', 'Dst Duration', 'Clip', 'Clip Media']
+ #csvHeader = ["Event", "Status", "Shot Name", "Reel", "Track", "Speed", "Src In", "Src Out","Src Duration", "Dst In", "Dst Out", "Dst Duration", "Clip", "Clip Media"]
# Get a CSV writer object
- f = open(savePath, 'w')
+ f = open(savePath, "w")
csvWriter = csv.writer(
- f, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL)
+ f, delimiter=',', quotechar="|", quoting=csv.QUOTE_MINIMAL)
# This is a list of the Column titles
csvHeader = []
diff --git a/openpype/hosts/hiero/startup/Python/Startup/Startup.py b/openpype/hosts/hiero/api/startup/Python/Startup/Startup.py
similarity index 100%
rename from openpype/hosts/hiero/startup/Python/Startup/Startup.py
rename to openpype/hosts/hiero/api/startup/Python/Startup/Startup.py
diff --git a/openpype/hosts/hiero/startup/Python/Startup/otioexporter/OTIOExportTask.py b/openpype/hosts/hiero/api/startup/Python/Startup/otioexporter/OTIOExportTask.py
similarity index 96%
rename from openpype/hosts/hiero/startup/Python/Startup/otioexporter/OTIOExportTask.py
rename to openpype/hosts/hiero/api/startup/Python/Startup/otioexporter/OTIOExportTask.py
index 7e1a8df2dc..e4ce2fe827 100644
--- a/openpype/hosts/hiero/startup/Python/Startup/otioexporter/OTIOExportTask.py
+++ b/openpype/hosts/hiero/api/startup/Python/Startup/otioexporter/OTIOExportTask.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = "Daniel Flehner Heen"
@@ -9,7 +8,7 @@ import hiero.core
from hiero.core import util
import opentimelineio as otio
-from openpype.hosts.hiero.otio import hiero_export
+from openpype.hosts.hiero.api.otio import hiero_export
class OTIOExportTask(hiero.core.TaskBase):
diff --git a/openpype/hosts/hiero/startup/Python/Startup/otioexporter/OTIOExportUI.py b/openpype/hosts/hiero/api/startup/Python/Startup/otioexporter/OTIOExportUI.py
similarity index 91%
rename from openpype/hosts/hiero/startup/Python/Startup/otioexporter/OTIOExportUI.py
rename to openpype/hosts/hiero/api/startup/Python/Startup/otioexporter/OTIOExportUI.py
index 9b83eefedf..af5593e484 100644
--- a/openpype/hosts/hiero/startup/Python/Startup/otioexporter/OTIOExportUI.py
+++ b/openpype/hosts/hiero/api/startup/Python/Startup/otioexporter/OTIOExportUI.py
@@ -1,11 +1,13 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = "Daniel Flehner Heen"
__credits__ = ["Jakub Jezek", "Daniel Flehner Heen"]
import hiero.ui
-import OTIOExportTask
+from .OTIOExportTask import (
+ OTIOExportTask,
+ OTIOExportPreset
+)
try:
# Hiero >= 11.x
@@ -20,14 +22,14 @@ except ImportError:
FormLayout = QFormLayout # lint:ok
-from openpype.hosts.hiero.otio import hiero_export
+from openpype.hosts.hiero.api.otio import hiero_export
class OTIOExportUI(hiero.ui.TaskUIBase):
def __init__(self, preset):
"""Initialize"""
hiero.ui.TaskUIBase.__init__(
self,
- OTIOExportTask.OTIOExportTask,
+ OTIOExportTask,
preset,
"OTIO Exporter"
)
@@ -67,6 +69,6 @@ class OTIOExportUI(hiero.ui.TaskUIBase):
hiero.ui.taskUIRegistry.registerTaskUI(
- OTIOExportTask.OTIOExportPreset,
+ OTIOExportPreset,
OTIOExportUI
)
diff --git a/openpype/hosts/hiero/api/startup/Python/Startup/otioexporter/__init__.py b/openpype/hosts/hiero/api/startup/Python/Startup/otioexporter/__init__.py
new file mode 100644
index 0000000000..33d3fc6c59
--- /dev/null
+++ b/openpype/hosts/hiero/api/startup/Python/Startup/otioexporter/__init__.py
@@ -0,0 +1,7 @@
+from .OTIOExportTask import OTIOExportTask
+from .OTIOExportUI import OTIOExportUI
+
+__all__ = [
+ "OTIOExportTask",
+ "OTIOExportUI"
+]
diff --git a/openpype/hosts/hiero/startup/Python/Startup/project_helpers.py b/openpype/hosts/hiero/api/startup/Python/Startup/project_helpers.py
similarity index 81%
rename from openpype/hosts/hiero/startup/Python/Startup/project_helpers.py
rename to openpype/hosts/hiero/api/startup/Python/Startup/project_helpers.py
index 7e274bd0a3..64b5c37d7b 100644
--- a/openpype/hosts/hiero/startup/Python/Startup/project_helpers.py
+++ b/openpype/hosts/hiero/api/startup/Python/Startup/project_helpers.py
@@ -21,7 +21,7 @@ def __trackActiveProjectHandler(event):
global gTrackedActiveProject
selection = event.sender.selection()
binSelection = selection
- if len(binSelection) > 0 and hasattr(binSelection[0], 'project'):
+ if len(binSelection) > 0 and hasattr(binSelection[0], "project"):
proj = binSelection[0].project()
# We only store this if its a valid, active User Project
@@ -30,18 +30,18 @@ def __trackActiveProjectHandler(event):
hiero.core.events.registerInterest(
- 'kSelectionChanged/kBin', __trackActiveProjectHandler)
+ "kSelectionChanged/kBin", __trackActiveProjectHandler)
hiero.core.events.registerInterest(
- 'kSelectionChanged/kTimeline', __trackActiveProjectHandler)
+ "kSelectionChanged/kTimeline", __trackActiveProjectHandler)
hiero.core.events.registerInterest(
- 'kSelectionChanged/Spreadsheet', __trackActiveProjectHandler)
+ "kSelectionChanged/Spreadsheet", __trackActiveProjectHandler)
def activeProject():
"""hiero.ui.activeProject() -> returns the current Project
- Note: There is not technically a notion of a 'active' Project in Hiero/NukeStudio, as it is a multi-project App.
- This method determines what is 'active' by going down the following rules...
+ Note: There is not technically a notion of a "active" Project in Hiero/NukeStudio, as it is a multi-project App.
+ This method determines what is "active" by going down the following rules...
# 1 - If the current Viewer (hiero.ui.currentViewer) contains a Clip or Sequence, this item is assumed to give the active Project
# 2 - If nothing is currently in the Viewer, look to the active View, determine project from active selection
@@ -54,7 +54,7 @@ def activeProject():
# Case 1 : Look for what the current Viewr tells us - this might not be what we want, and relies on hiero.ui.currentViewer() being robust.
cv = hiero.ui.currentViewer().player().sequence()
- if hasattr(cv, 'project'):
+ if hasattr(cv, "project"):
activeProject = cv.project()
else:
# Case 2: We can't determine a project from the current Viewer, so try seeing what's selected in the activeView
@@ -66,16 +66,16 @@ def activeProject():
# Handle the case where nothing is selected in the active view
if len(selection) == 0:
- # It's possible that there is no selection in a Timeline/Spreadsheet, but these views have 'sequence' method, so try that...
+ # It's possible that there is no selection in a Timeline/Spreadsheet, but these views have "sequence" method, so try that...
if isinstance(hiero.ui.activeView(), (hiero.ui.TimelineEditor, hiero.ui.SpreadsheetView)):
activeSequence = activeView.sequence()
- if hasattr(currentItem, 'project'):
+ if hasattr(currentItem, "project"):
activeProject = activeSequence.project()
# The active view has a selection... assume that the first item in the selection has the active Project
else:
currentItem = selection[0]
- if hasattr(currentItem, 'project'):
+ if hasattr(currentItem, "project"):
activeProject = currentItem.project()
# Finally, Cases 3 and 4...
@@ -156,9 +156,14 @@ class SaveAllProjects(QAction):
for proj in allProjects:
try:
proj.save()
- print 'Saved Project: %s to: %s ' % (proj.name(), proj.path())
+ print("Saved Project: {} to: {} ".format(
+ proj.name(), proj.path()
+ ))
except:
- print 'Unable to save Project: %s to: %s. Check file permissions.' % (proj.name(), proj.path())
+ print((
+ "Unable to save Project: {} to: {}. "
+ "Check file permissions.").format(
+ proj.name(), proj.path()))
def eventHandler(self, event):
event.menu.addAction(self)
@@ -190,32 +195,38 @@ class SaveNewProjectVersion(QAction):
v = None
prefix = None
try:
- (prefix, v) = version_get(path, 'v')
- except ValueError, msg:
- print msg
+ (prefix, v) = version_get(path, "v")
+ except ValueError as msg:
+ print(msg)
if (prefix is not None) and (v is not None):
v = int(v)
newPath = version_set(path, prefix, v, v + 1)
try:
proj.saveAs(newPath)
- print 'Saved new project version: %s to: %s ' % (oldName, newPath)
+ print("Saved new project version: {} to: {} ".format(
+ oldName, newPath))
except:
- print 'Unable to save Project: %s. Check file permissions.' % (oldName)
+ print((
+ "Unable to save Project: {}. Check file permissions."
+ ).format(oldName))
else:
newPath = path.replace(".hrox", "_v01.hrox")
answer = nuke.ask(
- '%s does not contain a version number.\nDo you want to save as %s?' % (proj, newPath))
+ "%s does not contain a version number.\nDo you want to save as %s?" % (proj, newPath))
if answer:
try:
proj.saveAs(newPath)
- print 'Saved new project version: %s to: %s ' % (oldName, newPath)
+ print("Saved new project version: {} to: {} ".format(
+ oldName, newPath))
except:
- print 'Unable to save Project: %s. Check file permissions.' % (oldName)
+ print((
+ "Unable to save Project: {}. Check file "
+ "permissions.").format(oldName))
def eventHandler(self, event):
self.selectedProjects = []
- if hasattr(event.sender, 'selection') and event.sender.selection() is not None and len(event.sender.selection()) != 0:
+ if hasattr(event.sender, "selection") and event.sender.selection() is not None and len(event.sender.selection()) != 0:
selection = event.sender.selection()
self.selectedProjects = uniquify(
[item.project() for item in selection])
diff --git a/openpype/hosts/hiero/api/startup/Python/Startup/selection_tracker.py b/openpype/hosts/hiero/api/startup/Python/Startup/selection_tracker.py
new file mode 100644
index 0000000000..a9789cf508
--- /dev/null
+++ b/openpype/hosts/hiero/api/startup/Python/Startup/selection_tracker.py
@@ -0,0 +1,9 @@
+"""Puts the selection project into "hiero.selection"""
+
+import hiero
+
+
+def selectionChanged(event):
+ hiero.selection = event.sender.selection()
+
+hiero.core.events.registerInterest("kSelectionChanged", selectionChanged)
diff --git a/openpype/hosts/hiero/startup/Python/Startup/setFrameRate.py b/openpype/hosts/hiero/api/startup/Python/Startup/setFrameRate.py
similarity index 90%
rename from openpype/hosts/hiero/startup/Python/Startup/setFrameRate.py
rename to openpype/hosts/hiero/api/startup/Python/Startup/setFrameRate.py
index ceb96a6fce..07ae48aef5 100644
--- a/openpype/hosts/hiero/startup/Python/Startup/setFrameRate.py
+++ b/openpype/hosts/hiero/api/startup/Python/Startup/setFrameRate.py
@@ -23,7 +23,7 @@ class SetFrameRateDialog(QDialog):
self._itemSelection = itemSelection
self._frameRateField = QLineEdit()
- self._frameRateField.setToolTip('Enter custom frame rate here.')
+ self._frameRateField.setToolTip("Enter custom frame rate here.")
self._frameRateField.setValidator(QDoubleValidator(1, 99, 3, self))
self._frameRateField.textChanged.connect(self._textChanged)
layout.addRow("Enter fps: ",self._frameRateField)
@@ -35,13 +35,13 @@ class SetFrameRateDialog(QDialog):
self._buttonbox.button(QDialogButtonBox.Ok).setEnabled(False)
layout.addRow("",self._buttonbox)
self.setLayout(layout)
-
+
def _updateOkButtonState(self):
# Cancel is always an option but only enable Ok if there is some text.
currentFramerate = float(self.currentFramerateString())
enableOk = False
enableOk = ((currentFramerate > 0.0) and (currentFramerate <= 250.0))
- print 'enabledOk',enableOk
+ print("enabledOk", enableOk)
self._buttonbox.button(QDialogButtonBox.Ok).setEnabled(enableOk)
def _textChanged(self, newText):
@@ -50,32 +50,32 @@ class SetFrameRateDialog(QDialog):
# Returns the current frame rate as a string
def currentFramerateString(self):
return str(self._frameRateField.text())
-
+
# Presents the Dialog and sets the Frame rate from a selection
def showDialogAndSetFrameRateFromSelection(self):
-
+
if self._itemSelection is not None:
if self.exec_():
# For the Undo loop...
-
+
# Construct an TimeBase object for setting the Frame Rate (fps)
fps = hiero.core.TimeBase().fromString(self.currentFramerateString())
-
+
# Set the frame rate for the selected BinItmes
for item in self._itemSelection:
- item.setFramerate(fps)
+ item.setFramerate(fps)
return
# This is just a convenience method for returning QActions with a title, triggered method and icon.
def makeAction(title, method, icon = None):
action = QAction(title,None)
action.setIcon(QIcon(icon))
-
+
# We do this magic, so that the title string from the action is used to set the frame rate!
def methodWrapper():
method(title)
-
+
action.triggered.connect( methodWrapper )
return action
@@ -88,13 +88,13 @@ class SetFrameRateMenu:
# ant: Could use hiero.core.defaultFrameRates() here but messes up with string matching because we seem to mix decimal points
- self.frameRates = ['8','12','12.50','15','23.98','24','25','29.97','30','48','50','59.94','60']
+ self.frameRates = ["8","12","12.50","15","23.98","24","25","29.97","30","48","50","59.94","60"]
hiero.core.events.registerInterest("kShowContextMenu/kBin", self.binViewEventHandler)
-
+
self.menuActions = []
def createFrameRateMenus(self,selection):
- selectedClipFPS = [str(bi.activeItem().framerate()) for bi in selection if (isinstance(bi,hiero.core.BinItem) and hasattr(bi,'activeItem'))]
+ selectedClipFPS = [str(bi.activeItem().framerate()) for bi in selection if (isinstance(bi,hiero.core.BinItem) and hasattr(bi,"activeItem"))]
selectedClipFPS = hiero.core.util.uniquify(selectedClipFPS)
sameFrameRate = len(selectedClipFPS)==1
self.menuActions = []
@@ -106,39 +106,41 @@ class SetFrameRateMenu:
self.menuActions+=[makeAction(fps,self.setFrameRateFromMenuSelection, icon="icons:remove active.png")]
else:
self.menuActions+=[makeAction(fps,self.setFrameRateFromMenuSelection, icon=None)]
-
+
# Now add Custom... menu
- self.menuActions+=[makeAction('Custom...',self.setFrameRateFromMenuSelection, icon=None)]
-
+ self.menuActions += [makeAction(
+ "Custom...", self.setFrameRateFromMenuSelection, icon=None)
+ ]
+
frameRateMenu = QMenu("Set Frame Rate")
for a in self.menuActions:
frameRateMenu.addAction(a)
-
+
return frameRateMenu
def setFrameRateFromMenuSelection(self, menuSelectionFPS):
-
- selectedBinItems = [bi.activeItem() for bi in self._selection if (isinstance(bi,hiero.core.BinItem) and hasattr(bi,'activeItem'))]
+
+ selectedBinItems = [bi.activeItem() for bi in self._selection if (isinstance(bi,hiero.core.BinItem) and hasattr(bi,"activeItem"))]
currentProject = selectedBinItems[0].project()
-
+
with currentProject.beginUndo("Set Frame Rate"):
- if menuSelectionFPS == 'Custom...':
+ if menuSelectionFPS == "Custom...":
self._frameRatesDialog = SetFrameRateDialog(itemSelection = selectedBinItems )
self._frameRatesDialog.showDialogAndSetFrameRateFromSelection()
else:
for b in selectedBinItems:
b.setFramerate(hiero.core.TimeBase().fromString(menuSelectionFPS))
-
+
return
# This handles events from the Project Bin View
def binViewEventHandler(self,event):
- if not hasattr(event.sender, 'selection'):
+ if not hasattr(event.sender, "selection"):
# Something has gone wrong, we should only be here if raised
# by the Bin view which gives a selection.
return
-
+
# Reset the selection to None...
self._selection = None
s = event.sender.selection()
@@ -151,9 +153,9 @@ class SetFrameRateMenu:
if len(self._selection)==0:
return
# Creating the menu based on items selected, to highlight which frame rates are contained
-
+
self._frameRateMenu = self.createFrameRateMenus(self._selection)
-
+
# Insert the Set Frame Rate Button before the Set Media Colour Transform Action
for action in event.menu.actions():
if str(action.text()) == "Set Media Colour Transform":
diff --git a/openpype/hosts/hiero/startup/Python/Startup/version_everywhere.py b/openpype/hosts/hiero/api/startup/Python/Startup/version_everywhere.py
similarity index 95%
rename from openpype/hosts/hiero/startup/Python/Startup/version_everywhere.py
rename to openpype/hosts/hiero/api/startup/Python/Startup/version_everywhere.py
index e85e02bfa5..3d60b213d5 100644
--- a/openpype/hosts/hiero/startup/Python/Startup/version_everywhere.py
+++ b/openpype/hosts/hiero/api/startup/Python/Startup/version_everywhere.py
@@ -16,29 +16,29 @@ except:
from PySide2.QtCore import *
-def whereAmI(self, searchType='TrackItem'):
+def whereAmI(self, searchType="TrackItem"):
"""returns a list of TrackItem or Sequnece objects in the Project which contain this Clip.
By default this will return a list of TrackItems where the Clip is used in its project.
- You can also return a list of Sequences by specifying the searchType to be 'Sequence'.
+ You can also return a list of Sequences by specifying the searchType to be "Sequence".
Should consider putting this into hiero.core.Clip by default?
Example usage:
- shotsForClip = clip.whereAmI('TrackItem')
- sequencesForClip = clip.whereAmI('Sequence')
+ shotsForClip = clip.whereAmI("TrackItem")
+ sequencesForClip = clip.whereAmI("Sequence")
"""
proj = self.project()
- if ('TrackItem' not in searchType) and ('Sequence' not in searchType):
- print "searchType argument must be 'TrackItem' or 'Sequence'"
+ if ("TrackItem" not in searchType) and ("Sequence" not in searchType):
+ print("searchType argument must be \"TrackItem\" or \"Sequence\"")
return None
# If user specifies a TrackItem, then it will return
searches = hiero.core.findItemsInProject(proj, searchType)
if len(searches) == 0:
- print 'Unable to find %s in any items of type: %s' % (str(self),
- str(searchType))
+ print("Unable to find {} in any items of type: {}".format(
+ str(self), searchType))
return None
# Case 1: Looking for Shots (trackItems)
@@ -110,7 +110,7 @@ class VersionAllMenu(object):
for shot in sequenceShotManifest[seq]:
updateReportString += ' %s\n (New Version: %s)\n' % (
shot.name(), shot.currentVersion().name())
- updateReportString += '\n'
+ updateReportString += "\n"
infoBox = QMessageBox(hiero.ui.mainWindow())
infoBox.setIcon(QMessageBox.Information)
@@ -202,7 +202,7 @@ class VersionAllMenu(object):
if len(bins) > 0:
# Grab the Clips inside of a Bin and append them to a list
for bin in bins:
- clips = hiero.core.findItemsInBin(bin, 'Clip')
+ clips = hiero.core.findItemsInBin(bin, "Clip")
for clip in clips:
if clip not in clipItems:
clipItems.append(clip)
@@ -291,7 +291,7 @@ class VersionAllMenu(object):
for clip in clipSelection:
# Look to see if it exists in a TrackItem somewhere...
- shotUsage = clip.whereAmI('TrackItem')
+ shotUsage = clip.whereAmI("TrackItem")
# Next, depending on the versionOption, make the appropriate update
# There's probably a more neat/compact way of doing this...
@@ -326,7 +326,7 @@ class VersionAllMenu(object):
# This handles events from the Project Bin View
def binViewEventHandler(self, event):
- if not hasattr(event.sender, 'selection'):
+ if not hasattr(event.sender, "selection"):
# Something has gone wrong, we should only be here if raised
# by the Bin view which gives a selection.
return
diff --git a/openpype/hosts/hiero/startup/Python/StartupUI/PimpMySpreadsheet.py b/openpype/hosts/hiero/api/startup/Python/StartupUI/PimpMySpreadsheet.py
similarity index 74%
rename from openpype/hosts/hiero/startup/Python/StartupUI/PimpMySpreadsheet.py
rename to openpype/hosts/hiero/api/startup/Python/StartupUI/PimpMySpreadsheet.py
index 3d40aa0293..39a65045a7 100644
--- a/openpype/hosts/hiero/startup/Python/StartupUI/PimpMySpreadsheet.py
+++ b/openpype/hosts/hiero/api/startup/Python/StartupUI/PimpMySpreadsheet.py
@@ -15,55 +15,55 @@ except:
from PySide2.QtWidgets import *
from PySide2.QtCore import *
-# Set to True, if you wat 'Set Status' right-click menu, False if not
+# Set to True, if you wat "Set Status" right-click menu, False if not
kAddStatusMenu = True
-# Set to True, if you wat 'Assign Artist' right-click menu, False if not
+# Set to True, if you wat "Assign Artist" right-click menu, False if not
kAssignArtistMenu = True
# Global list of Artist Name Dictionaries
# Note: Override this to add different names, icons, department, IDs.
gArtistList = [{
- 'artistName': 'John Smith',
- 'artistIcon': 'icons:TagActor.png',
- 'artistDepartment': '3D',
- 'artistID': 0
+ "artistName": "John Smith",
+ "artistIcon": "icons:TagActor.png",
+ "artistDepartment": "3D",
+ "artistID": 0
}, {
- 'artistName': 'Savlvador Dali',
- 'artistIcon': 'icons:TagActor.png',
- 'artistDepartment': 'Roto',
- 'artistID': 1
+ "artistName": "Savlvador Dali",
+ "artistIcon": "icons:TagActor.png",
+ "artistDepartment": "Roto",
+ "artistID": 1
}, {
- 'artistName': 'Leonardo Da Vinci',
- 'artistIcon': 'icons:TagActor.png',
- 'artistDepartment': 'Paint',
- 'artistID': 2
+ "artistName": "Leonardo Da Vinci",
+ "artistIcon": "icons:TagActor.png",
+ "artistDepartment": "Paint",
+ "artistID": 2
}, {
- 'artistName': 'Claude Monet',
- 'artistIcon': 'icons:TagActor.png',
- 'artistDepartment': 'Comp',
- 'artistID': 3
+ "artistName": "Claude Monet",
+ "artistIcon": "icons:TagActor.png",
+ "artistDepartment": "Comp",
+ "artistID": 3
}, {
- 'artistName': 'Pablo Picasso',
- 'artistIcon': 'icons:TagActor.png',
- 'artistDepartment': 'Animation',
- 'artistID': 4
+ "artistName": "Pablo Picasso",
+ "artistIcon": "icons:TagActor.png",
+ "artistDepartment": "Animation",
+ "artistID": 4
}]
# Global Dictionary of Status Tags.
# Note: This can be overwritten if you want to add a new status cellType or custom icon
-# Override the gStatusTags dictionary by adding your own 'Status':'Icon.png' key-value pairs.
-# Add new custom keys like so: gStatusTags['For Client'] = 'forClient.png'
+# Override the gStatusTags dictionary by adding your own "Status":"Icon.png" key-value pairs.
+# Add new custom keys like so: gStatusTags["For Client"] = "forClient.png"
gStatusTags = {
- 'Approved': 'icons:status/TagApproved.png',
- 'Unapproved': 'icons:status/TagUnapproved.png',
- 'Ready To Start': 'icons:status/TagReadyToStart.png',
- 'Blocked': 'icons:status/TagBlocked.png',
- 'On Hold': 'icons:status/TagOnHold.png',
- 'In Progress': 'icons:status/TagInProgress.png',
- 'Awaiting Approval': 'icons:status/TagAwaitingApproval.png',
- 'Omitted': 'icons:status/TagOmitted.png',
- 'Final': 'icons:status/TagFinal.png'
+ "Approved": "icons:status/TagApproved.png",
+ "Unapproved": "icons:status/TagUnapproved.png",
+ "Ready To Start": "icons:status/TagReadyToStart.png",
+ "Blocked": "icons:status/TagBlocked.png",
+ "On Hold": "icons:status/TagOnHold.png",
+ "In Progress": "icons:status/TagInProgress.png",
+ "Awaiting Approval": "icons:status/TagAwaitingApproval.png",
+ "Omitted": "icons:status/TagOmitted.png",
+ "Final": "icons:status/TagFinal.png"
}
@@ -78,17 +78,17 @@ class CustomSpreadsheetColumns(QObject):
# Ideally, we'd set this list on a Per Item basis, but this is expensive for a large mixed selection
standardColourSpaces = [
- 'linear', 'sRGB', 'rec709', 'Cineon', 'Gamma1.8', 'Gamma2.2',
- 'Panalog', 'REDLog', 'ViperLog'
+ "linear", "sRGB", "rec709", "Cineon", "Gamma1.8", "Gamma2.2",
+ "Panalog", "REDLog", "ViperLog"
]
arriColourSpaces = [
- 'Video - Rec709', 'LogC - Camera Native', 'Video - P3', 'ACES',
- 'LogC - Film', 'LogC - Wide Gamut'
+ "Video - Rec709", "LogC - Camera Native", "Video - P3", "ACES",
+ "LogC - Film", "LogC - Wide Gamut"
]
r3dColourSpaces = [
- 'Linear', 'Rec709', 'REDspace', 'REDlog', 'PDlog685', 'PDlog985',
- 'CustomPDlog', 'REDgamma', 'SRGB', 'REDlogFilm', 'REDgamma2',
- 'REDgamma3'
+ "Linear", "Rec709", "REDspace", "REDlog", "PDlog685", "PDlog985",
+ "CustomPDlog", "REDgamma", "SRGB", "REDlogFilm", "REDgamma2",
+ "REDgamma3"
]
gColourSpaces = standardColourSpaces + arriColourSpaces + r3dColourSpaces
@@ -97,52 +97,52 @@ class CustomSpreadsheetColumns(QObject):
# This is the list of Columns available
gCustomColumnList = [
{
- 'name': 'Tags',
- 'cellType': 'readonly'
+ "name": "Tags",
+ "cellType": "readonly"
},
{
- 'name': 'Colourspace',
- 'cellType': 'dropdown'
+ "name": "Colourspace",
+ "cellType": "dropdown"
},
{
- 'name': 'Notes',
- 'cellType': 'readonly'
+ "name": "Notes",
+ "cellType": "readonly"
},
{
- 'name': 'FileType',
- 'cellType': 'readonly'
+ "name": "FileType",
+ "cellType": "readonly"
},
{
- 'name': 'Shot Status',
- 'cellType': 'dropdown'
+ "name": "Shot Status",
+ "cellType": "dropdown"
},
{
- 'name': 'Thumbnail',
- 'cellType': 'readonly'
+ "name": "Thumbnail",
+ "cellType": "readonly"
},
{
- 'name': 'MediaType',
- 'cellType': 'readonly'
+ "name": "MediaType",
+ "cellType": "readonly"
},
{
- 'name': 'Width',
- 'cellType': 'readonly'
+ "name": "Width",
+ "cellType": "readonly"
},
{
- 'name': 'Height',
- 'cellType': 'readonly'
+ "name": "Height",
+ "cellType": "readonly"
},
{
- 'name': 'Pixel Aspect',
- 'cellType': 'readonly'
+ "name": "Pixel Aspect",
+ "cellType": "readonly"
},
{
- 'name': 'Artist',
- 'cellType': 'dropdown'
+ "name": "Artist",
+ "cellType": "dropdown"
},
{
- 'name': 'Department',
- 'cellType': 'readonly'
+ "name": "Department",
+ "cellType": "readonly"
},
]
@@ -156,7 +156,7 @@ class CustomSpreadsheetColumns(QObject):
"""
Return the name of a custom column
"""
- return self.gCustomColumnList[column]['name']
+ return self.gCustomColumnList[column]["name"]
def getTagsString(self, item):
"""
@@ -173,7 +173,7 @@ class CustomSpreadsheetColumns(QObject):
"""
Convenience method for returning all the Notes in a Tag as a string
"""
- notes = ''
+ notes = ""
tags = item.tags()
for tag in tags:
note = tag.note()
@@ -186,67 +186,67 @@ class CustomSpreadsheetColumns(QObject):
Return the data in a cell
"""
currentColumn = self.gCustomColumnList[column]
- if currentColumn['name'] == 'Tags':
+ if currentColumn["name"] == "Tags":
return self.getTagsString(item)
- if currentColumn['name'] == 'Colourspace':
+ if currentColumn["name"] == "Colourspace":
try:
colTransform = item.sourceMediaColourTransform()
except:
- colTransform = '--'
+ colTransform = "--"
return colTransform
- if currentColumn['name'] == 'Notes':
+ if currentColumn["name"] == "Notes":
try:
note = self.getNotes(item)
except:
- note = ''
+ note = ""
return note
- if currentColumn['name'] == 'FileType':
- fileType = '--'
+ if currentColumn["name"] == "FileType":
+ fileType = "--"
M = item.source().mediaSource().metadata()
- if M.hasKey('foundry.source.type'):
- fileType = M.value('foundry.source.type')
- elif M.hasKey('media.input.filereader'):
- fileType = M.value('media.input.filereader')
+ if M.hasKey("foundry.source.type"):
+ fileType = M.value("foundry.source.type")
+ elif M.hasKey("media.input.filereader"):
+ fileType = M.value("media.input.filereader")
return fileType
- if currentColumn['name'] == 'Shot Status':
+ if currentColumn["name"] == "Shot Status":
status = item.status()
if not status:
status = "--"
return str(status)
- if currentColumn['name'] == 'MediaType':
+ if currentColumn["name"] == "MediaType":
M = item.mediaType()
- return str(M).split('MediaType')[-1].replace('.k', '')
+ return str(M).split("MediaType")[-1].replace(".k", "")
- if currentColumn['name'] == 'Thumbnail':
+ if currentColumn["name"] == "Thumbnail":
return str(item.eventNumber())
- if currentColumn['name'] == 'Width':
+ if currentColumn["name"] == "Width":
return str(item.source().format().width())
- if currentColumn['name'] == 'Height':
+ if currentColumn["name"] == "Height":
return str(item.source().format().height())
- if currentColumn['name'] == 'Pixel Aspect':
+ if currentColumn["name"] == "Pixel Aspect":
return str(item.source().format().pixelAspect())
- if currentColumn['name'] == 'Artist':
+ if currentColumn["name"] == "Artist":
if item.artist():
- name = item.artist()['artistName']
+ name = item.artist()["artistName"]
return name
else:
- return '--'
+ return "--"
- if currentColumn['name'] == 'Department':
+ if currentColumn["name"] == "Department":
if item.artist():
- dep = item.artist()['artistDepartment']
+ dep = item.artist()["artistDepartment"]
return dep
else:
- return '--'
+ return "--"
return ""
@@ -262,10 +262,10 @@ class CustomSpreadsheetColumns(QObject):
Return the tooltip for a cell
"""
currentColumn = self.gCustomColumnList[column]
- if currentColumn['name'] == 'Tags':
+ if currentColumn["name"] == "Tags":
return str([item.name() for item in item.tags()])
- if currentColumn['name'] == 'Notes':
+ if currentColumn["name"] == "Notes":
return str(self.getNotes(item))
return ""
@@ -296,24 +296,24 @@ class CustomSpreadsheetColumns(QObject):
Return the icon for a cell
"""
currentColumn = self.gCustomColumnList[column]
- if currentColumn['name'] == 'Colourspace':
+ if currentColumn["name"] == "Colourspace":
return QIcon("icons:LUT.png")
- if currentColumn['name'] == 'Shot Status':
+ if currentColumn["name"] == "Shot Status":
status = item.status()
if status:
return QIcon(gStatusTags[status])
- if currentColumn['name'] == 'MediaType':
+ if currentColumn["name"] == "MediaType":
mediaType = item.mediaType()
if mediaType == hiero.core.TrackItem.kVideo:
return QIcon("icons:VideoOnly.png")
elif mediaType == hiero.core.TrackItem.kAudio:
return QIcon("icons:AudioOnly.png")
- if currentColumn['name'] == 'Artist':
+ if currentColumn["name"] == "Artist":
try:
- return QIcon(item.artist()['artistIcon'])
+ return QIcon(item.artist()["artistIcon"])
except:
return None
return None
@@ -322,9 +322,9 @@ class CustomSpreadsheetColumns(QObject):
"""
Return the size hint for a cell
"""
- currentColumnName = self.gCustomColumnList[column]['name']
+ currentColumnName = self.gCustomColumnList[column]["name"]
- if currentColumnName == 'Thumbnail':
+ if currentColumnName == "Thumbnail":
return QSize(90, 50)
return QSize(50, 50)
@@ -335,7 +335,7 @@ class CustomSpreadsheetColumns(QObject):
with the default cell painting.
"""
currentColumn = self.gCustomColumnList[column]
- if currentColumn['name'] == 'Tags':
+ if currentColumn["name"] == "Tags":
if option.state & QStyle.State_Selected:
painter.fillRect(option.rect, option.palette.highlight())
iconSize = 20
@@ -348,14 +348,14 @@ class CustomSpreadsheetColumns(QObject):
painter.setClipRect(option.rect)
for tag in item.tags():
M = tag.metadata()
- if not (M.hasKey('tag.status')
- or M.hasKey('tag.artistID')):
+ if not (M.hasKey("tag.status")
+ or M.hasKey("tag.artistID")):
QIcon(tag.icon()).paint(painter, r, Qt.AlignLeft)
r.translate(r.width() + 2, 0)
painter.restore()
return True
- if currentColumn['name'] == 'Thumbnail':
+ if currentColumn["name"] == "Thumbnail":
imageView = None
pen = QPen()
r = QRect(option.rect.x() + 2, (option.rect.y() +
@@ -409,35 +409,35 @@ class CustomSpreadsheetColumns(QObject):
self.currentView = view
currentColumn = self.gCustomColumnList[column]
- if currentColumn['cellType'] == 'readonly':
+ if currentColumn["cellType"] == "readonly":
cle = QLabel()
cle.setEnabled(False)
cle.setVisible(False)
return cle
- if currentColumn['name'] == 'Colourspace':
+ if currentColumn["name"] == "Colourspace":
cb = QComboBox()
for colourspace in self.gColourSpaces:
cb.addItem(colourspace)
cb.currentIndexChanged.connect(self.colourspaceChanged)
return cb
- if currentColumn['name'] == 'Shot Status':
+ if currentColumn["name"] == "Shot Status":
cb = QComboBox()
- cb.addItem('')
+ cb.addItem("")
for key in gStatusTags.keys():
cb.addItem(QIcon(gStatusTags[key]), key)
- cb.addItem('--')
+ cb.addItem("--")
cb.currentIndexChanged.connect(self.statusChanged)
return cb
- if currentColumn['name'] == 'Artist':
+ if currentColumn["name"] == "Artist":
cb = QComboBox()
- cb.addItem('')
+ cb.addItem("")
for artist in gArtistList:
- cb.addItem(artist['artistName'])
- cb.addItem('--')
+ cb.addItem(artist["artistName"])
+ cb.addItem("--")
cb.currentIndexChanged.connect(self.artistNameChanged)
return cb
return None
@@ -479,15 +479,15 @@ class CustomSpreadsheetColumns(QObject):
status = self.sender().currentText()
project = selection[0].project()
with project.beginUndo("Set Status"):
- # A string of '--' characters denotes clear the status
- if status != '--':
+ # A string of "--" characters denotes clear the status
+ if status != "--":
for trackItem in selection:
trackItem.setStatus(status)
else:
for trackItem in selection:
tTags = trackItem.tags()
for tag in tTags:
- if tag.metadata().hasKey('tag.status'):
+ if tag.metadata().hasKey("tag.status"):
trackItem.removeTag(tag)
break
@@ -500,15 +500,15 @@ class CustomSpreadsheetColumns(QObject):
name = self.sender().currentText()
project = selection[0].project()
with project.beginUndo("Assign Artist"):
- # A string of '--' denotes clear the assignee...
- if name != '--':
+ # A string of "--" denotes clear the assignee...
+ if name != "--":
for trackItem in selection:
trackItem.setArtistByName(name)
else:
for trackItem in selection:
tTags = trackItem.tags()
for tag in tTags:
- if tag.metadata().hasKey('tag.artistID'):
+ if tag.metadata().hasKey("tag.artistID"):
trackItem.removeTag(tag)
break
@@ -518,7 +518,7 @@ def _getArtistFromID(self, artistID):
global gArtistList
artist = [
element for element in gArtistList
- if element['artistID'] == int(artistID)
+ if element["artistID"] == int(artistID)
]
if not artist:
return None
@@ -530,7 +530,7 @@ def _getArtistFromName(self, artistName):
global gArtistList
artist = [
element for element in gArtistList
- if element['artistName'] == artistName
+ if element["artistName"] == artistName
]
if not artist:
return None
@@ -542,8 +542,8 @@ def _artist(self):
artist = None
tags = self.tags()
for tag in tags:
- if tag.metadata().hasKey('tag.artistID'):
- artistID = tag.metadata().value('tag.artistID')
+ if tag.metadata().hasKey("tag.artistID"):
+ artistID = tag.metadata().value("tag.artistID")
artist = self.getArtistFromID(artistID)
return artist
@@ -554,30 +554,30 @@ def _updateArtistTag(self, artistDict):
artistTag = None
tags = self.tags()
for tag in tags:
- if tag.metadata().hasKey('tag.artistID'):
+ if tag.metadata().hasKey("tag.artistID"):
artistTag = tag
break
if not artistTag:
- artistTag = hiero.core.Tag('Artist')
- artistTag.setIcon(artistDict['artistIcon'])
- artistTag.metadata().setValue('tag.artistID',
- str(artistDict['artistID']))
- artistTag.metadata().setValue('tag.artistName',
- str(artistDict['artistName']))
- artistTag.metadata().setValue('tag.artistDepartment',
- str(artistDict['artistDepartment']))
+ artistTag = hiero.core.Tag("Artist")
+ artistTag.setIcon(artistDict["artistIcon"])
+ artistTag.metadata().setValue("tag.artistID",
+ str(artistDict["artistID"]))
+ artistTag.metadata().setValue("tag.artistName",
+ str(artistDict["artistName"]))
+ artistTag.metadata().setValue("tag.artistDepartment",
+ str(artistDict["artistDepartment"]))
self.sequence().editFinished()
self.addTag(artistTag)
self.sequence().editFinished()
return
- artistTag.setIcon(artistDict['artistIcon'])
- artistTag.metadata().setValue('tag.artistID', str(artistDict['artistID']))
- artistTag.metadata().setValue('tag.artistName',
- str(artistDict['artistName']))
- artistTag.metadata().setValue('tag.artistDepartment',
- str(artistDict['artistDepartment']))
+ artistTag.setIcon(artistDict["artistIcon"])
+ artistTag.metadata().setValue("tag.artistID", str(artistDict["artistID"]))
+ artistTag.metadata().setValue("tag.artistName",
+ str(artistDict["artistName"]))
+ artistTag.metadata().setValue("tag.artistDepartment",
+ str(artistDict["artistDepartment"]))
self.sequence().editFinished()
return
@@ -588,8 +588,9 @@ def _setArtistByName(self, artistName):
artist = self.getArtistFromName(artistName)
if not artist:
- print 'Artist name: %s was not found in the gArtistList.' % str(
- artistName)
+ print((
+ "Artist name: {} was not found in "
+ "the gArtistList.").format(artistName))
return
# Do the update.
@@ -602,8 +603,8 @@ def _setArtistByID(self, artistID):
artist = self.getArtistFromID(artistID)
if not artist:
- print 'Artist name: %s was not found in the gArtistList.' % str(
- artistID)
+ print("Artist name: {} was not found in the gArtistList.".format(
+ artistID))
return
# Do the update.
@@ -625,15 +626,15 @@ def _status(self):
status = None
tags = self.tags()
for tag in tags:
- if tag.metadata().hasKey('tag.status'):
- status = tag.metadata().value('tag.status')
+ if tag.metadata().hasKey("tag.status"):
+ status = tag.metadata().value("tag.status")
return status
def _setStatus(self, status):
"""setShotStatus(status) -> Method to set the Status of a Shot.
Adds a special kind of status Tag to a TrackItem
- Example: myTrackItem.setStatus('Final')
+ Example: myTrackItem.setStatus("Final")
@param status - a string, corresponding to the Status name
"""
@@ -641,25 +642,25 @@ def _setStatus(self, status):
# Get a valid Tag object from the Global list of statuses
if not status in gStatusTags.keys():
- print 'Status requested was not a valid Status string.'
+ print("Status requested was not a valid Status string.")
return
# A shot should only have one status. Check if one exists and set accordingly
statusTag = None
tags = self.tags()
for tag in tags:
- if tag.metadata().hasKey('tag.status'):
+ if tag.metadata().hasKey("tag.status"):
statusTag = tag
break
if not statusTag:
- statusTag = hiero.core.Tag('Status')
+ statusTag = hiero.core.Tag("Status")
statusTag.setIcon(gStatusTags[status])
- statusTag.metadata().setValue('tag.status', status)
+ statusTag.metadata().setValue("tag.status", status)
self.addTag(statusTag)
statusTag.setIcon(gStatusTags[status])
- statusTag.metadata().setValue('tag.status', status)
+ statusTag.metadata().setValue("tag.status", status)
self.sequence().editFinished()
return
@@ -743,7 +744,7 @@ class SetStatusMenu(QMenu):
# This handles events from the Project Bin View
def eventHandler(self, event):
- if not hasattr(event.sender, 'selection'):
+ if not hasattr(event.sender, "selection"):
# Something has gone wrong, we should only be here if raised
# by the Timeline/Spreadsheet view which gives a selection.
return
@@ -781,9 +782,9 @@ class AssignArtistMenu(QMenu):
for artist in self.artists:
self.menuActions += [
titleStringTriggeredAction(
- artist['artistName'],
+ artist["artistName"],
self.setArtistFromMenuSelection,
- icon=artist['artistIcon'])
+ icon=artist["artistIcon"])
]
def setArtistFromMenuSelection(self, menuSelectionArtist):
@@ -818,7 +819,7 @@ class AssignArtistMenu(QMenu):
# This handles events from the Project Bin View
def eventHandler(self, event):
- if not hasattr(event.sender, 'selection'):
+ if not hasattr(event.sender, "selection"):
# Something has gone wrong, we should only be here if raised
# by the Timeline/Spreadsheet view which gives a selection.
return
@@ -833,7 +834,7 @@ class AssignArtistMenu(QMenu):
event.menu.addMenu(self)
-# Add the 'Set Status' context menu to Timeline and Spreadsheet
+# Add the "Set Status" context menu to Timeline and Spreadsheet
if kAddStatusMenu:
setStatusMenu = SetStatusMenu()
diff --git a/openpype/hosts/hiero/startup/Python/StartupUI/Purge.py b/openpype/hosts/hiero/api/startup/Python/StartupUI/Purge.py
similarity index 89%
rename from openpype/hosts/hiero/startup/Python/StartupUI/Purge.py
rename to openpype/hosts/hiero/api/startup/Python/StartupUI/Purge.py
index 4d2ab255ad..7b3cb11be3 100644
--- a/openpype/hosts/hiero/startup/Python/StartupUI/Purge.py
+++ b/openpype/hosts/hiero/api/startup/Python/StartupUI/Purge.py
@@ -23,7 +23,7 @@ class PurgeUnusedAction(QAction):
self.triggered.connect(self.PurgeUnused)
hiero.core.events.registerInterest("kShowContextMenu/kBin",
self.eventHandler)
- self.setIcon(QIcon('icons:TagDelete.png'))
+ self.setIcon(QIcon("icons:TagDelete.png"))
# Method to return whether a Bin is empty...
def binIsEmpty(self, b):
@@ -67,19 +67,19 @@ class PurgeUnusedAction(QAction):
msgBox.setDefaultButton(QMessageBox.Ok)
ret = msgBox.exec_()
if ret == QMessageBox.Cancel:
- print 'Not purging anything.'
+ print("Not purging anything.")
elif ret == QMessageBox.Ok:
- with proj.beginUndo('Purge Unused Clips'):
+ with proj.beginUndo("Purge Unused Clips"):
BINS = []
for clip in CLIPSTOREMOVE:
BI = clip.binItem()
B = BI.parentBin()
BINS += [B]
- print 'Removing:', BI
+ print("Removing: {}".format(BI))
try:
B.removeItem(BI)
except:
- print 'Unable to remove: ' + BI
+ print("Unable to remove: {}".format(BI))
return
# For each sequence, iterate through each track Item, see if the Clip is in the CLIPS list.
@@ -104,24 +104,24 @@ class PurgeUnusedAction(QAction):
ret = msgBox.exec_()
if ret == QMessageBox.Cancel:
- print 'Cancel'
+ print("Cancel")
return
elif ret == QMessageBox.Ok:
BINS = []
- with proj.beginUndo('Purge Unused Clips'):
+ with proj.beginUndo("Purge Unused Clips"):
# Delete the rest of the Clips
for clip in CLIPSTOREMOVE:
BI = clip.binItem()
B = BI.parentBin()
BINS += [B]
- print 'Removing:', BI
+ print("Removing: {}".format(BI))
try:
B.removeItem(BI)
except:
- print 'Unable to remove: ' + BI
+ print("Unable to remove: {}".format(BI))
def eventHandler(self, event):
- if not hasattr(event.sender, 'selection'):
+ if not hasattr(event.sender, "selection"):
# Something has gone wrong, we shouldn't only be here if raised
# by the Bin view which will give a selection.
return
diff --git a/openpype/hosts/hiero/startup/Python/StartupUI/nukeStyleKeyboardShortcuts.py b/openpype/hosts/hiero/api/startup/Python/StartupUI/nukeStyleKeyboardShortcuts.py
similarity index 72%
rename from openpype/hosts/hiero/startup/Python/StartupUI/nukeStyleKeyboardShortcuts.py
rename to openpype/hosts/hiero/api/startup/Python/StartupUI/nukeStyleKeyboardShortcuts.py
index 41c192ab15..4172b2ff85 100644
--- a/openpype/hosts/hiero/startup/Python/StartupUI/nukeStyleKeyboardShortcuts.py
+++ b/openpype/hosts/hiero/api/startup/Python/StartupUI/nukeStyleKeyboardShortcuts.py
@@ -13,21 +13,22 @@ except:
#----------------------------------------------
a = hiero.ui.findMenuAction('Import File(s)...')
-# Note: You probably best to make this 'Ctrl+R' - currently conflicts with 'Red' in the Viewer!
-a.setShortcut(QKeySequence('R'))
+# Note: You probably best to make this 'Ctrl+R' - currently conflicts with "Red" in the Viewer!
+a.setShortcut(QKeySequence("R"))
#----------------------------------------------
a = hiero.ui.findMenuAction('Import Folder(s)...')
a.setShortcut(QKeySequence('Shift+R'))
#----------------------------------------------
-a = hiero.ui.findMenuAction('Import EDL/XML/AAF...')
+a = hiero.ui.findMenuAction("Import EDL/XML/AAF...")
a.setShortcut(QKeySequence('Ctrl+Shift+O'))
#----------------------------------------------
-a = hiero.ui.findMenuAction('Metadata View')
-a.setShortcut(QKeySequence('I'))
+a = hiero.ui.findMenuAction("Metadata View")
+a.setShortcut(QKeySequence("I"))
#----------------------------------------------
-a = hiero.ui.findMenuAction('Edit Settings')
-a.setShortcut(QKeySequence('S'))
+a = hiero.ui.findMenuAction("Edit Settings")
+a.setShortcut(QKeySequence("S"))
#----------------------------------------------
-a = hiero.ui.findMenuAction('Monitor Output')
-a.setShortcut(QKeySequence('Ctrl+U'))
+a = hiero.ui.findMenuAction("Monitor Output")
+if a:
+ a.setShortcut(QKeySequence('Ctrl+U'))
#----------------------------------------------
diff --git a/openpype/hosts/hiero/startup/Python/StartupUI/otioimporter/OTIOImport.py b/openpype/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py
similarity index 94%
rename from openpype/hosts/hiero/startup/Python/StartupUI/otioimporter/OTIOImport.py
rename to openpype/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py
index 7efb352ed2..17c044f3ec 100644
--- a/openpype/hosts/hiero/startup/Python/StartupUI/otioimporter/OTIOImport.py
+++ b/openpype/hosts/hiero/api/startup/Python/StartupUI/otioimporter/OTIOImport.py
@@ -44,16 +44,16 @@ def get_transition_type(otio_item, otio_track):
_out = None
if _in and _out:
- return 'dissolve'
+ return "dissolve"
elif _in and not _out:
- return 'fade_out'
+ return "fade_out"
elif not _in and _out:
- return 'fade_in'
+ return "fade_in"
else:
- return 'unknown'
+ return "unknown"
def find_trackitem(name, hiero_track):
@@ -84,10 +84,10 @@ def apply_transition(otio_track, otio_item, track):
# Figure out track kind for getattr below
if isinstance(track, hiero.core.VideoTrack):
- kind = ''
+ kind = ""
else:
- kind = 'Audio'
+ kind = "Audio"
try:
# Gather TrackItems involved in trasition
@@ -98,7 +98,7 @@ def apply_transition(otio_track, otio_item, track):
)
# Create transition object
- if transition_type == 'dissolve':
+ if transition_type == "dissolve":
transition_func = getattr(
hiero.core.Transition,
'create{kind}DissolveTransition'.format(kind=kind)
@@ -111,7 +111,7 @@ def apply_transition(otio_track, otio_item, track):
otio_item.out_offset.value
)
- elif transition_type == 'fade_in':
+ elif transition_type == "fade_in":
transition_func = getattr(
hiero.core.Transition,
'create{kind}FadeInTransition'.format(kind=kind)
@@ -121,7 +121,7 @@ def apply_transition(otio_track, otio_item, track):
otio_item.out_offset.value
)
- elif transition_type == 'fade_out':
+ elif transition_type == "fade_out":
transition_func = getattr(
hiero.core.Transition,
'create{kind}FadeOutTransition'.format(kind=kind)
@@ -150,11 +150,11 @@ def apply_transition(otio_track, otio_item, track):
def prep_url(url_in):
url = unquote(url_in)
- if url.startswith('file://localhost/'):
- return url.replace('file://localhost/', '')
+ if url.startswith("file://localhost/"):
+ return url.replace("file://localhost/", "")
url = '{url}'.format(
- sep=url.startswith(os.sep) and '' or os.sep,
+ sep=url.startswith(os.sep) and "" or os.sep,
url=url.startswith(os.sep) and url[1:] or url
)
@@ -228,8 +228,8 @@ def add_metadata(metadata, hiero_item):
continue
if value is not None:
- if not key.startswith('tag.'):
- key = 'tag.' + key
+ if not key.startswith("tag."):
+ key = "tag." + key
hiero_item.metadata().setValue(key, str(value))
@@ -371,10 +371,10 @@ def build_sequence(
if not sequence:
# Create a Sequence
- sequence = hiero.core.Sequence(otio_timeline.name or 'OTIOSequence')
+ sequence = hiero.core.Sequence(otio_timeline.name or "OTIOSequence")
# Set sequence settings from otio timeline if available
- if hasattr(otio_timeline, 'global_start_time'):
+ if hasattr(otio_timeline, "global_start_time"):
if otio_timeline.global_start_time:
start_time = otio_timeline.global_start_time
sequence.setFramerate(start_time.rate)
@@ -414,7 +414,7 @@ def build_sequence(
if isinstance(otio_clip, otio.schema.Stack):
bar = hiero.ui.mainWindow().statusBar()
bar.showMessage(
- 'Nested sequences are created separately.',
+ "Nested sequences are created separately.",
timeout=3000
)
build_sequence(otio_clip, project, otio_track.kind)
diff --git a/openpype/hosts/hiero/startup/Python/StartupUI/otioimporter/__init__.py b/openpype/hosts/hiero/api/startup/Python/StartupUI/otioimporter/__init__.py
similarity index 84%
rename from openpype/hosts/hiero/startup/Python/StartupUI/otioimporter/__init__.py
rename to openpype/hosts/hiero/api/startup/Python/StartupUI/otioimporter/__init__.py
index 0f0a643909..91be4d02aa 100644
--- a/openpype/hosts/hiero/startup/Python/StartupUI/otioimporter/__init__.py
+++ b/openpype/hosts/hiero/api/startup/Python/StartupUI/otioimporter/__init__.py
@@ -9,19 +9,19 @@ import hiero.core
import PySide2.QtWidgets as qw
-from openpype.hosts.hiero.otio.hiero_import import load_otio
+from openpype.hosts.hiero.api.otio.hiero_import import load_otio
class OTIOProjectSelect(qw.QDialog):
def __init__(self, projects, *args, **kwargs):
super(OTIOProjectSelect, self).__init__(*args, **kwargs)
- self.setWindowTitle('Please select active project')
+ self.setWindowTitle("Please select active project")
self.layout = qw.QVBoxLayout()
self.label = qw.QLabel(
- 'Unable to determine which project to import sequence to.\n'
- 'Please select one.'
+ "Unable to determine which project to import sequence to.\n"
+ "Please select one."
)
self.layout.addWidget(self.label)
@@ -45,7 +45,7 @@ def get_sequence(view):
elif isinstance(view, hiero.ui.BinView):
for item in view.selection():
- if not hasattr(item, 'acitveItem'):
+ if not hasattr(item, "acitveItem"):
continue
if isinstance(item.activeItem(), hiero.core.Sequence):
@@ -57,13 +57,13 @@ def get_sequence(view):
def OTIO_menu_action(event):
# Menu actions
otio_import_action = hiero.ui.createMenuAction(
- 'Import OTIO...',
+ "Import OTIO...",
open_otio_file,
icon=None
)
otio_add_track_action = hiero.ui.createMenuAction(
- 'New Track(s) from OTIO...',
+ "New Track(s) from OTIO...",
open_otio_file,
icon=None
)
@@ -80,19 +80,19 @@ def OTIO_menu_action(event):
otio_add_track_action.setEnabled(True)
for action in event.menu.actions():
- if action.text() == 'Import':
+ if action.text() == "Import":
action.menu().addAction(otio_import_action)
action.menu().addAction(otio_add_track_action)
- elif action.text() == 'New Track':
+ elif action.text() == "New Track":
action.menu().addAction(otio_add_track_action)
def open_otio_file():
files = hiero.ui.openFileBrowser(
- caption='Please select an OTIO file of choice',
- pattern='*.otio',
- requiredExtension='.otio'
+ caption="Please select an OTIO file of choice",
+ pattern="*.otio",
+ requiredExtension=".otio"
)
selection = None
@@ -117,7 +117,7 @@ def open_otio_file():
else:
bar = hiero.ui.mainWindow().statusBar()
bar.showMessage(
- 'OTIO Import aborted by user',
+ "OTIO Import aborted by user",
timeout=3000
)
return
diff --git a/openpype/hosts/hiero/startup/Python/StartupUI/setPosterFrame.py b/openpype/hosts/hiero/api/startup/Python/StartupUI/setPosterFrame.py
similarity index 94%
rename from openpype/hosts/hiero/startup/Python/StartupUI/setPosterFrame.py
rename to openpype/hosts/hiero/api/startup/Python/StartupUI/setPosterFrame.py
index 18398aa119..8614d51bb0 100644
--- a/openpype/hosts/hiero/startup/Python/StartupUI/setPosterFrame.py
+++ b/openpype/hosts/hiero/api/startup/Python/StartupUI/setPosterFrame.py
@@ -10,15 +10,15 @@ except:
def setPosterFrame(posterFrame=.5):
- '''
+ """
Update the poster frame of the given clipItmes
posterFrame = .5 uses the centre frame, a value of 0 uses the first frame, a value of 1 uses the last frame
- '''
+ """
view = hiero.ui.activeView()
selectedBinItems = view.selection()
selectedClipItems = [(item.activeItem()
- if hasattr(item, 'activeItem') else item)
+ if hasattr(item, "activeItem") else item)
for item in selectedBinItems]
for clip in selectedClipItems:
diff --git a/openpype/hosts/hiero/startup/TaskPresets/10.5/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml b/openpype/hosts/hiero/api/startup/TaskPresets/10.5/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
similarity index 100%
rename from openpype/hosts/hiero/startup/TaskPresets/10.5/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
rename to openpype/hosts/hiero/api/startup/TaskPresets/10.5/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
diff --git a/openpype/hosts/hiero/startup/TaskPresets/11.1/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml b/openpype/hosts/hiero/api/startup/TaskPresets/11.1/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
similarity index 100%
rename from openpype/hosts/hiero/startup/TaskPresets/11.1/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
rename to openpype/hosts/hiero/api/startup/TaskPresets/11.1/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
diff --git a/openpype/hosts/hiero/startup/TaskPresets/11.2/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml b/openpype/hosts/hiero/api/startup/TaskPresets/11.2/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
similarity index 100%
rename from openpype/hosts/hiero/startup/TaskPresets/11.2/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
rename to openpype/hosts/hiero/api/startup/TaskPresets/11.2/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
diff --git a/openpype/hosts/hiero/api/workio.py b/openpype/hosts/hiero/api/workio.py
index 15ffbf84d8..dacb11624f 100644
--- a/openpype/hosts/hiero/api/workio.py
+++ b/openpype/hosts/hiero/api/workio.py
@@ -65,13 +65,9 @@ def open_file(filepath):
def current_file():
current_file = hiero.core.projects()[-1].path()
- normalised = os.path.normpath(current_file)
-
- # Unsaved current file
- if normalised == "":
+ if not current_file:
return None
-
- return normalised
+ return os.path.normpath(current_file)
def work_root(session):
diff --git a/openpype/hosts/hiero/plugins/publish/precollect_instances.py b/openpype/hosts/hiero/plugins/publish/precollect_instances.py
index 85b4e273d5..bf3a779ab1 100644
--- a/openpype/hosts/hiero/plugins/publish/precollect_instances.py
+++ b/openpype/hosts/hiero/plugins/publish/precollect_instances.py
@@ -1,11 +1,9 @@
import pyblish
import openpype
from openpype.hosts.hiero import api as phiero
-from openpype.hosts.hiero.otio import hiero_export
+from openpype.hosts.hiero.api.otio import hiero_export
import hiero
-from compiler.ast import flatten
-
# # developer reload modules
from pprint import pformat
@@ -339,10 +337,10 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
continue
track_index = track.trackIndex()
- _sub_track_items = flatten(track.subTrackItems())
+ _sub_track_items = phiero.flatten(track.subTrackItems())
# continue only if any subtrack items are collected
- if len(_sub_track_items) < 1:
+ if not list(_sub_track_items):
continue
enabled_sti = []
@@ -357,7 +355,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
enabled_sti.append(_sti)
# continue only if any subtrack items are collected
- if len(enabled_sti) < 1:
+ if not enabled_sti:
continue
# add collection of subtrackitems to dict
@@ -371,7 +369,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
Returns list of Clip's hiero.core.Annotation
"""
annotations = []
- subTrackItems = flatten(clip.subTrackItems())
+ subTrackItems = phiero.flatten(clip.subTrackItems())
annotations += [item for item in subTrackItems if isinstance(
item, hiero.core.Annotation)]
return annotations
@@ -382,7 +380,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
Returns list of Clip's hiero.core.SubTrackItem
"""
subtracks = []
- subTrackItems = flatten(clip.parent().subTrackItems())
+ subTrackItems = phiero.flatten(clip.parent().subTrackItems())
for item in subTrackItems:
if "TimeWarp" in item.name():
continue
diff --git a/openpype/hosts/hiero/plugins/publish/precollect_workfile.py b/openpype/hosts/hiero/plugins/publish/precollect_workfile.py
index 7db155048f..d48d6949bd 100644
--- a/openpype/hosts/hiero/plugins/publish/precollect_workfile.py
+++ b/openpype/hosts/hiero/plugins/publish/precollect_workfile.py
@@ -4,7 +4,7 @@ import hiero.ui
from openpype.hosts.hiero import api as phiero
from avalon import api as avalon
from pprint import pformat
-from openpype.hosts.hiero.otio import hiero_export
+from openpype.hosts.hiero.api.otio import hiero_export
from Qt.QtGui import QPixmap
import tempfile
diff --git a/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py b/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py
index de05414c88..f0e0f1a1a3 100644
--- a/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py
+++ b/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_retime.py
@@ -1,7 +1,7 @@
from pyblish import api
import hiero
import math
-from openpype.hosts.hiero.otio.hiero_export import create_otio_time_range
+from openpype.hosts.hiero.api.otio.hiero_export import create_otio_time_range
class PrecollectRetime(api.InstancePlugin):
"""Calculate Retiming of selected track items."""
diff --git a/openpype/hosts/hiero/startup/Python/Startup/otioexporter/__init__.py b/openpype/hosts/hiero/startup/Python/Startup/otioexporter/__init__.py
deleted file mode 100644
index 3c09655f01..0000000000
--- a/openpype/hosts/hiero/startup/Python/Startup/otioexporter/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from OTIOExportTask import OTIOExportTask
-from OTIOExportUI import OTIOExportUI
-
-__all__ = [
- 'OTIOExportTask',
- 'OTIOExportUI'
-]
diff --git a/openpype/hosts/hiero/startup/Python/Startup/selection_tracker.py b/openpype/hosts/hiero/startup/Python/Startup/selection_tracker.py
deleted file mode 100644
index b7e05fed7c..0000000000
--- a/openpype/hosts/hiero/startup/Python/Startup/selection_tracker.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""Puts the selection project into 'hiero.selection'"""
-
-import hiero
-
-
-def selectionChanged(event):
- hiero.selection = event.sender.selection()
-
-hiero.core.events.registerInterest('kSelectionChanged', selectionChanged)
diff --git a/openpype/hosts/nuke/api/__init__.py b/openpype/hosts/nuke/api/__init__.py
index e684b48fa3..1567189ed1 100644
--- a/openpype/hosts/nuke/api/__init__.py
+++ b/openpype/hosts/nuke/api/__init__.py
@@ -54,6 +54,10 @@ def install():
''' Installing all requarements for Nuke host
'''
+ # remove all registred callbacks form avalon.nuke
+ from avalon import pipeline
+ pipeline._registered_event_handlers.clear()
+
log.info("Registering Nuke plug-ins..")
pyblish.api.register_plugin_path(PUBLISH_PATH)
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
@@ -62,6 +66,7 @@ def install():
# Register Avalon event for workfiles loading.
avalon.api.on("workio.open_file", lib.check_inventory_versions)
+ avalon.api.on("taskChanged", menu.change_context_label)
pyblish.api.register_callback(
"instanceToggled", on_pyblish_instance_toggled)
diff --git a/openpype/hosts/nuke/api/menu.py b/openpype/hosts/nuke/api/menu.py
index 4636098604..86293edb99 100644
--- a/openpype/hosts/nuke/api/menu.py
+++ b/openpype/hosts/nuke/api/menu.py
@@ -1,101 +1,128 @@
import os
import nuke
-from avalon.api import Session
from avalon.nuke.pipeline import get_main_window
from .lib import WorkfileSettings
from openpype.api import Logger, BuildWorkfile, get_current_project_settings
from openpype.tools.utils import host_tools
-from avalon.nuke.pipeline import get_main_window
log = Logger().get_logger(__name__)
menu_label = os.environ["AVALON_LABEL"]
+context_label = None
-def install():
- main_window = get_main_window()
+def change_context_label(*args):
+ global context_label
menubar = nuke.menu("Nuke")
menu = menubar.findItem(menu_label)
- # replace reset resolution from avalon core to pype's
- name = "Work Files..."
- rm_item = [
- (i, item) for i, item in enumerate(menu.items()) if name in item.name()
- ][0]
-
- log.debug("Changing Item: {}".format(rm_item))
-
- menu.removeItem(rm_item[1].name())
- menu.addCommand(
- name,
- lambda: host_tools.show_workfiles(parent=main_window),
- index=2
+ label = "{0}, {1}".format(
+ os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"]
)
- menu.addSeparator(index=3)
- # replace reset resolution from avalon core to pype's
- name = "Reset Resolution"
- new_name = "Set Resolution"
+
rm_item = [
- (i, item) for i, item in enumerate(menu.items()) if name in item.name()
+ (i, item) for i, item in enumerate(menu.items())
+ if context_label in item.name()
][0]
- log.debug("Changing Item: {}".format(rm_item))
- # rm_item[1].setEnabled(False)
menu.removeItem(rm_item[1].name())
- menu.addCommand(
- new_name,
- lambda: WorkfileSettings().reset_resolution(),
+
+ context_action = menu.addCommand(
+ label,
index=(rm_item[0])
)
+ context_action.setEnabled(False)
- # replace reset frame range from avalon core to pype's
- name = "Reset Frame Range"
- new_name = "Set Frame Range"
- rm_item = [
- (i, item) for i, item in enumerate(menu.items()) if name in item.name()
- ][0]
- log.debug("Changing Item: {}".format(rm_item))
- # rm_item[1].setEnabled(False)
- menu.removeItem(rm_item[1].name())
- menu.addCommand(
- new_name,
- lambda: WorkfileSettings().reset_frame_range_handles(),
- index=(rm_item[0])
- )
+ log.info("Task label changed from `{}` to `{}`".format(
+ context_label, label))
- # add colorspace menu item
- name = "Set Colorspace"
- menu.addCommand(
- name, lambda: WorkfileSettings().set_colorspace()
- )
- log.debug("Adding menu item: {}".format(name))
+ context_label = label
- # add item that applies all setting above
- name = "Apply All Settings"
- menu.addCommand(
- name,
- lambda: WorkfileSettings().set_context_settings()
+
+
+def install():
+ from openpype.hosts.nuke.api import reload_config
+
+ global context_label
+
+ # uninstall original avalon menu
+ uninstall()
+
+ main_window = get_main_window()
+ menubar = nuke.menu("Nuke")
+ menu = menubar.addMenu(menu_label)
+
+ label = "{0}, {1}".format(
+ os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"]
)
- log.debug("Adding menu item: {}".format(name))
+ context_label = label
+ context_action = menu.addCommand(label)
+ context_action.setEnabled(False)
menu.addSeparator()
-
- # add workfile builder menu item
- name = "Build Workfile"
menu.addCommand(
- name, lambda: BuildWorkfile().process()
+ "Work Files...",
+ lambda: host_tools.show_workfiles(parent=main_window)
+ )
+
+ menu.addSeparator()
+ menu.addCommand(
+ "Create...",
+ lambda: host_tools.show_creator(parent=main_window)
+ )
+ menu.addCommand(
+ "Load...",
+ lambda: host_tools.show_loader(
+ parent=main_window,
+ use_context=True
+ )
+ )
+ menu.addCommand(
+ "Publish...",
+ lambda: host_tools.show_publish(parent=main_window)
+ )
+ menu.addCommand(
+ "Manage...",
+ lambda: host_tools.show_scene_inventory(parent=main_window)
+ )
+
+ menu.addSeparator()
+ menu.addCommand(
+ "Set Resolution",
+ lambda: WorkfileSettings().reset_resolution()
+ )
+ menu.addCommand(
+ "Set Frame Range",
+ lambda: WorkfileSettings().reset_frame_range_handles()
+ )
+ menu.addCommand(
+ "Set Colorspace",
+ lambda: WorkfileSettings().set_colorspace()
+ )
+ menu.addCommand(
+ "Apply All Settings",
+ lambda: WorkfileSettings().set_context_settings()
+ )
+
+ menu.addSeparator()
+ menu.addCommand(
+ "Build Workfile",
+ lambda: BuildWorkfile().process()
)
- log.debug("Adding menu item: {}".format(name))
- # Add experimental tools action
menu.addSeparator()
menu.addCommand(
"Experimental tools...",
lambda: host_tools.show_experimental_tools_dialog(parent=main_window)
)
+ # add reload pipeline only in debug mode
+ if bool(os.getenv("NUKE_DEBUG")):
+ menu.addSeparator()
+ menu.addCommand("Reload Pipeline", reload_config)
+
# adding shortcuts
add_shortcuts_from_presets()
diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py
index 4ad2246e21..9ce72c0519 100644
--- a/openpype/hosts/nuke/plugins/load/load_clip.py
+++ b/openpype/hosts/nuke/plugins/load/load_clip.py
@@ -116,16 +116,7 @@ class LoadClip(plugin.NukeLoader):
"Representation id `{}` is failing to load".format(repr_id))
return
- name_data = {
- "asset": repr_cont["asset"],
- "subset": repr_cont["subset"],
- "representation": context["representation"]["name"],
- "ext": repr_cont["representation"],
- "id": context["representation"]["_id"],
- "class_name": self.__class__.__name__
- }
-
- read_name = self.node_name_template.format(**name_data)
+ read_name = self._get_node_name(context["representation"])
# Create the Loader with the filename path set
read_node = nuke.createNode(
@@ -143,7 +134,7 @@ class LoadClip(plugin.NukeLoader):
elif iio_colorspace is not None:
read_node["colorspace"].setValue(iio_colorspace)
- self.set_range_to_node(read_node, first, last, start_at_workfile)
+ self._set_range_to_node(read_node, first, last, start_at_workfile)
# add additional metadata from the version to imprint Avalon knob
add_keys = ["frameStart", "frameEnd",
@@ -171,7 +162,7 @@ class LoadClip(plugin.NukeLoader):
data=data_imprint)
if version_data.get("retime", None):
- self.make_retimes(read_node, version_data)
+ self._make_retimes(read_node, version_data)
self.set_as_member(read_node)
@@ -230,6 +221,9 @@ class LoadClip(plugin.NukeLoader):
"Representation id `{}` is failing to load".format(repr_id))
return
+ read_name = self._get_node_name(representation)
+
+ read_node["name"].setValue(read_name)
read_node["file"].setValue(file)
# to avoid multiple undo steps for rest of process
@@ -242,7 +236,7 @@ class LoadClip(plugin.NukeLoader):
elif iio_colorspace is not None:
read_node["colorspace"].setValue(iio_colorspace)
- self.set_range_to_node(read_node, first, last, start_at_workfile)
+ self._set_range_to_node(read_node, first, last, start_at_workfile)
updated_dict = {
"representation": str(representation["_id"]),
@@ -279,21 +273,12 @@ class LoadClip(plugin.NukeLoader):
self.log.info("udated to version: {}".format(version.get("name")))
if version_data.get("retime", None):
- self.make_retimes(read_node, version_data)
+ self._make_retimes(read_node, version_data)
else:
self.clear_members(read_node)
self.set_as_member(read_node)
- def set_range_to_node(self, read_node, first, last, start_at_workfile):
- read_node['origfirst'].setValue(int(first))
- read_node['first'].setValue(int(first))
- read_node['origlast'].setValue(int(last))
- read_node['last'].setValue(int(last))
-
- # set start frame depending on workfile or version
- self.loader_shift(read_node, start_at_workfile)
-
def remove(self, container):
from avalon.nuke import viewer_update_and_undo_stop
@@ -307,7 +292,16 @@ class LoadClip(plugin.NukeLoader):
for member in members:
nuke.delete(member)
- def make_retimes(self, parent_node, version_data):
+ def _set_range_to_node(self, read_node, first, last, start_at_workfile):
+ read_node['origfirst'].setValue(int(first))
+ read_node['first'].setValue(int(first))
+ read_node['origlast'].setValue(int(last))
+ read_node['last'].setValue(int(last))
+
+ # set start frame depending on workfile or version
+ self._loader_shift(read_node, start_at_workfile)
+
+ def _make_retimes(self, parent_node, version_data):
''' Create all retime and timewarping nodes with coppied animation '''
speed = version_data.get('speed', 1)
time_warp_nodes = version_data.get('timewarps', [])
@@ -360,7 +354,7 @@ class LoadClip(plugin.NukeLoader):
for i, n in enumerate(dependent_nodes):
last_node.setInput(i, n)
- def loader_shift(self, read_node, workfile_start=False):
+ def _loader_shift(self, read_node, workfile_start=False):
""" Set start frame of read node to a workfile start
Args:
@@ -371,3 +365,17 @@ class LoadClip(plugin.NukeLoader):
if workfile_start:
read_node['frame_mode'].setValue("start at")
read_node['frame'].setValue(str(self.script_start))
+
+ def _get_node_name(self, representation):
+
+ repr_cont = representation["context"]
+ name_data = {
+ "asset": repr_cont["asset"],
+ "subset": repr_cont["subset"],
+ "representation": representation["name"],
+ "ext": repr_cont["representation"],
+ "id": representation["_id"],
+ "class_name": self.__class__.__name__
+ }
+
+ return self.node_name_template.format(**name_data)
\ No newline at end of file
diff --git a/openpype/modules/default_modules/royal_render/plugins/publish/collect_default_rr_path.py b/openpype/modules/default_modules/royal_render/plugins/publish/collect_default_rr_path.py
index cdca03bef0..3ce95e0c50 100644
--- a/openpype/modules/default_modules/royal_render/plugins/publish/collect_default_rr_path.py
+++ b/openpype/modules/default_modules/royal_render/plugins/publish/collect_default_rr_path.py
@@ -6,7 +6,7 @@ import pyblish.api
class CollectDefaultRRPath(pyblish.api.ContextPlugin):
"""Collect default Royal Render path."""
- order = pyblish.api.CollectorOrder + 0.01
+ order = pyblish.api.CollectorOrder
label = "Default Royal Render Path"
def process(self, context):
diff --git a/openpype/modules/default_modules/royal_render/plugins/publish/collect_rr_path_from_instance.py b/openpype/modules/default_modules/royal_render/plugins/publish/collect_rr_path_from_instance.py
index fb27a76d11..6a3dc276f3 100644
--- a/openpype/modules/default_modules/royal_render/plugins/publish/collect_rr_path_from_instance.py
+++ b/openpype/modules/default_modules/royal_render/plugins/publish/collect_rr_path_from_instance.py
@@ -5,7 +5,7 @@ import pyblish.api
class CollectRRPathFromInstance(pyblish.api.InstancePlugin):
"""Collect RR Path from instance."""
- order = pyblish.api.CollectorOrder
+ order = pyblish.api.CollectorOrder + 0.01
label = "Royal Render Path from the Instance"
families = ["rendering"]
@@ -38,8 +38,8 @@ class CollectRRPathFromInstance(pyblish.api.InstancePlugin):
if k in default_servers
}
- except AttributeError:
- # Handle situation were we had only one url for deadline.
+ except (AttributeError, KeyError):
+ # Handle situation were we had only one url for royal render.
return render_instance.context.data["defaultRRPath"]
return rr_servers[
diff --git a/openpype/modules/default_modules/royal_render/plugins/publish/collect_sequences_from_job.py b/openpype/modules/default_modules/royal_render/plugins/publish/collect_sequences_from_job.py
index 2505d671af..b389b022cf 100644
--- a/openpype/modules/default_modules/royal_render/plugins/publish/collect_sequences_from_job.py
+++ b/openpype/modules/default_modules/royal_render/plugins/publish/collect_sequences_from_job.py
@@ -168,9 +168,6 @@ class CollectSequencesFromJob(pyblish.api.ContextPlugin):
start = data.get("frameStart", indices[0])
end = data.get("frameEnd", indices[-1])
- # root = os.path.normpath(root)
- # self.log.info("Source: {}}".format(data.get("source", "")))
-
ext = list(collection)[0].split('.')[-1]
instance.data.update({
@@ -195,6 +192,8 @@ class CollectSequencesFromJob(pyblish.api.ContextPlugin):
'name': ext,
'ext': '{}'.format(ext),
'files': list(collection),
+ "frameStart": start,
+ "frameEnd": end,
"stagingDir": root,
"anatomy_template": "render",
"fps": fps,
diff --git a/openpype/modules/default_modules/royal_render/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py b/openpype/modules/default_modules/royal_render/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py
index 290f26a44a..7fedb51410 100644
--- a/openpype/modules/default_modules/royal_render/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py
+++ b/openpype/modules/default_modules/royal_render/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py
@@ -38,28 +38,20 @@ class OpenPypeContextSelector:
os.environ.get("PROGRAMFILES"),
"OpenPype", "openpype_console.exe"
)
- if os.path.exists(op_path):
- print(" - found OpenPype installation {}".format(op_path))
- else:
+ if not os.path.exists(op_path):
# try to find in user local context
op_path = os.path.join(
os.environ.get("LOCALAPPDATA"),
"Programs",
"OpenPype", "openpype_console.exe"
)
- if os.path.exists(op_path):
- print(
- " - found OpenPype installation {}".format(
- op_path))
- else:
+ if not os.path.exists(op_path):
raise Exception("Error: OpenPype was not found.")
- self.openpype_root = op_path
+ op_path = os.path.dirname(op_path)
+ print(" - found OpenPype installation {}".format(op_path))
- # TODO: this should try to find metadata file. Either using
- # jobs custom attributes or using environment variable
- # or just using plain existence of file.
- # self.context = self._process_metadata_file()
+ self.openpype_root = op_path
def _process_metadata_file(self):
"""Find and process metadata file.
@@ -86,8 +78,8 @@ class OpenPypeContextSelector:
automatically, no UI will be show and publishing will directly
proceed.
"""
- if not self.context:
- self.show()
+ if not self.context and not self.show():
+ return
self.context["user"] = self.job.userName
self.run_publish()
@@ -120,10 +112,15 @@ class OpenPypeContextSelector:
not self.context.get("asset") or \
not self.context.get("task"):
self._show_rr_warning("Context selection failed.")
- return
+ return False
# self.context["app_name"] = self.job.renderer.name
+ # there should be mapping between OpenPype and Royal Render
+ # app names and versions, but since app_name is not used
+ # currently down the line (but it is required by OP publish command
+ # right now).
self.context["app_name"] = "maya/2020"
+ return True
@staticmethod
def _show_rr_warning(text):
diff --git a/openpype/plugins/publish/collect_otio_subset_resources.py b/openpype/plugins/publish/collect_otio_subset_resources.py
index dd670ff850..571d0d56a4 100644
--- a/openpype/plugins/publish/collect_otio_subset_resources.py
+++ b/openpype/plugins/publish/collect_otio_subset_resources.py
@@ -171,8 +171,7 @@ class CollectOcioSubsetResources(pyblish.api.InstancePlugin):
instance.data["representations"].append(repre)
self.log.debug(">>>>>>>> {}".format(repre))
- import pprint
- self.log.debug(pprint.pformat(instance.data))
+ self.log.debug(instance.data)
def _create_representation(self, start, end, **kwargs):
"""
diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json
index cc80a94d3f..d536652581 100644
--- a/openpype/settings/defaults/system_settings/applications.json
+++ b/openpype/settings/defaults/system_settings/applications.json
@@ -107,7 +107,10 @@
"windows": "",
"darwin": "",
"linux": ""
- }
+ },
+ "FLAME_WIRETAP_HOSTNAME": "",
+ "FLAME_WIRETAP_VOLUME": "stonefs",
+ "FLAME_WIRETAP_GROUP": "staff"
},
"variants": {
"2021": {
@@ -502,7 +505,7 @@
"environment": {}
},
"__dynamic_keys_labels__": {
- "13-0": "13.0 (Testing only)",
+ "13-0": "13.0",
"12-2": "12.2",
"12-0": "12.0",
"11-3": "11.3",
@@ -639,7 +642,7 @@
"environment": {}
},
"__dynamic_keys_labels__": {
- "13-0": "13.0 (Testing only)",
+ "13-0": "13.0",
"12-2": "12.2",
"12-0": "12.0",
"11-3": "11.3",
diff --git a/poetry.lock b/poetry.lock
index c07a20253c..f513b76611 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -219,16 +219,16 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "clique"
-version = "1.5.0"
+version = "1.6.1"
description = "Manage collections with common numerical component"
category = "main"
optional = false
-python-versions = "*"
+python-versions = ">=2.7, <4.0"
[package.extras]
-dev = ["lowdown (>=0.1.0,<1)", "pytest (>=2.3.5,<3)", "pytest-cov (>=2,<3)", "pytest-runner (>=2.7,<3)", "sphinx (>=1.2.2,<2)", "sphinx-rtd-theme (>=0.1.6,<1)"]
-doc = ["lowdown (>=0.1.0,<1)", "sphinx (>=1.2.2,<2)", "sphinx-rtd-theme (>=0.1.6,<1)"]
-test = ["pytest (>=2.3.5,<3)", "pytest-cov (>=2,<3)", "pytest-runner (>=2.7,<3)"]
+dev = ["sphinx (>=2,<4)", "sphinx-rtd-theme (>=0.1.6,<1)", "lowdown (>=0.2.0,<1)", "pytest-runner (>=2.7,<3)", "pytest (>=2.3.5,<5)", "pytest-cov (>=2,<3)"]
+doc = ["sphinx (>=2,<4)", "sphinx-rtd-theme (>=0.1.6,<1)", "lowdown (>=0.2.0,<1)"]
+test = ["pytest-runner (>=2.7,<3)", "pytest (>=2.3.5,<5)", "pytest-cov (>=2,<3)"]
[[package]]
name = "colorama"
@@ -1580,7 +1580,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "3.7.*"
-content-hash = "fb6db80d126fe7ef2d1d06d0381b6d11445d6d3e54b33585f6b0a0b6b0b9d372"
+content-hash = "877c1c6292735f495d915fc6aa85450eb20fc63f266a9c6bf7ba1125af3579a5"
[metadata.files]
acre = []
@@ -1749,8 +1749,8 @@ click = [
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
]
clique = [
- {file = "clique-1.5.0-py2-none-any.whl", hash = "sha256:77efbf5d99a398a50ca4591373def45c9c70fb43232cdc32f521cf5257ce4330"},
- {file = "clique-1.5.0.tar.gz", hash = "sha256:c34a4eac30187a5b7d75bc8cf600ddc50ceef50a423772a4c96f1dc8440af5fa"},
+ {file = "clique-1.6.1-py2.py3-none-any.whl", hash = "sha256:8619774fa035661928dd8c93cd805acf2d42533ccea1b536c09815ed426c9858"},
+ {file = "clique-1.6.1.tar.gz", hash = "sha256:90165c1cf162d4dd1baef83ceaa1afc886b453e379094fa5b60ea470d1733e66"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
@@ -2180,9 +2180,13 @@ protobuf = [
{file = "protobuf-3.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2ae692bb6d1992afb6b74348e7bb648a75bb0d3565a3f5eea5bec8f62bd06d87"},
{file = "protobuf-3.17.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:99938f2a2d7ca6563c0ade0c5ca8982264c484fdecf418bd68e880a7ab5730b1"},
{file = "protobuf-3.17.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6902a1e4b7a319ec611a7345ff81b6b004b36b0d2196ce7a748b3493da3d226d"},
+ {file = "protobuf-3.17.3-cp38-cp38-win32.whl", hash = "sha256:59e5cf6b737c3a376932fbfb869043415f7c16a0cf176ab30a5bbc419cd709c1"},
+ {file = "protobuf-3.17.3-cp38-cp38-win_amd64.whl", hash = "sha256:ebcb546f10069b56dc2e3da35e003a02076aaa377caf8530fe9789570984a8d2"},
{file = "protobuf-3.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4ffbd23640bb7403574f7aff8368e2aeb2ec9a5c6306580be48ac59a6bac8bde"},
{file = "protobuf-3.17.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:26010f693b675ff5a1d0e1bdb17689b8b716a18709113288fead438703d45539"},
{file = "protobuf-3.17.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e76d9686e088fece2450dbc7ee905f9be904e427341d289acbe9ad00b78ebd47"},
+ {file = "protobuf-3.17.3-cp39-cp39-win32.whl", hash = "sha256:a38bac25f51c93e4be4092c88b2568b9f407c27217d3dd23c7a57fa522a17554"},
+ {file = "protobuf-3.17.3-cp39-cp39-win_amd64.whl", hash = "sha256:85d6303e4adade2827e43c2b54114d9a6ea547b671cb63fafd5011dc47d0e13d"},
{file = "protobuf-3.17.3-py2.py3-none-any.whl", hash = "sha256:2bfb815216a9cd9faec52b16fd2bfa68437a44b67c56bee59bc3926522ecb04e"},
{file = "protobuf-3.17.3.tar.gz", hash = "sha256:72804ea5eaa9c22a090d2803813e280fb273b62d5ae497aaf3553d141c4fdd7b"},
]
diff --git a/pyproject.toml b/pyproject.toml
index bde4263241..033458d436 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -36,7 +36,7 @@ opentimelineio = { version = "0.14.0.dev1", source = "openpype" }
appdirs = "^1.4.3"
blessed = "^1.17" # openpype terminal formatting
coolname = "*"
-clique = "1.5.*"
+clique = "1.6.*"
Click = "^7"
dnspython = "^2.1.0"
ftrack-python-api = "2.0.*"
diff --git a/tools/build.sh b/tools/build.sh
index bc79f03db7..301f26023a 100755
--- a/tools/build.sh
+++ b/tools/build.sh
@@ -178,10 +178,10 @@ main () {
fi
if [ "$disable_submodule_update" == 1 ]; then
- echo -e "${BIGreen}>>>${RST} Making sure submodules are up-to-date ..."
- git submodule update --init --recursive
+ echo -e "${BIYellow}***${RST} Not updating submodules ..."
else
- echo -e "${BIYellow}***${RST} Not updating submodules ..."
+ echo -e "${BIGreen}>>>${RST} Making sure submodules are up-to-date ..."
+ git submodule update --init --recursive
fi
echo -e "${BIGreen}>>>${RST} Building ..."
if [[ "$OSTYPE" == "linux-gnu"* ]]; then