六六の魔法世界

注册

Maya技能 - 技术分享 - 2022年8月29日

[Maya] 把用户创建的摄影机显示在热盒菜单中

最近的案件工作中需要频繁的在默认透视摄影机和渲染摄影机之间频繁切换,由于平时建模中非常习惯使用hotbox和标记菜单(marking menu)这样不需要把视线移到很远的地方的ui所以对hotbox只能在默认的几个摄影机之中切换感到了些许不满。最近做了个脚本往默认的hotbox中央区标记菜单菜单里添加了可以切换除了默认的persp透视摄影机之外的透视摄影机的功能。这个菜单还会根据场景中摄影机的新建及删除自动更新,不需要频繁地手动更新。

下面开始进入标题的后半部分:如何用python创建和编辑标记菜单。为什么一定要用python而不是mel呢?列表。python的列表比mel方便得不知道高到哪里去了,在这里获取摄影机,选取透视摄影机,去除默认persp摄影机这一过程python的代码会比mel简洁很多(另外也因为现在基本坚持python主义路线不动摇,不mel,不maxscript)。为什么要专门提用python做标记菜单这个事呢?因为默认的标记菜单编辑器不能选是mel还是python,敲进去的代码会被默认是mel。明明script editor和hotkey editor都可以的!在经过一系列不断地错误尝试之后总结出了两种通过python创建编辑标记菜单的方法。

使用默认的标记菜单编辑器,把python代码输入进去

这个方法首先打开标记菜单编辑器,选择CommonModelingPanes,在Hotbox Style下面的方框的地方点右键选Edit Menu Item…

在新弹出来的窗口的Label栏里输入User Cameras。

下面贴出来获取摄影机,创建摄影机子菜单的这段代码。把这段代码复制粘贴到上图窗口的Commands里,然后点Save and Close。请留意一下代码第5,6,13行的‘menuItem_userCameras’,这个字符串在后面会提到。

import maya.cmds as cmds
from functools import partial
list_camera = cmds.ls( type = 'camera' )
list_userCamera = [ c for c in list_camera if not cmds.getAttr( c + '.orthographic' ) + cmds.camera( c, q = 1, startupCamera = 1 ) ]
if cmds.popupMenu( 'menuItem_userCameras', ex = 1 ):
    list_oldMenuItem = cmds.menu( 'menuItem_userCameras', q = 1, itemArray = 1 )
    if list_oldMenuItem:
        cmds.deleteUI( list_oldMenuItem )
    if list_camera:
        for userCamera in list_userCamera:
            cameraTrfm = cmds.listRelatives( userCamera, parent = 1 )[0]
            cmds.menuItem( p = pntMenu, label = cameraTrfm, command = partial( cmds.lookThru, userCamera ), sourceType = 'python', boldFont = 1 )

现在还不能立刻执行脚本,因为maya会用mel去执行它,那结果自然会报错。所以现在需要把maya关掉,找到
C:\Users\用户名\Documents\maya\版本号\prefs\markingMenus
的 menu_CommonModelingPanes.mel 这个文件,用记事本或者其他文本编辑软件打开它,找到大概是最下面的地方,出现了之前输入到Label栏里的’User Cameras’的地方,这里就是之前新创建的菜单项被存储的地方了,现在要开始修改四个地方:

  1. 因为这个菜单项是用来打开子菜单的,所以把subMenu的flag的值改成1。
  2. 输入到command里的这段代码并不是要在点击这个菜单项时执行的,而是要点开它下面的子菜单时执行的,所以这个代码不应该用command这个flag,应该改成postMenuCommand
  3. 把语言sourceType这个flag的值改成”python”
  4. 把最后这个生成的菜单项的名称改成上文提到过的menuItem_userCameras,这样代码里自动生成的子菜单项就知道它要来这里的子菜单了。

改好之后是这个样子

现在就可以启动maya,打开hotbox中央区标记菜单的话应该就会变成这样的布局了。

这个方法利用了maya的标记菜单编辑器里虽然没有python的选项,但可以找到标记菜单被保存的mel文件里修改flag和它的值的“漏洞”来达成用python编辑标记菜单的目的。但是这个方法的缺点是需要重启maya,这样maya才会reload修改后的标记菜单。下面介绍另外一种方法。

在maya运行时执行python脚本修改当前的标记菜单

这种方法比较简单暴力,而且可以直接把中央区标记菜单的菜单项修改成动态的,而不是把子菜单做成动态。文章开头的图片中各个用户生成的相机会直接列在前视相机的下面,而不是再进入一级子菜单,这样就可以省去一步进入子菜单的步骤。

是这种方法也有几个缺陷。一个是菜单不会被保存,每次启动maya之后标记菜单会被重置,需要再次执行脚本。另一个是启动maya之后还不能立马执行。Maya的标记菜单的狗屁机制是在第一次调用之后才会被生成,判定是否是第一次调用的方式是看当前的标记菜单里是否存在菜单项,没有菜单项的话就是没有被调用过。在没有调用过的情况下先执行了修改标记菜单的脚本的话,这个标记菜单里就有了菜单项了,那么原本默认的正交视图的摄影机,以及默认透视摄影机persp的菜单项就不会出现了。

import maya.cmds as cmds
import maya.mel as mel
from functools import partial
 
def main():
    pntMenu = 'HotboxCenter1'
    cmds.loadPlugin( 'modelingToolkit.mll', quiet = 1 )
    if cmds.popupMenu( pntMenu, q = 1, itemArray = 1 ):
        pass
    else:
        initCmd = cmds.menu( pntMenu, q = 1, pmc = 1 )
        mel.eval( initCmd )
    cmds.menu( pntMenu, e = 1, pmc = createCameraMenuItem )
 
def createCameraMenuItem(*args):
    pntMenu = HotboxCenter1'
    items = cmds.menu( pntMenu, q = 1, itemArray = 1 )
    if items:
        for i in items:
            rp = cmds.menuItem( i, q = 1, radialPosition = 1 )
            if rp:
                pass
            else:
                cmds.deleteUI( i )
    list_camera = cmds.ls( type = 'camera' )
    list_userCamera = [ c for c in list_camera if not cmds.getAttr( c + '.orthographic' ) + cmds.camera( c, q = 1, startupCamera = 1 ) ]
    if list_userCamera:
        for userCamera in list_userCamera:
            cameraTrfm = cmds.listRelatives( userCamera, parent = 1 )[0]
            cmds.menuItem( p = pntMenu, label = cameraTrfm, command = partial( cmds.lookThru, userCamera ), sourceType = 'python', boldFont = 1 )
 
if __name__ == '__main__':
    main()

上面就是直接修改hotbox中央区标记菜单的python脚本。有几个值得注意的地方需要说一下。

  1. ‘HotboxCenter1’就是hotbox中央区标记菜单的名字。往这个菜单里添加菜单项的时候要注意加上 parent = ‘HotboxCenter1’ 的flag。
  2. 第11行的处理就是为了避免上文提到的在HotboxCenter1的菜单项还没生成出来的情况下先创建了其他的菜单项,导致默认菜单项不会被生成的尴尬局面的分歧处理。
  3. 第15~30行的代码和第一种方法里粘贴到User Cameras的command栏里的内容差不太多,区别在于父级层别。第一种方法的层别是 HotboxCenter1 -> menuItem_userCameras -> 各个用户创建摄影机,第二种方法是HotboxCenter1 -> 各个用户创建摄影机。
  4. 第二种方法里由于嫌麻烦所以用脚本把Hotbox Style这个子菜单顺便删掉了,这样即可以减少误操作的可能,也能把脚本简化一些。
第一种方法的结果
第二种方法的结果

上面左图是第一种方法的结果,右图是第二种方法的结果。用第二种方法也可以做出左图的效果,灵活性和可能性很多,但只有第一种方法的结果可以保存。

本文转载自 tong si。另外这里有一篇文章介绍了用python脚本生成一个全新的标记菜单,并覆盖掉旧的标记菜单的方法,可以参考。
https://bindpose.com/custom-marking-menu-maya-python/