Files
Nexus/2023/scripts/animation_tools/studiolibrary/studiolibrarymaya/baseitem.py
2025-11-24 00:15:32 +08:00

446 lines
12 KiB
Python

# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import os
import shutil
import logging
from studiovendor.Qt import QtGui
from studiovendor.Qt import QtCore
import studiolibrary
from studiolibrarymaya import basesavewidget
from studiolibrarymaya import baseloadwidget
try:
import mutils
import mutils.gui
import maya.cmds
except ImportError as error:
print(error)
logger = logging.getLogger(__name__)
class BaseItemSignals(QtCore.QObject):
""""""
loadValueChanged = QtCore.Signal(object, object)
class BaseItem(studiolibrary.LibraryItem):
_baseItemSignals = BaseItemSignals()
loadValueChanged = _baseItemSignals.loadValueChanged
"""Base class for anim, pose, mirror and sets transfer items."""
SAVE_WIDGET_CLASS = basesavewidget.BaseSaveWidget
LOAD_WIDGET_CLASS = baseloadwidget.BaseLoadWidget
TRANSFER_CLASS = None
TRANSFER_BASENAME = ""
def createLoadWidget(self, parent=None):
widget = self.LOAD_WIDGET_CLASS(item=self, parent=parent)
return widget
@classmethod
def createSaveWidget(cls, parent=None, item=None):
item = item or cls()
widget = cls.SAVE_WIDGET_CLASS(item=item, parent=parent)
return widget
@classmethod
def showSaveWidget(cls, libraryWindow=None, item=None):
"""
Overriding this method to set the destination location
for the save widget.
Triggered when the user clicks the item action in the new item menu.
:type libraryWindow: studiolibrary.LibraryWindow
:type item: studiolibrary.LibraryItem or None
"""
item = item or cls()
widget = cls.SAVE_WIDGET_CLASS(item=item, parent=libraryWindow)
if libraryWindow:
path = libraryWindow.selectedFolderPath()
widget.setFolderPath(path)
widget.setLibraryWindow(libraryWindow)
libraryWindow.setCreateWidget(widget)
libraryWindow.folderSelectionChanged.connect(widget.setFolderPath)
def __init__(self, *args, **kwargs):
"""
Initialise a new instance for the given path.
:type path: str
:type args: list
:type kwargs: dict
"""
self._transferObject = None
self._currentLoadValues = {}
studiolibrary.LibraryItem.__init__(self, *args, **kwargs)
def emitLoadValueChanged(self, field, value):
"""
Emit the load value changed to be validated.
:type field: str
:type value: object
"""
self.loadValueChanged.emit(field, value)
def namespaces(self):
"""
Return the namesapces for this item depending on the namespace option.
:rtype: list[str] or None
"""
return self.currentLoadValue("namespaces")
def namespaceOption(self):
"""
Return the namespace option for this item.
:rtype: NamespaceOption or None
"""
return self.currentLoadValue("namespaceOption")
def doubleClicked(self):
"""
This method is called when the user double clicks the item.
:rtype: None
"""
self.loadFromCurrentValues()
def transferPath(self):
"""
Return the disc location to transfer path.
:rtype: str
"""
if self.TRANSFER_BASENAME:
return os.path.join(self.path(), self.TRANSFER_BASENAME)
else:
return self.path()
def transferObject(self):
"""
Return the transfer object used to read and write the data.
:rtype: mutils.TransferObject
"""
if not self._transferObject:
path = self.transferPath()
self._transferObject = self.TRANSFER_CLASS.fromPath(path)
return self._transferObject
def currentLoadValue(self, name):
"""
Get the current field value for the given name.
:type name: str
:rtype: object
"""
return self._currentLoadValues.get(name)
def setCurrentLoadValues(self, values):
"""
Set the current field values for the the item.
:type values: dict
"""
self._currentLoadValues = values
def loadFromCurrentValues(self):
"""Load the mirror table using the settings for this item."""
kwargs = self._currentLoadValues
objects = maya.cmds.ls(selection=True) or []
try:
self.load(objects=objects, **kwargs)
except Exception as error:
self.showErrorDialog("Item Error", str(error))
raise
def contextMenu(self, menu, items=None):
"""
This method is called when the user right clicks on this item.
:type menu: QtWidgets.QMenu
:type items: list[BaseItem]
:rtype: None
"""
from studiolibrarymaya import setsmenu
action = setsmenu.selectContentAction(self, parent=menu)
menu.addAction(action)
menu.addSeparator()
subMenu = self.createSelectionSetsMenu(menu, enableSelectContent=False)
menu.addMenu(subMenu)
menu.addSeparator()
studiolibrary.LibraryItem.contextMenu(self, menu, items=items)
def showSelectionSetsMenu(self, **kwargs):
"""
Show the selection sets menu for this item at the cursor position.
:rtype: QtWidgets.QAction
"""
menu = self.createSelectionSetsMenu(**kwargs)
position = QtGui.QCursor().pos()
action = menu.exec_(position)
return action
def createSelectionSetsMenu(self, parent=None, enableSelectContent=True):
"""
Get a new instance of the selection sets menu.
:type parent: QtWidgets.QWidget
:type enableSelectContent: bool
:rtype: QtWidgets.QMenu
"""
from . import setsmenu
parent = parent or self.libraryWindow()
namespaces = self.namespaces()
menu = setsmenu.SetsMenu(
item=self,
parent=parent,
namespaces=namespaces,
enableSelectContent=enableSelectContent,
)
return menu
def selectContent(self, namespaces=None, **kwargs):
"""
Select the contents of this item in the Maya scene.
:type namespaces: list[str]
"""
namespaces = namespaces or self.namespaces()
kwargs = kwargs or mutils.selectionModifiers()
msg = "Select content: Item.selectContent(namespacea={0}, kwargs={1})"
msg = msg.format(namespaces, kwargs)
logger.debug(msg)
try:
self.transferObject().select(namespaces=namespaces, **kwargs)
except Exception as error:
self.showErrorDialog("Item Error", str(error))
raise
def loadSchema(self):
"""
Get schema used to load the item.
:rtype: list[dict]
"""
modified = self.itemData().get("modified")
if modified:
modified = studiolibrary.timeAgo(modified)
count = self.transferObject().objectCount()
plural = "s" if count > 1 else ""
contains = str(count) + " Object" + plural
return [
{
"name": "infoGroup",
"title": "Info",
"type": "group",
"order": 1,
},
{
"name": "name",
"value": self.name(),
},
{
"name": "owner",
"value": self.transferObject().owner(),
},
{
"name": "created",
"value": modified,
},
{
"name": "contains",
"value": contains,
},
{
"name": "comment",
"value": self.transferObject().description() or "No comment",
},
{
"name": "namespaceGroup",
"title": "Namespace",
"type": "group",
"order": 10,
},
{
"name": "namespaceOption",
"title": "",
"type": "radio",
"value": "From file",
"items": ["From file", "From selection", "Use custom"],
"persistent": True,
"persistentKey": "BaseItem",
},
{
"name": "namespaces",
"title": "",
"type": "tags",
"value": [],
"items": mutils.namespace.getAll(),
"persistent": True,
"label": {"visible": False},
"persistentKey": "BaseItem",
},
]
def loadValidator(self, **values):
"""
Called when the load fields change.
:type values: dict
"""
namespaces = values.get("namespaces")
namespaceOption = values.get("namespaceOption")
if namespaceOption == "From file":
namespaces = self.transferObject().namespaces()
elif namespaceOption == "From selection":
namespaces = mutils.namespace.getFromSelection()
fieldChanged = values.get("fieldChanged")
if fieldChanged == "namespaces":
values["namespaceOption"] = "Use custom"
else:
values["namespaces"] = namespaces
self._currentLoadValues = values
return [
{
"name": "namespaces",
"value": values.get("namespaces"),
},
{
"name": "namespaceOption",
"value": values.get("namespaceOption"),
},
]
def load(self, **kwargs):
"""
Load the data from the transfer object.
:rtype: None
"""
logger.debug(u'Loading: {0}'.format(self.transferPath()))
self.transferObject().load(**kwargs)
logger.debug(u'Loading: {0}'.format(self.transferPath()))
def saveSchema(self):
"""
The base save schema.
:rtype: list[dict]
"""
return [
{
"name": "folder",
"type": "path",
"layout": "vertical",
"visible": False,
},
{
"name": "name",
"type": "string",
"layout": "vertical",
},
{
"name": "comment",
"type": "text",
"layout": "vertical"
},
{
"name": "objects",
"type": "objects",
"label": {"visible": False}
},
]
def saveValidator(self, **kwargs):
"""
The save validator is called when an input field has changed.
:type kwargs: dict
:rtype: list[dict]
"""
fields = []
if not kwargs.get("folder"):
fields.append({
"name": "folder",
"error": "No folder selected. Please select a destination folder.",
})
if not kwargs.get("name"):
fields.append({
"name": "name",
"error": "No name specified. Please set a name before saving.",
})
selection = maya.cmds.ls(selection=True) or []
msg = ""
if not selection:
msg = "No objects selected. Please select at least one object."
fields.append({
"name": "objects",
"value": selection,
"error": msg,
},
)
return fields
def save(self, thumbnail="", **kwargs):
"""
Save all the given object data to the item path on disc.
:type thumbnail: str
:type kwargs: dict
"""
# Copy the icon path to the given path
if thumbnail:
basename = os.path.basename(thumbnail)
shutil.copyfile(thumbnail, self.path() + "/" + basename)