2010-11-19 12 views
1

Je cherche juste le moindre petit peu d'aide pour déchiffrer le code suivant (c'était un exemple tiré de - http://www.mail-archive.com/[email protected]/msg17197.html). Je suppose que c'est correct de republier ici basé sur le contenu de ce poste. S'il vous plaît ne soyez pas rebutés par la longueur du code que je suis inclus, je n'ai besoin de clarification sur une petite partie de celui-ci:pyqt: QTreeView - trouvé un exemple de glisser-déposer, mais besoin d'aide pour déchiffrer une petite partie

L'extrait de code dont je m'interroge est énuméré ci-dessous (je poste également l'exemple entier après cela pour l'exhaustivité). Mes questions sont les suivantes:

1) Quand removeRows est-il appelé?

2) Pourquoi l'auteur copie-t-il réellement le nœud en cours de déplacement (en utilisant une copie en profondeur)? J'aurais supposé qu'il est correct de simplement reparenter le noeud en question en utilisant appendChild et removeChild (deux méthodes des noeuds dans son magasin de données).

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 

Je suis également, y compris tout le code ci-dessous pour être complet:

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_()) 

Répondre

1

1) Le premier est expliqué à:

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) Deuxième. Si je comprends bien, l'auteur ne déplace pas les objets. Il est en train de créer les uns. Vous savez, c'est comme si j'ajoutais une sorte d'élément de modèle à la liste et à la liste juste remplit - de nouveaux éléments sont créés.

+0

Merci pour la réponse rapide. J'avais lu ce document mais je ne comprends toujours pas quel mécanisme provoque le déclenchement de cette méthode. Est-ce le QTreeView l'appelant comme le résultat de l'opération glisser-déposer? Et si elle est appelée automatiquement, pourquoi l'insertRows est-il appelé explicitement? En ce qui concerne le second point, lorsque je lance le code, c'est (au moins visuellement) le déplacement des éléments et non leur copie - donc ma confusion. Cela dit, j'apprécie les réponses jusqu'à présent, merci. – bvz

Questions connexes