Update
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
import MGP.loader
|
||||
import MGP.loadermanager
|
||||
|
||||
|
||||
def MGP_LoadMGPickerViaPythonAutoLoaders(mayaNamespace):
|
||||
return MGP.loadermanager.MGPickerLoaderManager.loadPicker(mayaNamespace)
|
||||
@@ -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])
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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))
|
||||
Reference in New Issue
Block a user