creator tool is using product name and type

This commit is contained in:
Jakub Trllo 2024-02-23 12:37:53 +01:00
parent 4974c492b0
commit 1089f6e587
4 changed files with 121 additions and 119 deletions

View file

@ -1,7 +1,7 @@
from qtpy import QtCore
FAMILY_ROLE = QtCore.Qt.UserRole + 1
PRODUCT_TYPE_ROLE = QtCore.Qt.UserRole + 1
ITEM_ID_ROLE = QtCore.Qt.UserRole + 2
SEPARATOR = "---"

View file

@ -4,7 +4,7 @@ from qtpy import QtGui, QtCore
from ayon_core.pipeline import discover_legacy_creator_plugins
from . constants import (
FAMILY_ROLE,
PRODUCT_TYPE_ROLE,
ITEM_ID_ROLE
)
@ -28,15 +28,15 @@ class CreatorsModel(QtGui.QStandardItemModel):
item_id = str(uuid.uuid4())
self._creators_by_id[item_id] = creator
label = creator.label or creator.family
label = creator.label or creator.product_type
item = QtGui.QStandardItem(label)
item.setEditable(False)
item.setData(item_id, ITEM_ID_ROLE)
item.setData(creator.family, FAMILY_ROLE)
item.setData(creator.product_type, PRODUCT_TYPE_ROLE)
items.append(item)
if not items:
item = QtGui.QStandardItem("No registered families")
item = QtGui.QStandardItem("No registered create plugins")
item.setEnabled(False)
item.setData(False, QtCore.Qt.ItemIsEnabled)
items.append(item)
@ -47,15 +47,15 @@ class CreatorsModel(QtGui.QStandardItemModel):
def get_creator_by_id(self, item_id):
return self._creators_by_id.get(item_id)
def get_indexes_by_family(self, family):
def get_indexes_by_product_type(self, product_type):
indexes = []
for row in range(self.rowCount()):
index = self.index(row, 0)
item_id = index.data(ITEM_ID_ROLE)
creator_plugin = self._creators_by_id.get(item_id)
if creator_plugin and (
creator_plugin.label.lower() == family.lower()
or creator_plugin.family.lower() == family.lower()
creator_plugin.label.lower() == product_type.lower()
or creator_plugin.product_type.lower() == product_type.lower()
):
indexes.append(index)
return indexes

View file

@ -5,7 +5,7 @@ from qtpy import QtWidgets, QtCore, QtGui
import qtawesome
from ayon_core.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS
from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS
from ayon_core.tools.utils import ErrorMessageBox
if hasattr(QtGui, "QRegularExpressionValidator"):
@ -19,16 +19,16 @@ else:
class CreateErrorMessageBox(ErrorMessageBox):
def __init__(
self,
family,
subset_name,
asset_name,
product_type,
product_name,
folder_path,
exc_msg,
formatted_traceback,
parent
):
self._family = family
self._subset_name = subset_name
self._asset_name = asset_name
self._product_type = product_type
self._product_name = product_name
self._folder_path = folder_path
self._exc_msg = exc_msg
self._formatted_traceback = formatted_traceback
super(CreateErrorMessageBox, self).__init__("Creation failed", parent)
@ -42,14 +42,14 @@ class CreateErrorMessageBox(ErrorMessageBox):
def _get_report_data(self):
report_message = (
"Failed to create Product: \"{subset}\""
" Type: \"{family}\""
" in Asset: \"{asset}\""
"Failed to create Product: \"{product_name}\""
" Type: \"{product_type}\""
" in Folder: \"{folder_path}\""
"\n\nError: {message}"
).format(
subset=self._subset_name,
family=self._family,
asset=self._asset_name,
product_name=self._product_name,
product_type=self._product_type,
folder_path=self._folder_path,
message=self._exc_msg
)
if self._formatted_traceback:
@ -74,7 +74,7 @@ class CreateErrorMessageBox(ErrorMessageBox):
item_name_widget = QtWidgets.QLabel(self)
item_name_widget.setText(
item_name_template.format(
self._family, self._subset_name, self._asset_name
self._product_type, self._product_name, self._folder_path
)
)
content_layout.addWidget(item_name_widget)
@ -94,16 +94,16 @@ class CreateErrorMessageBox(ErrorMessageBox):
content_layout.addWidget(tb_widget)
class SubsetNameValidator(RegularExpressionValidatorClass):
class ProductNameValidator(RegularExpressionValidatorClass):
invalid = QtCore.Signal(set)
pattern = "^[{}]*$".format(SUBSET_NAME_ALLOWED_SYMBOLS)
pattern = "^[{}]*$".format(PRODUCT_NAME_ALLOWED_SYMBOLS)
def __init__(self):
reg = RegularExpressionClass(self.pattern)
super(SubsetNameValidator, self).__init__(reg)
super(ProductNameValidator, self).__init__(reg)
def validate(self, text, pos):
results = super(SubsetNameValidator, self).validate(text, pos)
results = super(ProductNameValidator, self).validate(text, pos)
if results[0] == self.Invalid:
self.invalid.emit(self.invalid_chars(text))
return results
@ -131,7 +131,7 @@ class VariantLineEdit(QtWidgets.QLineEdit):
def __init__(self, *args, **kwargs):
super(VariantLineEdit, self).__init__(*args, **kwargs)
validator = SubsetNameValidator()
validator = ProductNameValidator()
self.setValidator(validator)
self.setToolTip("Only alphanumeric characters (A-Z a-z 0-9), "
"'_' and '.' are allowed.")
@ -183,24 +183,24 @@ class VariantLineEdit(QtWidgets.QLineEdit):
)
class FamilyDescriptionWidget(QtWidgets.QWidget):
"""A family description widget.
class ProductTypeDescriptionWidget(QtWidgets.QWidget):
"""A product type description widget.
Shows a family icon, family name and a help description.
Shows a product type icon, name and a help description.
Used in creator header.
_________________
| ____ |
| |icon| FAMILY |
| |____| help |
|_________________|
_______________________
| ____ |
| |icon| PRODUCT TYPE |
| |____| help |
|_______________________|
"""
SIZE = 35
def __init__(self, parent=None):
super(FamilyDescriptionWidget, self).__init__(parent=parent)
super(ProductTypeDescriptionWidget, self).__init__(parent=parent)
icon_label = QtWidgets.QLabel(self)
icon_label.setSizePolicy(
@ -215,14 +215,14 @@ class FamilyDescriptionWidget(QtWidgets.QWidget):
label_layout = QtWidgets.QVBoxLayout()
label_layout.setSpacing(0)
family_label = QtWidgets.QLabel(self)
family_label.setObjectName("CreatorFamilyLabel")
family_label.setAlignment(QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft)
product_type_label = QtWidgets.QLabel(self)
product_type_label.setObjectName("CreatorProductTypeLabel")
product_type_label.setAlignment(QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft)
help_label = QtWidgets.QLabel(self)
help_label.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
label_layout.addWidget(family_label)
label_layout.addWidget(product_type_label)
label_layout.addWidget(help_label)
layout = QtWidgets.QHBoxLayout(self)
@ -232,14 +232,15 @@ class FamilyDescriptionWidget(QtWidgets.QWidget):
layout.addLayout(label_layout)
self._help_label = help_label
self._family_label = family_label
self._product_type_label = product_type_label
self._icon_label = icon_label
def set_item(self, creator_plugin):
"""Update elements to display information of a family item.
"""Update elements to display information of a product type item.
Args:
item (dict): A family item as registered with name, help and icon
creator_plugin (dict): A product type item as registered with
name, help and icon.
Returns:
None
@ -247,7 +248,7 @@ class FamilyDescriptionWidget(QtWidgets.QWidget):
"""
if not creator_plugin:
self._icon_label.setPixmap(None)
self._family_label.setText("")
self._product_type_label.setText("")
self._help_label.setText("")
return
@ -268,5 +269,5 @@ class FamilyDescriptionWidget(QtWidgets.QWidget):
creator_help = docstring.splitlines()[0] if docstring else ""
self._icon_label.setPixmap(pixmap)
self._family_label.setText(creator_plugin.family)
self._product_type_label.setText(creator_plugin.product_type)
self._help_label.setText(creator_help)

View file

@ -14,7 +14,7 @@ from ayon_core.pipeline import (
get_current_task_name,
)
from ayon_core.pipeline.create import (
SUBSET_NAME_ALLOWED_SYMBOLS,
PRODUCT_NAME_ALLOWED_SYMBOLS,
legacy_create,
CreatorError,
)
@ -23,7 +23,7 @@ from .model import CreatorsModel
from .widgets import (
CreateErrorMessageBox,
VariantLineEdit,
FamilyDescriptionWidget
ProductTypeDescriptionWidget
)
from .constants import (
ITEM_ID_ROLE,
@ -45,7 +45,7 @@ class CreatorWindow(QtWidgets.QDialog):
self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint
)
creator_info = FamilyDescriptionWidget(self)
creator_info = ProductTypeDescriptionWidget(self)
creators_model = CreatorsModel()
@ -56,19 +56,19 @@ class CreatorWindow(QtWidgets.QDialog):
creators_view.setObjectName("CreatorsView")
creators_view.setModel(creators_proxy)
asset_name_input = QtWidgets.QLineEdit(self)
folder_path_input = QtWidgets.QLineEdit(self)
variant_input = VariantLineEdit(self)
subset_name_input = QtWidgets.QLineEdit(self)
subset_name_input.setEnabled(False)
product_name_input = QtWidgets.QLineEdit(self)
product_name_input.setEnabled(False)
subset_button = QtWidgets.QPushButton()
subset_button.setFixedWidth(18)
subset_menu = QtWidgets.QMenu(subset_button)
subset_button.setMenu(subset_menu)
variants_btn = QtWidgets.QPushButton()
variants_btn.setFixedWidth(18)
variants_menu = QtWidgets.QMenu(variants_btn)
variants_btn.setMenu(variants_menu)
name_layout = QtWidgets.QHBoxLayout()
name_layout.addWidget(variant_input)
name_layout.addWidget(subset_button)
name_layout.addWidget(variants_btn)
name_layout.setSpacing(3)
name_layout.setContentsMargins(0, 0, 0, 0)
@ -76,13 +76,13 @@ class CreatorWindow(QtWidgets.QDialog):
body_layout.setContentsMargins(0, 0, 0, 0)
body_layout.addWidget(creator_info, 0)
body_layout.addWidget(QtWidgets.QLabel("Family", self), 0)
body_layout.addWidget(QtWidgets.QLabel("Product type", self), 0)
body_layout.addWidget(creators_view, 1)
body_layout.addWidget(QtWidgets.QLabel("Asset", self), 0)
body_layout.addWidget(asset_name_input, 0)
body_layout.addWidget(QtWidgets.QLabel("Subset", self), 0)
body_layout.addWidget(QtWidgets.QLabel("Folder path", self), 0)
body_layout.addWidget(folder_path_input, 0)
body_layout.addWidget(QtWidgets.QLabel("Product name", self), 0)
body_layout.addLayout(name_layout, 0)
body_layout.addWidget(subset_name_input, 0)
body_layout.addWidget(product_name_input, 0)
useselection_chk = QtWidgets.QCheckBox("Use selection", self)
useselection_chk.setCheckState(QtCore.Qt.Checked)
@ -116,7 +116,7 @@ class CreatorWindow(QtWidgets.QDialog):
variant_input.returnPressed.connect(self._on_create)
variant_input.textChanged.connect(self._on_data_changed)
variant_input.report.connect(self.echo)
asset_name_input.textChanged.connect(self._on_data_changed)
folder_path_input.textChanged.connect(self._on_data_changed)
creators_view.selectionModel().currentChanged.connect(
self._on_selection_changed
)
@ -134,15 +134,15 @@ class CreatorWindow(QtWidgets.QDialog):
self._create_btn = create_btn
self._useselection_chk = useselection_chk
self._variant_input = variant_input
self._subset_name_input = subset_name_input
self._asset_name_input = asset_name_input
self._product_name_input = product_name_input
self._folder_path_input = folder_path_input
self._creators_model = creators_model
self._creators_proxy = creators_proxy
self._creators_view = creators_view
self._subset_btn = subset_button
self._subset_menu = subset_menu
self._variants_btn = variants_btn
self._variants_menu = variants_menu
self._msg_label = msg_label
@ -160,7 +160,7 @@ class CreatorWindow(QtWidgets.QDialog):
self._create_btn.setEnabled(valid)
def _build_menu(self, default_names=None):
"""Create optional predefined subset names
"""Create optional predefined variants.
Args:
default_names(list): all predefined names
@ -171,8 +171,8 @@ class CreatorWindow(QtWidgets.QDialog):
if not default_names:
default_names = []
menu = self._subset_menu
button = self._subset_btn
menu = self._variants_menu
button = self._variants_btn
# Get and destroy the action group
group = button.findChild(QtWidgets.QActionGroup)
@ -211,10 +211,10 @@ class CreatorWindow(QtWidgets.QDialog):
item_id = index.data(ITEM_ID_ROLE)
creator_plugin = self._creators_model.get_creator_by_id(item_id)
user_input_text = self._variant_input.text()
asset_name = self._asset_name_input.text()
folder_path = self._folder_path_input.text()
# Early exit if no asset name
if not asset_name:
# Early exit if no folder path
if not folder_path:
self._build_menu()
self.echo("Asset name is required ..")
self._set_valid_state(False)
@ -225,64 +225,63 @@ class CreatorWindow(QtWidgets.QDialog):
if creator_plugin:
# Get the asset from the database which match with the name
asset_doc = get_asset_by_name(
project_name, asset_name, fields=["_id"]
project_name, folder_path, fields=["_id"]
)
# Get plugin
if not asset_doc or not creator_plugin:
subset_name = user_input_text
self._build_menu()
if not creator_plugin:
self.echo("No registered families ..")
self.echo("No registered product types ..")
else:
self.echo("Asset '%s' not found .." % asset_name)
self.echo("Folder '{}' not found ..".format(folder_path))
self._set_valid_state(False)
return
asset_id = asset_doc["_id"]
folder_id = asset_doc["_id"]
task_name = get_current_task_name()
# Calculate subset name with Creator plugin
subset_name = creator_plugin.get_subset_name(
user_input_text, task_name, asset_id, project_name
# Calculate product name with Creator plugin
product_name = creator_plugin.get_product_name(
user_input_text, task_name, folder_id, project_name
)
# Force replacement of prohibited symbols
# QUESTION should Creator care about this and here should be only
# validated with schema regex?
# Allow curly brackets in subset name for dynamic keys
# Allow curly brackets in product name for dynamic keys
curly_left = "__cbl__"
curly_right = "__cbr__"
tmp_subset_name = (
subset_name
tmp_product_name = (
product_name
.replace("{", curly_left)
.replace("}", curly_right)
)
# Replace prohibited symbols
tmp_subset_name = re.sub(
"[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS),
tmp_product_name = re.sub(
"[^{}]+".format(PRODUCT_NAME_ALLOWED_SYMBOLS),
"",
tmp_subset_name
tmp_product_name
)
subset_name = (
tmp_subset_name
product_name = (
tmp_product_name
.replace(curly_left, "{")
.replace(curly_right, "}")
)
self._subset_name_input.setText(subset_name)
self._product_name_input.setText(product_name)
# Get all subsets of the current asset
# Get all products of the current folder
subset_docs = get_subsets(
project_name, asset_ids=[asset_id], fields=["name"]
project_name, asset_ids=[folder_id], fields=["name"]
)
existing_subset_names = {
existing_product_names = {
subset_doc["name"]
for subset_doc in subset_docs
}
existing_subset_names_low = set(
existing_product_names_low = set(
_name.lower()
for _name in existing_subset_names
for _name in existing_product_names
)
# Defaults to dropdown
@ -296,26 +295,26 @@ class CreatorWindow(QtWidgets.QDialog):
# Replace
compare_regex = re.compile(re.sub(
user_input_text, "(.+)", subset_name, flags=re.IGNORECASE
user_input_text, "(.+)", product_name, flags=re.IGNORECASE
))
subset_hints = set()
variant_hints = set()
if user_input_text:
for _name in existing_subset_names:
for _name in existing_product_names:
_result = compare_regex.search(_name)
if _result:
subset_hints |= set(_result.groups())
variant_hints |= set(_result.groups())
if subset_hints:
if variant_hints:
if defaults:
defaults.append(SEPARATOR)
defaults.extend(subset_hints)
defaults.extend(variant_hints)
self._build_menu(defaults)
# Indicate subset existence
# Indicate product existence
if not user_input_text:
self._variant_input.as_empty()
elif subset_name.lower() in existing_subset_names_low:
# validate existence of subset name with lowered text
elif product_name.lower() in existing_product_names_low:
# validate existence of product name with lowered text
# - "renderMain" vs. "rensermain" mean same path item for
# windows
self._variant_input.as_exists()
@ -323,7 +322,7 @@ class CreatorWindow(QtWidgets.QDialog):
self._variant_input.as_new()
# Update the valid state
valid = subset_name.strip() != ""
valid = product_name.strip() != ""
self._set_valid_state(valid)
@ -373,7 +372,7 @@ class CreatorWindow(QtWidgets.QDialog):
self.setStyleSheet(style.load_stylesheet())
def refresh(self):
self._asset_name_input.setText(get_current_asset_name())
self._folder_path_input.setText(get_current_asset_name())
self._creators_model.reset()
@ -385,7 +384,7 @@ class CreatorWindow(QtWidgets.QDialog):
["product_types_smart_select"]
)
current_index = None
family = None
product_type = None
task_name = get_current_task_name() or None
lowered_task_name = task_name.lower()
if task_name:
@ -395,13 +394,15 @@ class CreatorWindow(QtWidgets.QDialog):
}
for _task_name in _low_task_names:
if _task_name in lowered_task_name:
family = smart_item["name"]
product_type = smart_item["name"]
break
if family:
if product_type:
break
if family:
indexes = self._creators_model.get_indexes_by_family(family)
if product_type:
indexes = self._creators_model.get_indexes_by_product_type(
product_type
)
if indexes:
index = indexes[0]
current_index = self._creators_proxy.mapFromSource(index)
@ -422,8 +423,8 @@ class CreatorWindow(QtWidgets.QDialog):
if creator_plugin is None:
return
subset_name = self._subset_name_input.text()
asset_name = self._asset_name_input.text()
product_name = self._product_name_input.text()
folder_path = self._folder_path_input.text()
use_selection = self._useselection_chk.isChecked()
variant = self._variant_input.text()
@ -432,8 +433,8 @@ class CreatorWindow(QtWidgets.QDialog):
try:
legacy_create(
creator_plugin,
subset_name,
asset_name,
product_name,
folder_path,
options={"useSelection": use_selection},
data={"variant": variant}
)
@ -453,9 +454,9 @@ class CreatorWindow(QtWidgets.QDialog):
if error_info:
box = CreateErrorMessageBox(
creator_plugin.family,
subset_name,
asset_name,
creator_plugin.product_type,
product_name,
folder_path,
*error_info,
parent=self
)
@ -464,7 +465,7 @@ class CreatorWindow(QtWidgets.QDialog):
self._message_dialog = box
else:
self.echo("Created %s .." % subset_name)
self.echo("Created %s .." % product_name)
def _on_msg_timer(self):
self._msg_label.setText("")
@ -475,7 +476,7 @@ class CreatorWindow(QtWidgets.QDialog):
def show(parent=None):
"""Display asset creator GUI
"""Display product creator GUI
Arguments:
debug (bool, optional): Run loader in debug-mode,