Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cuesubmit jobs from config file with dynamic widgets #1425

Merged
merged 36 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d4390b0
feat: add a `Browse` button, not visible by default
KernAttila Mar 27, 2023
1094b19
feat: add 2 functions `getFolder` and `getFile` (with optional file f…
KernAttila Mar 27, 2023
2c78cb0
feat: add wrappers to `getFile` and `getFolder` to set the returned r…
KernAttila Mar 27, 2023
9b93f72
feat: add functions to display the browse button and attach wrappers …
KernAttila Mar 27, 2023
ac4ef69
feat: Make `BaseMayaSettings` and `InMayaSettings` browsable with fil…
KernAttila Mar 27, 2023
ca2fa7d
feat: Make `BaseNukeSettings` and `InNukeSettings` browsable with fil…
KernAttila Mar 27, 2023
9ffd3a6
feat: Make `BaseBlenderSettings` browsable with file filter
KernAttila Mar 27, 2023
f8cd0f9
feat!: Group all job specific widgets in a groupBox with title
KernAttila Mar 27, 2023
088c84b
feat: moved job specific widget group below job setup widgets
KernAttila Mar 27, 2023
b4085a2
feat: add styling for QGroupBox
KernAttila Mar 27, 2023
f4f11a4
feat: add `greyOut()` function to `CueLabelLineEdit`
KernAttila Mar 27, 2023
1c07ab7
feat: add styling for disabled `CueLabelLineEdit`
KernAttila Mar 27, 2023
694de33
fix: make maya command use start and end frames instead of fixed sing…
KernAttila Mar 27, 2023
7ea6eb1
feat!: add `silent` argument to `build*Cmd()` functions so we can fee…
KernAttila Mar 27, 2023
52d7291
fix!: remove redundant functions and use the new `silent` argument
KernAttila Mar 27, 2023
fc96503
feat: add constants `FRAME_START` and `FRAME_END`
KernAttila Mar 27, 2023
28a3d02
fix!: FRAME_TOKEN must not be configurable as it is not forwarded to …
KernAttila Mar 27, 2023
23142c5
feat: add commandFeedback widget in `CueSubmitWidget` and its `update…
KernAttila Mar 27, 2023
59093ef
fix: revert 2 deleted lines
KernAttila Mar 31, 2023
a9f6193
fix: update blender command when changing output format
KernAttila Mar 31, 2023
425d7fa
fix: update layout var
KernAttila Mar 31, 2023
67fee41
clean: move line up with sibling
KernAttila Mar 31, 2023
abd2cca
fix: change expected maya command test result
KernAttila Apr 24, 2023
2c9757d
feat: add Constants `MAYA_FILE_FILTERS`, `NUKE_FILE_FILTERS`, `BLENDE…
KernAttila Apr 24, 2023
c30b146
fix: use Constants `MAYA_FILE_FILTERS`, `NUKE_FILE_FILTERS`, `BLENDER…
KernAttila Apr 24, 2023
e2b508a
Merge branch 'AcademySoftwareFoundation:master' into cuesubmit-browse…
KernAttila Aug 8, 2023
7cabddd
Merge branch 'AcademySoftwareFoundation:master' into cuesubmit-comman…
KernAttila Aug 8, 2023
19764f5
Merge branch 'AcademySoftwareFoundation:master' into cuesubmit-group-…
KernAttila Aug 8, 2023
6453a23
Merge remote-tracking branch 'origin/master' into pr/1281
lithorus Jul 17, 2024
d2e8e23
Merge remote-tracking branch 'origin/master' into pr/1282
lithorus Jul 17, 2024
ec02d97
Merge branch 'pr/1282' into pr/1281
lithorus Jul 17, 2024
8d15c2d
Merge remote-tracking branch 'origin/master' into pr/1283
lithorus Jul 17, 2024
33854b8
Merge branch 'pr/1283' into pr/1281
lithorus Jul 17, 2024
5c173cc
Added missing CueLabelLineEdit.setter and fixed some linting issues
lithorus Jul 17, 2024
2458adb
Merge branch 'AcademySoftwareFoundation:master' into pr/1281
lithorus Jul 18, 2024
c1039f7
Merge remote-tracking branch 'origin/master' into pr/1281
lithorus Jul 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions cuesubmit/cuesubmit/Constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@
'#JOB#': 'Name of the Job',
'#FRAME#': 'Name of the Frame'
}

MAYA_FILE_FILTERS = [
'Maya Ascii file (*.ma)',
'Maya Binary file (*.mb)',
'Maya file (*.ma *.mb)'
]
NUKE_FILE_FILTERS = ['Nuke script file (*.nk)']
BLENDER_FILE_FILTERS = ['Blender file (*.blend)']


BLENDER_FORMATS = ['', 'AVIJPEG', 'AVIRAW', 'BMP', 'CINEON', 'DPX', 'EXR', 'HDR', 'IRIS', 'IRIZ',
'JP2', 'JPEG', 'MPEG', 'MULTILAYER', 'PNG', 'RAWTGA', 'TGA', 'TIFF']
BLENDER_OUTPUT_OPTIONS_URL = \
Expand Down
69 changes: 28 additions & 41 deletions cuesubmit/cuesubmit/Submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,27 @@
from cuesubmit import JobTypes


def buildMayaCmd(layerData):
def buildMayaCmd(layerData, silent=False):
"""From a layer, builds a Maya Render command."""
camera = layerData.cmd.get('camera')
mayaFile = layerData.cmd.get('mayaFile')
if not mayaFile:
if not mayaFile and not silent:
raise ValueError('No Maya File provided. Cannot submit job.')
renderCommand = '{renderCmd} -r file -s {frameToken} -e {frameToken}'.format(
renderCmd=Constants.MAYA_RENDER_CMD, frameToken=Constants.FRAME_TOKEN)
renderCommand = '{renderCmd} -r file -s {frameStart} -e {frameEnd}'.format(
renderCmd=Constants.MAYA_RENDER_CMD,
frameStart=Constants.FRAME_START_TOKEN,
frameEnd=Constants.FRAME_END_TOKEN)
if camera:
renderCommand += ' -cam {}'.format(camera)
renderCommand += ' {}'.format(mayaFile)
return renderCommand


def buildNukeCmd(layerData):
def buildNukeCmd(layerData, silent=False):
"""From a layer, builds a Nuke Render command."""
writeNodes = layerData.cmd.get('writeNodes')
nukeFile = layerData.cmd.get('nukeFile')
if not nukeFile:
if not nukeFile and not silent:
raise ValueError('No Nuke file provided. Cannot submit job.')
renderCommand = '{renderCmd} -F {frameToken} '.format(
renderCmd=Constants.NUKE_RENDER_CMD, frameToken=Constants.FRAME_TOKEN)
Expand All @@ -59,13 +61,13 @@ def buildNukeCmd(layerData):
return renderCommand


def buildBlenderCmd(layerData):
def buildBlenderCmd(layerData, silent=False):
"""From a layer, builds a Blender render command."""
blenderFile = layerData.cmd.get('blenderFile')
outputPath = layerData.cmd.get('outputPath')
outputFormat = layerData.cmd.get('outputFormat')
frameRange = layerData.layerRange
if not blenderFile:
if not blenderFile and not silent:
raise ValueError('No Blender file provided. Cannot submit job.')

renderCommand = '{renderCmd} -b -noaudio {blenderFile}'.format(
Expand Down Expand Up @@ -110,46 +112,31 @@ def buildLayer(layerData, command, lastLayer=None):
layer.depend_on(lastLayer)
return layer


def buildMayaLayer(layerData, lastLayer):
"""Builds a PyOutline layer running a Maya command."""
mayaCmd = buildMayaCmd(layerData)
return buildLayer(layerData, mayaCmd, lastLayer)


def buildNukeLayer(layerData, lastLayer):
"""Builds a PyOutline layer running a Nuke command."""
nukeCmd = buildNukeCmd(layerData)
return buildLayer(layerData, nukeCmd, lastLayer)


def buildBlenderLayer(layerData, lastLayer):
"""Builds a PyOutline layer running a Blender command."""
blenderCmd = buildBlenderCmd(layerData)
return buildLayer(layerData, blenderCmd, lastLayer)


def buildShellLayer(layerData, lastLayer):
"""Builds a PyOutline layer running a shell command."""
return buildLayer(layerData, layerData.cmd['commandTextBox'], lastLayer)

def buildLayerCommand(layerData, silent=False):
"""Builds the command to be sent per jobType"""
if layerData.layerType == JobTypes.JobTypes.MAYA:
command = buildMayaCmd(layerData, silent)
elif layerData.layerType == JobTypes.JobTypes.SHELL:
command = layerData.cmd.get('commandTextBox') if silent else layerData.cmd['commandTextBox']
elif layerData.layerType == JobTypes.JobTypes.NUKE:
command = buildNukeCmd(layerData, silent)
elif layerData.layerType == JobTypes.JobTypes.BLENDER:
command = buildBlenderCmd(layerData, silent)
else:
if silent:
command = 'Error: unrecognized layer type {}'.format(layerData.layerType)
else:
raise ValueError('unrecognized layer type {}'.format(layerData.layerType))
return command

def submitJob(jobData):
"""Submits the job using the PyOutline API."""
ol = outline.Outline(
jobData['name'], shot=jobData['shot'], show=jobData['show'], user=jobData['username'])
lastLayer = None
for layerData in jobData['layers']:
if layerData.layerType == JobTypes.JobTypes.MAYA:
layer = buildMayaLayer(layerData, lastLayer)
elif layerData.layerType == JobTypes.JobTypes.SHELL:
layer = buildShellLayer(layerData, lastLayer)
elif layerData.layerType == JobTypes.JobTypes.NUKE:
layer = buildNukeLayer(layerData, lastLayer)
elif layerData.layerType == JobTypes.JobTypes.BLENDER:
layer = buildBlenderLayer(layerData, lastLayer)
else:
raise ValueError('unrecognized layer type %s' % layerData.layerType)
command = buildLayerCommand(layerData)
layer = buildLayer(layerData, command, lastLayer)
ol.add_layer(layer)
lastLayer = layer

Expand Down
51 changes: 41 additions & 10 deletions cuesubmit/cuesubmit/ui/SettingsWidgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class BaseSettingsWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(BaseSettingsWidget, self).__init__(parent)
self.mainLayout = QtWidgets.QVBoxLayout()
self.groupBox = QtWidgets.QGroupBox('options')
self.groupLayout = QtWidgets.QVBoxLayout()
self.groupBox.setLayout(self.groupLayout)
self.groupBox.setStyleSheet(Widgets.Style.GROUP_BOX)
self.mainLayout.addWidget(self.groupBox)
self.setLayout(self.mainLayout)
self.mainLayout.setContentsMargins(0, 0, 0, 0)

Expand All @@ -53,17 +58,25 @@ class InMayaSettings(BaseSettingsWidget):
# pylint: disable=keyword-arg-before-vararg,unused-argument
def __init__(self, cameras=None, filename=None, parent=None, *args, **kwargs):
super(InMayaSettings, self).__init__(parent=parent)
self.groupBox.setTitle('Maya options')
self.mayaFileInput = Widgets.CueLabelLineEdit('Maya File:', filename)
self.fileFilters = Constants.MAYA_FILE_FILTERS
self.cameraSelector = Widgets.CueSelectPulldown('Render Cameras', options=cameras)
self.selectorLayout = QtWidgets.QHBoxLayout()
self.setupUi()
self.setupConnections()

def setupUi(self):
"""Creates the Maya-specific widget layout."""
self.mainLayout.addWidget(self.mayaFileInput)
self.groupLayout.addWidget(self.mayaFileInput)
self.selectorLayout.addWidget(self.cameraSelector)
self.selectorLayout.addSpacerItem(Widgets.CueSpacerItem(Widgets.SpacerTypes.HORIZONTAL))
self.mainLayout.addLayout(self.selectorLayout)
self.groupLayout.addLayout(self.selectorLayout)

def setupConnections(self):
"""Sets up widget signals."""
self.mayaFileInput.lineEdit.textChanged.connect(self.dataChanged.emit) # pylint: disable=no-member
self.mayaFileInput.setFileBrowsable(fileFilter=self.fileFilters)

def setCommandData(self, commandData):
self.mayaFileInput.setText(commandData.get('mayaFile', ''))
Expand All @@ -82,17 +95,20 @@ class BaseMayaSettings(BaseSettingsWidget):
# pylint: disable=keyword-arg-before-vararg,unused-argument
def __init__(self, parent=None, *args, **kwargs):
super(BaseMayaSettings, self).__init__(parent=parent)
self.groupBox.setTitle('Maya options')
self.mayaFileInput = Widgets.CueLabelLineEdit('Maya File:')
self.fileFilters = Constants.MAYA_FILE_FILTERS
self.setupUi()
self.setupConnections()

def setupUi(self):
"""Creates the widget layout with a single input for the path to the Maya scene."""
self.mainLayout.addWidget(self.mayaFileInput)
self.groupLayout.addWidget(self.mayaFileInput)

def setupConnections(self):
"""Sets up widget signals."""
self.mayaFileInput.lineEdit.textChanged.connect(self.dataChanged.emit) # pylint: disable=no-member
self.mayaFileInput.setFileBrowsable(fileFilter=self.fileFilters)

def setCommandData(self, commandData):
self.mayaFileInput.setText(commandData.get('mayaFile', ''))
Expand All @@ -109,18 +125,26 @@ class InNukeSettings(BaseSettingsWidget):
# pylint: disable=keyword-arg-before-vararg,unused-argument
def __init__(self, writeNodes=None, filename=None, parent=None, *args, **kwargs):
super(InNukeSettings, self).__init__(parent=parent)
self.groupBox.setTitle('Nuke options')
self.fileInput = Widgets.CueLabelLineEdit('Nuke File:', filename)
self.fileFilters = Constants.NUKE_FILE_FILTERS
self.writeNodeSelector = Widgets.CueSelectPulldown('Write Nodes:', emptyText='[All]',
options=writeNodes)
self.selectorLayout = QtWidgets.QHBoxLayout()
self.setupUi()
self.setupConnections()

def setupUi(self):
"""Creates the Nuke-specific widget layout."""
self.mainLayout.addWidget(self.fileInput)
self.groupLayout.addWidget(self.fileInput)
self.selectorLayout.addWidget(self.writeNodeSelector)
self.selectorLayout.addSpacerItem(Widgets.CueSpacerItem(Widgets.SpacerTypes.HORIZONTAL))
self.mainLayout.addLayout(self.selectorLayout)
self.groupLayout.addLayout(self.selectorLayout)

def setupConnections(self):
"""Sets up widget signals."""
self.fileInput.lineEdit.textChanged.connect(self.dataChanged.emit) # pylint: disable=no-member
self.fileInput.setFileBrowsable(fileFilter=self.fileFilters)

def setCommandData(self, commandData):
self.fileInput.setText(commandData.get('nukeFile', ''))
Expand All @@ -139,17 +163,20 @@ class BaseNukeSettings(BaseSettingsWidget):
# pylint: disable=keyword-arg-before-vararg,unused-argument
def __init__(self, parent=None, *args, **kwargs):
super(BaseNukeSettings, self).__init__(parent=parent)
self.groupBox.setTitle('Nuke options')
self.fileInput = Widgets.CueLabelLineEdit('Nuke File:')
self.fileFilters = Constants.NUKE_FILE_FILTERS
self.setupUi()
self.setupConnections()

def setupUi(self):
"""Creates the widget layout with a single input for the path to the Nuke script."""
self.mainLayout.addWidget(self.fileInput)
self.groupLayout.addWidget(self.fileInput)

def setupConnections(self):
"""Sets up widget signals."""
self.fileInput.lineEdit.textChanged.connect(self.dataChanged.emit) # pylint: disable=no-member
self.fileInput.setFileBrowsable(fileFilter=self.fileFilters)

def setCommandData(self, commandData):
self.fileInput.setText(commandData.get('nukeFile', ''))
Expand All @@ -166,15 +193,15 @@ class ShellSettings(BaseSettingsWidget):
# pylint: disable=keyword-arg-before-vararg,unused-argument
def __init__(self, parent=None, *args, **kwargs):
super(ShellSettings, self).__init__(parent=parent)

self.groupBox.setTitle('Shell options')
self.commandTextBox = Command.CueCommandWidget()

self.setupUi()
self.setupConnections()

def setupUi(self):
"""Creates the widget layout with a single input for the shell command."""
self.mainLayout.addWidget(self.commandTextBox)
self.groupLayout.addWidget(self.commandTextBox)

def setupConnections(self):
"""Sets up widget signals."""
Expand All @@ -193,6 +220,8 @@ class BaseBlenderSettings(BaseSettingsWidget):
# pylint: disable=keyword-arg-before-vararg,unused-argument
def __init__(self, parent=None, *args, **kwargs):
super(BaseBlenderSettings, self).__init__(parent=parent)
self.groupBox.setTitle('Blender options')
self.fileFilters = Constants.BLENDER_FILE_FILTERS
self.fileInput = Widgets.CueLabelLineEdit('Blender File:')
self.outputPath = Widgets.CueLabelLineEdit(
'Output Path (Optional):',
Expand All @@ -207,8 +236,8 @@ def __init__(self, parent=None, *args, **kwargs):

def setupUi(self):
"""Creates the Blender-specific widget layout."""
self.mainLayout.addWidget(self.fileInput)
self.mainLayout.addLayout(self.outputLayout)
self.groupLayout.addWidget(self.fileInput)
self.groupLayout.addLayout(self.outputLayout)
self.outputLayout.addWidget(self.outputPath)
self.outputLayout.addWidget(self.outputSelector)

Expand All @@ -217,6 +246,8 @@ def setupConnections(self):
# pylint: disable=no-member
self.fileInput.lineEdit.textChanged.connect(self.dataChanged.emit)
self.outputPath.lineEdit.textChanged.connect(self.dataChanged.emit)
self.outputSelector.optionsMenu.triggered.connect(self.dataChanged.emit)
self.fileInput.setFileBrowsable(fileFilter=self.fileFilters)
# pylint: enable=no-member

def setCommandData(self, commandData):
Expand Down
17 changes: 17 additions & 0 deletions cuesubmit/cuesubmit/ui/Style.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,23 @@
}
"""

DISABLED_LINE_EDIT = """
QLineEdit {
color: rgb(110, 110, 110);
border: 0px solid;
background-color: rgb(30, 35, 40);
border-radius: 4px;
}
"""

GROUP_BOX = """
QGroupBox {
border: 3px solid rgb(30, 40, 50);
border-radius: 6px;
font-size: 8pt;
}
"""

SEPARATOR_LINE = 'border: 1px solid rgb(20, 30, 40)'

TEXT = 'background-color: rgb(40, 50, 60); color: rgb(250, 250, 250); font-weight: regular;'
Expand Down
16 changes: 14 additions & 2 deletions cuesubmit/cuesubmit/ui/Submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ def __init__(
self.facilitySelector.setChecked(selected_facility)

self.settingsWidget = self.jobTypes.build(self.primaryWidgetType, *args, **kwargs)
self.commandFeedback = Widgets.CueLabelLineEdit( labelText='Final command:' )
self.commandFeedback.greyOut()
self.jobTreeWidget = Job.CueJobWidget()
self.submitButtons = CueSubmitButtons()
self.setupUi()
Expand Down Expand Up @@ -264,8 +266,6 @@ def setupUi(self):
self.scrollingLayout.addSpacerItem(Widgets.CueSpacerItem(Widgets.SpacerTypes.VERTICAL))
self.scrollingLayout.addWidget(Widgets.CueLabelLine('Layer Info'))
self.layerInfoLayout.addWidget(self.layerNameInput)
self.settingsLayout.addWidget(self.settingsWidget)
self.layerInfoLayout.addLayout(self.settingsLayout)
self.layerInfoLayout.addSpacerItem(Widgets.CueSpacerItem(Widgets.SpacerTypes.VERTICAL))
self.layerInfoLayout.addWidget(self.frameBox)

Expand All @@ -280,8 +280,12 @@ def setupUi(self):
self.coresLayout.addWidget(self.dependSelector)
self.coresLayout.addSpacerItem(Widgets.CueSpacerItem(Widgets.SpacerTypes.HORIZONTAL))
self.layerInfoLayout.addLayout(self.coresLayout)
self.layerInfoLayout.addWidget(self.commandFeedback)
self.scrollingLayout.addLayout(self.layerInfoLayout)

self.settingsLayout.addWidget(self.settingsWidget)
self.layerInfoLayout.addLayout(self.settingsLayout)

self.scrollingLayout.addSpacerItem(Widgets.CueSpacerItem(Widgets.SpacerTypes.VERTICAL))
self.scrollingLayout.addWidget(Widgets.CueLabelLine('Submission Details'))

Expand Down Expand Up @@ -329,6 +333,7 @@ def jobLayerSelectionChanged(self, layerObject):
self.dependSelector.clearChecked()
self.dependSelector.setChecked([layerObject.dependType])
self.settingsWidget.setCommandData(layerObject.cmd)
self.updateFeedbackCommand(layerObject)
self.skipDataChangedEvent = False

def jobDataChanged(self):
Expand All @@ -351,6 +356,13 @@ def jobDataChanged(self):
dependsOn=None
)
self.jobTreeWidget.updateJobData(self.jobNameInput.text())
self.updateFeedbackCommand(self.jobTreeWidget.currentLayerData)

def updateFeedbackCommand(self, layerData):
""" Builds the final command for this layer and displays it in the feedback widget """
command = Submission.buildLayerCommand(layerData=layerData,
silent=True)
self.commandFeedback.setText(text=command)

def jobTypeChanged(self):
"""Action when the job type is changed."""
Expand Down
Loading
Loading