获取 wrapInstance 的父级会导致 window 中断

Getting parent of wrapInstance causes window to break

我一直在使用 this 代码的修改版本将 Qt window 包装在 Maya 自己的 workspaceControl 中。为了让小部件的外部级别查询几何、大小、移动等,我实际上需要做 self.parent().parent().parent().parent().parent(),因为它包含在相当多的小部件中。

我一直在解决由此引起的一两个问题,但今天我遇到了一些更重要的问题,并决定找出确切的原因。

通过测试,我尽可能地缩小了代码范围,我发现它是在尝试获取 shiboken2.wrapInstance 的父级时使用的。之后尝试创建 window 时出现错误 RuntimeError: Internal C++ object (PySide2.QtWidgets.QWidget) already deleted..

import maya.OpenMayaUI as omUI
import pymel.core as pm
import shiboken2
from PySide2 import QtWidgets

win_id = 'test'
#Delete existing window
if pm.workspaceControl(win_id, exists=True):
    pm.deleteUI(win_id)

#Create window and setup wrapper
pm.workspaceControl(win_id)
c_pointer = omUI.MQtUtil.findControl(win_id)
parent_wrap = shiboken2.wrapInstance(int(c_pointer), QtWidgets.QWidget)
print parent_wrap.parent()

#Create actual window
#This will error if parent_wrap.parent() was called
win = QtWidgets.QMainWindow(parent_object)

如何在不引起问题的情况下获取包装实例的父级?我认为这与过早地从内存中取消引用有关,但我不确定如何修复它。

我找到了一个修复程序,它有点脏,但到目前为止似乎工作可靠。

所以首先我发现如果您创建上述 parent_wrap 的两个实例,并在创建第二个实例之前获取父实例,则可以单独传递到 window 而不会导致问题。它一直工作到 window 为 docked/detached 为止,但是这会删除旧的父级并提供新的父级,因此旧指针不再有效。

似乎在初始化 window 之前提供这个父级允许我在不破坏代码的情况下用 self.parent().parent().... 重新生成它。

如果状态发生变化 (floating/docked),并且每当 RuntimeError 发生时,它都需要更新,但由于它不会对性能造成巨大影响,为简单起见,我'我只是在每次需要时重新生成它。

要覆盖一个函数,例如 move,这就是如何使用它:

def move(self, x, y):
    if self.dockable:
        return self._parent_override().move(x, y)
    return QtWidgets.QMainWindow.move(self, x, y)

这是解决问题的覆盖 class:

def _parent_override(self, create=True):
    #Determine if it's a new window, we need to get the C++ pointer again
    if not hasattr(self, '__temp_parent'):
        base = qt_get_window(self.ID)
    else:
        base = self.parent()

    #Get the correct parent level
    if pm.workspaceControl(self.ID, query=True, floating=True):
        parent = base.parent().parent().parent().parent()
    else:
        parent = base.parent().parent()

    #Even though this attribute is never used,
    #PySide2 is dumb and deletes the parent if it's not set
    self.__temp_parent = parent
    return parent