QTableView中行的内部拖放

QTableView中行的内部拖放

本文介绍了QT:QTableView中行的内部拖放,改变QTableModel中行的顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to perform a sort of rows in QTableView, so that the underlying TableModel would have its data sorted, too:

如果我没记错的话,QTableView 中的内置排序不会影响底层 TableModel 中的行顺序,所以我不得不编写自定义 QTableView 和自定义 QAbstractTableModel 内部拖放实现.

If I'm not mistaken, built-in sorts in QTableView don't affect the order of rows in underlying TableModel, so I had to write a custom QTableView and custom QAbstractTableModel implementation of internal drag and drop.

为了测试它是否有效,我通过重新排序第一行和第二行来响应任何单元格的拖动:

To test, if it works at all, I respond to any drag of cell by reordering first and second rows:

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 内死亡,并显示消息:

For some reason, this code doesn't work. It successfully starts the drag (well, almost successfully, cause it shows the previous row, instead of the current one as the drag icon), invokes mousePressEvent, startDrag, dropEvent and moveRows function, but then dies within moveRows with message:

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

(错误消息中的段落重复是故意的 - 这就是它逐字输出的内容).

(Duplication of paragraph in error message is intentional - that's what it outputs verbatim).

如何调试此错误?(在 moveRows 中插入 try-except 没有帮助)

How do I debug this error? (inserting try-except in moveRows doesn't help)

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

Do you have a better recipe for performing internal drag-and-drops, affecting the model in tableviews?

推荐答案

你的代码有几个问题,我在这里只解决两个:

You have several problems in your code I will address just two here:

  1. 您使用错误的参数调用 Model.moveRows:
    更改 self.model().moveRows(QModelIndex(), 0, 0, QModelIndex(), 1)
    by 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)

Note: problem 1 is the one who is provoking the exception on your code. Also note that is a bad idea naming an instance variable and a function the same (Model.data)

这篇关于QT:QTableView中行的内部拖放,改变QTableModel中行的顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-20 17:35