You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Maya的PySide2窗口中删除大纲面板子控件?

如何在Maya的PySide2窗口中删除大纲面板子控件?

看起来你遇到的问题核心是Maya UI系统和Qt UI系统的层级隔离——你错误地把Qt布局的名称传给了Maya的outlinerPanel作为父级,但Maya根本识别不了Qt的布局对象,结果大纲面板其实是被创建在Maya主窗口的默认层级里,而不是你的Qt窗口中。这就导致后续的deleteUI调用无法正确定位并删除这个面板,Maya自然会不断递增新面板的名称。

下面是完整的修复方案和代码解析:


修正后的完整代码

import typing as _t
from maya import cmds as _cmds
from maya import OpenMayaUI as omui
from PySide2 import QtCore as _QtCore
from PySide2 import QtWidgets as _QtWidgets
from shiboken2 import wrapInstance


class MayaOutliner(_QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(MayaOutliner, self).__init__(parent)
        # 开启关闭时自动删除
        self.setAttribute(_QtCore.Qt.WA_DeleteOnClose, True)
        self._outliner_panel: _t.Optional[str] = None
        self._main_layout = _QtWidgets.QVBoxLayout(self)
        self._main_layout.setObjectName(f"MayaOutlinerMainLayout_{id(self)}")
        self._maya_container: _t.Optional[_QtWidgets.QWidget] = None
        self._setup_ui()

    def _setup_ui(self):
        self.setWindowTitle("Maya Outliner")
        self.resize(300, 400)
        
        # 1. 创建专门的Qt容器承载Maya UI
        self._maya_container = _QtWidgets.QWidget()
        self._main_layout.addWidget(self._maya_container)
        self._maya_container.setObjectName(f"MayaOutlinerContainer_{id(self)}")
        
        # 2. 把Qt容器转换成Maya能识别的UI控件名称
        container_win_id = self._maya_container.winId()
        maya_container_name = omui.MQtUtil.fullName(int(container_win_id))
        
        # 3. 正确创建outlinerPanel,父级是转换后的Maya容器
        self._outliner_panel = _cmds.outlinerPanel(parent=maya_container_name)
        print(f"创建的大纲面板: {self._outliner_panel}")
        print(f"面板实际父级: {_cmds.outlinerPanel(self._outliner_panel, query=True, parent=True)}")

    def closeEvent(self, event):
        if self._outliner_panel and _cmds.outlinerPanel(self._outliner_panel, exists=True):
            # 用evalDeferred异步删除,避免Qt和Maya事件循环冲突
            _cmds.evalDeferred(lambda: _cmds.deleteUI(self._outliner_panel, panel=True))
            # 延迟验证删除结果(调试用)
            _cmds.evalDeferred(lambda: print(f"删除后面板是否存在: {_cmds.outlinerPanel(self._outliner_panel, exists=True)}"))
        
        super(MayaOutliner, self).closeEvent(event)


# 测试代码
if __name__ == "__main__":
    # 清理旧窗口(调试用)
    for win in _QtWidgets.QApplication.topLevelWidgets():
        if isinstance(win, MayaOutliner):
            win.close()
    
    window = MayaOutliner()
    window.show()

关键修复点解释

  1. 正确的UI桥接

    • 我们创建了一个独立的Qt QWidget作为Maya UI的专属容器,而不是直接用Qt布局。
    • omui.MQtUtil.fullName把Qt容器的winId转换成Maya能识别的UI控件名称,这样Maya的outlinerPanel就能真正嵌入到你的Qt窗口内,而不是Maya主窗口。
  2. 异步删除操作

    • 使用cmds.evalDeferred让Maya在自己的事件循环中执行deleteUI,避免Qt的closeEvent和Maya UI事件的同步冲突。
    • 删除时加上panel=True明确指定要删除的是面板类型UI,确保Maya正确识别操作对象。
  3. 调试验证

    • 打印面板的父级名称,可以验证outlinerPanel确实被创建在Qt容器对应的Maya控件中。
    • 延迟验证面板是否存在,能看到删除操作的实际结果(因为异步执行,立即检查会显示存在,延迟后才会显示不存在)。

为什么原代码不生效?

  • 原代码中cmds.outlinerPanel(parent=self._main_layout.objectName())的父级是Qt布局的名称,Maya的UI系统完全不认识这个名称,所以会忽略父级参数,把outlinerPanel创建在Maya主窗口的默认布局里。
  • 即使你调用cmds.deleteUI,可能因为Maya的UI状态还没更新,或者面板被dock到主窗口,导致删除操作没有立即生效,看起来面板还存在。

现在运行修正后的代码,关闭窗口后再创建新窗口,Maya不会再递增面板名称,因为旧的面板已经被正确删除了。

火山引擎 最新活动