如果你使用过劲爆羊工具盒,那么你会发现在你的Maya窗口最上方的菜单栏会出现一个劲爆羊工具盒的菜单,通过它可以启动羊盒。那么这个自定义菜单是怎么实现的呢,我将介绍两种方法书写这个菜单。
第一种:mel
和我前两篇文章一样,创建控件需要一个父级,而这个菜单栏的父级是一个Maya内部定义好的$gMainWindow.
global string $gMainWindow;
setParent $gMainWindow;
然后就是为这个父级添加菜单和子菜单了。
global string $gMainWindow;
setParent $gMainWindow;
menu -to true -label "lala" new_window;
//增加子集
setParent -menu new_window;
menuItem -to true -label "我的第一个插件";
这和前面的写shelf栏和热盒一样,不断通过指定父级来添加子级。让我们再写长一些。
global string $gMainWindow;
setParent $gMainWindow;
menu -to true -label "lala" new_window;
//增加子集
setParent -menu new_window;
menuItem -to true -label "我的第一个插件";
menuItem -divider true;
menuItem -subMenu true -tearOff true -label "subMenu";
menuItem -label "怎么回事呢" -c "print \"怎么回事呢\"";
通过向menuItem指定divider标志可以为菜单添加分割线。而且你可能已经发现,在我为这个子菜单submenu设置为拥有子菜单时,并没有设置任何变量去接受,而是直接书写其子菜单,而你也可以通过声明一个字符串类型的变量用来接收这个子菜单,然后在需要书写这个子菜单的子菜单时用-p标志来指定到这个字符串变量。
当需要返回上级继续书写时,需要通过setParent来重新指定到父级,这里还可以使用简便写法。
global string $gMainWindow;
setParent $gMainWindow;
menu -to true -label "lala" new_window;
//增加子集
setParent -menu new_window;
menuItem -to true -label "我的第一个插件";
menuItem -divider true;
menuItem -subMenu true -tearOff true -label "subMenu";
menuItem -label "怎么回事呢" -c "print \"怎么回事呢\"";
setParent -menu ..;
menuItem -divider true;
menuItem -tearOff true -label "这是父级";
注意,为了让你的代码在每次执行时都会呈现一个全新的干净的菜单栏,我们同样要为这个控件提前生成一个判断条件。当这个控件已经存在时,删除它,并重新生成一个完整的菜单。
if (`menu -ex new_window`){deleteUI -menu new_window;}
至此,相信你已经学会了如何用mel书写自定义菜单。
第二种:python
和mel一样,我们同样需要提前拿到一个父级,而这需要使用到pymel,因为它已经包装好了一些Mayaapi的方法,而避免了我们去思考复杂枯燥的api。
import pymel.core as pm
mainWindow = pm.language.melGlobals['gMainWindow']
继而我们可以向其中添加菜单、子菜单。
import maya.cmds as mc
import pymel.core as pm
myWindow = pm.language.melGlobals['gMainWindow']
my_menu = mc.menu(to = True, l = '~萌萌菜单栏~', p = myWindow)
mc.menuItem(to = 1, p = my_menu, l = '菜单1', c = 'print "菜单一"')
除开获取Maya主窗口的父级时会使用pymel,其它功能仍可以继续使用cmds。因为pymel并非Autodesk官方产品,所以我并不想多使用它。而且通过Maya2022开始在安装时不再自动安装该模块可以看出,Autodesk似乎已经在有意疏远它,所以能不用pymel就不要用。
不要忘记,我们仍然要为其添加判断条件来确保每次执行代码时都没有旧的控件来干扰新的菜单生成。
if mc.menu(my_menu, ex = True):
mc.deleteUI(my_menu)
由于为其添加子菜单和分割线或图片等功能我已经在前面文章内反复叙述过,所以这里不再累述,如果你想为其添加其它效果,可以翻看我的上两篇文章。现在我要介绍的是如果将他们添加到userSetup文件中以便让你的Maya在每次启动时都能自动加载这个菜单,而非每次都要手动执行一次代码。
将你写好的代码储存为名称为userSetUp的文件,后缀为代码本身的语言格式,如:userSetup.mel或userSetup.py。将其放置到Maya的环境变量中,一般我们将自己的脚本放到我的文档->maya->scripts文件夹中,这将使你电脑安装的所有Maya在启动时都读取这个代码文件,而如果你将其放入到对应的Maya版本文件夹如:我的文档->maya->2022->scripts文件夹中,那么只有对应版本的Maya启动时才会读取到这个文件。
如果是mel,这个菜单功能可以直接放入,在Maya启动时会正常读取,但如果是python,在书写这个菜单时需要单独注意:
将mc生成的控件换成pymel、将其封装成函数,并且将函数调用用evalDeferred单独写出。
具体细则我也不明(因为我是垃圾),如果使用cmds将会在Maya启动时对这些控件进行报错:运行时错误。evalDeferred方法是使Maya在其它线程跑完后,进入空闲时候再来跑指定的对象。如果您使用过Maya应该知道,臃肿的Maya在启动时总会耗费大量的时间来布置它自己的东西,而在读取userSetup.py时可能Maya的菜单栏还没有生成,所以要为其添加子级会产生错误,遂需要等Maya跑完自己的东西后再来为其生成我们自定义的菜单栏。用pymel调用这个方法应当如下书写:
import pymel.core as pm
def createMenu():
if pm.menu(my_menu, ex = True):
pm.deleteUI(my_menu)
myWindow = pm.language.melGlobals['gMainWindow']
my_menu = pm.menu(to = True, l = '~萌萌菜单栏~', p = myWindow)
pm.menuItem(to = 1, p = my_menu, l = '菜单1', c = 'print "菜单一"')
pmc.general.evalDeferred('createMenu()')
如果您不需要在userSetup.py中书写这个菜单栏,那仍然可以适用cmds来生成控件,当然,除开获取Maya窗口的父级。
这两个创建方式我将放到下方链接,如果需要可以自提,或向我留言讨论。