我希望在QTableView中对行进行排序,以便底层的TableModel也能按照排序后的数据显示:
![enter image description here](https://istack.dev59.com/qoie3.webp)
如果我没搞错的话,在QTableView中内置的排序并不会影响底层的TableModel中的行顺序,所以我不得不编写自定义的QTableView和自定义的QAbstractTableModel实现内部拖放。
为了测试它是否起作用,我通过重新排序第一行和第二行来响应任何单元格的拖动:
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_())
由于某种原因,该代码无法正常工作。它成功启动了拖动(几乎成功,因为它显示的是上一行,而不是当前行作为拖动图标),调用了 mousePressEvent
、startDrag
、dropEvent
和 moveRows
函数,但是在 moveRows
中出现错误,并显示以下信息:
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
错误信息中的段落重复是故意的 - 这就是它按原样输出的内容。
我该如何调试此错误?(在moveRows
中插入try
-except
并不起作用)
您是否有更好的方法执行影响表视图模型的内部拖放操作?
self.data
,它已经被绑定了: http://doc.qt.io/qt-4.8/qabstractitemmodel.html#data - Flint