PyQt4:在QTreeView中拖放

问题描述:

我使用PyQt4制作UI。它有一个treeView,我想处理它。 treeView由模型库组成。我在.py文件中创建一个数据并导入它。 因此,我可以在我的treeView中看到数据树。 但我不能拖放它,所以不能改变顺序。 我介绍了一些文章,所以将其添加到我的脚本中,但它们无法工作。 我种了一些“印刷品”,所以我追逐了我的问题。 我发现当拖动一个项目时,它将转移到MIME数据。 但是,当它被丢弃时,我找不到任何输出。 看来,该脚本不会调用“dropMimeData”方法。 我该如何修复我的脚本?PyQt4:在QTreeView中拖放

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 

添加脚本 这里UI创建(上面的脚本在该脚本导入)

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) 

和数据是

data = [("root",[("upper",[("hand",[]), 
          ("head",[]) 
          ]), 
       ("lower",[("leg",[]), 
          ("foot",[]) 
          ]) 
       ]) 
     ] 
+0

你能发布完整的脚本吗? – ekhumoro

+0

我添加了脚本,但我认为增加了一个没有太大的帮助 –

QTreeView.dragMoveEventQTreeView.dragEnterEvent方法均检查event.mimeData()返回的对象,看看它是否可以返回模型支持的任何格式的数据(即,那些由model.mimeTypes()返回)。

但您的PyMimeData子类不支持任何格式,因为它从未成功设置传递给其构造函数的数据。

问题位于PyMimeData.__init__

... 
try: 
    pdata = dumps(data) 
except: 
    return 
self.setData(self.MIME_TYPE, dumps(data.__class__) + pdata) 

datatreeModel.mimeData方法传入:

def mimeData(self, index): 
    node = self.nodeFromIndex(index[0]) 
    mimeData = PyMimeData(node) 
    return mimeData 

但是,如果你检查的类型data/node你会看到,它是treeModel实例,所以dumps(data)将失败,因为data不能被酸洗。因此,PyMimeData对象未正确初始化,因此它被拖动事件忽略。

+0

感谢您的建议,但我无法解决。 我刚刚粘贴拖放脚本,所以我不理解工作流程。 我想在“dropMimeData”中删除并插入选定的行,它必须移动它的子项。对? –

+0

@ Hyun-geunKim。为了清楚起见,我已经更新了我的答案,以解释为什么至少_one_部分代码不起作用。但是我不知道代码的哪些部分是你的,哪些部分是你粘贴的 - 所以很难进一步评论。你真的需要实现自己的树模型吗? 'QTreeWidget'类具有内置的拖放支持,这可以让你更简单。 – ekhumoro

+0

按照你的建议,我最终使用了QTreeWidget :)所以它适用于任何子分类。但我无法弄清楚基于模型的树结构。 –