diff --git a/pype/nukestudio/__init__.py b/pype/nukestudio/__init__.py
index a1ee6dedf7..f3ef69608f 100644
--- a/pype/nukestudio/__init__.py
+++ b/pype/nukestudio/__init__.py
@@ -19,27 +19,18 @@ import nuke
from pypeapp import Logger
-# #removing logger handler created in avalon_core
-# for name, handler in [(handler.get_name(), handler)
-# for handler in Logger.logging.root.handlers[:]]:
-# if "pype" not in str(name).lower():
-# Logger.logging.root.removeHandler(handler)
-
-
log = Logger().get_logger(__name__, "nuke")
-# log = api.Logger.getLogger(__name__, "nuke")
-
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
PARENT_DIR = os.path.dirname(__file__)
PACKAGE_DIR = os.path.dirname(PARENT_DIR)
PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins")
-PUBLISH_PATH = os.path.join(PLUGINS_DIR, "nuke", "publish")
-LOAD_PATH = os.path.join(PLUGINS_DIR, "nuke", "load")
-CREATE_PATH = os.path.join(PLUGINS_DIR, "nuke", "create")
-INVENTORY_PATH = os.path.join(PLUGINS_DIR, "nuke", "inventory")
+PUBLISH_PATH = os.path.join(PLUGINS_DIR, "nukestudio", "publish")
+LOAD_PATH = os.path.join(PLUGINS_DIR, "nukestudio", "load")
+CREATE_PATH = os.path.join(PLUGINS_DIR, "nukestudio", "create")
+INVENTORY_PATH = os.path.join(PLUGINS_DIR, "nukestudio", "inventory")
self = sys.modules[__name__]
self.nLogger = None
@@ -48,42 +39,6 @@ if os.getenv("PYBLISH_GUI", None):
pyblish.register_gui(os.getenv("PYBLISH_GUI", None))
-# class NukeHandler(Logger.logging.Handler):
-# '''
-# Nuke Handler - emits logs into nuke's script editor.
-# warning will emit nuke.warning()
-# critical and fatal would popup msg dialog to alert of the error.
-# '''
-#
-# def __init__(self):
-# api.Logger.logging.Handler.__init__(self)
-# self.set_name("Pype_Nuke_Handler")
-#
-# def emit(self, record):
-# # Formated message:
-# msg = self.format(record)
-#
-# if record.levelname.lower() in [
-# # "warning",
-# "critical",
-# "fatal",
-# "error"
-# ]:
-# nuke.message(msg)
-
-#
-# '''Adding Nuke Logging Handler'''
-# nuke_handler = NukeHandler()
-# if nuke_handler.get_name() \
-# not in [handler.get_name()
-# for handler in Logger.logging.root.handlers[:]]:
-# api.Logger.logging.getLogger().addHandler(nuke_handler)
-# api.Logger.logging.getLogger().setLevel(Logger.logging.INFO)
-#
-# if not self.nLogger:
-# self.nLogger = Logger
-
-
def reload_config():
"""Attempt to reload pipeline at run-time.
diff --git a/pype/nukestudio/menu.py b/pype/nukestudio/menu.py
index 9180c924ba..b62a20559d 100644
--- a/pype/nukestudio/menu.py
+++ b/pype/nukestudio/menu.py
@@ -1,7 +1,83 @@
-import nuke
from avalon.api import Session
-from pype.nuke import lib
+from pype.nukestudio import lib
+import hiero.core
+
+try:
+ from PySide.QtGui import *
+except Exception:
+ from PySide2.QtGui import *
+ from PySide2.QtWidgets import *
+
+from hiero.ui import findMenuAction
+
+
+#
def install():
+ # here is the best place to add menu
+ from avalon.tools import (
+ creator,
+ publish,
+ workfiles,
+ cbloader,
+ cbsceneinventory,
+ contextmanager,
+ libraryloader
+ )
+
+ menu_name = os.environ['PYPE_STUDIO_NAME']
+ # Grab Hiero's MenuBar
+ M = hiero.ui.menuBar()
+
+ # Add a Menu to the MenuBar
+ file_action = None
+ try:
+ check_made_menu = findMenuAction(menu_name)
+ except:
+ pass
+
+ if not check_made_menu:
+ menu = M.addMenu(menu_name)
+ else:
+ menu = check_made_menu.menu()
+
+ actions = [{
+ 'action': QAction(QIcon('icons:Position.png'), 'Set Context', None),
+ 'function': contextmanager.show
+ },
+ {
+ 'action': QAction(QIcon('icons:ColorAdd.png'), 'Create...', None),
+ 'function': creator.show
+ },
+ {
+ 'action': QAction(QIcon('icons:CopyRectangle.png'), 'Load...', None),
+ 'function': cbloader.show
+ },
+ {
+ 'action': QAction(QIcon('icons:Output.png'), 'Publish...', None),
+ 'function': publish.show
+ },
+ {
+ 'action': QAction(QIcon('icons:ModifyMetaData.png'), 'Manage...', None),
+ 'function': cbsceneinventory.show
+ },
+ {
+ 'action': QAction(QIcon('icons:ColorAdd.png'), 'Library...', None),
+ 'function': libraryloader.show
+ }]
+
+
+ # Create menu items
+ for a in actions:
+ # create action
+ for k in a.keys():
+ if 'action' in k:
+ action = a[k]
+ elif 'function' in k:
+ action.triggered.connect(a[k])
+ else:
+ pass
+ # add action to menu
+ menu.addAction(action)
diff --git a/setup/nukestudio/hiero_plugin_path/HieroPlayer/PlayerPresets.hrox b/setup/nukestudio/hiero_plugin_path/HieroPlayer/PlayerPresets.hrox
new file mode 100644
index 0000000000..ec50e123f0
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/HieroPlayer/PlayerPresets.hrox
@@ -0,0 +1,1108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 50
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+ 2
+ 70
+
+
+ 2
+ 70
+ 17
+
+
+ 126935040
+ 70
+ -1
+
+
+ 2
+ 70
+ 2
+
+
+
+
+
+
diff --git a/setup/nukestudio/hiero_plugin_path/Python/Startup/SpreadsheetExport.py b/setup/nukestudio/hiero_plugin_path/Python/Startup/SpreadsheetExport.py
new file mode 100644
index 0000000000..3adea8051c
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Python/Startup/SpreadsheetExport.py
@@ -0,0 +1,140 @@
+# This action adds itself to the Spreadsheet View context menu allowing the contents of the Spreadsheet be exported as a CSV file.
+# Usage: Right-click in Spreadsheet > "Export as .CSV"
+# Note: This only prints the text data that is visible in the active Spreadsheet View.
+# If you've filtered text, only the visible text will be printed to the CSV file
+# Usage: Copy to ~/.hiero/Python/StartupUI
+import hiero.core.events
+import hiero.ui
+import os, csv
+try:
+ from PySide.QtGui import *
+ from PySide.QtCore import *
+except:
+ from PySide2.QtGui import *
+ from PySide2.QtWidgets import *
+ from PySide2.QtCore import *
+
+
+### 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():
+ foundryWidgets += [w]
+
+ for c in w.children():
+ findWidget(c)
+ return foundryWidgets
+
+
+def getFoundryWidgetsWithClassName(filter=None):
+ global foundryWidgets
+ foundryWidgets = []
+ widgets = []
+ app = QApplication.instance()
+ for w in app.topLevelWidgets():
+ findWidget(w)
+
+ filteredWidgets = foundryWidgets
+ if filter:
+ filteredWidgets = []
+ for widget in foundryWidgets:
+ if filter in widget.metaObject().className():
+ filteredWidgets += [widget]
+ return filteredWidgets
+
+
+# When right click, get the Sequence Name
+def activeSpreadsheetTreeView():
+ """
+ Does some PySide widget Magic to detect the Active Spreadsheet TreeView.
+ """
+ spreadsheetViews = getFoundryWidgetsWithClassName(
+ filter='SpreadsheetTreeView')
+ for spreadSheet in spreadsheetViews:
+ if spreadSheet.hasFocus():
+ activeSpreadSheet = spreadSheet
+ return activeSpreadSheet
+ return None
+
+
+#### Adds "Export .CSV" action to the Spreadsheet Context menu ####
+class SpreadsheetExportCSVAction(QAction):
+ def __init__(self):
+ QAction.__init__(self, "Export as .CSV", None)
+ self.triggered.connect(self.exportCSVFromActiveSpreadsheetView)
+ hiero.core.events.registerInterest("kShowContextMenu/kSpreadsheet",
+ self.eventHandler)
+ self.setIcon(QIcon("icons:FBGridView.png"))
+
+ def eventHandler(self, event):
+ # Insert the action to the Export CSV menu
+ event.menu.addAction(self)
+
+ #### The guts!.. Writes a CSV file from a Sequence Object ####
+ def exportCSVFromActiveSpreadsheetView(self):
+
+ # Get the active QTreeView from the active Spreadsheet
+ spreadsheetTreeView = activeSpreadsheetTreeView()
+
+ if not spreadsheetTreeView:
+ 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.'
+ return
+
+ # The data model of the QTreeView
+ model = spreadsheetTreeView.model()
+
+ 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)
+
+ # Saving was cancelled...
+ if len(savePath) == 0:
+ return
+
+ # 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']
+
+ # Get a CSV writer object
+ f = open(savePath, 'w')
+ csvWriter = csv.writer(
+ f, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL)
+
+ # This is a list of the Column titles
+ csvHeader = []
+
+ for col in range(0, model.columnCount()):
+ if not spreadsheetTreeView.isColumnHidden(col):
+ csvHeader += [model.headerData(col, Qt.Horizontal)]
+
+ # Write the Header row to the CSV file
+ csvWriter.writerow(csvHeader)
+
+ # Go through each row/column and print
+ for row in range(model.rowCount()):
+ row_data = []
+ for col in range(model.columnCount()):
+ if not spreadsheetTreeView.isColumnHidden(col):
+ row_data.append(
+ model.index(row, col, QModelIndex()).data(
+ Qt.DisplayRole))
+
+ # Write row to CSV file...
+ csvWriter.writerow(row_data)
+
+ f.close()
+ # Conveniently show the CSV file in the native file browser...
+ QDesktopServices.openUrl(
+ QUrl('file:///%s' % (os.path.dirname(savePath))))
+
+
+# Add the action...
+csvActions = SpreadsheetExportCSVAction()
diff --git a/setup/nukestudio/hiero_plugin_path/Python/Startup/setFrameRate.py b/setup/nukestudio/hiero_plugin_path/Python/Startup/setFrameRate.py
new file mode 100644
index 0000000000..ceb96a6fce
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Python/Startup/setFrameRate.py
@@ -0,0 +1,164 @@
+# setFrameRate - adds a Right-click menu to the Project Bin view, allowing multiple BinItems (Clips/Sequences) to have their frame rates set.
+# Install in: ~/.hiero/Python/StartupUI
+# Requires 1.5v1 or later
+
+import hiero.core
+import hiero.ui
+try:
+ from PySide.QtGui import *
+ from PySide.QtCore import *
+except:
+ from PySide2.QtGui import *
+ from PySide2.QtCore import *
+ from PySide2.QtWidgets import *
+
+# Dialog for setting a Custom frame rate.
+class SetFrameRateDialog(QDialog):
+
+ def __init__(self,itemSelection=None,parent=None):
+ super(SetFrameRateDialog, self).__init__(parent)
+ self.setWindowTitle("Set Custom Frame Rate")
+ self.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed )
+ layout = QFormLayout()
+ self._itemSelection = itemSelection
+
+ self._frameRateField = QLineEdit()
+ 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)
+
+ # Standard buttons for Add/Cancel
+ self._buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self._buttonbox.accepted.connect(self.accept)
+ self._buttonbox.rejected.connect(self.reject)
+ 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
+ self._buttonbox.button(QDialogButtonBox.Ok).setEnabled(enableOk)
+
+ def _textChanged(self, newText):
+ self._updateOkButtonState()
+
+ # 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)
+ 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
+
+# Menu which adds a Set Frame Rate Menu to Project Bin view
+class SetFrameRateMenu:
+
+ def __init__(self):
+ self._frameRateMenu = None
+ self._frameRatesDialog = None
+
+
+ # 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']
+ 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 = hiero.core.util.uniquify(selectedClipFPS)
+ sameFrameRate = len(selectedClipFPS)==1
+ self.menuActions = []
+ for fps in self.frameRates:
+ if fps in selectedClipFPS:
+ if sameFrameRate:
+ self.menuActions+=[makeAction(fps,self.setFrameRateFromMenuSelection, icon="icons:Ticked.png")]
+ else:
+ 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)]
+
+ 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'))]
+ currentProject = selectedBinItems[0].project()
+
+ with currentProject.beginUndo("Set Frame Rate"):
+ 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'):
+ # 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()
+
+ # Return if there's no Selection. We won't add the Menu.
+ if s == None:
+ return
+ # Filter the selection to BinItems
+ self._selection = [item for item in s if isinstance(item, hiero.core.BinItem)]
+ 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":
+ event.menu.insertMenu(action, self._frameRateMenu)
+ break
+
+# Instantiate the Menu to get it to register itself.
+SetFrameRateMenu = SetFrameRateMenu()
\ No newline at end of file
diff --git a/setup/nukestudio/hiero_plugin_path/Python/Startup/version_everywhere.py b/setup/nukestudio/hiero_plugin_path/Python/Startup/version_everywhere.py
new file mode 100644
index 0000000000..e85e02bfa5
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Python/Startup/version_everywhere.py
@@ -0,0 +1,352 @@
+# version_up_everywhere.py
+# Adds action to enable a Clip/Shot to be Min/Max/Next/Prev versioned in all shots used in a Project.
+#
+# Usage:
+# 1) Copy file to /Python/Startup
+# 2) Right-click on Clip(s) or Bins containing Clips in in the Bin View, or on Shots in the Timeline/Spreadsheet
+# 3) Set Version for all Shots > OPTION to update the version in all shots where the Clip is used in the Project.
+
+import hiero.core
+try:
+ from PySide.QtGui import *
+ from PySide.QtCore import *
+except:
+ from PySide2.QtGui import *
+ from PySide2.QtWidgets import *
+ from PySide2.QtCore import *
+
+
+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'.
+ Should consider putting this into hiero.core.Clip by default?
+
+ Example usage:
+
+ 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'"
+ 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))
+ return None
+
+ # Case 1: Looking for Shots (trackItems)
+ clipUsedIn = []
+ if isinstance(searches[0], hiero.core.TrackItem):
+ for shot in searches:
+ # We have to wrap this in a try/except because it's possible through the Python API for a Shot to exist without a Clip in the Bin
+ try:
+
+ # For versioning to work, we must look to the BinItem that a Clip is wrapped in.
+ if shot.source().binItem() == self.binItem():
+ clipUsedIn.append(shot)
+
+ # If we throw an exception here its because the Shot did not have a Source Clip in the Bin.
+ except RuntimeError:
+ hiero.core.log.info(
+ 'Unable to find Parent Clip BinItem for Shot: %s, Source:%s'
+ % (shot, shot.source()))
+ pass
+
+ # Case 1: Looking for Shots (trackItems)
+ elif isinstance(searches[0], hiero.core.Sequence):
+ for seq in searches:
+ # Iterate tracks > shots...
+ tracks = seq.items()
+ for track in tracks:
+ shots = track.items()
+ for shot in shots:
+ if shot.source().binItem() == self.binItem():
+ clipUsedIn.append(seq)
+
+ return clipUsedIn
+
+
+# Add whereAmI method to Clip object
+hiero.core.Clip.whereAmI = whereAmI
+
+
+#### MAIN VERSION EVERYWHERE GUBBINS #####
+class VersionAllMenu(object):
+
+ # These are a set of action names we can use for operating on multiple Clip/TrackItems
+ eMaxVersion = "Max Version"
+ eMinVersion = "Min Version"
+ eNextVersion = "Next Version"
+ ePreviousVersion = "Previous Version"
+
+ # This is the title used for the Version Menu title. It's long isn't it?
+ actionTitle = "Set Version for all Shots"
+
+ def __init__(self):
+ self._versionEverywhereMenu = None
+ self._versionActions = []
+
+ hiero.core.events.registerInterest("kShowContextMenu/kBin",
+ self.binViewEventHandler)
+ hiero.core.events.registerInterest("kShowContextMenu/kTimeline",
+ self.binViewEventHandler)
+ hiero.core.events.registerInterest("kShowContextMenu/kSpreadsheet",
+ self.binViewEventHandler)
+
+ def showVersionUpdateReportFromShotManifest(self, sequenceShotManifest):
+ """This just displays an info Message box, based on a Sequence[Shot] manifest dictionary"""
+
+ # Now present an info dialog, explaining where shots were updated
+ updateReportString = "The following Versions were updated:\n"
+ for seq in sequenceShotManifest.keys():
+ updateReportString += "%s:\n Shots:\n" % (seq.name())
+ for shot in sequenceShotManifest[seq]:
+ updateReportString += ' %s\n (New Version: %s)\n' % (
+ shot.name(), shot.currentVersion().name())
+ updateReportString += '\n'
+
+ infoBox = QMessageBox(hiero.ui.mainWindow())
+ infoBox.setIcon(QMessageBox.Information)
+
+ if len(sequenceShotManifest) <= 0:
+ infoBox.setText("No Shot Versions were updated")
+ infoBox.setInformativeText(
+ "Clip could not be found in any Shots in this Project")
+ else:
+ infoBox.setText(
+ "Versions were updated in %i Sequences of this Project." %
+ (len(sequenceShotManifest)))
+ infoBox.setInformativeText("Show Details for more info.")
+ infoBox.setDetailedText(updateReportString)
+
+ infoBox.exec_()
+
+ def makeVersionActionForSingleClip(self, version):
+ """This is used to populate the QAction list of Versions when a single Clip is selected in the BinView.
+ It also triggers the Version Update action based on the version passed to it.
+ (Not sure if this is good design practice, but it's compact!)"""
+ action = QAction(version.name(), None)
+ action.setData(lambda: version)
+
+ def updateAllTrackItems():
+ currentClip = version.item()
+ trackItems = currentClip.whereAmI()
+ if not trackItems:
+ return
+
+ proj = currentClip.project()
+
+ # A Sequence-Shot manifest dictionary
+ sequenceShotManifest = {}
+
+ # Make this all undo-able in a single Group undo
+ with proj.beginUndo(
+ "Update All Versions for %s" % currentClip.name()):
+ for shot in trackItems:
+ seq = shot.parentSequence()
+ if seq not in sequenceShotManifest.keys():
+ sequenceShotManifest[seq] = [shot]
+ else:
+ sequenceShotManifest[seq] += [shot]
+ shot.setCurrentVersion(version)
+
+ # We also should update the current Version of the selected Clip for completeness...
+ currentClip.binItem().setActiveVersion(version)
+
+ # Now disaplay a Dialog which informs the user of where and what was changed
+ self.showVersionUpdateReportFromShotManifest(sequenceShotManifest)
+
+ action.triggered.connect(updateAllTrackItems)
+ return action
+
+ # This is just a convenience method for returning QActions with a title, triggered method and icon.
+ def makeAction(self, 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 trigger the version change
+ def methodWrapper():
+ method(title)
+
+ action.triggered.connect(methodWrapper)
+ return action
+
+ def clipSelectionFromView(self, view):
+ """Helper method to return a list of Clips in the Active View"""
+ selection = hiero.ui.activeView().selection()
+
+ if len(selection) == 0:
+ return None
+
+ if isinstance(view, hiero.ui.BinView):
+ # We could have a mixture of Bins and Clips selected, so sort of the Clips and Clips inside Bins
+ clipItems = [
+ item.activeItem() for item in selection
+ if hasattr(item, "activeItem")
+ and isinstance(item.activeItem(), hiero.core.Clip)
+ ]
+
+ # We'll also append Bins here, and see if can find Clips inside
+ bins = [
+ item for item in selection if isinstance(item, hiero.core.Bin)
+ ]
+
+ # We search inside of a Bin for a Clip which is not already in clipBinItems
+ 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')
+ for clip in clips:
+ if clip not in clipItems:
+ clipItems.append(clip)
+
+ elif isinstance(view,
+ (hiero.ui.TimelineEditor, hiero.ui.SpreadsheetView)):
+ # Here, we have shots. To get to the Clip froma TrackItem, just call source()
+ clipItems = [
+ item.source() for item in selection if hasattr(item, "source")
+ and isinstance(item, hiero.core.TrackItem)
+ ]
+
+ return clipItems
+
+ # This generates the Version Up Everywhere menu
+ def createVersionEveryWhereMenuForView(self, view):
+
+ versionEverywhereMenu = QMenu(self.actionTitle)
+ self._versionActions = []
+ # We look to the activeView for a selection of Clips
+ clips = self.clipSelectionFromView(view)
+
+ # And bail if nothing is found
+ if len(clips) == 0:
+ return versionEverywhereMenu
+
+ # Now, if we have just one Clip selected, we'll form a special menu, which lists all versions
+ if len(clips) == 1:
+
+ # Get a reversed list of Versions, so that bigger ones appear at top
+ versions = list(reversed(clips[0].binItem().items()))
+ for version in versions:
+ self._versionActions += [
+ self.makeVersionActionForSingleClip(version)
+ ]
+
+ elif len(clips) > 1:
+ # We will add Max/Min/Prev/Next options, which can be called on a TrackItem, without the need for a Version object
+ self._versionActions += [
+ self.makeAction(
+ self.eMaxVersion,
+ self.setTrackItemVersionForClipSelection,
+ icon=None)
+ ]
+ self._versionActions += [
+ self.makeAction(
+ self.eMinVersion,
+ self.setTrackItemVersionForClipSelection,
+ icon=None)
+ ]
+ self._versionActions += [
+ self.makeAction(
+ self.eNextVersion,
+ self.setTrackItemVersionForClipSelection,
+ icon=None)
+ ]
+ self._versionActions += [
+ self.makeAction(
+ self.ePreviousVersion,
+ self.setTrackItemVersionForClipSelection,
+ icon=None)
+ ]
+
+ for act in self._versionActions:
+ versionEverywhereMenu.addAction(act)
+
+ return versionEverywhereMenu
+
+ def setTrackItemVersionForClipSelection(self, versionOption):
+
+ view = hiero.ui.activeView()
+ if not view:
+ return
+
+ clipSelection = self.clipSelectionFromView(view)
+
+ if len(clipSelection) == 0:
+ return
+
+ proj = clipSelection[0].project()
+
+ # Create a Sequence-Shot Manifest, to report to users where a Shot was updated
+ sequenceShotManifest = {}
+
+ with proj.beginUndo("Update multiple Versions"):
+ for clip in clipSelection:
+
+ # Look to see if it exists in a TrackItem somewhere...
+ shotUsage = clip.whereAmI('TrackItem')
+
+ # Next, depending on the versionOption, make the appropriate update
+ # There's probably a more neat/compact way of doing this...
+ for shot in shotUsage:
+
+ # This step is done for reporting reasons
+ seq = shot.parentSequence()
+ if seq not in sequenceShotManifest.keys():
+ sequenceShotManifest[seq] = [shot]
+ else:
+ sequenceShotManifest[seq] += [shot]
+
+ if versionOption == self.eMaxVersion:
+ shot.maxVersion()
+ elif versionOption == self.eMinVersion:
+ shot.minVersion()
+ elif versionOption == self.eNextVersion:
+ shot.nextVersion()
+ elif versionOption == self.ePreviousVersion:
+ shot.prevVersion()
+
+ # Finally, for completeness, set the Max/Min version of the Clip too (if chosen)
+ # Note: It doesn't make sense to do Next/Prev on a Clip here because next/prev means different things for different Shots
+ if versionOption == self.eMaxVersion:
+ clip.binItem().maxVersion()
+ elif versionOption == self.eMinVersion:
+ clip.binItem().minVersion()
+
+ # Now disaplay a Dialog which informs the user of where and what was changed
+ self.showVersionUpdateReportFromShotManifest(sequenceShotManifest)
+
+ # This handles events from the Project Bin View
+ def binViewEventHandler(self, event):
+
+ 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
+ selection = event.sender.selection()
+
+ # Return if there's no Selection. We won't add the Localise Menu.
+ if selection == None:
+ return
+
+ view = hiero.ui.activeView()
+ # Only add the Menu if Bins or Sequences are selected (this ensures menu isn't added in the Tags Pane)
+ if len(selection) > 0:
+ self._versionEverywhereMenu = self.createVersionEveryWhereMenuForView(
+ view)
+ hiero.ui.insertMenuAction(
+ self._versionEverywhereMenu.menuAction(),
+ event.menu,
+ after="foundry.menu.version")
+ return
+
+
+# Instantiate the Menu to get it to register itself.
+VersionAllMenu = VersionAllMenu()
diff --git a/setup/nukestudio/hiero_plugin_path/Python/StartupUI/PimpMySpreadsheet.py b/setup/nukestudio/hiero_plugin_path/Python/StartupUI/PimpMySpreadsheet.py
new file mode 100644
index 0000000000..3d40aa0293
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Python/StartupUI/PimpMySpreadsheet.py
@@ -0,0 +1,844 @@
+# PimpMySpreadsheet 1.0, Antony Nasce, 23/05/13.
+# Adds custom spreadsheet columns and right-click menu for setting the Shot Status, and Artist Shot Assignement.
+# gStatusTags is a global dictionary of key(status)-value(icon) pairs, which can be overridden with custom icons if required
+# Requires Hiero 1.7v2 or later.
+# Install Instructions: Copy to ~/.hiero/Python/StartupUI
+
+import hiero.core
+import hiero.ui
+
+try:
+ from PySide.QtGui import *
+ from PySide.QtCore import *
+except:
+ from PySide2.QtGui import *
+ from PySide2.QtWidgets import *
+ from PySide2.QtCore import *
+
+# 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
+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': 'Savlvador Dali',
+ 'artistIcon': 'icons:TagActor.png',
+ 'artistDepartment': 'Roto',
+ 'artistID': 1
+}, {
+ 'artistName': 'Leonardo Da Vinci',
+ 'artistIcon': 'icons:TagActor.png',
+ 'artistDepartment': 'Paint',
+ 'artistID': 2
+}, {
+ 'artistName': 'Claude Monet',
+ 'artistIcon': 'icons:TagActor.png',
+ 'artistDepartment': 'Comp',
+ 'artistID': 3
+}, {
+ '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'
+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'
+}
+
+
+# The Custom Spreadsheet Columns
+class CustomSpreadsheetColumns(QObject):
+ """
+ A class defining custom columns for Hiero's spreadsheet view. This has a similar, but
+ slightly simplified, interface to the QAbstractItemModel and QItemDelegate classes.
+ """
+ global gStatusTags
+ global gArtistList
+
+ # 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'
+ ]
+ arriColourSpaces = [
+ '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'
+ ]
+ gColourSpaces = standardColourSpaces + arriColourSpaces + r3dColourSpaces
+
+ currentView = hiero.ui.activeView()
+
+ # This is the list of Columns available
+ gCustomColumnList = [
+ {
+ 'name': 'Tags',
+ 'cellType': 'readonly'
+ },
+ {
+ 'name': 'Colourspace',
+ 'cellType': 'dropdown'
+ },
+ {
+ 'name': 'Notes',
+ 'cellType': 'readonly'
+ },
+ {
+ 'name': 'FileType',
+ 'cellType': 'readonly'
+ },
+ {
+ 'name': 'Shot Status',
+ 'cellType': 'dropdown'
+ },
+ {
+ 'name': 'Thumbnail',
+ 'cellType': 'readonly'
+ },
+ {
+ 'name': 'MediaType',
+ 'cellType': 'readonly'
+ },
+ {
+ 'name': 'Width',
+ 'cellType': 'readonly'
+ },
+ {
+ 'name': 'Height',
+ 'cellType': 'readonly'
+ },
+ {
+ 'name': 'Pixel Aspect',
+ 'cellType': 'readonly'
+ },
+ {
+ 'name': 'Artist',
+ 'cellType': 'dropdown'
+ },
+ {
+ 'name': 'Department',
+ 'cellType': 'readonly'
+ },
+ ]
+
+ def numColumns(self):
+ """
+ Return the number of custom columns in the spreadsheet view
+ """
+ return len(self.gCustomColumnList)
+
+ def columnName(self, column):
+ """
+ Return the name of a custom column
+ """
+ return self.gCustomColumnList[column]['name']
+
+ def getTagsString(self, item):
+ """
+ Convenience method for returning all the Notes in a Tag as a string
+ """
+ tagNames = []
+ tags = item.tags()
+ for tag in tags:
+ tagNames += [tag.name()]
+ tagNameString = ','.join(tagNames)
+ return tagNameString
+
+ def getNotes(self, item):
+ """
+ Convenience method for returning all the Notes in a Tag as a string
+ """
+ notes = ''
+ tags = item.tags()
+ for tag in tags:
+ note = tag.note()
+ if len(note) > 0:
+ notes += tag.note() + ', '
+ return notes[:-2]
+
+ def getData(self, row, column, item):
+ """
+ Return the data in a cell
+ """
+ currentColumn = self.gCustomColumnList[column]
+ if currentColumn['name'] == 'Tags':
+ return self.getTagsString(item)
+
+ if currentColumn['name'] == 'Colourspace':
+ try:
+ colTransform = item.sourceMediaColourTransform()
+ except:
+ colTransform = '--'
+ return colTransform
+
+ if currentColumn['name'] == 'Notes':
+ try:
+ note = self.getNotes(item)
+ except:
+ note = ''
+ return note
+
+ 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')
+ return fileType
+
+ if currentColumn['name'] == 'Shot Status':
+ status = item.status()
+ if not status:
+ status = "--"
+ return str(status)
+
+ if currentColumn['name'] == 'MediaType':
+ M = item.mediaType()
+ return str(M).split('MediaType')[-1].replace('.k', '')
+
+ if currentColumn['name'] == 'Thumbnail':
+ return str(item.eventNumber())
+
+ if currentColumn['name'] == 'Width':
+ return str(item.source().format().width())
+
+ if currentColumn['name'] == 'Height':
+ return str(item.source().format().height())
+
+ if currentColumn['name'] == 'Pixel Aspect':
+ return str(item.source().format().pixelAspect())
+
+ if currentColumn['name'] == 'Artist':
+ if item.artist():
+ name = item.artist()['artistName']
+ return name
+ else:
+ return '--'
+
+ if currentColumn['name'] == 'Department':
+ if item.artist():
+ dep = item.artist()['artistDepartment']
+ return dep
+ else:
+ return '--'
+
+ return ""
+
+ def setData(self, row, column, item, data):
+ """
+ Set the data in a cell - unused in this example
+ """
+
+ return None
+
+ def getTooltip(self, row, column, item):
+ """
+ Return the tooltip for a cell
+ """
+ currentColumn = self.gCustomColumnList[column]
+ if currentColumn['name'] == 'Tags':
+ return str([item.name() for item in item.tags()])
+
+ if currentColumn['name'] == 'Notes':
+ return str(self.getNotes(item))
+ return ""
+
+ def getFont(self, row, column, item):
+ """
+ Return the tooltip for a cell
+ """
+ return None
+
+ def getBackground(self, row, column, item):
+ """
+ Return the background colour for a cell
+ """
+ if not item.source().mediaSource().isMediaPresent():
+ return QColor(80, 20, 20)
+ return None
+
+ def getForeground(self, row, column, item):
+ """
+ Return the text colour for a cell
+ """
+ #if column == 1:
+ # return QColor(255, 64, 64)
+ return None
+
+ def getIcon(self, row, column, item):
+ """
+ Return the icon for a cell
+ """
+ currentColumn = self.gCustomColumnList[column]
+ if currentColumn['name'] == 'Colourspace':
+ return QIcon("icons:LUT.png")
+
+ if currentColumn['name'] == 'Shot Status':
+ status = item.status()
+ if status:
+ return QIcon(gStatusTags[status])
+
+ 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':
+ try:
+ return QIcon(item.artist()['artistIcon'])
+ except:
+ return None
+ return None
+
+ def getSizeHint(self, row, column, item):
+ """
+ Return the size hint for a cell
+ """
+ currentColumnName = self.gCustomColumnList[column]['name']
+
+ if currentColumnName == 'Thumbnail':
+ return QSize(90, 50)
+
+ return QSize(50, 50)
+
+ def paintCell(self, row, column, item, painter, option):
+ """
+ Paint a custom cell. Return True if the cell was painted, or False to continue
+ with the default cell painting.
+ """
+ currentColumn = self.gCustomColumnList[column]
+ if currentColumn['name'] == 'Tags':
+ if option.state & QStyle.State_Selected:
+ painter.fillRect(option.rect, option.palette.highlight())
+ iconSize = 20
+ r = QRect(option.rect.x(),
+ option.rect.y() + (option.rect.height() - iconSize) / 2,
+ iconSize, iconSize)
+ tags = item.tags()
+ if len(tags) > 0:
+ painter.save()
+ painter.setClipRect(option.rect)
+ for tag in item.tags():
+ M = tag.metadata()
+ 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':
+ imageView = None
+ pen = QPen()
+ r = QRect(option.rect.x() + 2, (option.rect.y() +
+ (option.rect.height() - 46) / 2),
+ 85, 46)
+ if not item.source().mediaSource().isMediaPresent():
+ imageView = QImage("icons:Offline.png")
+ pen.setColor(QColor(Qt.red))
+
+ if item.mediaType() == hiero.core.TrackItem.MediaType.kAudio:
+ imageView = QImage("icons:AudioOnly.png")
+ #pen.setColor(QColor(Qt.green))
+ painter.fillRect(r, QColor(45, 59, 45))
+
+ if option.state & QStyle.State_Selected:
+ painter.fillRect(option.rect, option.palette.highlight())
+
+ tags = item.tags()
+ painter.save()
+ painter.setClipRect(option.rect)
+
+ if not imageView:
+ try:
+ imageView = item.thumbnail(item.sourceIn())
+ pen.setColor(QColor(20, 20, 20))
+ # If we're here, we probably have a TC error, no thumbnail, so get it from the source Clip...
+ except:
+ pen.setColor(QColor(Qt.red))
+
+ if not imageView:
+ try:
+ imageView = item.source().thumbnail()
+ pen.setColor(QColor(Qt.yellow))
+ except:
+ imageView = QImage("icons:Offline.png")
+ pen.setColor(QColor(Qt.red))
+
+ QIcon(QPixmap.fromImage(imageView)).paint(painter, r,
+ Qt.AlignCenter)
+ painter.setPen(pen)
+ painter.drawRoundedRect(r, 1, 1)
+ painter.restore()
+ return True
+
+ return False
+
+ def createEditor(self, row, column, item, view):
+ """
+ Create an editing widget for a custom cell
+ """
+ self.currentView = view
+
+ currentColumn = self.gCustomColumnList[column]
+ if currentColumn['cellType'] == 'readonly':
+ cle = QLabel()
+ cle.setEnabled(False)
+ cle.setVisible(False)
+ return cle
+
+ 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':
+ cb = QComboBox()
+ cb.addItem('')
+ for key in gStatusTags.keys():
+ cb.addItem(QIcon(gStatusTags[key]), key)
+ cb.addItem('--')
+ cb.currentIndexChanged.connect(self.statusChanged)
+
+ return cb
+
+ if currentColumn['name'] == 'Artist':
+ cb = QComboBox()
+ cb.addItem('')
+ for artist in gArtistList:
+ cb.addItem(artist['artistName'])
+ cb.addItem('--')
+ cb.currentIndexChanged.connect(self.artistNameChanged)
+ return cb
+ return None
+
+ def setModelData(self, row, column, item, editor):
+ return False
+
+ def dropMimeData(self, row, column, item, data, items):
+ """
+ Handle a drag and drop operation - adds a Dragged Tag to the shot
+ """
+ for thing in items:
+ if isinstance(thing, hiero.core.Tag):
+ item.addTag(thing)
+ return None
+
+ def colourspaceChanged(self, index):
+ """
+ This method is called when Colourspace widget changes index.
+ """
+ index = self.sender().currentIndex()
+ colourspace = self.gColourSpaces[index]
+ selection = self.currentView.selection()
+ project = selection[0].project()
+ with project.beginUndo("Set Colourspace"):
+ items = [
+ item for item in selection
+ if (item.mediaType() == hiero.core.TrackItem.MediaType.kVideo)
+ ]
+ for trackItem in items:
+ trackItem.setSourceMediaColourTransform(colourspace)
+
+ def statusChanged(self, arg):
+ """
+ This method is called when Shot Status widget changes index.
+ """
+ view = hiero.ui.activeView()
+ selection = view.selection()
+ status = self.sender().currentText()
+ project = selection[0].project()
+ with project.beginUndo("Set 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'):
+ trackItem.removeTag(tag)
+ break
+
+ def artistNameChanged(self, arg):
+ """
+ This method is called when Artist widget changes index.
+ """
+ view = hiero.ui.activeView()
+ selection = view.selection()
+ name = self.sender().currentText()
+ project = selection[0].project()
+ with project.beginUndo("Assign Artist"):
+ # 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'):
+ trackItem.removeTag(tag)
+ break
+
+
+def _getArtistFromID(self, artistID):
+ """ getArtistFromID -> returns an artist dictionary, by their given ID"""
+ global gArtistList
+ artist = [
+ element for element in gArtistList
+ if element['artistID'] == int(artistID)
+ ]
+ if not artist:
+ return None
+ return artist[0]
+
+
+def _getArtistFromName(self, artistName):
+ """ getArtistFromID -> returns an artist dictionary, by their given ID """
+ global gArtistList
+ artist = [
+ element for element in gArtistList
+ if element['artistName'] == artistName
+ ]
+ if not artist:
+ return None
+ return artist[0]
+
+
+def _artist(self):
+ """_artist -> Returns the artist dictionary assigned to this shot"""
+ artist = None
+ tags = self.tags()
+ for tag in tags:
+ if tag.metadata().hasKey('tag.artistID'):
+ artistID = tag.metadata().value('tag.artistID')
+ artist = self.getArtistFromID(artistID)
+ return artist
+
+
+def _updateArtistTag(self, artistDict):
+ # A shot will only have one artist assigned. Check if one exists and set accordingly
+
+ artistTag = None
+ tags = self.tags()
+ for tag in tags:
+ 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']))
+ 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']))
+ self.sequence().editFinished()
+ return
+
+
+def _setArtistByName(self, artistName):
+ """ setArtistByName(artistName) -> sets the artist tag on a TrackItem by a given artistName string"""
+ global gArtistList
+
+ artist = self.getArtistFromName(artistName)
+ if not artist:
+ print 'Artist name: %s was not found in the gArtistList.' % str(
+ artistName)
+ return
+
+ # Do the update.
+ self.updateArtistTag(artist)
+
+
+def _setArtistByID(self, artistID):
+ """ setArtistByID(artistID) -> sets the artist tag on a TrackItem by a given artistID integer"""
+ global gArtistList
+
+ artist = self.getArtistFromID(artistID)
+ if not artist:
+ print 'Artist name: %s was not found in the gArtistList.' % str(
+ artistID)
+ return
+
+ # Do the update.
+ self.updateArtistTag(artist)
+
+
+# Inject status getter and setter methods into hiero.core.TrackItem
+hiero.core.TrackItem.artist = _artist
+hiero.core.TrackItem.setArtistByName = _setArtistByName
+hiero.core.TrackItem.setArtistByID = _setArtistByID
+hiero.core.TrackItem.getArtistFromName = _getArtistFromName
+hiero.core.TrackItem.getArtistFromID = _getArtistFromID
+hiero.core.TrackItem.updateArtistTag = _updateArtistTag
+
+
+def _status(self):
+ """status -> Returns the Shot status. None if no Status is set."""
+
+ status = None
+ tags = self.tags()
+ for tag in tags:
+ 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')
+
+ @param status - a string, corresponding to the Status name
+ """
+ global gStatusTags
+
+ # 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.'
+ 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'):
+ statusTag = tag
+ break
+
+ if not statusTag:
+ statusTag = hiero.core.Tag('Status')
+ statusTag.setIcon(gStatusTags[status])
+ statusTag.metadata().setValue('tag.status', status)
+ self.addTag(statusTag)
+
+ statusTag.setIcon(gStatusTags[status])
+ statusTag.metadata().setValue('tag.status', status)
+
+ self.sequence().editFinished()
+ return
+
+
+# Inject status getter and setter methods into hiero.core.TrackItem
+hiero.core.TrackItem.setStatus = _setStatus
+hiero.core.TrackItem.status = _status
+
+
+# This is a convenience method for returning QActions with a triggered method based on the title string
+def titleStringTriggeredAction(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 status
+ def methodWrapper():
+ method(title)
+
+ action.triggered.connect(methodWrapper)
+ return action
+
+
+# Menu which adds a Set Status Menu to Timeline and Spreadsheet Views
+class SetStatusMenu(QMenu):
+ def __init__(self):
+ QMenu.__init__(self, "Set Status", None)
+
+ global gStatusTags
+ self.statuses = gStatusTags
+ self._statusActions = self.createStatusMenuActions()
+
+ # Add the Actions to the Menu.
+ for act in self.menuActions:
+ self.addAction(act)
+
+ hiero.core.events.registerInterest("kShowContextMenu/kTimeline",
+ self.eventHandler)
+ hiero.core.events.registerInterest("kShowContextMenu/kSpreadsheet",
+ self.eventHandler)
+
+ def createStatusMenuActions(self):
+ self.menuActions = []
+ for status in self.statuses:
+ self.menuActions += [
+ titleStringTriggeredAction(
+ status,
+ self.setStatusFromMenuSelection,
+ icon=gStatusTags[status])
+ ]
+
+ def setStatusFromMenuSelection(self, menuSelectionStatus):
+ selectedShots = [
+ item for item in self._selection
+ if (isinstance(item, hiero.core.TrackItem))
+ ]
+ selectedTracks = [
+ item for item in self._selection
+ if (isinstance(item, (hiero.core.VideoTrack,
+ hiero.core.AudioTrack)))
+ ]
+
+ # If we have a Track Header Selection, no shots could be selected, so create shotSelection list
+ if len(selectedTracks) >= 1:
+ for track in selectedTracks:
+ selectedShots += [
+ item for item in track.items()
+ if (isinstance(item, hiero.core.TrackItem))
+ ]
+
+ # It's possible no shots exist on the Track, in which case nothing is required
+ if len(selectedShots) == 0:
+ return
+
+ currentProject = selectedShots[0].project()
+
+ with currentProject.beginUndo("Set Status"):
+ # Shots selected
+ for shot in selectedShots:
+ shot.setStatus(menuSelectionStatus)
+
+ # This handles events from the Project Bin View
+ def eventHandler(self, event):
+ 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
+
+ # Set the current selection
+ self._selection = event.sender.selection()
+
+ # Return if there's no Selection. We won't add the Menu.
+ if len(self._selection) == 0:
+ return
+
+ event.menu.addMenu(self)
+
+
+# Menu which adds a Set Status Menu to Timeline and Spreadsheet Views
+class AssignArtistMenu(QMenu):
+ def __init__(self):
+ QMenu.__init__(self, "Assign Artist", None)
+
+ global gArtistList
+ self.artists = gArtistList
+ self._artistsActions = self.createAssignArtistMenuActions()
+
+ # Add the Actions to the Menu.
+ for act in self.menuActions:
+ self.addAction(act)
+
+ hiero.core.events.registerInterest("kShowContextMenu/kTimeline",
+ self.eventHandler)
+ hiero.core.events.registerInterest("kShowContextMenu/kSpreadsheet",
+ self.eventHandler)
+
+ def createAssignArtistMenuActions(self):
+ self.menuActions = []
+ for artist in self.artists:
+ self.menuActions += [
+ titleStringTriggeredAction(
+ artist['artistName'],
+ self.setArtistFromMenuSelection,
+ icon=artist['artistIcon'])
+ ]
+
+ def setArtistFromMenuSelection(self, menuSelectionArtist):
+ selectedShots = [
+ item for item in self._selection
+ if (isinstance(item, hiero.core.TrackItem))
+ ]
+ selectedTracks = [
+ item for item in self._selection
+ if (isinstance(item, (hiero.core.VideoTrack,
+ hiero.core.AudioTrack)))
+ ]
+
+ # If we have a Track Header Selection, no shots could be selected, so create shotSelection list
+ if len(selectedTracks) >= 1:
+ for track in selectedTracks:
+ selectedShots += [
+ item for item in track.items()
+ if (isinstance(item, hiero.core.TrackItem))
+ ]
+
+ # It's possible no shots exist on the Track, in which case nothing is required
+ if len(selectedShots) == 0:
+ return
+
+ currentProject = selectedShots[0].project()
+
+ with currentProject.beginUndo("Assign Artist"):
+ # Shots selected
+ for shot in selectedShots:
+ shot.setArtistByName(menuSelectionArtist)
+
+ # This handles events from the Project Bin View
+ def eventHandler(self, event):
+ 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
+
+ # Set the current selection
+ self._selection = event.sender.selection()
+
+ # Return if there's no Selection. We won't add the Menu.
+ if len(self._selection) == 0:
+ return
+
+ event.menu.addMenu(self)
+
+
+# Add the 'Set Status' context menu to Timeline and Spreadsheet
+if kAddStatusMenu:
+ setStatusMenu = SetStatusMenu()
+
+if kAssignArtistMenu:
+ assignArtistMenu = AssignArtistMenu()
+
+# Register our custom columns
+hiero.ui.customColumn = CustomSpreadsheetColumns()
diff --git a/setup/nukestudio/hiero_plugin_path/Python/StartupUI/Purge.py b/setup/nukestudio/hiero_plugin_path/Python/StartupUI/Purge.py
new file mode 100644
index 0000000000..4d2ab255ad
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Python/StartupUI/Purge.py
@@ -0,0 +1,142 @@
+# Purge Unused Clips - Removes any unused Clips from a Project
+# Usage: Copy to ~/.hiero/Python/StartupUI
+# Demonstrates the use of hiero.core.find_items module.
+# Usage: Right-click on an item in the Bin View > "Purge Unused Clips"
+# Result: Any Clips not used in a Sequence in the active project will be removed
+# Requires Hiero 1.5v1 or later.
+# Version 1.1
+
+import hiero
+import hiero.core.find_items
+try:
+ from PySide.QtGui import *
+ from PySide.QtCore import *
+except:
+ from PySide2.QtGui import *
+ from PySide2.QtWidgets import *
+ from PySide2.QtCore import *
+
+
+class PurgeUnusedAction(QAction):
+ def __init__(self):
+ QAction.__init__(self, "Purge Unused Clips", None)
+ self.triggered.connect(self.PurgeUnused)
+ hiero.core.events.registerInterest("kShowContextMenu/kBin",
+ self.eventHandler)
+ self.setIcon(QIcon('icons:TagDelete.png'))
+
+ # Method to return whether a Bin is empty...
+ def binIsEmpty(self, b):
+ numBinItems = 0
+ bItems = b.items()
+ empty = False
+
+ if len(bItems) == 0:
+ empty = True
+ return empty
+ else:
+ for b in bItems:
+ if isinstance(b, hiero.core.BinItem) or isinstance(
+ b, hiero.core.Bin):
+ numBinItems += 1
+ if numBinItems == 0:
+ empty = True
+
+ return empty
+
+ def PurgeUnused(self):
+
+ #Get selected items
+ item = self.selectedItem
+ proj = item.project()
+
+ # Build a list of Projects
+ SEQS = hiero.core.findItems(proj, "Sequences")
+
+ # Build a list of Clips
+ CLIPSTOREMOVE = hiero.core.findItems(proj, "Clips")
+
+ if len(SEQS) == 0:
+ # Present Dialog Asking if User wants to remove Clips
+ msgBox = QMessageBox()
+ msgBox.setText("Purge Unused Clips")
+ msgBox.setInformativeText(
+ "You have no Sequences in this Project. Do you want to remove all Clips (%i) from Project: %s?"
+ % (len(CLIPSTOREMOVE), proj.name()))
+ msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
+ msgBox.setDefaultButton(QMessageBox.Ok)
+ ret = msgBox.exec_()
+ if ret == QMessageBox.Cancel:
+ print 'Not purging anything.'
+ elif ret == QMessageBox.Ok:
+ with proj.beginUndo('Purge Unused Clips'):
+ BINS = []
+ for clip in CLIPSTOREMOVE:
+ BI = clip.binItem()
+ B = BI.parentBin()
+ BINS += [B]
+ print 'Removing:', BI
+ try:
+ B.removeItem(BI)
+ except:
+ print 'Unable to remove: ' + BI
+ return
+
+ # For each sequence, iterate through each track Item, see if the Clip is in the CLIPS list.
+ # Remaining items in CLIPS will be removed
+
+ for seq in SEQS:
+
+ #Loop through selected and make folders
+ for track in seq:
+ for trackitem in track:
+
+ if trackitem.source() in CLIPSTOREMOVE:
+ CLIPSTOREMOVE.remove(trackitem.source())
+
+ # Present Dialog Asking if User wants to remove Clips
+ msgBox = QMessageBox()
+ msgBox.setText("Purge Unused Clips")
+ msgBox.setInformativeText("Remove %i unused Clips from Project %s?" %
+ (len(CLIPSTOREMOVE), proj.name()))
+ msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
+ msgBox.setDefaultButton(QMessageBox.Ok)
+ ret = msgBox.exec_()
+
+ if ret == QMessageBox.Cancel:
+ print 'Cancel'
+ return
+ elif ret == QMessageBox.Ok:
+ BINS = []
+ 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
+ try:
+ B.removeItem(BI)
+ except:
+ print 'Unable to remove: ' + BI
+
+ def eventHandler(self, event):
+ 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
+
+ self.selectedItem = None
+ s = event.sender.selection()
+
+ if len(s) >= 1:
+ self.selectedItem = s[0]
+ title = "Purge Unused Clips"
+ self.setText(title)
+ event.menu.addAction(self)
+
+ return
+
+
+# Instantiate the action to get it to register itself.
+PurgeUnusedAction = PurgeUnusedAction()
diff --git a/setup/nukestudio/hiero_plugin_path/Python/StartupUI/nukeStyleKeyboardShortcuts.py b/setup/nukestudio/hiero_plugin_path/Python/StartupUI/nukeStyleKeyboardShortcuts.py
new file mode 100644
index 0000000000..36a30e3a3c
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Python/StartupUI/nukeStyleKeyboardShortcuts.py
@@ -0,0 +1,36 @@
+# nukeStyleKeyboardShortcuts, v1, 30/07/2012, Ant Nasce.
+# A few Nuke-Style File menu shortcuts for those whose muscle memory has set in...
+# Usage: Copy this file to ~/.hiero/Python/StartupUI/
+
+import hiero.ui
+try:
+ from PySide.QtGui import *
+ from PySide.QtCore import *
+except:
+ from PySide2.QtGui import *
+ from PySide2.QtWidgets import *
+ from PySide2.QtCore import *
+
+#----------------------------------------------
+a = hiero.ui.findMenuAction('Import Clips...')
+# 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...')
+a.setShortcut(QKeySequence('Shift+R'))
+#----------------------------------------------
+a = hiero.ui.findMenuAction('Import EDL/XML...')
+a.setShortcut(QKeySequence('Ctrl+Shift+O'))
+#----------------------------------------------
+a = hiero.ui.findMenuAction('Show Metadata')
+a.setShortcut(QKeySequence('I'))
+#----------------------------------------------
+a = hiero.ui.findMenuAction('Edit Settings')
+a.setShortcut(QKeySequence('S'))
+#----------------------------------------------
+a = hiero.ui.findMenuAction('Monitor Controls')
+a.setShortcut(QKeySequence('Ctrl+U'))
+#----------------------------------------------
+a = hiero.ui.findMenuAction('New Viewer')
+a.setShortcut(QKeySequence('Ctrl+I'))
+#----------------------------------------------
diff --git a/setup/nukestudio/hiero_plugin_path/Python/StartupUI/setPosterFrame.py b/setup/nukestudio/hiero_plugin_path/Python/StartupUI/setPosterFrame.py
new file mode 100644
index 0000000000..18398aa119
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Python/StartupUI/setPosterFrame.py
@@ -0,0 +1,45 @@
+import hiero.core
+import hiero.ui
+try:
+ from PySide.QtGui import *
+ from PySide.QtCore import *
+except:
+ from PySide2.QtGui import *
+ from PySide2.QtWidgets import *
+ from PySide2.QtCore import *
+
+
+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)
+ for item in selectedBinItems]
+
+ for clip in selectedClipItems:
+ centreFrame = int(clip.duration() * posterFrame)
+ clip.setPosterFrame(centreFrame)
+
+
+class SetPosterFrameAction(QAction):
+ def __init__(self):
+ QAction.__init__(self, "Set Poster Frame (centre)", None)
+ self._selection = None
+
+ self.triggered.connect(lambda: setPosterFrame(.5))
+ hiero.core.events.registerInterest("kShowContextMenu/kBin",
+ self.eventHandler)
+
+ def eventHandler(self, event):
+ view = event.sender
+ # Add the Menu to the right-click menu
+ event.menu.addAction(self)
+
+
+# The act of initialising the action adds it to the right-click menu...
+SetPosterFrameAction()
diff --git a/setup/nukestudio/hiero_plugin_path/Startup_old/pyblish_startup.py b/setup/nukestudio/hiero_plugin_path/Startup_old/pyblish_startup.py
new file mode 100644
index 0000000000..4459be6713
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Startup_old/pyblish_startup.py
@@ -0,0 +1,14 @@
+import traceback
+
+try:
+ __import__("pype.nukestudio")
+ __import__("pyblish")
+
+except ImportError as e:
+ print traceback.format_exc()
+ print("pyblish: Could not load integration: %s " % e)
+
+else:
+ # Setup integration
+ import pype.nukestudio.lib
+ pype.nukestudio.lib.setup()
diff --git a/setup/nukestudio/hiero_plugin_path/Startup_old/selection_tracker.py b/setup/nukestudio/hiero_plugin_path/Startup_old/selection_tracker.py
new file mode 100644
index 0000000000..b7e05fed7c
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Startup_old/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/setup/nukestudio/hiero_plugin_path/TaskPresets/10.5/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml b/setup/nukestudio/hiero_plugin_path/TaskPresets/10.5/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
new file mode 100644
index 0000000000..e24a4dbe4e
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/TaskPresets/10.5/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
@@ -0,0 +1,198 @@
+
+ 991
+ //10.11.0.184/171001_ftrack/tgbvfx/editorial/nukestudio/workspace/
+ 1
+ True
+ 3
+
+
+ {shot}/editorial_raw.%04d.{fileext}
+
+
+ default
+ exr
+ False
+ all
+ False
+ False
+ False
+ False
+ True
+
+
+ 8 bit
+ (auto detect)
+ True
+ False
+
+ False
+
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+
+
+ Zip (16 scanline)
+ 32 bit float
+ False
+ False
+ False
+ channels, layers and views
+ 45.0
+ False
+ all metadata
+
+ Write_{ext}
+
+ Cubic
+ None
+ 1.0
+ True
+ width
+
+ False
+ Blend
+
+
+
+
+ {shot}/editorial.%04d.{ext}
+
+
+ default
+ exr
+ False
+ all
+ False
+ False
+ False
+ False
+ True
+
+
+ 8 bit
+ (auto detect)
+ True
+ False
+
+ True
+
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+
+
+ Zip (16 scanline)
+ 16 bit half
+ False
+ False
+ False
+ channels, layers and views
+ 45.0
+ False
+ all metadata
+
+ Write_{ext}
+
+ Cubic
+ To Sequence Resolution
+ 1.0
+ True
+ width
+
+ False
+ Blend
+
+
+
+
+ {shot}/editorial.nk
+
+
+ True
+ default
+ mov
+
+ rgb
+ False
+
+ False
+ False
+ False
+
+ True
+ True
+
+ {shot}/editorial_raw.%04d.{fileext}
+
+
+ Cubic
+ None
+ 1.0
+ True
+ width
+
+ False
+ Blend
+ False
+ True
+ True
+
+ 0
+ 40000000
+ 12
+ 31
+ 2
+ avc1 H.264
+ Auto
+ mov32
+ 20000
+
+ False
+ True
+ True
+ False
+ False
+ {shot}/editorial_raw.%04d.{fileext}
+
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+
+
+ 8 bit
+ (auto detect)
+ True
+ False
+
+ Write_{ext}
+ False
+
+
+
+
+
+
+ False
+ Custom
+ True
+ 10
+
diff --git a/setup/nukestudio/hiero_plugin_path/TaskPresets/11.1/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml b/setup/nukestudio/hiero_plugin_path/TaskPresets/11.1/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
new file mode 100644
index 0000000000..e24a4dbe4e
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/TaskPresets/11.1/Processors/hiero.exporters.FnShotProcessor.ShotProcessor/pipeline.xml
@@ -0,0 +1,198 @@
+
+ 991
+ //10.11.0.184/171001_ftrack/tgbvfx/editorial/nukestudio/workspace/
+ 1
+ True
+ 3
+
+
+ {shot}/editorial_raw.%04d.{fileext}
+
+
+ default
+ exr
+ False
+ all
+ False
+ False
+ False
+ False
+ True
+
+
+ 8 bit
+ (auto detect)
+ True
+ False
+
+ False
+
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+
+
+ Zip (16 scanline)
+ 32 bit float
+ False
+ False
+ False
+ channels, layers and views
+ 45.0
+ False
+ all metadata
+
+ Write_{ext}
+
+ Cubic
+ None
+ 1.0
+ True
+ width
+
+ False
+ Blend
+
+
+
+
+ {shot}/editorial.%04d.{ext}
+
+
+ default
+ exr
+ False
+ all
+ False
+ False
+ False
+ False
+ True
+
+
+ 8 bit
+ (auto detect)
+ True
+ False
+
+ True
+
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+
+
+ Zip (16 scanline)
+ 16 bit half
+ False
+ False
+ False
+ channels, layers and views
+ 45.0
+ False
+ all metadata
+
+ Write_{ext}
+
+ Cubic
+ To Sequence Resolution
+ 1.0
+ True
+ width
+
+ False
+ Blend
+
+
+
+
+ {shot}/editorial.nk
+
+
+ True
+ default
+ mov
+
+ rgb
+ False
+
+ False
+ False
+ False
+
+ True
+ True
+
+ {shot}/editorial_raw.%04d.{fileext}
+
+
+ Cubic
+ None
+ 1.0
+ True
+ width
+
+ False
+ Blend
+ False
+ True
+ True
+
+ 0
+ 40000000
+ 12
+ 31
+ 2
+ avc1 H.264
+ Auto
+ mov32
+ 20000
+
+ False
+ True
+ True
+ False
+ False
+ {shot}/editorial_raw.%04d.{fileext}
+
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+ None
+
+
+ 8 bit
+ (auto detect)
+ True
+ False
+
+ Write_{ext}
+ False
+
+
+
+
+
+
+ False
+ Custom
+ True
+ 10
+
diff --git a/setup/nukestudio/hiero_plugin_path/Templates/vfx_aces.hrox b/setup/nukestudio/hiero_plugin_path/Templates/vfx_aces.hrox
new file mode 100644
index 0000000000..684cd0d1a2
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Templates/vfx_aces.hrox
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+ 2
+ 70
+
+
+ 2
+ 70
+ 13
+
+
+ 2
+ 70
+ 17
+
+
+ 2
+ 70
+ 2
+
+
+
+
+
+
+
+
+
diff --git a/setup/nukestudio/hiero_plugin_path/Templates/vfx_linear.hrox b/setup/nukestudio/hiero_plugin_path/Templates/vfx_linear.hrox
new file mode 100644
index 0000000000..e915a24084
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Templates/vfx_linear.hrox
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+ 2
+ 70
+
+
+ 2
+ 70
+ 13
+
+
+ 2
+ 70
+ 17
+
+
+ 2
+ 70
+ 2
+
+
+
+
+
+
+
+
+
diff --git a/setup/nukestudio/hiero_plugin_path/Templates/vfx_rec709.hrox b/setup/nukestudio/hiero_plugin_path/Templates/vfx_rec709.hrox
new file mode 100644
index 0000000000..42659cf81b
--- /dev/null
+++ b/setup/nukestudio/hiero_plugin_path/Templates/vfx_rec709.hrox
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+ 2
+ 70
+
+
+ 2
+ 70
+
+
+ 2
+ 70
+ 13
+
+
+ 2
+ 70
+ 17
+
+
+ 2
+ 70
+ 2
+
+
+
+
+
+
+
+
+