From f957b9bf41dffe306c5ef4df0a6fdc1904887652 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 8 Mar 2024 22:13:41 +0800 Subject: [PATCH 1/7] allow users to tweak material duplicate options in loader setting --- .../hosts/max/plugins/load/load_max_scene.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 39bb3b568d..915abc75a1 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -1,5 +1,6 @@ import os +from ayon_core.lib import EnumDef from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( unique_namespace, @@ -25,14 +26,28 @@ class MaxSceneLoader(load.LoaderPlugin): order = -8 icon = "code-fork" color = "green" + mtl_dup_default = "promptMtlDups" + mtl_dup_enum = ["promptMtlDups", "useMergedMtlDups", + "useSceneMtlDups", "renameMtlDups"] - def load(self, context, name=None, namespace=None, data=None): + @classmethod + def get_options(cls, contexts): + return [ + EnumDef("mtldup", + cls.mtl_dup_enum, + default=cls.mtl_dup_default, + label="Material Duplicate Options") + ] + + def load(self, context, name=None, namespace=None, options=None): from pymxs import runtime as rt + mat_dup_options = options.get("mtldup", self.mtl_dup_default) path = self.filepath_from_context(context) path = os.path.normpath(path) # import the max scene by using "merge file" path = path.replace('\\', '/') - rt.MergeMaxFile(path, quiet=True, includeFullGroup=True) + rt.MergeMaxFile(path, rt.Name(mat_dup_options), + quiet=True, includeFullGroup=True) max_objects = rt.getLastMergedNodes() max_object_names = [obj.name for obj in max_objects] # implement the OP/AYON custom attributes before load From 4092d177f56a48e6cb5f79e690a341f83d16ca53 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 8 Mar 2024 23:21:15 +0800 Subject: [PATCH 2/7] QtWidget for setting the material options when updating --- .../hosts/max/plugins/load/load_max_scene.py | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 915abc75a1..9daf9bb72d 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -1,5 +1,5 @@ import os - +from qtpy import QtWidgets, QtCore from ayon_core.lib import EnumDef from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( @@ -13,6 +13,47 @@ remove_container_data ) from ayon_core.pipeline import get_representation_path, load +from ayon_core.settings import get_project_settings + + +class MaterialDupOptionsWindow(QtWidgets.QDialog): + + def __init__(self, material_options): + super(MaterialDupOptionsWindow, self).__init__() + self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) + + self.material_option = None + + self.widgets = { + "label": QtWidgets.QLabel( + "Select material duplicate options before loading the max scene."), + "material_options_list": QtWidgets.QListWidget(), + "warning": QtWidgets.QLabel("No material options selected!"), + "okButton": QtWidgets.QPushButton("Ok"), + } + for option in material_options: + self.widgets["material_options_list"].addItem(option) + # Build buttons. + layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) + layout.addWidget(self.widgets["okButton"]) + # Build layout. + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(self.widgets["label"]) + layout.addWidget(self.widgets["list"]) + layout.addWidget(self.widgets["buttons"]) + + self.widgets["okButton"].pressed.connect(self.on_ok_pressed) + self.widgets["material_options_list"].itemPressed.connect( + self.on_material_optionsPressed) + + def on_material_optionsPressed(self, item): + self.material_option = item.text() + + def on_ok_pressed(self): + if self.material_option is None: + self.widgets["warning"].setVisible(True) + return + self.close() class MaxSceneLoader(load.LoaderPlugin): @@ -82,7 +123,9 @@ def update(self, container, context): for prev_max_obj in prev_max_objects: if rt.isValidNode(prev_max_obj): # noqa rt.Delete(prev_max_obj) - rt.MergeMaxFile(path, quiet=True) + window = MaterialDupOptionsWindow(self.mtl_dup_enum) + window.exec_() + rt.MergeMaxFile(path, rt.Name(window.material_option), quiet=True) current_max_objects = rt.getLastMergedNodes() From 142206c71b75303143a969c5fd58e5d8f9718fa3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Sat, 9 Mar 2024 00:01:37 +0800 Subject: [PATCH 3/7] make sure Pop up window works --- .../ayon_core/hosts/max/plugins/load/load_max_scene.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 9daf9bb72d..f30d214cbe 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -13,7 +13,6 @@ remove_container_data ) from ayon_core.pipeline import get_representation_path, load -from ayon_core.settings import get_project_settings class MaterialDupOptionsWindow(QtWidgets.QDialog): @@ -29,20 +28,24 @@ def __init__(self, material_options): "Select material duplicate options before loading the max scene."), "material_options_list": QtWidgets.QListWidget(), "warning": QtWidgets.QLabel("No material options selected!"), + "buttons": QtWidgets.QWidget(), "okButton": QtWidgets.QPushButton("Ok"), + "cancelButton": QtWidgets.QPushButton("Cancel") } for option in material_options: self.widgets["material_options_list"].addItem(option) # Build buttons. layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) layout.addWidget(self.widgets["okButton"]) + layout.addWidget(self.widgets["cancelButton"]) # Build layout. layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.widgets["label"]) - layout.addWidget(self.widgets["list"]) + layout.addWidget(self.widgets["material_options_list"]) layout.addWidget(self.widgets["buttons"]) self.widgets["okButton"].pressed.connect(self.on_ok_pressed) + self.widgets["cancelButton"].pressed.connect(self.on_cancel_pressed) self.widgets["material_options_list"].itemPressed.connect( self.on_material_optionsPressed) @@ -55,6 +58,9 @@ def on_ok_pressed(self): return self.close() + def on_cancel_pressed(self): + self.material_option = "promptMtlDups" + self.close() class MaxSceneLoader(load.LoaderPlugin): """Max Scene Loader.""" From b3048277e2d34054c1420c435c0800d8276d69ee Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Sat, 9 Mar 2024 22:18:52 +0800 Subject: [PATCH 4/7] make sure the code is also running when it is at headless mode --- .../hosts/max/plugins/load/load_max_scene.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index f30d214cbe..3e8a918424 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -5,7 +5,8 @@ from ayon_core.hosts.max.api.lib import ( unique_namespace, get_namespace, - object_transform_set + object_transform_set, + is_headless ) from ayon_core.hosts.max.api.pipeline import ( containerise, get_previous_loaded_object, @@ -129,9 +130,12 @@ def update(self, container, context): for prev_max_obj in prev_max_objects: if rt.isValidNode(prev_max_obj): # noqa rt.Delete(prev_max_obj) - window = MaterialDupOptionsWindow(self.mtl_dup_enum) - window.exec_() - rt.MergeMaxFile(path, rt.Name(window.material_option), quiet=True) + material_option = self.mtl_dup_default + if not is_headless(): + window = MaterialDupOptionsWindow(self.mtl_dup_enum) + window.exec_() + material_option = window.material_option + rt.MergeMaxFile(path, rt.Name(material_option), quiet=True) current_max_objects = rt.getLastMergedNodes() From 5137eabaafc480b3fafe52a3ec0cdc9a690896c8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 12 Mar 2024 22:54:57 +0800 Subject: [PATCH 5/7] update the options to be more artist-friendly --- .../hosts/max/plugins/load/load_max_scene.py | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 3e8a918424..beb465a6ef 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -1,6 +1,6 @@ import os from qtpy import QtWidgets, QtCore -from ayon_core.lib import EnumDef +from ayon_core.lib.attribute_definitions import EnumDef from ayon_core.hosts.max.api import lib from ayon_core.hosts.max.api.lib import ( unique_namespace, @@ -23,6 +23,7 @@ def __init__(self, material_options): self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) self.material_option = None + self.material_options = material_options self.widgets = { "label": QtWidgets.QLabel( @@ -33,7 +34,7 @@ def __init__(self, material_options): "okButton": QtWidgets.QPushButton("Ok"), "cancelButton": QtWidgets.QPushButton("Cancel") } - for option in material_options: + for option in material_options.values(): self.widgets["material_options_list"].addItem(option) # Build buttons. layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) @@ -51,7 +52,9 @@ def __init__(self, material_options): self.on_material_optionsPressed) def on_material_optionsPressed(self, item): - self.material_option = item.text() + self.material_option = next((key for key in self.material_options.keys() + if self.material_options[key]== item.text()), + None) def on_ok_pressed(self): if self.material_option is None: @@ -75,14 +78,17 @@ class MaxSceneLoader(load.LoaderPlugin): icon = "code-fork" color = "green" mtl_dup_default = "promptMtlDups" - mtl_dup_enum = ["promptMtlDups", "useMergedMtlDups", - "useSceneMtlDups", "renameMtlDups"] - + mtl_dup_enum_dict = { + "promptMtlDups": "Prompt Material", + "useMergedMtlDups": "Use Incoming Material", + "useSceneMtlDups": "Use Scene Material", + "renameMtlDups": "Merge and Rename Incoming Material" + } @classmethod def get_options(cls, contexts): return [ EnumDef("mtldup", - cls.mtl_dup_enum, + items=cls.mtl_dup_enum_dict, default=cls.mtl_dup_default, label="Material Duplicate Options") ] @@ -132,7 +138,7 @@ def update(self, container, context): rt.Delete(prev_max_obj) material_option = self.mtl_dup_default if not is_headless(): - window = MaterialDupOptionsWindow(self.mtl_dup_enum) + window = MaterialDupOptionsWindow(self.mtl_dup_enum_dict) window.exec_() material_option = window.material_option rt.MergeMaxFile(path, rt.Name(material_option), quiet=True) From 68a95078a87b9d0d3c2dcd139b78f1b38bd28aa1 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 12 Mar 2024 23:10:07 +0800 Subject: [PATCH 6/7] rename prompt materials as Prompt on Duplicate Materials --- client/ayon_core/hosts/max/plugins/load/load_max_scene.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index beb465a6ef..5c55455d00 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -79,7 +79,7 @@ class MaxSceneLoader(load.LoaderPlugin): color = "green" mtl_dup_default = "promptMtlDups" mtl_dup_enum_dict = { - "promptMtlDups": "Prompt Material", + "promptMtlDups": "Prompt on Duplicate Materials", "useMergedMtlDups": "Use Incoming Material", "useSceneMtlDups": "Use Scene Material", "renameMtlDups": "Merge and Rename Incoming Material" From 4c9280b3a7161f9d26fc0725db6f8cb51f93640e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 13 Mar 2024 14:08:55 +0800 Subject: [PATCH 7/7] add docstring to describe the material pop-up dialog & some code tweaks --- .../hosts/max/plugins/load/load_max_scene.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py index 5c55455d00..9707297c2f 100644 --- a/client/ayon_core/hosts/max/plugins/load/load_max_scene.py +++ b/client/ayon_core/hosts/max/plugins/load/load_max_scene.py @@ -17,7 +17,10 @@ class MaterialDupOptionsWindow(QtWidgets.QDialog): - + """The pop-up dialog allows users to choose material + duplicate options for importing Max objects when updating + or switching assets. + """ def __init__(self, material_options): super(MaterialDupOptionsWindow, self).__init__() self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) @@ -34,8 +37,10 @@ def __init__(self, material_options): "okButton": QtWidgets.QPushButton("Ok"), "cancelButton": QtWidgets.QPushButton("Cancel") } - for option in material_options.values(): - self.widgets["material_options_list"].addItem(option) + for key, value in material_options.items(): + item = QtWidgets.QListWidgetItem(value) + self.widgets["material_options_list"].addItem(item) + item.setData(QtCore.Qt.UserRole, key) # Build buttons. layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) layout.addWidget(self.widgets["okButton"]) @@ -49,12 +54,10 @@ def __init__(self, material_options): self.widgets["okButton"].pressed.connect(self.on_ok_pressed) self.widgets["cancelButton"].pressed.connect(self.on_cancel_pressed) self.widgets["material_options_list"].itemPressed.connect( - self.on_material_optionsPressed) + self.on_material_options_pressed) - def on_material_optionsPressed(self, item): - self.material_option = next((key for key in self.material_options.keys() - if self.material_options[key]== item.text()), - None) + def on_material_options_pressed(self, item): + self.material_option = item.data(QtCore.Qt.UserRole) def on_ok_pressed(self): if self.material_option is None: