PyQt4:在 QTreeView 中拖放

发布于 2024-12-19 16:14:58 字数 6164 浏览 5 评论 0原文

我用 PyQt4 制作了一个 UI。它有一个树视图,我想处理它。 树视图由模型库组成。我在 .py 文件中创建数据并导入它。 所以,我可以在我的树视图中看到数据树。 但我无法拖放它,因此无法更改顺序。 我引用了一些文章,因此将其添加到我的脚本中,但它们无法工作。 我种植了一些“打印”,所以我追查了我的问题。 我发现当拖动一个项目时,它会传输到 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

添加脚本 这里是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",[])
                           ])
                 ])
        ]

I make a UI with PyQt4. It has a treeView and I want to deal with it.
The treeView is made up with model-base. I create a data in .py file and import it.
So, I can see the data tree in my treeView.
But I can't drag and drop it, so can't change the order.
I referred some articles so add it in my script, but they couldn't work.
I plant some "print", so I chased my problem.
I found that when drag a item, it transferred to MIME data.
But when it is dropped, I can't find any outputs.
It seems that the script doesn't call "dropMimeData" method.
How can I fix my 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

added script
here is ui creation (the script above is imported in this 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)

and data is

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

尾戒 2024-12-26 16:14:58

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)

data是从treeModel.mimeData方法传入的:

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

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

The QTreeView.dragMoveEvent and QTreeView.dragEnterEvent methods both check the object returned by event.mimeData() to see if it can return data for any of the formats supported by the model (i.e. those returned by model.mimeTypes()).

But your PyMimeData subclass doesn't support any formats, because it never successfully sets the data passed to its constructor.

The problem is located in PyMimeData.__init__:

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

The data is passed in from the treeModel.mimeData method:

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

But if you check the type of data/node you'll see that it is a treeModel instance, and so dumps(data) will fail because data can't be pickled. As a result of this, the PyMimeData object is not initialized properly, and so it is ignored by drag events.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文