Questo sembra comportamento predefinito molto bizzarro. Comunque, seguendo il codice nello bug report you linked to, ho portato con successo qualcosa a PyQt. Potrebbe, o potrebbe non essere robusto come quel codice, ma almeno sembra funzionare per il semplice caso di test che fornisci nei tuoi screenshot!
I potenziali problemi con l'implementazione di seguito sono:
La riga attualmente selezionata non segue il drag and drop (quindi se si sposta la terza fila, la terza fila rimane selezionato dopo la mossa) . Questo probabilmente non è troppo difficile da risolvere!
Potrebbe non funzionare per righe con righe figlio. Non sono nemmeno sicuro che un QTableWidgetItem
possa avere figli, quindi forse va bene.
non ho testato con selezionando più righe, ma penso che dovrebbe funzionare
Per qualche ragione non ho dovuto rimuovere la riga che veniva spostato, nonostante l'inserimento di una nuova riga nella tavolo. Questo mi sembra molto strano. Sembra quasi come inserire una riga ovunque ma la fine non aumenta il rowCount()
della tabella.
La mia implementazione di GetSelectedRowsFast
è leggermente diversa dalla loro. Potrebbe non essere veloce e potrebbe avere qualche bug in esso (non controllo se gli oggetti sono abilitati o selezionabili) come hanno fatto. Questo sarebbe anche facile da risolvere, penso, ma è solo un problema se si disabilita una riga mentre è selezionata e qualcuno quindi esegue un drag/drop. In questa situazione, penso che la soluzione migliore potrebbe essere quella di deselezionare le righe in quanto disabilitate, ma dipende da cosa si sta facendo con esso, immagino!
Se si sta utilizzando questo codice in un ambiente di produzione, si sarebbe probabilmente vuole andare su di esso con un pettine a denti-pettine e assicurarsi che tutto aveva un senso. Ci sono probabilmente problemi con la mia porta PyQt, e probabilmente problemi con l'algoritmo C++ originale su cui si basava la mia porta. Tuttavia, serve come una prova che ciò che si desidera può essere raggiunto utilizzando un QTableWidget
.
Codice:
import sys, os
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class TableWidgetDragRows(QTableWidget):
def __init__(self, *args, **kwargs):
QTableWidget.__init__(self, *args, **kwargs)
self.setDragEnabled(True)
self.setAcceptDrops(True)
self.viewport().setAcceptDrops(True)
self.setDragDropOverwriteMode(False)
self.setDropIndicatorShown(True)
self.setSelectionMode(QAbstractItemView.SingleSelection)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setDragDropMode(QAbstractItemView.InternalMove)
def dropEvent(self, event):
if event.source() == self and (event.dropAction() == Qt.MoveAction or self.dragDropMode() == QAbstractItemView.InternalMove):
success, row, col, topIndex = self.dropOn(event)
if success:
selRows = self.getSelectedRowsFast()
top = selRows[0]
# print 'top is %d'%top
dropRow = row
if dropRow == -1:
dropRow = self.rowCount()
# print 'dropRow is %d'%dropRow
offset = dropRow - top
# print 'offset is %d'%offset
for i, row in enumerate(selRows):
r = row + offset
if r > self.rowCount() or r < 0:
r = 0
self.insertRow(r)
# print 'inserting row at %d'%r
selRows = self.getSelectedRowsFast()
# print 'selected rows: %s'%selRows
top = selRows[0]
# print 'top is %d'%top
offset = dropRow - top
# print 'offset is %d'%offset
for i, row in enumerate(selRows):
r = row + offset
if r > self.rowCount() or r < 0:
r = 0
for j in range(self.columnCount()):
# print 'source is (%d, %d)'%(row, j)
# print 'item text: %s'%self.item(row,j).text()
source = QTableWidgetItem(self.item(row, j))
# print 'dest is (%d, %d)'%(r,j)
self.setItem(r, j, source)
# Why does this NOT need to be here?
# for row in reversed(selRows):
# self.removeRow(row)
event.accept()
else:
QTableView.dropEvent(event)
def getSelectedRowsFast(self):
selRows = []
for item in self.selectedItems():
if item.row() not in selRows:
selRows.append(item.row())
return selRows
def droppingOnItself(self, event, index):
dropAction = event.dropAction()
if self.dragDropMode() == QAbstractItemView.InternalMove:
dropAction = Qt.MoveAction
if event.source() == self and event.possibleActions() & Qt.MoveAction and dropAction == Qt.MoveAction:
selectedIndexes = self.selectedIndexes()
child = index
while child.isValid() and child != self.rootIndex():
if child in selectedIndexes:
return True
child = child.parent()
return False
def dropOn(self, event):
if event.isAccepted():
return False, None, None, None
index = QModelIndex()
row = -1
col = -1
if self.viewport().rect().contains(event.pos()):
index = self.indexAt(event.pos())
if not index.isValid() or not self.visualRect(index).contains(event.pos()):
index = self.rootIndex()
if self.model().supportedDropActions() & event.dropAction():
if index != self.rootIndex():
dropIndicatorPosition = self.position(event.pos(), self.visualRect(index), index)
if dropIndicatorPosition == QAbstractItemView.AboveItem:
row = index.row()
col = index.column()
# index = index.parent()
elif dropIndicatorPosition == QAbstractItemView.BelowItem:
row = index.row() + 1
col = index.column()
# index = index.parent()
else:
row = index.row()
col = index.column()
if not self.droppingOnItself(event, index):
# print 'row is %d'%row
# print 'col is %d'%col
return True, row, col, index
return False, None, None, None
def position(self, pos, rect, index):
r = QAbstractItemView.OnViewport
margin = 2
if pos.y() - rect.top() < margin:
r = QAbstractItemView.AboveItem
elif rect.bottom() - pos.y() < margin:
r = QAbstractItemView.BelowItem
elif rect.contains(pos, True):
r = QAbstractItemView.OnItem
if r == QAbstractItemView.OnItem and not (self.model().flags(index) & Qt.ItemIsDropEnabled):
r = QAbstractItemView.AboveItem if pos.y() < rect.center().y() else QAbstractItemView.BelowItem
return r
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
layout = QHBoxLayout()
self.setLayout(layout)
self.table_widget = TableWidgetDragRows()
layout.addWidget(self.table_widget)
# setup table widget
self.table_widget.setColumnCount(2)
self.table_widget.setHorizontalHeaderLabels(['Colour', 'Model'])
items = [('Red', 'Toyota'), ('Blue', 'RV'), ('Green', 'Beetle')]
for i, (colour, model) in enumerate(items):
c = QTableWidgetItem(colour)
m = QTableWidgetItem(model)
self.table_widget.insertRow(self.table_widget.rowCount())
self.table_widget.setItem(i, 0, c)
self.table_widget.setItem(i, 1, m)
self.show()
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
qt-project.org è morto, ora al bug https://bugreports.qt.io/browse/QTBUG-13873 – handle