PyQt4: перетаскивание в QTreeView

Я делаю UI с PyQt4. У него есть treeView, и я хочу с ним разобраться. TreeView состоит из модели-основы. Я создаю данные в файле .py и импортирую их. Итак, я вижу дерево данных в моем treeView. Но я не могу перетащить его, поэтому не могу изменить порядок. Я сослался на несколько статей, поэтому добавляю их в свой сценарий, но они не работают. Присаживаю какой-то "отпечаток", вот и погнался за своей проблемой. Я обнаружил, что при перетаскивании элемента он передается в данные MIME. Но когда он сбрасывается, я не могу найти никаких выходов. Похоже, что скрипт не вызывает метод dropMimeData. Как я могу исправить свой сценарий?

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

добавленный скрипт здесь - создание пользовательского интерфейса (приведенный выше скрипт импортирован в этот скрипт)

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",[])
                           ])
                 ])
        ]

person Hyun-geun Kim    schedule 07.12.2011    source источник
comment
Можете выложить полный сценарий?   -  person ekhumoro    schedule 07.12.2011
comment
Я добавил скрипт, но думаю, что добавленный не особо полезен   -  person Hyun-geun Kim    schedule 08.12.2011


Ответы (1)


Оба метода QTreeView.dragMoveEvent и QTreeView.dragEnterEvent проверяют объект, возвращаемый event.mimeData(), чтобы узнать, может ли он возвращать данные для любого из форматов, поддерживаемых моделью (т. Е. Тех, которые возвращаются model.mimeTypes()).

Но ваш подкласс PyMimeData не поддерживает какие-либо форматы, потому что он никогда не устанавливает данные, передаваемые его конструктору.

Проблема находится в PyMimeData.__init__:

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

data передается из метода treeModel.mimeData:

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

Но если вы проверите тип data/node, вы увидите, что это treeModel экземпляр, и поэтому dumps(data) не удастся, потому что data нельзя мариновать. В результате объект PyMimeData не инициализируется должным образом, и поэтому он игнорируется событиями перетаскивания.

person ekhumoro    schedule 08.12.2011
comment
спасибо за совет, но я не могу решить. Я просто вставил скрипт для перетаскивания, поэтому не понимал, как работает эта работа. Я предполагаю, что в dropMimeData удалить и вставить выбранную строку, он должен переместить ее дочерние элементы. правильно? - person Hyun-geun Kim; 12.12.2011
comment
@ Hyun-geunKim. Для ясности я обновил свой ответ, чтобы объяснить, почему хотя бы одна часть кода не работает. Но я не знаю, какие части кода принадлежат вам, а какие вы вставили, поэтому дальнейшие комментарии затруднены. Вам действительно нужно реализовать свою собственную древовидную модель? Класс QTreeWidget имеет встроенную поддержку перетаскивания, которая может значительно упростить вам задачу. - person ekhumoro; 12.12.2011
comment
Как вы и рекомендовали, я наконец использовал QTreeWidget :) Так что он хорошо работает с любыми подклассами. Но я пока не могу разобраться в древовидной структуре на основе моделей. - person Hyun-geun Kim; 15.12.2011