I have a problem with my custom QTreeWidget losing the associated widgets for each QTreeWidgetItem when it gets dragged and dropped. I understand that this is due to the item being destroyed and re-created at it's new position, and that it has something to do with the QMimeType, I believe? But I'm not sure how to work around this to get it to work and preserve/re-create these widgets? Could I possibly un-parent & reparent them, somehow? The examples I've been able to find have largely been in QT, which I can read, but I'm not really sure how to implement a solution in PySide2/PyQt. Would anybody be able to give me any pointers?
My code is written for Maya, but hopefully it should be able to work outside of Maya as well. (The custom dropEvent is needed due to a Maya/Mac bug that causes drag and drop actions to completely remove the item on drop.) You can see that item5 has been re-ordered, but lost it's associated button.
import sys
from PySide2 import QtCore, QtWidgets
def main():
try:
app = QtWidgets.QApplication(sys.argv)
ui = TreeUI()
ui.show()
app.exec_()
except RuntimeError:
from maya import OpenMayaUI as omui
try:
import shiboken2 as shiboken
except ImportError:
import shiboken
pointer = omui.MQtUtil.mainWindow()
win = shiboken.wrapInstance(long(pointer), QtWidgets.QWidget)
ui = TreeUI(parent=win)
ui.show()
class Tree(QtWidgets.QTreeWidget):
def __init__(self, parent=None):
super(Tree, self).__init__(parent)
self.setHeaderLabels(('name', 'widget'))
self.setSelectionMode(self.SingleSelection)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)
self.setDragDropMode(self.InternalMove)
self.reparented_widgets = {}
def dropEvent(self, event):
index = self.indexAt(event.pos())
parent = index.parent()
self.model().dropMimeData(
event.mimeData(),
event.dropAction(),
index.row(),
index.column(),
parent
)
event.accept()
class TreeUI(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(TreeUI, self).__init__(parent)
widget = QtWidgets.QWidget(self)
self.setCentralWidget(widget)
tree = Tree()
for x in range(0, 6):
item = QtWidgets.QTreeWidgetItem(tree, ('item{}'.format(x), None))
item.setFlags(item.flags() & ~QtCore.Qt.ItemIsDropEnabled)
button = QtWidgets.QPushButton('Button{}'.format(x))
tree.setItemWidget(item, 1, button)
layout = QtWidgets.QVBoxLayout(widget)
layout.addWidget(tree)
main()
UPDATE #1: I'm able to store the widgets for the QTreeWidgetItem within the startDrag function, while removing them from the item. But then, when I go to add them back to the new QTreeWidgetItem, the application crashes, unfortunately...
def startDrag(self, actions):
column_count = self.columnCount()
items = self.selectedItems()
for item in items:
for x in range(column_count):
widget = self.itemWidget(item, x)
if widget:
self.removeItemWidget(item, x)
self.reparent_widgets[x] = widget
super(Tree, self).startDrag(actions)
def dropEvent(self, event):
# Fix Mac/Maya drag and drop bug
index = self.indexAt(event.pos())
parent = index.parent()
self.model().dropMimeData(
event.mimeData(),
event.dropAction(),
index.row(),
index.column(),
parent
)
event.accept()
# Re-create widget(s)
pos = event.pos()
item = self.itemAt(pos)
for x in self.reparent_widgets.keys():
self.setItemWidget(item, x, self.reparent_widgets[x])
