前言

  最近研究 Maya 的工具架的图标,让我感到很困惑。
  Maya 工具架上的图标找不到文件路径,如果可以找到的话就可以替换图标实现对 Maya 内置图标的替换了 (~ ̄▽ ̄)~

alt

  工具架的图标选择是可以选择很多内置的图标的,但是这些图标来自何方呢?
  最初的想法是去到 Maya 相关目录去查找,首先是 我的文档 的个人设置路径,其次是 Maya 安装目录。
  然而并没有找到这些图标保存的路径。

目录寻找

  倒是在 Maya 安装目录的 icons 目录下找到了很多插件相关的图标。
  也有启动页面的图片,修改 MayaStartupImage.png 这张图片就可以替换掉启动页面了 o( ̄▽ ̄)d
  但是我想要找的图片还是没有找到。


  这种操作让我想起以前安装非官方 3ds Max 时候还有在启动页面嵌入广告的,大概也是那么简单(:з」∠)

代码寻找

  既然目录寻找失败,也可以从 Maya 的代码机制进行寻找。
  看看图片是怎么通过代码加载出来的,毕竟 Maya 大部分的界面都是开源可编辑的。

  在工具架面板上点击图标打开选择菜单弹出的是一段奇奇怪怪的代码

1
2
3
4

formLayout -nd 100;
// Result: |formLayout115 //

  根本没办法复现代码,于是只好去翻找 工具架编辑面板的 UI 一探究竟。
  依然是使用 Maya 的命令回显功能查看。

1
2
3
4
5
6
7
/*dSBRMBMI*/python("import maya.app.general.shelfEditorWindow as myTempSEW\nmyTempSEW.doIt(selectedShelfButton='shelfButton4',selectedTabIndex=1)\ndel myTempSEW");
import maya.app.general.shelfEditorWindow as myTempSEW
myTempSEW.doIt(selectedShelfButton='shelfButton4',selectedTabIndex=1)
del myTempSEW
global string $gShelfTopLevel; string $tmp=$gShelfTopLevel;
// Result: Shelf|MainShelfLayout|formLayout13|ShelfLayout //
if(! `exists shelfLabel_uiToMel` ) {source "shelfLabel.mel";};

  可以看到主要的 mel 语句是一行 Python 的代码调用。
  于是我抽丝剥茧,可以通过下面这段 python 代码打开编辑窗口。

1
2
3
import maya.app.general.shelfEditorWindow as myTempSEW
myTempSEW.doIt(selectedShelfButton='shelfButton4',selectedTabIndex=1)
del myTempSEW

  可以看到这个包的目录是个 shelfEditorWindow.py 文件,可以手动 print myTempSEW一下查找到 路径。

1
2
3
import maya.app.general.shelfEditorWindow as myTempSEW
print myTempSEW
<module 'maya.app.general.shelfEditorWindow' from 'C:\Program Files\Autodesk\Maya2017\Python\lib\site-packages\maya\app\general\shelfEditorWindow.py'>

  直接在脚本里面搜索 icon 可以找到 itemIcon 函数,注释里面提到是改变图标的函数,就是它没错了。
  另外在第 360 行开始有 InternalIcon 内部图标的构建。
  于是我执行判断里面的代码

1
2
3
4
import maya.app.general.resourceBrowser as resourceBrowser
resBrowser = resourceBrowser.resourceBrowser()
path = resBrowser.run()
del resBrowser

  上面的代码就可以打开图标替换的窗口了。
  感觉离目标越来越近了,于是继续追查 resourceBrowser
  这个包就在同级目录的 resourceBrowser.py 里面

  下面的重点是 saveCopy 这个函数,就是像知道 另存为的功能是怎么实现的。
  于是看到最后的 95 行

1
cmds.resourceManager(saveAs=(resName, path))

  没想到居然会用到 cmds 命令的函数,失算了。
  于是去查 cmds 文档说明
  万万没想到原理这些内置的图标居然是通过内置的 cmds 命令进行读取和输出的。

dll 查找

  经过上面的一番搜索依然是没能达成目标,只知道可以通过 resourceManager cmds 命令来输出。
  于是我就网上查了一番,没想到还真的找到论坛上的提问 链接

  链接中提到可以去到 Maya 的 bin 目录找到 MayaRes.dll 文件,然后用 dll 资源读取工具来读取 dll 的图标。
  然而我找了一圈也没有找到 MayaRes.dll 文件。
  看了回答的时间才豁然开朗,原来是 2008 年回复,那个时候 Maya 还没有使用 Qt 框架。
  因此调用机制大概是不太一样的。


  但是藏在 dll 里面是个好思路,于是我用回答里面推荐的 restorer 软件一个一个 dll 打开查看。
  然而看了大部分的dll依然是一无所获。

搜索 resourceManager

  没了办法只能死马当活马医了,于是我用 VScode 打开 Maya 的安装目录,直接搜索 resourceManager 这个关键字。
  没想到居然还搜到了 bin 目录下的 commandList 文件,这个文件刚好记录的是 cmds 命令调用 对用的 dll 文件。
  而 resourceManager 指向的是 bin 目录下的 sharedUI.dll , 然而我用之前的软件打开 dll 什么都没有。
  貌似是真相大白了,还需要其他更好的 dll 查看器。

dll查看

  我用了好机款 dll 查看器,但是都已失败告终。
  只有 eXescope 软件是报错的, 貌似是 autodesk 有保护, eXescope 读取会提示 EAccessViolation 导致内存错误无法访问。
  另外 dll export viewer 可以看到 dll 内部包含的函数表,但是没有什么用。
  我也尝试用 python 去读取 dll , 读取的 dll 没有报错,但是什么函数都没有(:з」∠)

1
2
3
4
5
6
import ctypes

testlib = ctypes.CDLL(r"C:\Program Files\Autodesk\Maya2017\bin\SharedUI.dll")

print dir(testlib)
['_FuncPtr', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattr__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_func_flags_', '_func_restype_', '_handle', '_name']

总结

  最后研究一轮之后,还是以失败告终了(:з」∠)
  不过这个过程还是挺有趣的,就简单记录下来。

2020-5-10 补充

  虽然无法直接找到 Maya 内置的图标,但是可以通过 Qt 的机制获取 Maya 图标。
  这个操作我是从 Rj 大神的 Command Search 插件里面学到的,后来我进一步开发 CommandLauncher 的时候有了更深的理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Qt import QtCore,QtWidgets,QtGui

class TestWidget(QtWidgets.QWidget):
def __init__(self):
super(TestWidget,self).__init__()

layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)

pixmap = QtGui.QPixmap(":\hyper_s_OFF.png")
label = QtWidgets.QLabel()
label.setPixmap(pixmap)
layout.addWidget(label)


if __name__ == "__main__":
widget = TestWidget()
widget.show()

   hyper_s_OFF 就是 Maya 内置的图标,可以在工具架上修改图标查看 Maya 所有内置的图标。
  通过 :\ 前缀即可获取得到这些图标。
  另外这些图标是通过 Qt 的机制加载的,完全可以利用 Qrc 编译的机制来添加新的内置图标。
  具体操作可以参考我后来写的脚本 flimSkin2GameSkin
  通过 QtCore.qRegisterResourceData 注册的图标也可以通过 Maya 命令 resourceManager 进行获取。