QT: arrastrar y soltar filas internas en QTableView, que cambia el orden de las filas en QTableModel
model-view-controller drag-and-drop (1)
Usted tiene varios problemas en su código, abordaré solo dos aquí:
- Está llamando a
Model.moveRows
con los argumentos incorrectos:
cambieself.model().moveRows(QModelIndex(), 0, 0, QModelIndex(), 1)
porself.model().moveRows(QModelIndex(), 1, 1, QModelIndex(), 0)
- Está cambiando sus datos de la manera incorrecta:
changeself.data = self.data[1] + self.data[0] + self.data[2]
porself.data = [self.data[1], self.data[0] , self.data[2]]
Nota: el problema 1 es el que está provocando la excepción en su código. También tenga en cuenta que es una mala idea nombrar una variable de instancia y una función igual ( Model.data
)
Quiero realizar un tipo de filas en QTableView, de modo que el TableModel subyacente tenga sus datos ordenados también:
Si no me equivoco, los ordenamientos incorporados en QTableView no afectan el orden de las filas en TableModel subyacente, así que tuve que escribir una implementación personalizada de QTableView y QAbstractTableModel personalizada de arrastrar y soltar interno.
Para probar, si funciona, respondo a cualquier arrastre de celda reordenando la primera y la segunda fila:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Model(QAbstractTableModel):
def __init__(self):
QAbstractTableModel.__init__(self, parent=None)
self.data = [("elem1", "ACDC"), ("elem2", "GUNSNROSES"), ("elem3", "UFO")]
self.setSupportedDragActions(Qt.MoveAction)
def flags(self, index):
if index.isValid():
return Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled | Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
else:
return Qt.ItemIsDropEnabled | Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
def rowCount(self, parent=QModelIndex()):
return len(self.data)
def columnCount(self, parent=QModelIndex()):
return 1
def data(self, index, role):
if role == Qt.DisplayRole:
print "row = %s" % int(index.row())
return QVariant(self.data[int(index.row())][1])
return QVariant()
def headerData(self, index, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return QVariant(str(index))
elif orientation == Qt.Vertical and role == Qt.DisplayRole:
return self.data[index][0]
def dragMoveEvent(self, event):
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
def moveRows(self, parent, source_first, source_last, parent2, dest):
print "moveRows called, self.data = %s" % self.data
self.beginMoveRows(parent, source_first, source_last, parent2, dest)
self.data = self.data[1] + self.data[0] + self.data[2]
self.endMoveRows()
print "moveRows finished, self.data = %s" % self.data
class View(QTableView):
def __init__(self, parent=None):
QTableView.__init__(self, parent=None)
self.setSelectionMode(self.ExtendedSelection)
self.setDragEnabled(True)
self.acceptDrops()
self.setDragDropMode(self.InternalMove)
self.setDropIndicatorShown(True)
def dragEnterEvent(self, event):
event.accept()
def dragMoveEvent(self, event):
event.accept()
def dropEvent(self, event):
print "dropEvent called"
point = event.pos()
self.model().moveRows(QModelIndex(), 0, 0, QModelIndex(), 1)
event.accept()
def mousePressEvent(self, event):
print "mousePressEvent called"
self.startDrag(event)
def startDrag(self, event):
print "startDrag called"
index = self.indexAt(event.pos())
if not index.isValid():
return
self.moved_data = self.model().data[index.row()]
drag = QDrag(self)
mimeData = QMimeData()
mimeData.setData("application/blabla", "")
drag.setMimeData(mimeData)
pixmap = QPixmap()
pixmap = pixmap.grabWidget(self, self.visualRect(index))
drag.setPixmap(pixmap)
result = drag.start(Qt.MoveAction)
class Application(object):
def __init__(self):
app = QApplication(sys.argv)
self.window = QWidget()
self.window.show()
layout = QVBoxLayout(self.window)
self.view = View()
self.view.setModel(Model())
layout.addWidget(self.view)
sys.exit(app.exec_())
Por alguna razón, este código no funciona. Inicia con éxito el arrastre (bueno, casi con éxito, porque muestra la fila anterior, en lugar del actual como el icono de arrastrar), invoca mousePressEvent
, startDrag
, dropEvent
y moveRows
, pero luego muere en moveRows
con el mensaje:
Qt has caught an exception thrown from an event handler. Throwing
exceptions from an event handler is not supported in Qt. You must
reimplement QApplication::notify() and catch all exceptions there.
Qt has caught an exception thrown from an event handler. Throwing
exceptions from an event handler is not supported in Qt. You must
reimplement QApplication::notify() and catch all exceptions there.
terminate called after throwing an instance of ''std::bad_alloc''
what(): std::bad_alloc
Aborted
(La duplicación del párrafo en el mensaje de error es intencional, eso es lo que se produce al pie de la letra).
¿Cómo depuro este error? (insertando try
- except
en moveRows
no ayuda)
¿Tiene una mejor receta para realizar arrastrar y soltar internamente, lo que afecta al modelo en las vistas de tabla?