PyQt - Drag & Drop



Drag and Drop is a method of moving images or files on a computer by clicking and dragging them from one location to another using a mouse or other pointing device. It is found in many desktop applications where the user can copy or move objects from one window to another.

Multipurpose Internet Mail Extensions(MIME) based drag and drop data transfer is based on QDrag class. QMimeData objects associate the data with their corresponding MIME type. It is stored on the clipboard and then used in the drag and drop process.

MIME is a standard format for e-mail messages that support sets of character text other than ASCII. This extension also supports to various format such as audio, images, video, and application programs.

The following QMimeData class functions allow the MIME type to be detected and used conveniently.

Tester Getter Setter MIME Types
hasText() text() setText() text/plain
hasHtml() html() setHtml() text/html
hasUrls() urls() setUrls() text/uri-list
hasImage() imageData() setImageData() image/ *
hasColor() colorData() setColorData() application/x-color

Many QWidget objects support the drag and drop activity. Those that allow their data to be dragged have setDragEnabled() which must be set to true. On the other hand, the widgets should respond to the drag and drop events in order to store the data dragged into them.

  • DragEnterEvent provides an event which is sent to the target widget as dragging action enters it.

  • DragMoveEvent is used when the drag and drop action is in progress.

  • DragLeaveEvent is generated as the drag and drop action leaves the widget.

  • DropEvent, on the other hand, occurs when the drop is completed. The events proposed action can be accepted or rejected conditionally.

Example 1

In the following example, we illustrate the concept of events and demonstrate the dragging widgets from one location to another.

from PyQt6.QtCore import QMimeData, Qt
from PyQt6.QtGui import QDrag, QPixmap
from PyQt6.QtWidgets import QApplication, QHBoxLayout, QPushButton, QWidget


class DragButton(QPushButton):
   def mouseMoveEvent(self, e):
      if e.buttons() == Qt.MouseButton.LeftButton:
         drag = QDrag(self)
         mime = QMimeData()
         drag.setMimeData(mime)

         pixmap = QPixmap(self.size())
         self.render(pixmap)
         drag.setPixmap(pixmap)

         drag.exec(Qt.DropAction.MoveAction)
class Window(QWidget):
   def __init__(self):
      super().__init__()
      self.setAcceptDrops(True)

      self.blayout = QHBoxLayout()
      for l in ["A", "B", "C", "D"]:
         btn = DragButton(l)
         self.blayout.addWidget(btn)

      self.setLayout(self.blayout)

   def dragEnterEvent(self, e):
      e.accept()

   def dropEvent(self, e):
      pos = e.position()
      widget = e.source()
      self.blayout.removeWidget(widget)

      for n in range(self.blayout.count()):
         # Get the widget at each index in turn.
         w = self.blayout.itemAt(n).widget()
         if pos.x() < w.x() + w.size().width() // 2:
            # We didn't drag past this widget.
            # insert to the left of it.
            break
         else:
            # We aren't on the left hand side of any widget,
            # so we're at the end. Increment 1 to insert after.
            n += 1

      self.blayout.insertWidget(n, widget)
      e.accept()

app = QApplication([])
w = Window()
w.show()

app.exec()

Output

The above code produces the following output −

Default Output −

pyqt_drag_and_drop_ex_one

Now, we drag and drop to change the positioning of widgets.

pyqt_drag_and_drop_ex_two

Example 2

Following example demonstrate the code snippet for drag and drop using PyQt5.

import os
import sys
from PyQt5.QtWidgets import QWidget, QVBoxLayout
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QLabel
from PyQt5.QtGui import QPixmap
class ImageLabel(QLabel):
   def __init__(self):
      super().__init__()

      self.setAlignment(Qt.AlignCenter)
      self.setText('\n\n Drop Image Here \n\n')
      self.setStyleSheet('''
         QLabel{
            border: 4px dashed #aaa
         }
      ''')

   def setPixmap(self, image):
      super().setPixmap(image)

class AppDemo(QWidget):
   def __init__(self):
      super().__init__()
      self.resize(400, 400)
      self.setAcceptDrops(True)

      mainLayout = QVBoxLayout()

      self.photoViewer = ImageLabel()
      mainLayout.addWidget(self.photoViewer)

      self.setLayout(mainLayout)

   def dragEnterEvent(self, event):
      if event.mimeData().hasImage:
         event.accept()
      else:
         event.ignore()

   def dragMoveEvent(self, event):
      if event.mimeData().hasImage:
         event.accept()
      else:
         event.ignore()

   def dropEvent(self, event):
      if event.mimeData().hasImage:
         event.setDropAction(Qt.CopyAction)
         file_path = event.mimeData().urls()[0].toLocalFile()
         self.set_image(file_path)

         event.accept()
      else:
         event.ignore()

   def set_image(self, file_path):
      self.photoViewer.setPixmap(QPixmap(file_path))

app = QApplication(sys.argv)
demo = AppDemo()
demo.show()
sys.exit(app.exec())

Output

The above code produces the following output-

drag_and_drop
Advertisements