PYQT4: Arraste e solte no QtreeView
-
29-10-2019 - |
Pergunta
Eu faço uma IU com PyQt4.Ele tem um treeView e eu quero lidar com isso. O treeView é feito com base no modelo.Eu crio um dado em um arquivo .py e o importo. Então, posso ver a árvore de dados em meu treeView. Mas não posso arrastar e soltar, então não posso alterar a ordem. Referi alguns artigos, então inclua no meu script, mas eles não funcionaram. Plantei algumas "estampas", então persegui meu problema. Descobri que, ao arrastar um item, ele é transferido para dados MIME. Mas quando ele é descartado, não consigo encontrar nenhuma saída. Parece que o script não chama o método "dropMimeData". Como posso corrigir meu script?
from PyQt4 import QtCore, QtGui
from setting import *
from copy import deepcopy
from cPickle import dumps, load, loads
from cStringIO import StringIO
class PyMimeData(QtCore.QMimeData):
MIME_TYPE = QtCore.QString('text/plain')
def __init__(self, data=None):
QtCore.QMimeData.__init__(self)
self._local_instance = data
if data is not None:
try:
pdata = dumps(data)
except:
return
self.setData(self.MIME_TYPE, dumps(data.__class__) + pdata)
@classmethod
def coerce(cls, md):
if isinstance(md, cls):
return md
if not md.hasFormat(cls.MIME_TYPE):
return None
nmd = cls()
nmd.setData(cls.MIME_TYPE, md.data())
return nmd
def instance(self):
if self._local_instance is not None:
return self._local_instance
io = StringIO(str(self.data(self.MIME_TYPE)))
try:
load(io)
return load(io)
except:
pass
return None
def instanceType(self):
if self._local_instance is not None:
return self._local_instance.__class__
try:
return loads(str(self.data(self.MIME_TYPE)))
except:
pass
return None
class treeItem(QtGui.QStandardItem):
def __init__(self, data, parent=None):
super(treeItem, self).__init__(data)
self.parentItem = parent
self.itemData = data
self.childItems = []
def appendChild(self, item):
self.childItems.append(item)
def parent(self):
return self.parentItem
def childAtRow(self, row):
return self.childItems[row]
def rowOfChild(self, child):
for i, item in enumerate(self.childItems):
if item == child:
return i
return -1
class treeModel(QtGui.QStandardItemModel):
def __init__(self, name, parent=None):
super(treeModel, self).__init__(parent)
self.headerName = name
self.childItems = []
def appendChild(self, item):
self.childItems.append(item)
def removeRowAll(self):
pass
def addItemList(self, parent, elements):
for text, children in elements:
item = treeItem(text, parent)
self.addItems(parent, item)
if children:
self.addItemList(item, children)
def addItems(self, parent, inputItem):
parent.appendRow(inputItem)
parent.appendChild(inputItem)
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.headerName
def supportedDropActions(self):
return QtCore.Qt.MoveAction | QtCore.Qt.CopyAction
def flags(self, index):
defaultFlags = QtCore.QAbstractItemModel.flags(self, index)
if index.isValid():
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled | defaultFlags
else:
return QtCore.Qt.ItemIsDropEnabled | defaultFlags
def mimeTypes(self):
types = QtCore.QStringList()
types.append('text/plain')
return types
def mimeData(self, index):
node = self.nodeFromIndex(index[0])
mimeData = PyMimeData(node)
return mimeData
def dropMimeData(self, mimedata, action, row, column, parentIndex):
print mimedata, action, row, column, parentIndex
if action == QtCore.Qt.IgnoreAction:
return True
dragNode = mimedata.instance()
print dragNode
parentNode = self.nodeFromIndex(parentIndex)
# copy of node being moved
newNode = deepcopy(dragNode)
print newNode
newNode.setParent(parentNode)
self.insertRow(len(parentNode)-1, parentIndex)
self.emit(QtCore.SIGNAL("dataChanged(QtCore.QModelIndex,QtCore.QModelIndex)"), parentIndex, parentIndex)
return True
def nodeFromIndex(self, index):
##return index.internalPointer() if index.isValid() else self.root
return index.model() if index.isValid() else self.parent()
def insertRow(self, row, parent):
return self.insertRows(row, 1, parent)
def insertRows(self, row, count, parent):
self.beginInsertRows(parent, row, (row + (count - 1)))
self.endInsertRows()
return True
def removeRow(self, row, parentIndex):
return self.removeRows(row, 1, parentIndex)
def removeRows(self, row, count, parentIndex):
self.beginRemoveRows(parentIndex, row, row)
node = self.nodeFromIndex(parentIndex)
node.removeChild(row)
self.endRemoveRows()
return True
adicionado script aqui está a criação da interface do usuário (o script acima é importado para este script)
class RigControlWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent = getMayaWindow()):
super(RigControlWindow, self).__init__(parent)
self.setupUi(self)
self.bodyrig_treelist.setDragEnabled(1)
self.bodyrig_treelist.setAcceptDrops(1)
self.bodyrig_treelist.setDropIndicatorShown(1)
self.bodyrig_treelist.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
QtCore.QObject.connect(self.finalize_button, QtCore.SIGNAL("clicked()"), self.AddData_treeList)
def AddData_treeList(self):
self.localtreeModel = treeModel("objects")
self.bodyrig_treelist.setModel(self.localtreeModel)
self.localtreeModel.addItemList(self.localtreeModel, data)
e os dados são
data = [("root",[("upper",[("hand",[]),
("head",[])
]),
("lower",[("leg",[]),
("foot",[])
])
])
]
Solução
Os métodos QTreeView.dragMoveEvent
e QTreeView.dragEnterEvent
verificam o objeto retornado por event.mimeData()
para ver se ele pode retornar dados para qualquer um dos formatos suportados pelo modelo (ou seja, aqueles retornados por model.mimeTypes()
).
Mas sua subclasse PyMimeData
não suporta nenhum formato, porque nunca define com sucesso os dados passados para seu construtor.
O problema está localizado em PyMimeData.__init__
:
...
try:
pdata = dumps(data)
except:
return
self.setData(self.MIME_TYPE, dumps(data.__class__) + pdata)
O data
é passado a partir do método treeModel.mimeData
:
def mimeData(self, index):
node = self.nodeFromIndex(index[0])
mimeData = PyMimeData(node)
return mimeData
Mas se você verificar o tipo de data/node
, verá que é uma instância treeModel
e, portanto, dumps(data)
falhará porque data
não pode ser conservado.Como resultado disso, o objeto PyMimeData
não é inicializado corretamente e, portanto, é ignorado pelos eventos de arrastar.