pyqt:QTreeView - 找到了一个拖放示例,但需要帮助破译一小部分

发布于 2024-10-03 18:57:20 字数 11656 浏览 4 评论 0 原文

我正在寻找破译以下代码的最小帮助(这是一个来自 - http://www.mail-archive.com/ ) 。我认为根据该帖子的内容在此处重新发布是可以的。请不要因我所包含的代码的长度而推迟,我只需要对其一小部分进行澄清:

下面列出了我想知道的代码片段(为了完整性,我还在之后发布了整个示例) 。我的问题有两个:

1)何时调用removeRows?

2)为什么作者实际上复制了正在移动的节点(使用深复制)?我认为可以使用appendChild和removeChild(数据存储中节点的两种方法)简单地重新设置相关节点的父级。

    def dropMimeData(self, mimedata, action, row, column, parentIndex): 
        if action == Qt.IgnoreAction: 
            return True 

        dragNode = mimedata.instance() 
        parentNode = self.nodeFromIndex(parentIndex) 

        # make an copy of the node being moved 
        newNode = deepcopy(dragNode) #<------ why copy? Why not just reparent?
        newNode.setParent(parentNode) 
        self.insertRow(len(parentNode)-1, parentIndex) 
        self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), parentIndex, parentIndex) 
        return True 


    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): #<-- when does this ever get called?
        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 

为了完整性,我还包含了以下所有代码:

import sys 
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 
from copy import deepcopy 
from cPickle import dumps, load, loads 
from cStringIO import StringIO 


class PyMimeData(QMimeData): 
    """ 
    The PyMimeData wraps a Python instance as MIME data. 
    """ 
    # The MIME type for instances. 
    MIME_TYPE = QString('application/x-ets-qt4-instance') 

    def __init__(self, data=None): 
        """ 
        Initialise the instance. 
        """ 
        QMimeData.__init__(self) 

        # Keep a local reference to be returned if possible. 
        self._local_instance = data 

        if data is not None: 
            # We may not be able to pickle the data. 
            try: 
                pdata = dumps(data) 
            except: 
                return 

            # This format (as opposed to using a single sequence) allows the 
            # type to be extracted without unpickling the data itself. 
            self.setData(self.MIME_TYPE, dumps(data.__class__) + pdata) 

    @classmethod 
    def coerce(cls, md): 
        """ 
        Coerce a QMimeData instance to a PyMimeData instance if possible. 
        """ 
        # See if the data is already of the right type.  If it is then we know 
        # we are in the same process. 
        if isinstance(md, cls): 
            return md 

        # See if the data type is supported. 
        if not md.hasFormat(cls.MIME_TYPE): 
            return None 

        nmd = cls() 
        nmd.setData(cls.MIME_TYPE, md.data()) 

        return nmd 

    def instance(self): 
        """ 
        Return the instance. 
        """ 
        if self._local_instance is not None: 
            return self._local_instance 

        io = StringIO(str(self.data(self.MIME_TYPE))) 

        try: 
            # Skip the type. 
            load(io) 

            # Recreate the instance. 
            return load(io) 
        except: 
            pass 

        return None 

    def instanceType(self): 
        """ 
        Return the type of the instance. 
        """ 
        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 myNode(object): 
    def __init__(self, name, state, description, parent=None): 

        self.name = QString(name) 
        self.state = QString(state) 
        self.description = QString(description) 

        self.parent = parent 
        self.children = [] 

        self.setParent(parent) 

    def setParent(self, parent): 
        if parent != None: 
            self.parent = parent 
            self.parent.appendChild(self) 
        else: 
            self.parent = None 

    def appendChild(self, child): 
        self.children.append(child) 

    def childAtRow(self, row): 
        return self.children[row] 

    def rowOfChild(self, child):       
        for i, item in enumerate(self.children): 
            if item == child: 
                return i 
        return -1 

    def removeChild(self, row): 
        value = self.children[row] 
        self.children.remove(value) 

        return True 

    def __len__(self): 
        return len(self.children) 


class myModel(QAbstractItemModel): 

    def __init__(self, parent=None): 
        super(myModel, self).__init__(parent) 

        self.treeView = parent 
        self.headers = ['Item','State','Description'] 

        self.columns = 3 

        # Create items 
        self.root = myNode('root', 'on', 'this is root', None) 

        itemA = myNode('itemA', 'on', 'this is item A', self.root) 
        itemA1 = myNode('itemA1', 'on', 'this is item A1', itemA) 

        itemB = myNode('itemB', 'on', 'this is item B', self.root) 
        itemB1 = myNode('itemB1', 'on', 'this is item B1', itemB) 

        itemC = myNode('itemC', 'on', 'this is item C', self.root) 
        itemC1 = myNode('itemC1', 'on', 'this is item C1', itemC) 


    def supportedDropActions(self): 
        return Qt.CopyAction | Qt.MoveAction 


    def flags(self, index): 
        defaultFlags = QAbstractItemModel.flags(self, index) 

        if index.isValid(): 
            return Qt.ItemIsEditable | Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled | defaultFlags 

        else: 
            return Qt.ItemIsDropEnabled | defaultFlags 


    def headerData(self, section, orientation, role): 
        if orientation == Qt.Horizontal and role == Qt.DisplayRole: 
            return QVariant(self.headers[section]) 
        return QVariant() 

    def mimeTypes(self): 
        types = QStringList() 
        types.append('application/x-ets-qt4-instance') 
        return types 

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


    def dropMimeData(self, mimedata, action, row, column, parentIndex): 
        if action == Qt.IgnoreAction: 
            return True 

        dragNode = mimedata.instance() 
        parentNode = self.nodeFromIndex(parentIndex) 

        # make an copy of the node being moved 
        newNode = deepcopy(dragNode) 
        newNode.setParent(parentNode) 
        self.insertRow(len(parentNode)-1, parentIndex) 
        self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), parentIndex, parentIndex) 
        return True 


    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 


    def index(self, row, column, parent): 
        node = self.nodeFromIndex(parent) 
        return self.createIndex(row, column, node.childAtRow(row)) 


    def data(self, index, role): 
        if role == Qt.DecorationRole: 
            return QVariant() 

        if role == Qt.TextAlignmentRole: 
            return QVariant(int(Qt.AlignTop | Qt.AlignLeft)) 

        if role != Qt.DisplayRole: 
            return QVariant() 

        node = self.nodeFromIndex(index) 

        if index.column() == 0: 
            return QVariant(node.name) 

        elif index.column() == 1: 
            return QVariant(node.state) 

        elif index.column() == 2: 
            return QVariant(node.description) 
        else: 
            return QVariant() 


    def columnCount(self, parent): 
        return self.columns 


    def rowCount(self, parent): 
        node = self.nodeFromIndex(parent) 
        if node is None: 
            return 0 
        return len(node) 


    def parent(self, child): 
        if not child.isValid(): 
            return QModelIndex() 

        node = self.nodeFromIndex(child) 

        if node is None: 
            return QModelIndex() 

        parent = node.parent 

        if parent is None: 
            return QModelIndex() 

        grandparent = parent.parent 
        if grandparent is None: 
            return QModelIndex() 
        row = grandparent.rowOfChild(parent) 

        assert row != - 1 
        return self.createIndex(row, 0, parent) 


    def nodeFromIndex(self, index): 
        return index.internalPointer() if index.isValid() else self.root 



class myTreeView(QTreeView): 

    def __init__(self, parent=None): 
        super(myTreeView, self).__init__(parent) 

        self.myModel = myModel() 
        self.setModel(self.myModel) 

        self.dragEnabled() 
        self.acceptDrops() 
        self.showDropIndicator() 
        self.setDragDropMode(QAbstractItemView.InternalMove) 

        self.connect(self.model(), SIGNAL("dataChanged(QModelIndex,QModelIndex)"), self.change) 
        self.expandAll() 

    def change(self, topLeftIndex, bottomRightIndex): 
        self.update(topLeftIndex) 
        self.expandAll() 
        self.expanded() 

    def expanded(self): 
        for column in range(self.model().columnCount(QModelIndex())): 
            self.resizeColumnToContents(column) 



class Ui_MainWindow(object): 
    def setupUi(self, MainWindow): 
        MainWindow.setObjectName("MainWindow") 
        MainWindow.resize(600, 400) 
        self.centralwidget = QWidget(MainWindow) 
        self.centralwidget.setObjectName("centralwidget") 
        self.horizontalLayout = QHBoxLayout(self.centralwidget) 
        self.horizontalLayout.setObjectName("horizontalLayout") 
        self.treeView = myTreeView(self.centralwidget) 
        self.treeView.setObjectName("treeView") 
        self.horizontalLayout.addWidget(self.treeView) 
        MainWindow.setCentralWidget(self.centralwidget) 
        self.menubar = QMenuBar(MainWindow) 
        self.menubar.setGeometry(QRect(0, 0, 600, 22)) 
        self.menubar.setObjectName("menubar") 
        MainWindow.setMenuBar(self.menubar) 
        self.statusbar = QStatusBar(MainWindow) 
        self.statusbar.setObjectName("statusbar") 
        MainWindow.setStatusBar(self.statusbar) 

        self.retranslateUi(MainWindow) 
        QMetaObject.connectSlotsByName(MainWindow) 

    def retranslateUi(self, MainWindow): 
        MainWindow.setWindowTitle(QApplication.translate("MainWindow", "MainWindow", None, QApplication.UnicodeUTF8)) 


if __name__ == "__main__": 
    app = QApplication(sys.argv) 
    MainWindow = QMainWindow() 
    ui = Ui_MainWindow() 
    ui.setupUi(MainWindow) 
    MainWindow.show() 
    sys.exit(app.exec_()) 

I am looking for just the tinyest bit of help deciphering the following code ( it was an example taken from - http://www.mail-archive.com/[email protected]/msg17197.html ). I assume it is ok to repost here based on the contents of that post. Please don't be put off by the length of the code I am including, I only need clarification on a small portion of it:

The code snippet I am wondering about is listed below (I also post the entire example after that for completeness). My questions are two:

1) When does removeRows ever get called?

2) Why does the author actually copy the node being moved (using deep copy)? I would have assumed that it is ok to simply reparent the node in question using appendChild and removeChild (two methods of the nodes in his data store).

    def dropMimeData(self, mimedata, action, row, column, parentIndex): 
        if action == Qt.IgnoreAction: 
            return True 

        dragNode = mimedata.instance() 
        parentNode = self.nodeFromIndex(parentIndex) 

        # make an copy of the node being moved 
        newNode = deepcopy(dragNode) #<------ why copy? Why not just reparent?
        newNode.setParent(parentNode) 
        self.insertRow(len(parentNode)-1, parentIndex) 
        self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), parentIndex, parentIndex) 
        return True 


    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): #<-- when does this ever get called?
        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 

I am also including all of the code below for completeness:

import sys 
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 
from copy import deepcopy 
from cPickle import dumps, load, loads 
from cStringIO import StringIO 


class PyMimeData(QMimeData): 
    """ 
    The PyMimeData wraps a Python instance as MIME data. 
    """ 
    # The MIME type for instances. 
    MIME_TYPE = QString('application/x-ets-qt4-instance') 

    def __init__(self, data=None): 
        """ 
        Initialise the instance. 
        """ 
        QMimeData.__init__(self) 

        # Keep a local reference to be returned if possible. 
        self._local_instance = data 

        if data is not None: 
            # We may not be able to pickle the data. 
            try: 
                pdata = dumps(data) 
            except: 
                return 

            # This format (as opposed to using a single sequence) allows the 
            # type to be extracted without unpickling the data itself. 
            self.setData(self.MIME_TYPE, dumps(data.__class__) + pdata) 

    @classmethod 
    def coerce(cls, md): 
        """ 
        Coerce a QMimeData instance to a PyMimeData instance if possible. 
        """ 
        # See if the data is already of the right type.  If it is then we know 
        # we are in the same process. 
        if isinstance(md, cls): 
            return md 

        # See if the data type is supported. 
        if not md.hasFormat(cls.MIME_TYPE): 
            return None 

        nmd = cls() 
        nmd.setData(cls.MIME_TYPE, md.data()) 

        return nmd 

    def instance(self): 
        """ 
        Return the instance. 
        """ 
        if self._local_instance is not None: 
            return self._local_instance 

        io = StringIO(str(self.data(self.MIME_TYPE))) 

        try: 
            # Skip the type. 
            load(io) 

            # Recreate the instance. 
            return load(io) 
        except: 
            pass 

        return None 

    def instanceType(self): 
        """ 
        Return the type of the instance. 
        """ 
        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 myNode(object): 
    def __init__(self, name, state, description, parent=None): 

        self.name = QString(name) 
        self.state = QString(state) 
        self.description = QString(description) 

        self.parent = parent 
        self.children = [] 

        self.setParent(parent) 

    def setParent(self, parent): 
        if parent != None: 
            self.parent = parent 
            self.parent.appendChild(self) 
        else: 
            self.parent = None 

    def appendChild(self, child): 
        self.children.append(child) 

    def childAtRow(self, row): 
        return self.children[row] 

    def rowOfChild(self, child):       
        for i, item in enumerate(self.children): 
            if item == child: 
                return i 
        return -1 

    def removeChild(self, row): 
        value = self.children[row] 
        self.children.remove(value) 

        return True 

    def __len__(self): 
        return len(self.children) 


class myModel(QAbstractItemModel): 

    def __init__(self, parent=None): 
        super(myModel, self).__init__(parent) 

        self.treeView = parent 
        self.headers = ['Item','State','Description'] 

        self.columns = 3 

        # Create items 
        self.root = myNode('root', 'on', 'this is root', None) 

        itemA = myNode('itemA', 'on', 'this is item A', self.root) 
        itemA1 = myNode('itemA1', 'on', 'this is item A1', itemA) 

        itemB = myNode('itemB', 'on', 'this is item B', self.root) 
        itemB1 = myNode('itemB1', 'on', 'this is item B1', itemB) 

        itemC = myNode('itemC', 'on', 'this is item C', self.root) 
        itemC1 = myNode('itemC1', 'on', 'this is item C1', itemC) 


    def supportedDropActions(self): 
        return Qt.CopyAction | Qt.MoveAction 


    def flags(self, index): 
        defaultFlags = QAbstractItemModel.flags(self, index) 

        if index.isValid(): 
            return Qt.ItemIsEditable | Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled | defaultFlags 

        else: 
            return Qt.ItemIsDropEnabled | defaultFlags 


    def headerData(self, section, orientation, role): 
        if orientation == Qt.Horizontal and role == Qt.DisplayRole: 
            return QVariant(self.headers[section]) 
        return QVariant() 

    def mimeTypes(self): 
        types = QStringList() 
        types.append('application/x-ets-qt4-instance') 
        return types 

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


    def dropMimeData(self, mimedata, action, row, column, parentIndex): 
        if action == Qt.IgnoreAction: 
            return True 

        dragNode = mimedata.instance() 
        parentNode = self.nodeFromIndex(parentIndex) 

        # make an copy of the node being moved 
        newNode = deepcopy(dragNode) 
        newNode.setParent(parentNode) 
        self.insertRow(len(parentNode)-1, parentIndex) 
        self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), parentIndex, parentIndex) 
        return True 


    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 


    def index(self, row, column, parent): 
        node = self.nodeFromIndex(parent) 
        return self.createIndex(row, column, node.childAtRow(row)) 


    def data(self, index, role): 
        if role == Qt.DecorationRole: 
            return QVariant() 

        if role == Qt.TextAlignmentRole: 
            return QVariant(int(Qt.AlignTop | Qt.AlignLeft)) 

        if role != Qt.DisplayRole: 
            return QVariant() 

        node = self.nodeFromIndex(index) 

        if index.column() == 0: 
            return QVariant(node.name) 

        elif index.column() == 1: 
            return QVariant(node.state) 

        elif index.column() == 2: 
            return QVariant(node.description) 
        else: 
            return QVariant() 


    def columnCount(self, parent): 
        return self.columns 


    def rowCount(self, parent): 
        node = self.nodeFromIndex(parent) 
        if node is None: 
            return 0 
        return len(node) 


    def parent(self, child): 
        if not child.isValid(): 
            return QModelIndex() 

        node = self.nodeFromIndex(child) 

        if node is None: 
            return QModelIndex() 

        parent = node.parent 

        if parent is None: 
            return QModelIndex() 

        grandparent = parent.parent 
        if grandparent is None: 
            return QModelIndex() 
        row = grandparent.rowOfChild(parent) 

        assert row != - 1 
        return self.createIndex(row, 0, parent) 


    def nodeFromIndex(self, index): 
        return index.internalPointer() if index.isValid() else self.root 



class myTreeView(QTreeView): 

    def __init__(self, parent=None): 
        super(myTreeView, self).__init__(parent) 

        self.myModel = myModel() 
        self.setModel(self.myModel) 

        self.dragEnabled() 
        self.acceptDrops() 
        self.showDropIndicator() 
        self.setDragDropMode(QAbstractItemView.InternalMove) 

        self.connect(self.model(), SIGNAL("dataChanged(QModelIndex,QModelIndex)"), self.change) 
        self.expandAll() 

    def change(self, topLeftIndex, bottomRightIndex): 
        self.update(topLeftIndex) 
        self.expandAll() 
        self.expanded() 

    def expanded(self): 
        for column in range(self.model().columnCount(QModelIndex())): 
            self.resizeColumnToContents(column) 



class Ui_MainWindow(object): 
    def setupUi(self, MainWindow): 
        MainWindow.setObjectName("MainWindow") 
        MainWindow.resize(600, 400) 
        self.centralwidget = QWidget(MainWindow) 
        self.centralwidget.setObjectName("centralwidget") 
        self.horizontalLayout = QHBoxLayout(self.centralwidget) 
        self.horizontalLayout.setObjectName("horizontalLayout") 
        self.treeView = myTreeView(self.centralwidget) 
        self.treeView.setObjectName("treeView") 
        self.horizontalLayout.addWidget(self.treeView) 
        MainWindow.setCentralWidget(self.centralwidget) 
        self.menubar = QMenuBar(MainWindow) 
        self.menubar.setGeometry(QRect(0, 0, 600, 22)) 
        self.menubar.setObjectName("menubar") 
        MainWindow.setMenuBar(self.menubar) 
        self.statusbar = QStatusBar(MainWindow) 
        self.statusbar.setObjectName("statusbar") 
        MainWindow.setStatusBar(self.statusbar) 

        self.retranslateUi(MainWindow) 
        QMetaObject.connectSlotsByName(MainWindow) 

    def retranslateUi(self, MainWindow): 
        MainWindow.setWindowTitle(QApplication.translate("MainWindow", "MainWindow", None, QApplication.UnicodeUTF8)) 


if __name__ == "__main__": 
    app = QApplication(sys.argv) 
    MainWindow = QMainWindow() 
    ui = Ui_MainWindow() 
    ui.setupUi(MainWindow) 
    MainWindow.show() 
    sys.exit(app.exec_()) 

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

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

发布评论

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

评论(1

星光不落少年眉 2024-10-10 18:57:20

1)第一个解释如下:

http://doc.qt.io /archives/qt-4.7/qabstractitemmodel.html#removeRows

http://doc.qt.io/archives/qt-4.7/model-view-programming.html#creating-new-models

2)第二个。据我了解,作者没有移动物品。他实际上正在创造。你知道,这就像我向列表中添加某种模板项目,然后列表就填满了——创建了新项目。

1) First one is explained at:

http://doc.qt.io/archives/qt-4.7/qabstractitemmodel.html#removeRows

http://doc.qt.io/archives/qt-4.7/model-view-programming.html#creating-new-models

2) Second one. As I understand author is not moving items. He is actually creating ones. You know, it's like I adding some kind of template item to list and list just fills up -- new items are created.

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