更新 2020-5-27

  这个工具会破坏 Maya 的工具架机制,无法中键拖拽图标,修改图标,而且一不小心就导致图标数据丢失。
  推荐使用第三方的工具架 SiShelf 源github链接

前言

  最近工作上,动画制作人员都想要更好地管理工具架上的图标。
  他们突发奇想给我提了个需求 , 希望让 Maya 的工具架有类似于 StatusLine 上面那样有伸缩功能。
  我想了一下,如果通过 Qt 的骚操作的未必不能实现。
  于是我就愉快地接受了挑战。

github 仓库

前期研究

  由于工具架的数据是会报错到本地 mel 文件里面去的。
  因此如果我自己添加一个可以收缩的组件的话会影响到 工具架 的按钮存储的。
  因此需要用现有的widget来转成可伸缩的效果。

  最初我是想到要写一个新的 mel runtimeCommand 的形式生成一个带layout的组件,后续的伸缩可以转成 Qt 来实现。
  但是这样就需要改写 工具架的 mel 脚本 , 还是不够方便。
  于是我注意到了 工具架的一些分隔图标。
  在 Maya 里面是无法通过 工具架 的编辑器进行添加的。
  不过可以通过修改 用户文档的 pref/shelves 里面的mel来实现 separator 的添加。

遇到的问题

  于是我就开始着手写方案。
  其实整个流程我之前都有所接触过了,因此主要的框架搭建起来算是驾轻就熟。

工具架加载问题

  之前开发 commandLauncher 的时候踩过 工具架 的坑。
  Maya默认是不会加载所有的工具架的,只会加载当前显示的工具架,这样就导致无法获取工具架上的图标信息。
  我之前开发 commandLauncher 的时候就已经弄了一个 自动加载所有工具架的函数。
  于是这里可以直接套用毫无问题。

伸缩组件搭建

  我之前在 开发 QtLib (或者说更早的时候),就已经弄好了一个 Collapsible 组件。
  效果是带动画的。
  这里的伸缩效果也是参考当时的效果进行修改开发,不过是纵向改为了横向。


  当然这个过程中遇到了一些伸缩的坑,特别是一开始不知道要给主组件添加宽度。
  导致添加进去的图标都挤在了一起,后面通过 setFixedWidth 解决了问题。

添加工具选项

  我一开始也是打算类似于 commandLauncher 一样在 StatusLine 上添加一个 图标 进行提示的。
  后来还是打算添加到 工具架 的 option 按钮上了,这样更加合乎道理。

  在原先的 menu 基础上添加 选项其实不难, 直接用 cmds 库也可以解决。

* 程序退出卸载功能

  这个功能就非常坎坷了。
  上面的功能虽然完美,但是一旦影响到了整个工具架的 mel 文件信息就完蛋了。
  而且由于用了 Qt 的骚操作,那些可以伸缩的组件全部无法被 Maya 识别出来了。
  如果是以伸缩组件的形式推出 Maya 会导致这些伸缩部分的图标丢失的。

  所以为了避免这个情况,我需要在 Maya 推出保存 shelf 的时候触发一个事件来还原工具架。
  我最初想到的是通过 scriptJob 的事件来完成这个操作。
  去看了 scriptJob 的文档,果然发现了 quitApplication 的事件。
  于是我用了测试代码如下

1
2
3
import sys
from maya import cmds
cmds.scriptJob(runOnce=1,event=['quitApplication',lambda:sys.stdout.write("quit")])

  但是经过我的测试,上述的命令是无法在Maya的脚本编辑器看到输出的。
  于是为了验证这个事件是否触发了,我又单独写了一个文件读写的操作,
  这样我就可以通过查看目标文件有没有写入我预期的数据来确定程序是否触发。


  经过一番折腾,我的确可以通过 IO 操作看到程序是触发了的,但是再这个阶段运行卸载程序似乎已经没有意义了。
  Maya已经完成了保存 mel 的操作。

  于是我开始寻找更好的触发条件,最开始我想到了 OpenMaya 的 Message 相关的类。
  我之前也有接触过,可以参考我的文章 Maya 轴心移动研究

  于是我又查了一下,在 OpenMaya2.0 的 MEventMessage 里面的确找到了退出事件。
  然而这个事件本质上和 scriptJob 是一样的,也是 quitApplication


  最后又折腾了一番之后,感觉 Maya 的确没有提供一个退出之前执行的勾子。
  于是没办法了,只好抓住最好的救命稻草,通过 Qt 的 eventFilter 来获取退出事件。
  但是 Filter 出来的事件太多,而且程序退出的时候并不是所有的事件都可以显示到 脚本编辑器 里面的。
  于是我就改写为每获取一个事件就进行一个 IO 操作将数据输出到 txt 文本里面。
  综合上面的显示的事件列表,我发现每次退出最后执行的事件类型都是 ChildRemoved
  当然如果点击右上角的关闭按钮还有 Close 的事件触发,这个事件感觉更为准确,但是如果是在 菜单 里面点击退出的则没有触发 Close
  但是 ChildRemoved 每次都能触发出来。
  于是我抱着最后的希望在 ChildRemoved 事件下触发 uninstall 事件。
  然后就成功解决了目前的问题了,无论是用什么形式的退出,都可以还原 工具架 而不会导致图标丢失。


2020-3-21 更新

  后来我网上查了一下,发现 QCoreApplication 有 aboutToQuit 的信号槽
  可以通过这个信号槽在程序退出的时候触发相应的函数。
  虽然触发的时候,工具架已经存档了一次了。
  但是没有关系,我可以还原工具架然后再保存一遍就不会有问题了。
  这个方案就不会导致一些意外误触的情况。

总结

  这次开发感觉有之前的研究铺路,明显要踩的坑少了很多了。
  后续还需要完善一下 github 仓库的说明 , 还有 UI 的美化也可以考虑一下的。