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

asset HDA LOP: Implement and use select product dialog #39

Merged
merged 7 commits into from
Jul 30, 2024
150 changes: 121 additions & 29 deletions client/ayon_houdini/api/hda_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

from ayon_houdini.api import lib

from qtpy import QtCore, QtWidgets
from qtpy import QtCore, QtWidgets, QtGui
import hou


Expand Down Expand Up @@ -129,14 +129,6 @@ def update_info(node, context):
if node.evalParm(key) != value}
parms["load_message"] = "" # clear any warnings/errors

# Update the product type filter to match the type
current = node.evalParm("product_type")
product_type = context["product"]["productType"]
if current and current != product_type:
# If current is empty we consider no filtering applied and we allow
# that to be a state that needs no switching
parms["product_type"] = product_type

# Note that these never trigger any parm callbacks since we do not
# trigger the `parm.pressButton` and programmatically setting values
# in Houdini does not trigger callbacks automatically
Expand Down Expand Up @@ -555,38 +547,138 @@ def _select_folder_path():
folder_parm.pressButton() # allow any callbacks to trigger


def get_available_products(node):
"""Return products menu items
It gets a list of available products of the specified product types
within the specified folder path with in the specified project.
Users can specify those in the HDA parameters.
class SelectProductDialog(QtWidgets.QDialog):
"""Simple dialog to allow a user to select a product."""

Args:
node (hou.OpNode): The HDA node.
def __init__(self, project_name, folder_id, parent=None):
super(SelectProductDialog, self).__init__(parent)
self.setWindowTitle("Select a Product")
self.setStyleSheet(load_stylesheet())

self.project_name = project_name
self.folder_id = folder_id

# Create widgets and layout
product_types_widget = QtWidgets.QComboBox()
products_widget = QtWidgets.QListWidget()
accept_button = QtWidgets.QPushButton("Set product name")

main_layout = QtWidgets.QVBoxLayout(self)
main_layout.setContentsMargins(0, 0, 0, 0)
main_layout.addWidget(product_types_widget, 0)
main_layout.addWidget(products_widget, 1)
main_layout.addWidget(accept_button, 0)

self.product_types_widget = product_types_widget
self.products_widget = products_widget

# Connect Signals
product_types_widget.currentTextChanged.connect(self.on_product_type_changed)
products_widget.itemDoubleClicked.connect(self.accept)
accept_button.clicked.connect(self.accept)

# Initialize widgets contents
product_types_widget.addItems(self.get_product_types())
product_type = self.get_selected_product_type()
self.set_product_type(product_type)

def get_selected_product(self) -> str:
if self.products_widget.currentItem():
return self.products_widget.currentItem().text()
return ""

def get_selected_product_type(self) -> str:
return self.product_types_widget.currentText()

def get_product_types(self) -> List[str]:
"""return default product types.
"""

return [
"*",
"animation",
"camera",
"model",
"pointcache",
"usd",
]

def on_product_type_changed(self, product_type: str):
self.set_product_type(product_type)

def set_product_type(self, product_type: str):
self.product_types_widget.setCurrentText(product_type)

if self.product_types_widget.currentText() != product_type:
# Product type does not exist
return

# Populate products list
products = self.get_available_products(product_type)
self.products_widget.clear()
if products:
self.products_widget.addItems(products)

def set_selected_product_name(self, product_name: str):
matching_items = self.products_widget.findItems(
product_name, QtCore.Qt.MatchFixedString)
if matching_items:
self.products_widget.setCurrentItem(matching_items[0])

def get_available_products(self, product_type):

if product_type == "*":
product_type = ""

product_types = [product_type] if product_type else None

products = ayon_api.get_products(
self.project_name,
folder_ids=[self.folder_id],
product_types=product_types
)

return list(sorted(product["name"] for product in products))


def select_product_name(node):
"""Show a modal pop-up dialog to allow user to select a product name
under the current folder entity as defined on the node's parameters.

Applies the chosen value to the `product_name` parm on the node."""

cursor_pos = QtGui.QCursor.pos()

Returns:
list[str]: Product names for Products menu.
"""
project_name = node.evalParm("project_name")
folder_path = node.evalParm("folder_path")
product_type = node.evalParm("product_type")
product_parm = node.parm("product_name")

folder_entity = ayon_api.get_folder_by_path(project_name,
folder_path,
fields={"id"})
if not folder_entity:
return []

# Apply filter only if any value is set
product_types = [product_type] if product_type else None

products = ayon_api.get_products(
return

dialog = SelectProductDialog(
project_name,
folder_ids=[folder_entity["id"]],
product_types=product_types
folder_entity["id"],
parent=lib.get_main_window()
)
dialog.set_selected_product_name(product_parm.eval())

dialog.resize(300, 600)
dialog.setWindowFlags(QtCore.Qt.Popup)
pos = dialog.mapToGlobal(cursor_pos - QtCore.QPoint(300, 0))
dialog.move(pos)
result = dialog.exec_()

if result != QtWidgets.QDialog.Accepted:
return
selected_product = dialog.get_selected_product()

return list(sorted(product["name"] for product in products))
if selected_product:
product_parm.set(selected_product)
product_parm.pressButton() # allow any callbacks to trigger


def set_to_latest_version(node):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,36 +47,13 @@
parmtag { "script_callback" "hou.phm().on_representation_parms_changed(kwargs['node'])" }
parmtag { "script_callback_language" "python" }
}
parm {
name "product_type"
label "Product Type"
type string
default { "usd" }
menu {
"" "*"
"animation" "animation"
"camera" "camera"
"model" "model"
"pointcache" "pointcache"
"usd" "usd"
}
}
parm {
name "product_name"
label "Product"
type string
default { "usdAsset" }
menureplace {
[ "products = hou.phm().get_available_products(kwargs['node'])" ]
[ "" ]
[ "result = []" ]
[ "for product in products:" ]
[ " result.append(product)" ]
[ " result.append(product)" ]
[ " " ]
[ "return result" ]
language python
}
parmtag { "script_action" "from ayon_houdini.api.hda_utils import select_product_name;select_product_name(kwargs['node'])" }
parmtag { "script_action_icon" "BUTTONS_reselect" }
parmtag { "script_callback" "hou.phm().set_to_latest_version(kwargs['node'])\nhou.phm().on_representation_parms_changed(kwargs['node'])" }
parmtag { "script_callback_language" "python" }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ from ayon_houdini.api.hda_utils import (
on_representation_parms_changed,
setup_flag_changed_callback,
get_available_versions,
get_available_products,
select_product_name,
set_to_latest_version
)