diff --git a/pype/tools/workfiles/app.py b/pype/tools/workfiles/app.py
index 7ffdf89247..f73baab624 100644
--- a/pype/tools/workfiles/app.py
+++ b/pype/tools/workfiles/app.py
@@ -36,92 +36,140 @@ class NameWindow(QtWidgets.QDialog):
"""
- def __init__(self, parent, root, session=None):
+ def __init__(self, parent, root, anatomy, template_key, session=None):
super(NameWindow, self).__init__(parent=parent)
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint)
self.result = None
+ self.result_note = None
self.host = api.registered_host()
self.root = root
self.work_file = None
- if session is None:
+ if not session:
# Fallback to active session
session = api.Session
# Set work file data for template formatting
- project = io.find_one({
+ asset_name = session["AVALON_ASSET"]
+ project_doc = io.find_one({
"type": "project"
})
+ asset_doc = io.find_one({
+ "type": "asset",
+ "name": asset_name
+ })
self.data = {
"project": {
- "name": project["name"],
- "code": project["data"].get("code")
+ "name": project_doc["name"],
+ "code": project_doc["data"].get("code")
},
- "asset": session["AVALON_ASSET"],
+ "asset": asset_name,
"task": session["AVALON_TASK"],
"version": 1,
"user": getpass.getuser(),
- "comment": ""
+ "comment": "",
+ "ext": None,
+ "note": ""
}
- # Define work files template
- anatomy = Anatomy(project["name"])
- self.template = anatomy.templates["work"]["file"]
+ # Store project anatomy
+ self.anatomy = anatomy
+ self.template = anatomy.templates[template_key]["file"]
+ self.template_key = template_key
+ self.asset_doc = asset_doc
- self.widgets = {
- "preview": QtWidgets.QLabel("Preview filename"),
- "comment": QtWidgets.QLineEdit(),
- "version": QtWidgets.QWidget(),
- "versionValue": QtWidgets.QSpinBox(),
- "versionCheck": QtWidgets.QCheckBox("Next Available Version"),
- "inputs": QtWidgets.QWidget(),
- "buttons": QtWidgets.QWidget(),
- "okButton": QtWidgets.QPushButton("Ok"),
- "cancelButton": QtWidgets.QPushButton("Cancel")
- }
+ # Btns widget
+ btns_widget = QtWidgets.QWidget(self)
- # Build version
- self.widgets["versionValue"].setMinimum(1)
- self.widgets["versionValue"].setMaximum(9999)
- self.widgets["versionCheck"].setCheckState(QtCore.Qt.CheckState(2))
- layout = QtWidgets.QHBoxLayout(self.widgets["version"])
- layout.setContentsMargins(0, 0, 0, 0)
- layout.addWidget(self.widgets["versionValue"])
- layout.addWidget(self.widgets["versionCheck"])
+ btn_ok = QtWidgets.QPushButton("Ok", btns_widget)
+ btn_cancel = QtWidgets.QPushButton("Cancel", btns_widget)
- # Build buttons
- layout = QtWidgets.QHBoxLayout(self.widgets["buttons"])
- layout.addWidget(self.widgets["okButton"])
- layout.addWidget(self.widgets["cancelButton"])
+ btns_layout = QtWidgets.QHBoxLayout(btns_widget)
+ btns_layout.addWidget(btn_ok)
+ btns_layout.addWidget(btn_cancel)
+
+ # Inputs widget
+ inputs_widget = QtWidgets.QWidget(self)
+
+ # Version widget
+ version_widget = QtWidgets.QWidget(inputs_widget)
+
+ # Version number input
+ version_input = QtWidgets.QSpinBox(version_widget)
+ version_input.setMinimum(1)
+ version_input.setMaximum(9999)
+
+ # Last version checkbox
+ last_version_check = QtWidgets.QCheckBox(
+ "Next Available Version", version_widget
+ )
+ last_version_check.setChecked(True)
+
+ version_layout = QtWidgets.QHBoxLayout(version_widget)
+ version_layout.setContentsMargins(0, 0, 0, 0)
+ version_layout.addWidget(version_input)
+ version_layout.addWidget(last_version_check)
+
+ # Preview widget
+ preview_label = QtWidgets.QLabel("Preview filename", inputs_widget)
+
+ # Comment input
+ comment_input = QtWidgets.QLineEdit(inputs_widget)
+ comment_input.setPlaceholderText("Will be part of filename.")
+
+ # Extensions combobox
+ ext_combo = QtWidgets.QComboBox(inputs_widget)
+ ext_combo.addItems(self.host.file_extensions())
+
+ # Note input
+ note_input = QtWidgets.QLineEdit(inputs_widget)
+ note_input.setPlaceholderText("Artist note to workfile")
# Build inputs
- layout = QtWidgets.QFormLayout(self.widgets["inputs"])
- layout.addRow("Version:", self.widgets["version"])
- layout.addRow("Comment:", self.widgets["comment"])
- layout.addRow("Preview:", self.widgets["preview"])
+ inputs_layout = QtWidgets.QFormLayout(inputs_widget)
+ inputs_layout.addRow("Version:", version_widget)
+ inputs_layout.addRow("Comment:", comment_input)
+ inputs_layout.addRow("Extension:", ext_combo)
+ inputs_layout.addRow("Preview:", preview_label)
+ inputs_layout.addRow("Note:", note_input)
# Build layout
- layout = QtWidgets.QVBoxLayout(self)
- layout.addWidget(self.widgets["inputs"])
- layout.addWidget(self.widgets["buttons"])
+ main_layout = QtWidgets.QVBoxLayout(self)
+ main_layout.addWidget(inputs_widget)
+ main_layout.addWidget(btns_widget)
- self.widgets["versionValue"].valueChanged.connect(
- self.on_version_spinbox_changed
- )
- self.widgets["versionCheck"].stateChanged.connect(
+ # Singal callback registration
+ version_input.valueChanged.connect(self.on_version_spinbox_changed)
+ last_version_check.stateChanged.connect(
self.on_version_checkbox_changed
)
- self.widgets["comment"].textChanged.connect(self.on_comment_changed)
- self.widgets["okButton"].pressed.connect(self.on_ok_pressed)
- self.widgets["cancelButton"].pressed.connect(self.on_cancel_pressed)
+
+ comment_input.textChanged.connect(self.on_comment_changed)
+ ext_combo.currentIndexChanged.connect(self.on_extension_changed)
+ note_input.textChanged.connect(self.on_note_changed)
+
+ btn_ok.pressed.connect(self.on_ok_pressed)
+ btn_cancel.pressed.connect(self.on_cancel_pressed)
# Allow "Enter" key to accept the save.
- self.widgets["okButton"].setDefault(True)
+ btn_ok.setDefault(True)
# Force default focus to comment, some hosts didn't automatically
# apply focus to this line edit (e.g. Houdini)
- self.widgets["comment"].setFocus()
+ comment_input.setFocus()
+
+ # Store widgets
+ self.btn_ok = btn_ok
+
+ self.version_widget = version_widget
+ self.version_input = version_input
+ self.last_version_check = last_version_check
+
+ self.preview_label = preview_label
+ self.comment_input = comment_input
+ self.ext_combo = ext_combo
+ self.note_input = note_input
self.refresh()
@@ -129,15 +177,26 @@ class NameWindow(QtWidgets.QDialog):
self.data["version"] = value
self.refresh()
- def on_version_checkbox_changed(self, value):
+ def on_version_checkbox_changed(self, _value):
self.refresh()
def on_comment_changed(self, text):
self.data["comment"] = text
self.refresh()
+ def on_note_changed(self, text):
+ self.data["note"] = text
+
+ def on_extension_changed(self):
+ ext = self.ext_combo.currentText()
+ if ext == self.data["ext"]:
+ return
+ self.data["ext"] = ext
+ self.refresh()
+
def on_ok_pressed(self):
- self.result = self.work_file.replace("\\", "/")
+ self.result = self.work_file
+ self.result_note = self.data["note"]
self.close()
def on_cancel_pressed(self):
@@ -146,42 +205,57 @@ class NameWindow(QtWidgets.QDialog):
def get_result(self):
return self.result
- def get_work_file(self, template=None):
+ def get_result_note(self):
+ return self.result_note
+
+ def get_work_file(self):
data = copy.deepcopy(self.data)
- template = template or self.template
-
- # Define saving file extension
- current_file = self.host.current_file()
- if current_file:
- # Match the extension of current file
- _, extension = os.path.splitext(current_file)
- else:
- # Fall back to the first extension supported for this host.
- extension = self.host.file_extensions()[0]
-
- data["ext"] = extension
-
if not data["comment"]:
data.pop("comment", None)
- return api.format_template_with_optional_keys(data, template)
+ anatomy_filled = self.anatomy.format(data)
+ return anatomy_filled[self.template_key]["file"]
def refresh(self):
# Since the version can be padded with "{version:0>4}" we only search
# for "{version".
if "{version" not in self.template:
- # todo: hide the full row
- self.widgets["version"].setVisible(False)
+ # TODO hide the full row
+ self.version_widget.setVisible(False)
# Build comment
if "{comment}" not in self.template:
- # todo: hide the full row
- self.widgets["comment"].setVisible(False)
+ # TODO hide the full row
+ self.comment_input.setVisible(False)
- if self.widgets["versionCheck"].isChecked():
- self.widgets["versionValue"].setEnabled(False)
+ extensions = self.host.file_extensions()
+ extension = self.data["ext"]
+ if extension is None:
+ # Define saving file extension
+ current_file = self.host.current_file()
+ if current_file:
+ # Match the extension of current file
+ _, extension = os.path.splitext(current_file)
+ else:
+ extension = extensions[0]
+
+ if extension != self.data["ext"]:
+ self.data["ext"] = extension
+ index = self.ext_combo.findText(
+ extension, QtCore.Qt.MatchFixedString
+ )
+ if index >= 0:
+ self.ext_combo.setCurrentIndex(index)
+
+ if not self.last_version_check.isChecked():
+ self.version_input.setEnabled(True)
+ self.data["version"] = self.version_input.value()
+
+ work_file = self.get_work_file()
+
+ else:
+ self.version_input.setEnabled(False)
- extensions = self.host.file_extensions()
data = copy.deepcopy(self.data)
template = str(self.template)
@@ -197,32 +271,49 @@ class NameWindow(QtWidgets.QDialog):
else:
version += 1
- self.data["version"] = version
+ found_valid_version = False
+ # Check if next version is valid version and give a chance to try
+ # next 100 versions
+ for idx in range(100):
+ # Store version to data
+ self.data["version"] = version
- # safety check
- path = os.path.join(self.root, self.get_work_file())
- assert not os.path.exists(path), \
- "This is a bug, file exists: %s" % path
+ work_file = self.get_work_file()
+ # Safety check
+ path = os.path.join(self.root, work_file)
+ if not os.path.exists(path):
+ found_valid_version = True
+ break
- else:
- self.widgets["versionValue"].setEnabled(True)
- self.data["version"] = self.widgets["versionValue"].value()
+ # Try next version
+ version += 1
+ # Log warning
+ if idx == 0:
+ log.warning((
+ "BUG: Function `last_workfile_with_version` "
+ "didn't return last version."
+ ))
+ # Raise exception if even 100 version fallback didn't help
+ if not found_valid_version:
+ raise AssertionError(
+ "This is a bug. Couldn't find valid version!"
+ )
- self.work_file = self.get_work_file()
+ self.work_file = work_file
- preview = self.widgets["preview"]
- ok = self.widgets["okButton"]
- preview.setText(
- "{0}".format(self.work_file)
- )
- if os.path.exists(os.path.join(self.root, self.work_file)):
- preview.setText(
+ path_exists = os.path.exists(os.path.join(self.root, work_file))
+
+ self.btn_ok.setEnabled(not path_exists)
+
+ if path_exists:
+ self.preview_label.setText(
"Cannot create \"{0}\" because file exists!"
- "".format(self.work_file)
+ "".format(work_file)
)
- ok.setEnabled(False)
else:
- ok.setEnabled(True)
+ self.preview_label.setText(
+ "{0}".format(work_file)
+ )
class TasksWidget(QtWidgets.QWidget):
@@ -334,6 +425,15 @@ class FilesWidget(QtWidgets.QWidget):
# Setup
self._asset = None
self._task = None
+
+ # Pype's anatomy object for current project
+ self.anatomy = Anatomy(io.Session["AVALON_PROJECT"])
+ # Template key used to get work template from anatomy templates
+ # TODO change template key based on task
+ self.template_key = "work"
+
+ # Do not set root with Anatomy's roots because it would break host's
+ # implementation of `work_root` function
self.root = None
self.host = api.registered_host()
@@ -518,6 +618,8 @@ class FilesWidget(QtWidgets.QWidget):
window = NameWindow(
parent=self,
root=self.root,
+ anatomy=self.anatomy,
+ template_key=self.template_key,
session=session
)
window.exec_()
@@ -785,7 +887,6 @@ class Window(QtWidgets.QMainWindow):
self.widgets["tasks"].select_task(context["task"])
def refresh(self):
-
# Refresh asset widget
self.widgets["assets"].refresh()
@@ -806,7 +907,6 @@ class Window(QtWidgets.QMainWindow):
self.widgets["tasks"].set_asset(asset)
def _on_task_changed(self):
-
asset = self.widgets["assets"].get_selected_assets() or None
if asset is not None:
asset = asset[0]