在PyQt4中进行多次拖放

Gia*_*lli 1 python drag-and-drop pyqt pyqt4

我找不到一个用Qt/PyQt拖动(和删除)多个元素的例子; 在我的情况下,我需要从这个QTableView拖动元素:

class DragTable(QTableView):
    def __init__(self, parent = None):
        super(DragTable, self).__init__(parent)
        self.setDragEnabled(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat("application/pubmedrecord"):
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()

    def startDrag(self, event):
        print type(event)
        index = self.indexAt(event.pos())
        if not index.isValid():
            return

        selected = index.row()
        bstream = cPickle.dumps(selected)
        mimeData = QMimeData()
        mimeData.setData("application/pubmedrecord", bstream)
        drag = QDrag(self)
        drag.setMimeData(mimeData)
        pixmap = QPixmap(":/drag.png")

        drag.setHotSpot(QPoint(pixmap.width()/3, pixmap.height()/3))
        drag.setPixmap(pixmap)
        result = drag.start(Qt.MoveAction)

    def mouseMoveEvent(self, event):
        self.startDrag(event)
Run Code Online (Sandbox Code Playgroud)

对于这个QLabel(我的dropzone):

class TagLabel(QLabel):
    def __init__(self, text, color, parent = None):
        super(TagLabel, self).__init__(parent)
        self.tagColor = color
        self.setText(text)
        self.setStyleSheet("QLabel { background-color: %s; font-size: 14pt; }" % self.tagColor)
        self.defaultStyle = self.styleSheet()
        self.setAlignment(Qt.AlignHCenter|Qt.AlignVCenter)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat("application/pubmedrecord"):
            self.set_bg(True)
            event.accept()
        else:
            event.reject()

    def dragLeaveEvent(self, event):
        self.set_bg(False)
        event.accept()

    def dropEvent(self, event):
        self.set_bg(False)
        data = event.mimeData()
        bstream = data.retrieveData("application/pubmedrecord", QVariant.ByteArray)
        selected = pickle.loads(bstream.toByteArray())
        event.accept()
        self.emit(SIGNAL("dropAccepted(PyQt_PyObject)"), (selected, str(self.text()), str(self.tagColor)))

    def set_bg(self, active = False):
        if active:
            style = "QLabel {background: yellow; font-size: 14pt;}"
            self.setStyleSheet(style)
        else:
            self.setStyleSheet(self.defaultStyle)
Run Code Online (Sandbox Code Playgroud)

有小费吗?谢谢!

Ivo*_*Ivo 6

这是一个完整的工作示例:

from PyQt4 import QtCore, QtGui, Qt
import cPickle
import pickle
Run Code Online (Sandbox Code Playgroud)

你为什么用得cPickle那么好pickle

class DragTable(QtGui.QTableView):
    def __init__(self, parent = None):
        super(DragTable, self).__init__(parent)
        self.setDragEnabled(True)
        self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
Run Code Online (Sandbox Code Playgroud)

您可能想在此处设置选择行为,因为我假设基于行的数据表示.你当然可以改变它.

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat("application/pubmedrecord"):
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()

    def startDrag(self, event):
Run Code Online (Sandbox Code Playgroud)

根据事件位置,您的代码在此处仅假定一个索引.对于a QTableView,这是不必要的,因为它已经处理鼠标单击本身.相反,最好依靠Qt 一如既往地为您提供实际需要的信息.在这里,我选择使用selectedIndexes().

        indices = self.selectedIndexes()
Run Code Online (Sandbox Code Playgroud)

索引现在是一个QModelIndex实例列表,我选择将其转换为一组行号.QPersistentModelIndex根据您的需要,也可以将这些转换为es 列表.

这里可能会让您大吃一惊的是,索引包含表中所有单元格的索引,而不是所有行,无论选择行为如何.这就是为什么我选择使用set而不是a list.

        selected = set()
        for index in indices:
            selected.add(index.row())
Run Code Online (Sandbox Code Playgroud)

我假设你知道你在那里做什么,我没有动过.

        bstream = cPickle.dumps(selected)
        mimeData = QtCore.QMimeData()
        mimeData.setData("application/pubmedrecord", bstream)
        drag = QtGui.QDrag(self)
        drag.setMimeData(mimeData)
        pixmap = QtGui.QPixmap(":/drag.png")

        drag.setHotSpot(QtCore.QPoint(pixmap.width()/3, pixmap.height()/3))
        drag.setPixmap(pixmap)
        result = drag.start(QtCore.Qt.MoveAction)

    def mouseMoveEvent(self, event):
        self.startDrag(event)


class TagLabel(QtGui.QLabel):
    def __init__(self, text, color, parent = None):
        super(TagLabel, self).__init__(parent)
        self.tagColor = color
        self.setText(text)
        self.setStyleSheet("QLabel { background-color: %s; font-size: 14pt; }" % self.tagColor)
        self.defaultStyle = self.styleSheet()
        self.setAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat("application/pubmedrecord"):
            self.set_bg(True)
            event.accept()
        else:
            event.reject()

    def dragLeaveEvent(self, event):
        self.set_bg(False)
        event.accept()

    def dropEvent(self, event):
        self.set_bg(False)
        data = event.mimeData()
        bstream = data.retrieveData("application/pubmedrecord", QtCore.QVariant.ByteArray)
        selected = pickle.loads(bstream.toByteArray())
        event.accept()
        self.emit(QtCore.SIGNAL("dropAccepted(PyQt_PyObject)"), (selected, str(self.text()), str(self.tagColor)))
Run Code Online (Sandbox Code Playgroud)

除非你使用这个信号连接C++代码,否则没有必要在这里添加一个信号参数,你也可以dropAccepted不使用括号,PyQt4会做正确的事情.

    def set_bg(self, active = False):
        if active:
            style = "QLabel {background: yellow; font-size: 14pt;}"
            self.setStyleSheet(style)
        else:
            self.setStyleSheet(self.defaultStyle)



app = QtGui.QApplication([])

l = TagLabel("bla bla bla bla bla bla bla", "red")
l.show()

m = QtGui.QStandardItemModel()
for _ in xrange(4):
    m.appendRow([QtGui.QStandardItem(x) for x in ["aap", "noot", "mies"]])

t = DragTable()
t.setModel(m)
t.show()

def h(o):
    print "signal handled", o
l.connect(l, QtCore.SIGNAL("dropAccepted(PyQt_PyObject)"), h)

app.exec_()
Run Code Online (Sandbox Code Playgroud)