Drag and Drop QLabels with PyQt5

10,825

In the case of QLabel you must create everything from the beginning, for this you can follow the examples of the docs.

In the following example I have placed an example where one class only accepts the drop and the other the drag so that you can see each part and understand better.

import sys

from PyQt5.QtWidgets import QApplication, QLabel, QWidget
from PyQt5.QtGui import QDrag, QPixmap, QPainter, QCursor
from PyQt5.QtCore import QMimeData, Qt


class DraggableLabel(QLabel):
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_start_position = event.pos()

    def mouseMoveEvent(self, event):
        if not (event.buttons() & Qt.LeftButton):
            return
        if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
            return
        drag = QDrag(self)
        mimedata = QMimeData()
        mimedata.setText(self.text())
        drag.setMimeData(mimedata)
        pixmap = QPixmap(self.size())
        painter = QPainter(pixmap)
        painter.drawPixmap(self.rect(), self.grab())
        painter.end()
        drag.setPixmap(pixmap)
        drag.setHotSpot(event.pos())
        drag.exec_(Qt.CopyAction | Qt.MoveAction)

class DropLabel(QLabel):
    def __init__(self, *args, **kwargs):
        QLabel.__init__(self, *args, **kwargs)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasText():
            event.acceptProposedAction()

    def dropEvent(self, event):
        pos = event.pos()
        text = event.mimeData().text()
        self.setText(text)
        event.acceptProposedAction()


class Widget(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        label = DropLabel("drop there",self)
        label.setGeometry(190, 65, 100,100)

        label_to_drag = DraggableLabel("drag this",self)  
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

Update:

Do not confuse the QDrag with the QMimeData, if you want to use imageData() you must set it with setImageData() as indicated by the docs:

class DraggableLabel(QLabel):
    def __init__(self,parent,image):
        super(QLabel,self).__init__(parent)
        self.setPixmap(QPixmap(image))    
        self.show()
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_start_position = event.pos()

    def mouseMoveEvent(self, event):
        if not (event.buttons() & Qt.LeftButton):
            return
        if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
            return
        drag = QDrag(self)
        mimedata = QMimeData()
        mimedata.setText(self.text())
        mimedata.setImageData(self.pixmap().toImage())

        drag.setMimeData(mimedata)
        pixmap = QPixmap(self.size())
        painter = QPainter(pixmap)
        painter.drawPixmap(self.rect(), self.grab())
        painter.end()
        drag.setPixmap(pixmap)
        drag.setHotSpot(event.pos())
        drag.exec_(Qt.CopyAction | Qt.MoveAction)

class my_label(QLabel):
    def __init__(self,title,parent):
        super().__init__(title,parent)
        self.setAcceptDrops(True)

    def dragEnterEvent(self,event):
        if event.mimeData().hasImage():
            print("event accepted")
            event.accept()
        else:
            print("event rejected")
            event.ignore()
    def dropEvent(self,event):
        if event.mimeData().hasImage():
            self.setPixmap(QPixmap.fromImage(QImage(event.mimeData().imageData())))
Share:
10,825
EinderJam
Author by

EinderJam

Updated on June 04, 2022

Comments

  • EinderJam
    EinderJam almost 2 years

    I'm trying to drag and drop a Qlabel on another Qlabel with PyQt5:

    from PyQt5.QtWidgets import QApplication, QWidget, QToolTip, QPushButton, QMessageBox, QHBoxLayout, QVBoxLayout, QGridLayout,QFrame, QComboBox, QLabel, QLineEdit
    from PyQt5.QtGui import QIcon, QFont, QPixmap, QImage
    import sys
    
    
    class my_label(QLabel):
        def __init__(self,title,parent):
            super().__init__(title,parent)
            self.setAcceptDrops(True)
    
        def dragEnterEvent(self,event):
            if event.mimeData().hasFormat("text/plain"):
                event.accept()
            else:
                event.ignore()
        def dropEvent(self,event):
            self.setText(event.mimeData().text())
    
    
    class application(QWidget):
    
        def __init__(self):
            super().__init__()
            self.initUI()
    
        def initUI(self):
    
    
            label = my_label("drop there",self)
            label.resize(100,100)
            label.move(190,65)
    
            label_to_drag = QLabel("drag this",self)
            #label_to_drag.setDragEnabled(True)    #doesn't work with QLabel        
            self.show()
    
    
        def closeEvent(self,event):
            message = QMessageBox.question(self,"Message","Quit ?",QMessageBox.Yes | QMessageBox.No,QMessageBox.No)
    
            if message == QMessageBox.Yes:
                event.accept()
            else:
                event.ignore()
    
    app = QApplication(sys.argv)
    fenetre = application()
    sys.exit(app.exec_())
    

    I wanted to make my first label "draggable",by calling the setDragEnabled(True) method (as i was doing for QLineEdit),and drop it on the second label to change his text. Dropping text from another app is working fine,but I can't find how to drag the QLabel inside my own app... What did I miss ?

    EDIT : modified the code to try to Drag&Drop Images :

    class DraggableLabel(QLabel):
    def __init__(self,parent,image):
        super(QLabel,self).__init__(parent)
        self.setPixmap(QPixmap(image))    
        self.show()
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_start_position = event.pos()
    
    def mouseMoveEvent(self, event):
        if not (event.buttons() & Qt.LeftButton):
            return
        if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
            return
        drag = QDrag(self)
        mimedata = QMimeData()
        mimedata.setText(self.text())
    
        drag.setMimeData(mimedata)
        pixmap = QPixmap(self.size())
        painter = QPainter(pixmap)
        painter.drawPixmap(self.rect(), self.grab())
        painter.end()
        drag.setPixmap(pixmap)
        drag.setHotSpot(event.pos())
        drag.exec_(Qt.CopyAction | Qt.MoveAction)
    
       class my_label(QLabel):
        def __init__(self,title,parent):
            super().__init__(title,parent)
            self.setAcceptDrops(True)
    
        def dragEnterEvent(self,event):
            if event.mimeData().hasFormat("text/plain"):
                print("event accepted")
                event.accept()
            else:
                print("event rejected")
                event.ignore()
        def dropEvent(self,event):
            if event.mimeData().hasImage():
                self.setPixmap(QPixmap.fromImage(QImage(event.mimeData().imageData())))
    
    class application(QWidget):
    
        def __init__(self):
            super().__init__()
            self.initUI()
    
        def initUI(self):
    
            label_to_drag = DraggableLabel(self,"index.jpg")
    
            label = my_label("drop there",self)
            label.resize(100,100)
            label.move(190,65)
    
    
    
            self.show()
    

    When I drop the DraggableLabel (displaying the image) on my_label,the event is accepted, but hasImage() always returns false...Is the way I set the image up wrong ?

  • EinderJam
    EinderJam almost 6 years
    Thanks ! I've another question : I tried to display an image with setPixmap(),then drag this image over another label,and make this label display the same image than the first one,but it doesn't work (see edit)
  • eyllanesc
    eyllanesc almost 6 years
    @EinderJam A QLabel does not show the text or shows the QPixmap, it can not be displayed both. Which edition? If my answer helps you, do not forget to mark it as correct, if you do not know how to do it, review the tour, that is the best way to thank.
  • Elan
    Elan over 3 years
    very helpful answer. Thanks +1