This commit is contained in:
2025-11-23 23:31:18 +08:00
parent d60cdc52fd
commit 9f7667a475
710 changed files with 252869 additions and 6 deletions

View File

@@ -0,0 +1,6 @@
import MGP.loader
import MGP.loadermanager
def MGP_LoadMGPickerViaPythonAutoLoaders(mayaNamespace):
return MGP.loadermanager.MGPickerLoaderManager.loadPicker(mayaNamespace)

View File

@@ -0,0 +1,106 @@
import os
import inspect
from maya import cmds
class HotkeyTip(object):
def __init__(self, keyList, tip):
self._ctrl = False
self._shift = False
self._alt = False
self._key = ""
self.tip = tip
self.html = True # Use html format for str()
for k in keyList:
k = k.lower()
if k == "ctrl":
self._ctrl = True
elif k == "shift":
self._shift = True
elif k == "alt":
self._alt = True
else:
self._key = k
def isValid(self):
return bool(self._key)
def __str__(self, *args, **kwargs):
if not self.isValid():
return ""
rep = []
prefix = ""
suffix = ""
if self.html:
prefix = "<b>"
suffix = "</b>"
if self._ctrl:
if cmds.about(mac=True):
rep.append(prefix + "Command" + suffix)
else:
rep.append(prefix + "Ctrl" + suffix)
if self._shift:
rep.append(prefix + "Shift" + suffix)
if self._alt:
rep.append(prefix + "Alt" + suffix)
rep.append(prefix + self._key.capitalize() + suffix)
return (" + ".join(rep)) + " : " + self.tip
def getKey(self):
return self._key
class HotkeyLister(object):
def __init__(self):
self.html = True
self._animatorHotkeys = {}
self._designerHotkeys = {}
self.readHotkeys()
def getHotkeyDirectory(self):
dirpath = inspect.getfile(inspect.currentframe())
dirpath = os.path.dirname(dirpath)
dirpath = os.path.dirname(dirpath)
dirpath = os.path.dirname(dirpath)
return "/".join([dirpath, "HotkeySets/"])
def readHotkeysDoit(self, filePath, dictVar):
dictVar.clear()
with open(filePath, "r") as f:
for cLine in f:
cLine = cLine.strip()
if not cLine:
continue
if cLine.startswith("#"):
continue
keyValue = cLine.split(":")
if len(keyValue) < 2:
continue
keys = keyValue[0].strip().split(" ")
tips = keyValue[-1].strip()
cKey = HotkeyTip(keys, tips)
if not cKey.isValid():
continue
keyStroke = cKey.getKey()
oldValues = dictVar.get(keyStroke, [])
oldValues.append(cKey)
dictVar[keyStroke] = oldValues
def readHotkeys(self):
hotkeyDir = self.getHotkeyDirectory()
animHotkeyFile = hotkeyDir + "animator.txt"
designerHotkeyFile = hotkeyDir + "designer.txt"
self.readHotkeysDoit(animHotkeyFile, self._animatorHotkeys)
self.readHotkeysDoit(designerHotkeyFile, self._designerHotkeys)
def queryTip(self, mode, key):
if not mode:
keyObjList = self._animatorHotkeys.get(key)
else:
keyObjList = self._designerHotkeys.get(key)
if not keyObjList:
return ""
newLine = "\n"
if self.html:
newLine = "<br>"
return newLine.join([str(key) for key in keyObjList])

View File

@@ -0,0 +1,92 @@
from maya import cmds
import os
import logging
logger = logging.getLogger(__name__)
class MGPickerLoaderBase(object):
"""
Base class for all user-implemented loader, which loads picker file for selected rig/rigs.
Implement subclass to make your own picker auto-loader, and put the python module in the /AutoLoaders
folder, or put the module path or python full path in environment variable "MGPICKER_LOADER_PY_MODULES"
to make it recognized.
"""
def pickerFileForAssetName(self, assetName):
"""
This method is the only method that need to implemented in the derived class,
which returns a full path to a .mgpkr file for the specific maya node with namespace.
"""
raise NotImplementedError("This method should be implemented by derived class")
def loadPicker(self, assetName):
"""
This method don't need to be overridden, it is used by picker program to load the actual picker.
"""
if not hasattr(cmds, "MGPicker") or not cmds.MGPicker(q=True, ex=True):
raise RuntimeError("The MG-Picker Studio is not loaded.")
pickerFile = self.pickerFileForAssetName(assetName)
if (
(not pickerFile)
or (not os.path.isfile(pickerFile))
or (not pickerFile.endswith(".mgpkr"))
):
className = self.__class__.__name__
logger.info(
"[%s]: Can not find picker file using loader: %s"
% (assetName, className)
)
return False
# Active if already loaded:
viewId = cmds.MGPicker(q=True, findPickerView=(pickerFile, True, assetName))
if viewId:
cmds.MGPickerView(e=True, activate=viewId)
return True
# Otherwise try to load it:
viewId = cmds.MGPicker(e=True, readPickerFile=(pickerFile, True))
if not viewId:
return False
cmds.MGPickerView(viewId, edit=True, namespace=assetName, tabLabel=assetName)
logger.info(
"[%s]: Find picker file using loader: %s"
% (assetName, self.__class__.__name__)
)
return True
class MGPickerRigListerBase(object):
"""
Base class for all user-implemented maya scene rig lister, which list out the possible rig names that
need to load a picker, when user click submenu of the menu "Load All Pickers".
For each rig name returned, loader is responsible to load the actual picker, will use default loader if
no custom loader class found.
Implement subclass to make your own scene rig lister, and put the python module in the /AutoLoaders
folder, or put the module path or python full path in environment variable "MGPICKER_LOADER_LISTER_PY_MODULES"
to make it recognized.
Each method starts with mayaScene_*, the * will be used as sub-menuitem label in the "Load All Pickers" menu.
eg. If you wanna add "Load All Creatures" menu item, you implement "mayaScene_creatures" method.
Examples (All these methods should return list or tuple):
def mayaScene_creatures(self) ---> "Load All Pickers / Load All Creatures"
def mayaScene_characters(self) ---> "Load All Pickers / Load All Characters"
def mayaScene_male_characters(self) ---> "Load All Pickers / Load All Male Characters"
def mayaScene_props(self) ---> "Load All Pickers / Load All Props"
def mayaScene_rigged_sets(self) ---> "Load All Pickers / Load All Rigged Sets"
def mayaScene_rigs(self) ---> "Load All Pickers / Load All Rigs"
!! You can define multiple scene rig lister, but for special category of rigs, if one lister return non-empty
list, it won't use further lister to list it. eg. the first lister returns non-empty list for mayaScene_characters,
then second lister won't be used.
"""
pass

View File

@@ -0,0 +1,334 @@
import os
import inspect
import logging
import sys
from functools import partial
from maya import cmds
from maya import mel
from MGP import translate as tr
logger = logging.getLogger(__name__)
MGPICKER_LOADER_LISTER_MODULES_ENV_NAME = "MGPICKER_LOADER_PY_MODULES"
MGPICKER_LOADER_BASE_CLASS_NAME = "MGPickerLoaderBase"
MGPICKER_RIG_LISTER_BASE_CLASS_NAME = "MGPickerRigListerBase"
MGPICKER_LOAD_MEL_PROCEDURE_NAME = "MGP_FindAndLoadPickerForName"
class MGPickerLoaderManager(object):
"""
Internally used for finding all loaders in /Autoloaders folder and autoload the picker file.
"""
_managerInstance = None
_mayaSceneRigLister = []
def __init__(self):
pass
@classmethod
def _autoloaderDir(cls):
return mel.eval("MGP_GetAutoLoaderDir")
@classmethod
def _instanceFromLoaderModule(cls, module, baseClassName):
for item in module.__dict__.values():
if not inspect.isclass(item):
continue
baseCls = item.__bases__
if not baseCls:
continue
for c in baseCls:
if c.__name__ == baseClassName:
try:
return item()
except:
logger.exception(
"Unable to create instance of loader/lister: %s"
% c.__name__
)
@classmethod
def _loaderInstanceFromLoaderModule(cls, module):
return cls._instanceFromLoaderModule(module, MGPICKER_LOADER_BASE_CLASS_NAME)
@classmethod
def _listerInstanceFromLoaderModule(cls, module):
return cls._instanceFromLoaderModule(
module, MGPICKER_RIG_LISTER_BASE_CLASS_NAME
)
@classmethod
def _loadModuleDoit(cls, moduleName, reloadModule=False):
try:
mod = __import__(moduleName, locals(), globals())
if reloadModule:
if sys.version_info.major < 3:
reload(mod)
else: # In Maya, python-3 starts with python-3.7
import importlib
importlib.reload(mod)
return mod
except:
logger.exception("Unable to import module: %s" % (moduleName))
return
@classmethod
def _loadModule(cls, moduleNameOrFilePath, reloadModule=False):
mod = None
if "/" in moduleNameOrFilePath or os.path.isfile(moduleNameOrFilePath):
dirPath = os.path.dirname(moduleNameOrFilePath)
oldPyPaths = sys.path
if not dirPath in sys.path:
sys.path.insert(0, dirPath)
try:
moduleName = os.path.splitext(os.path.basename(moduleNameOrFilePath))[0]
mod = cls._loadModuleDoit(moduleName, reloadModule=reloadModule)
sys.path = oldPyPaths
return mod
finally:
sys.path = oldPyPaths
else:
return cls._loadModuleDoit(moduleNameOrFilePath, reloadModule=reloadModule)
@classmethod
def _instance(cls):
if not cls._managerInstance:
cls._managerInstance = cls()
return cls._managerInstance
@classmethod
def _modulesFromEnvVar(cls, reloadModule=False):
loaderModStr = os.environ.get(MGPICKER_LOADER_LISTER_MODULES_ENV_NAME, "")
if not loaderModStr:
return []
loaderMods = loaderModStr.split(";")
if not loaderMods:
return []
result = []
for moduleNameOrFullpath in loaderMods:
mod = cls._loadModule(moduleNameOrFullpath, reloadModule=reloadModule)
if mod:
result.append(mod)
return result
@classmethod
def _loadPickerUsingLoaderFromEnvVar(cls, mayaNamespace):
loaderMods = cls._modulesFromEnvVar()
if not loaderMods:
return False
logger.debug(
"Find mgpicker loader modules in the environment variable: %s, will try to use them."
% MGPICKER_LOADER_LISTER_MODULES_ENV_NAME
)
for mod in loaderMods:
try:
loader = cls._loaderInstanceFromLoaderModule(mod)
if not loader:
continue
if loader.loadPicker(mayaNamespace):
return True
except:
logger.exception("Error using loader: %s" % (mod.__name__))
return False
@classmethod
def _modulesFromDir(cls, reloadModule=False):
autoloadDir = cls._autoloaderDir()
if not autoloadDir or not os.path.isdir(autoloadDir):
return []
result = []
loadedModules = []
for f in os.listdir(autoloadDir):
extension = os.path.splitext(f)[1]
if not extension in [".py", ".pyc"]:
continue
if f == "__init__.py":
continue
moduleName = os.path.splitext(f)[0]
if moduleName in loadedModules:
continue
loadedModules.append(moduleName)
fullpath = os.path.join(autoloadDir, f)
module = cls._loadModule(fullpath, reloadModule=reloadModule)
if module:
result.append(module)
return result
@classmethod
def _loadPickerUsingLoaderFromDir(cls, mayaNamespace):
mods = cls._modulesFromDir()
if not mods:
return False
for mod in mods:
try:
loader = cls._loaderInstanceFromLoaderModule(mod)
if not loader:
continue
if loader.loadPicker(mayaNamespace):
return True
except:
logger.exception("Error using loader: %s" % mod.__name__)
return False
@classmethod
def _initialiseRigLister(cls, reloadModule=False):
cls._mayaSceneRigLister = []
mods = cls._modulesFromEnvVar(reloadModule=reloadModule)
mods.extend(cls._modulesFromDir(reloadModule=reloadModule))
if not mods:
return
for mod in mods:
lister = cls._listerInstanceFromLoaderModule(mod)
if lister:
cls._mayaSceneRigLister.append(lister)
@classmethod
def _rigNamesForListerCategory(cls, lister, funcName):
if not funcName in dir(lister):
return []
try:
function = getattr(lister, funcName)
return function()
except:
logger.exception(
"Error try to get rig names lister: %s.%s"
% (lister.__class__.__name__, funcName)
)
return []
@classmethod
def _listRigNameOfCategory(cls, category):
if not cls._mayaSceneRigLister:
return []
if not category or category == "*":
methodName = "mayaScene_rigs"
else:
methodName = "mayaScene_%s" % (category)
result = []
for lister in cls._mayaSceneRigLister:
names = cls._rigNamesForListerCategory(lister, methodName)
if names:
result.extend(names)
return list(set(result))
@classmethod
def loadPicker(cls, mayaNamespace):
if cls._loadPickerUsingLoaderFromEnvVar(mayaNamespace):
return True
return cls._loadPickerUsingLoaderFromDir(mayaNamespace)
@classmethod
def listSceneRigs(cls, category="*"):
cls._initialiseRigLister(reloadModule=False)
return cls._listRigNameOfCategory(category)
@classmethod
def loadPickerForSceneRigs(cls, category, *args):
names = cls.listSceneRigs(category=category)
if not names:
return
for name in names:
mel.eval("%s %s" % (MGPICKER_LOAD_MEL_PROCEDURE_NAME, name))
@classmethod
def _executeMel(cls, melCmd, *_):
mel.eval(melCmd)
@classmethod
def _addNoLoaderMenu(cls, parentMenuItem):
logger.debug("No effective lister function found.")
label = tr.MGP_Translate("pkr.loadAll.none.mi")
cmds.menuItem(label=label, enable=False, p=parentMenuItem)
cls._addReloadMenu(parentMenuItem)
@classmethod
def _addReloadMenu(cls, parentMenuItem):
cmds.menuItem(divider=True, p=parentMenuItem)
label = tr.MGP_Translate("pkr.loadAll.updateMenu.lbl")
ann = tr.MGP_Translate("pkr.loadAll.updateMenu.ann")
cmd = partial(
cls._executeMel,
'MGP_RegenerateAllLoaderMenuDeferred "{}"'.format(parentMenuItem),
)
cmds.menuItem(label=label, c=cmd, p=parentMenuItem, ann=ann)
@classmethod
def reloadLoaderAndListerModules(cls):
cls._initialiseRigLister(reloadModule=True)
@classmethod
def reloadAndReturnCategories(cls):
cls._initialiseRigLister(reloadModule=True)
if not cls._mayaSceneRigLister:
return []
hasAllRigs = False
builtCategories = []
prefix = "mayaScene_"
prefixLen = len(prefix)
for lister in cls._mayaSceneRigLister:
for name in dir(lister):
if name.startswith(prefix):
if len(name) == prefixLen or name[prefixLen:].lower() == "rigs":
hasAllRigs = True
continue
builtCategories.append(name[prefixLen:])
builtCategories = sorted(list(set(builtCategories)))
if hasAllRigs:
builtCategories.insert(0, "rigs")
return builtCategories
@classmethod
def refreshAndRebuildLoadAllMenu(cls, parentMenuItem):
builtCategories = cls.reloadAndReturnCategories()
cmds.menu(parentMenuItem, e=True, dai=True)
if not builtCategories:
cls._addNoLoaderMenu(parentMenuItem)
return 0
for category in builtCategories:
label = tr.MGP_Translate_rep1(
"pkr.loadAll.loadAll.prefix.mi", category.title()
)
ann = tr.MGP_Translate_rep1(
"pkr.loadAll.loadAll.prefix.mi", category.title()
)
cmds.menuItem(
label=label,
c=partial(cls.loadPickerForSceneRigs, category),
p=parentMenuItem,
ann=ann,
)
cls._addReloadMenu(parentMenuItem)
count = len(builtCategories)
logger.debug("%s loadable categories found." % count)
return count

View File

@@ -0,0 +1,22 @@
from maya import cmds
def MGP_Translate(idStr):
if not hasattr(cmds, "MGPickerService"):
return idStr
return cmds.MGPickerService(tr=idStr)
def MGP_Translate_rep1(idStr, sub):
if not hasattr(cmds, "MGPickerService"):
return idStr
return cmds.MGPickerService(tr1=(idStr, sub))
def MGP_Translate_rep2(idStr, sub1, sub2):
if not hasattr(cmds, "MGPickerService"):
return idStr
return cmds.MGPickerService(tr2=(idStr, sub1, sub2))