DragDropTableWidget.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. from PyQt5.QtCore import Qt, QModelIndex
  2. from PyQt5.QtWidgets import QTableWidget, QAbstractItemView, QTableWidgetItem, QTableView
  3. class DragDropTableWidget(QTableWidget):
  4. def __init__(self, *args, **kwargs):
  5. QTableWidget.__init__(self, *args, **kwargs)
  6. self.setDragEnabled(True)
  7. self.setAcceptDrops(True)
  8. self.viewport().setAcceptDrops(True)
  9. self.setDragDropOverwriteMode(False)
  10. self.setDropIndicatorShown(True)
  11. self.setSelectionMode(QAbstractItemView.SingleSelection)
  12. self.setSelectionBehavior(QAbstractItemView.SelectRows)
  13. self.setDragDropMode(QAbstractItemView.InternalMove)
  14. def dropEvent(self, event):
  15. if event.source() == self and (event.dropAction() == Qt.MoveAction or self.dragDropMode() == QAbstractItemView.InternalMove):
  16. success, row, col, topIndex = self.dropOn(event)
  17. if success:
  18. selRows = self.getSelectedRowsFast()
  19. top = selRows[0]
  20. # print 'top is %d'%top
  21. dropRow = row
  22. if dropRow == -1:
  23. dropRow = self.rowCount()
  24. # print 'dropRow is %d'%dropRow
  25. offset = dropRow - top
  26. # print 'offset is %d'%offset
  27. for i, row in enumerate(selRows):
  28. r = row + offset
  29. if r > self.rowCount() or r < 0:
  30. r = 0
  31. self.insertRow(r)
  32. # print 'inserting row at %d'%r
  33. selRows = self.getSelectedRowsFast()
  34. # print 'selected rows: %s'%selRows
  35. top = selRows[0]
  36. # print 'top is %d'%top
  37. offset = dropRow - top
  38. # print 'offset is %d'%offset
  39. for i, row in enumerate(selRows):
  40. r = row + offset
  41. if r > self.rowCount() or r < 0:
  42. r = 0
  43. for j in range(self.columnCount()):
  44. # print 'source is (%d, %d)'%(row, j)
  45. # print 'item text: %s'%self.item(row,j).text()
  46. source = QTableWidgetItem(self.item(row, j))
  47. # print 'dest is (%d, %d)'%(r,j)
  48. self.setItem(r, j, source)
  49. # Why does this NOT need to be here?
  50. # for row in reversed(selRows):
  51. # self.removeRow(row)
  52. event.accept()
  53. else:
  54. QTableView.dropEvent(event)
  55. def getSelectedRowsFast(self):
  56. selRows = []
  57. for item in self.selectedItems():
  58. if item.row() not in selRows:
  59. selRows.append(item.row())
  60. return selRows
  61. def droppingOnItself(self, event, index):
  62. dropAction = event.dropAction()
  63. if self.dragDropMode() == QAbstractItemView.InternalMove:
  64. dropAction = Qt.MoveAction
  65. if event.source() == self and event.possibleActions() & Qt.MoveAction and dropAction == Qt.MoveAction:
  66. selectedIndexes = self.selectedIndexes()
  67. child = index
  68. while child.isValid() and child != self.rootIndex():
  69. if child in selectedIndexes:
  70. return True
  71. child = child.parent()
  72. return False
  73. def dropOn(self, event):
  74. if event.isAccepted():
  75. return False, None, None, None
  76. index = QModelIndex()
  77. row = -1
  78. col = -1
  79. if self.viewport().rect().contains(event.pos()):
  80. index = self.indexAt(event.pos())
  81. if not index.isValid() or not self.visualRect(index).contains(event.pos()):
  82. index = self.rootIndex()
  83. if self.model().supportedDropActions() & event.dropAction():
  84. if index != self.rootIndex():
  85. dropIndicatorPosition = self.position(event.pos(), self.visualRect(index), index)
  86. if dropIndicatorPosition == QAbstractItemView.AboveItem:
  87. row = index.row()
  88. col = index.column()
  89. # index = index.parent()
  90. elif dropIndicatorPosition == QAbstractItemView.BelowItem:
  91. row = index.row() + 1
  92. col = index.column()
  93. # index = index.parent()
  94. else:
  95. row = index.row()
  96. col = index.column()
  97. if not self.droppingOnItself(event, index):
  98. # print 'row is %d'%row
  99. # print 'col is %d'%col
  100. return True, row, col, index
  101. return False, None, None, None
  102. def position(self, pos, rect, index):
  103. r = QAbstractItemView.OnViewport
  104. margin = 2
  105. if pos.y() - rect.top() < margin:
  106. r = QAbstractItemView.AboveItem
  107. elif rect.bottom() - pos.y() < margin:
  108. r = QAbstractItemView.BelowItem
  109. elif rect.contains(pos, True):
  110. r = QAbstractItemView.OnItem
  111. if r == QAbstractItemView.OnItem and not (self.model().flags(index) & Qt.ItemIsDropEnabled):
  112. r = QAbstractItemView.AboveItem if pos.y() < rect.center().y() else QAbstractItemView.BelowItem
  113. return r