Многократное перетаскивание в PyQt4

1

Я не могу найти пример перетаскивания (и удаления) нескольких элементов с помощью 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)

В эту QLabel (My 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)

Какие-нибудь советы? Спасибо!

Теги:
pyqt4
drag-and-drop
pyqt

1 ответ

6
Лучший ответ

Вот полный рабочий пример:

from PyQt4 import QtCore, QtGui, Qt
import cPickle
import pickle

Почему вы используете cPickle, а также pickle?

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

Вероятно, вы захотите установить здесь поведение выбора, потому что я предполагаю представление данных на основе строк. Вы можете, конечно, изменить это.

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

    def startDrag(self, event):

В вашем коде предполагается только один индекс, основанный на позиции события. Для QTableView это необязательно, поскольку он уже обрабатывает щелчок мыши. Вместо этого лучше зависеть от Qt, чтобы предоставить вам необходимую вам информацию, как всегда. Здесь я решил использовать selectedIndexes().

        indices = self.selectedIndexes()

Индексы теперь представляют собой список экземпляров QModelIndex, которые я решил преобразовать в набор номеров строк. Также возможно преобразовать их в список QPersistentModelIndex es, в зависимости от ваших потребностей.

Одна вещь, которая может вас удивить, состоит в том, что индексы содержат индексы для всех ячеек в таблице, а не все строки, независимо от поведения выбора. Поэтому я решил использовать set вместо list.

        selected = set()
        for index in indices:
            selected.add(index.row())

Я оставил остальное нетронутым, предполагая, что вы знаете, что вы там делаете.

        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)))

Если вы не взаимодействуете с С++-кодом с этим сигналом, нет необходимости добавлять здесь аргумент сигнала, вы также можете использовать 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_()
  • 0
    Хорошее объяснение, я не мог просить больше :) Спасибо!
  • 0
    Почему это не работает с QListWidget вместо QLabel?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню