title: PyQt5 tree model: drag&drop problem dragging from back to front within the same level
body: <p>I tried the code from <a href="https://stackoverflow.com/questions/22020091/how-to-handle-drag-and-drop-properly-using-pyqt-qabstractitemmodel/66864441#66864441">How to handle Drag and Drop Properly using PYQT QAbstractItemModel</a></p>
  <p>and found that every time I drag, I can only insert the dragged node to the end of a parent. This is because appendChild() is called in the setParent() method. Based on the above code, I tried to modify the setParent() method to insert the dragged node into the placement point (rather than the end). The revised setParent() method is given as:</p>
  <pre><code>    def setParent1(self, position, parent):
  
          if parent != None:
  
              # if self.parent.name == parent.name:  # 解决从后往前移动出问题，时行时不行
              #     row0 = self.parent.rowOfChild(self)
              #     if position &lt; row0:
              #         print(f&quot;yes,row0={row0}, position={position}&quot;)
              #         parent.children[row0] = parent.children[row0 - 1]
  
              self.parent = parent
              self.parent.insertChild(position, self)
          else:
              self.parent = None
  </code></pre>
  <p>together with the insertChild() method as:</p>
  <pre><code>    def insertChild(self, position, child):
          if position &lt; 0 | position &gt; self.__len__():
              return False
          self.children.insert(position, child)
  </code></pre>
  <p>When the above method is adopted, I can insert the dragged node into the placement point, but for the children of the same level, if you drag the lower child to the front, there will be problems. For example, the original Tree is as follows:</p>
  <pre><code>level A
      level B1
      level B2
      level B3
      level B4
  </code></pre>
  <p>If I drag level B3 before level B1, the expected result will become as follows:</p>
  <pre><code>level A
    level B3
    level B1
    level B2
    level B4
  </code></pre>
  <p>However, the actual result becomes:</p>
  <pre><code>level A
    level B3
    level B1
    level B3
    level B4
  </code></pre>
  <p>In addition, when dragging from the back to the front, sometimes the following error will appear:</p>
  <pre><code>Traceback (most recent call last):
    File &quot;D:/treeModel_drag&amp;drop_eg3.py&quot;, line 114, in parent
      parent = node.parent
  AttributeError:'list_iterator' object has no attribute'parent'
  </code></pre>
  <p>But if I drag level B1 backward, for example after level B3, no error will be reported and the result will be correct:</p>
  <pre><code>level A
    level B2
    level B3
    level B1
    level B4
  </code></pre>
  <p>The following is the complete code, I hope someone can help me, thanks</p>
  <pre><code># -*- coding: utf-8 -*-
  
  import sys, os
  from PyQt5.QtGui import *
  from PyQt5.QtCore import *
  from PyQt5.QtWidgets import *
  from copy import deepcopy
  import pickle
  
  
  class TreeItem(object):
      def __init__(self, name, parent=None):
  
          self.name = str(name)
          self.parent = parent
          self.children = []
          self.setParent(parent)
          # self.setParent1(self.childCount(), parent)  #
  
      def childCount(self):
          return len(self.children)
  
      def setParent(self, parent):
          if parent != None:
              self.parent = parent
              self.parent.appendChild(self)
          else:
              self.parent = None
  
      def setParent1(self, position, parent):
  
          if parent != None:
  
              # if self.parent.name == parent.name:  # 解决从后往前移动出问题，时行时不行
              #     row0 = self.parent.rowOfChild(self)
              #     if position &lt; row0:
              #         print(f&quot;yes,row0={row0}, position={position}&quot;)
              #         parent.children[row0] = parent.children[row0 - 1]
  
              self.parent = parent
              self.parent.insertChild(position, self)
          else:
              self.parent = None
  
      def appendChild(self, child):
          self.children.append(child)
  
      def insertChild(self, position, child):
          if position &lt; 0 | position &gt; self.__len__():
              return False
          self.children.insert(position, child)
  
      def childAtRow(self, row):
          if len(self.children)&gt;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 TreeModel(QAbstractItemModel):
  
      def __init__(self):
  
          QAbstractItemModel.__init__(self)
  
          self.columns = 1
          self.clickedItem=None
  
          self.root = TreeItem('root', None)
          levelA = TreeItem('levelA', self.root)
          levelB = TreeItem('levelB', levelA)
          levelC1 = TreeItem('levelC1', levelB)
          levelC2 = TreeItem('levelC2', levelB)
          levelC3 = TreeItem('levelC3', levelB)
          levelD = TreeItem('levelD', levelC3)
  
          levelE = TreeItem('levelE', levelD)
          levelF = TreeItem('levelF', levelE)
  
      def nodeFromIndex(self, index):
          return index.internalPointer() if index.isValid() else self.root
  
      def index(self, row, column, parent):
          node = self.nodeFromIndex(parent)
          return self.createIndex(row, column, node.childAtRow(row))
  
      def parent(self, index):
          if not index.isValid():
              return QModelIndex()
          node = self.nodeFromIndex(index)
          if node is None:
              return QModelIndex()
          parent = node.parent
          if parent is None:
              return QModelIndex()
          grandparent = parent.parent
  
          if grandparent==None:
              return QModelIndex()
  
          row = grandparent.rowOfChild(parent)
          assert row != - 1
          return self.createIndex(row, 0, parent)
  
      def rowCount(self, parent):
          node = self.nodeFromIndex(parent)
          if node is None: return 0
          return len(node)
  
      def columnCount(self, parent):
          return self.columns
  
      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 supportedDropActions(self):
          return Qt.MoveAction  # | Qt.CopyAction
  
      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 setData(self, index, value, role):
          if role == Qt.EditRole:
              if value.toString() and len(value.toString()) &gt; 0:
                  self.nodeFromIndex(index).name = value.toString()
                  self.dataChanged.emit(index, index)
              return True
  
      def mimeTypes(self):
          return ['bstream', 'text/xml']
  
      def mimeData(self, indexes):
  
          mimedata = QMimeData()
          bstream = pickle.dumps(self.nodeFromIndex(indexes[0]))
          mimedata.setData('bstream', bstream)
          return mimedata
  
      def dropMimeData(self, mimedata, action, row, column, parentIndex):
  
          if action == Qt.IgnoreAction:
              return True
  
          droppedNode=pickle.loads(mimedata.data('bstream'))
  
          #
          beginRow = -1
          if row != -1:  # 表示
              print(&quot;case 1: ROW IS NOT -1, meaning inserting in between, above or below an existing node&quot;)
              beginRow = row
          elif parentIndex.isValid():
              print(&quot;case 2: PARENT IS VALID, inserting ONTO something since row was not -1, &quot;
                    &quot;beginRow becomes 0 because we want to &quot;
                    &quot;insert it at the beginning of this parents children&quot;)
              beginRow = parentIndex.row()
          else:
              print(&quot;case 3: PARENT IS INVALID, inserting to root, &quot;
                    &quot;can change to 0 if you want it to appear at the top&quot;)
              beginRow = self.rowCount(QModelIndex())
          print(f&quot;row={row}, beginRow={beginRow}&quot;)
          # ************尝试使得插入点为拖拽落点
          droppedIndex = self.createIndex(row, column, droppedNode)
          parentNode = self.nodeFromIndex(parentIndex)
          newNode = deepcopy(droppedNode)
          #
  
          # newNode.setParent(parentNode)  
          newNode.setParent1(beginRow, parentNode)
          self.dataChanged.emit(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
  
  
  class GUI(QDialog):
      def build(self, myWindow):
          myWindow.resize(600, 400)
          self.myWidget = QWidget(myWindow)
          self.boxLayout = QVBoxLayout(self.myWidget)
  
          self.treeView = QTreeView()
          self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection)
  
          self.treeModel = TreeModel()
          self.treeView.setModel(self.treeModel)
          self.treeView.expandAll()
          self.treeView.setDragDropMode(QAbstractItemView.InternalMove)
          # self.treeView.connect(self.treeView.model(), pyqtSignal(&quot;dataChanged(QModelIndex,QModelIndex)&quot;), self.onDataChanged)
          # QObject.connect(self.treeView, pyqtSignal(&quot;clicked (QModelIndex)&quot;),  self.treeItemClicked)
          self.boxLayout.addWidget(self.treeView)
  
  
          self.PrintButton= QPushButton(&quot;Print&quot;)
          self.PrintButton.clicked.connect(self.PrintOut)
          self.boxLayout.addWidget(self.PrintButton)
  
          self.DeleteButton= QPushButton(&quot;Delete&quot;)
          self.DeleteButton.clicked.connect(self.DeleteLevel)
          self.boxLayout.addWidget(self.DeleteButton)
  
          self.insertButton= QPushButton(&quot;Insert&quot;)
          self.insertButton.clicked.connect(self.insertLevel)
          self.boxLayout.addWidget(self.insertButton)
  
          self.duplicateButton= QPushButton(&quot;Duplicate&quot;)
          self.duplicateButton.clicked.connect(self.duplicateLevel)
          self.boxLayout.addWidget(self.duplicateButton)
  
          myWindow.setCentralWidget(self.myWidget)
  
  
      def make_dirs_from_dict(self, dirDict, current_dir='/'):
          for key, val in dirDict.items():
              #os.mkdir(os.path.join(current_dir, key))
              print(&quot;\t\t Creating directory: &quot;, os.path.join(current_dir, key))
              if type(val) == dict:
                  self.make_dirs_from_dict(val, os.path.join(current_dir, key))
  
      def PrintOut(self):
          result_dict = {}
          for a1 in self.treeView.model().root.children:
              result_dict[str(a1.name)]={}
              for a2 in a1.children:
                  result_dict[str(a1.name)][str(a2.name)]={}
                  for a3 in a2.children:
                      result_dict[str(a1.name)][str(a2.name)][str(a3.name)]={}
                      for a4 in a3.children:
                          result_dict[ str(a1.name)][str(a2.name)][str(a3.name)][str(a4.name)]={}
                          for a5 in a4.children:
                              result_dict[ str(a1.name)][str(a2.name)][str(a3.name)][str(a4.name)][str(a5.name)]={}
                              for a6 in a5.children:
                                  result_dict[str(a1.name)][str(a2.name)][str(a3.name)][str(a4.name)][str(a5.name)][str(a6.name)]={}
                                  for a7 in a6.children:
                                      result_dict[str(a1.name)][str(a2.name)][str(a3.name)][str(a4.name)][str(a5.name)][str(a6.name)][str(a7.name)]={}
  
  
          self.make_dirs_from_dict(result_dict)
  
  
      def DeleteLevel(self):
          if len(self.treeView.selectedIndexes())==0: return
  
          currentIndex = self.treeView.selectedIndexes()[0]
          currentRow=currentIndex.row()
          currentColumn=currentIndex.column()
          currentNode = currentIndex.internalPointer()
  
          parentNode = currentNode.parent
          parentIndex = self.treeView.model().createIndex(currentRow, currentColumn, parentNode)
          print('\n\t\t\t CurrentNode:', currentNode.name, ', ParentNode:', currentNode.parent.name, ', currentColumn:', currentColumn, ', currentRow:', currentRow)
  
          # self.treeView.model().removeRow(len(parentNode)-1, parentIndex)
  
          self.treeView.model().removeRows(currentRow, 1, parentIndex )
  
          #self.treeView.model().removeRow(len(parentNode), parentIndex)
          #self.treeView.model().emit(SIGNAL(&quot;dataChanged(QModelIndex,QModelIndex)&quot;), parentIndex, parentIndex)
  
      def insertLevel(self):
          if len(self.treeView.selectedIndexes())==0: return
  
          currentIndex = self.treeView.selectedIndexes()[0]
          currentNode = currentIndex.internalPointer()
          newItem = TreeItem('Brand New', currentNode)
          self.treeView.model().insertRow(len(currentNode)-1, currentIndex)
          self.treeView.model().emit(pyqtSignal(&quot;dataChanged(QModelIndex,QModelIndex)&quot;), currentIndex, currentIndex)
  
      def duplicateLevel(self):
          if len(self.treeView.selectedIndexes())==0: return
  
          currentIndex = self.treeView.selectedIndexes()[0]
          currentRow=currentIndex.row()
          currentColumn=currentIndex.column()
          currentNode=currentIndex.internalPointer()
  
          parentNode=currentNode.parent
          parentIndex=self.treeView.model().createIndex(currentRow, currentColumn, parentNode)
          parentRow=parentIndex.row()
          parentColumn=parentIndex.column()
  
          newNode = deepcopy(currentNode)
          newNode.setParent(parentNode)
  
          self.treeView.model().insertRow(len(parentNode)-1, parentIndex)
          self.treeView.model().emit(pyqtSignal(&quot;dataChanged(QModelIndex,QModelIndex)&quot;), parentIndex, parentIndex)
  
          print('\n\t\t\t CurrentNode:', currentNode.name, ', ParentNode:', parentNode.name, ', currentColumn:', currentColumn, ', currentRow:', currentRow, ', parentColumn:', parentColumn, ', parentRow:', parentRow)
          self.treeView.update()
          self.treeView.expandAll()
  
  
      def treeItemClicked(self, index):
          print(&quot;\n clicked item -----------&gt;&quot;, index.internalPointer().name)
  
      def onDataChanged(self, indexA, indexB):
          print(&quot;\n onDataChanged NEVER TRIGGERED! ####################### \n &quot;, indexB.internalPointer().name)
          self.treeView.update(indexA)
          self.treeView.expandAll()
          self.treeView.expanded()
  
  
  
  if __name__ == '__main__':
  
      app = QApplication(sys.argv)
  
      myWindow = QMainWindow()
      myGui = GUI()
      myGui.build(myWindow)
      myWindow.show()
      sys.exit(app.exec_())
  
  </code></pre>