【问题标题】:QT: internal drag and drop of rows in QTableView, that changes order of rows in QTableModelQT:QTableView中行的内部拖放,改变QTableModel中行的顺序
【发布时间】:2014-03-17 12:45:52
【问题描述】:

我想在 QTableView 中对行进行排序,以便底层 TableModel 也对其数据进行排序:

如果我没记错的话,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_())

由于某种原因,此代码不起作用。它成功开始拖动(嗯,几乎成功,因为它显示上一行,而不是当前行作为拖动图标),调用 mousePressEventstartDragdropEventmoveRows 函数,但随后死亡在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 没有帮助)

您是否有更好的方法来执行内部拖放,从而影响 tableview 中的模型?

【问题讨论】:

标签: qt model-view-controller drag-and-drop pyqt


【解决方案1】:

您的代码中有几个问题,我将在这里仅解决两个问题:

  1. 您使用错误的参数调用Model.moveRows
    更改self.model().moveRows(QModelIndex(), 0, 0, QModelIndex(), 1)
    self.model().moveRows(QModelIndex(), 1, 1, QModelIndex(), 0)
  2. 您以错误的方式更改数据:
    更改self.data = self.data[1] + self.data[0] + self.data[2]
    self.data = [self.data[1], self.data[0] , self.data[2]]

注意:问题 1 是在您的代码中引发异常的问题。另请注意,将实例变量和函数命名为相同是一个坏主意 (Model.data)

【讨论】:

  • 为了移动单个项目,我使用了这个:morf.lv/modules.php?name=tutorials&lasit=9 结合了您在答案中显示的一些概念。谢谢。
  • @xndrme 数据的更改没有区别,但由于它与数据模型相关,因此可以弄清楚如何处理它(交换或插入)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-14
  • 1970-01-01
  • 2016-07-07
相关资源
最近更新 更多