Перетаскивание (drag & drop) в PyQt5

В этой части руководства по PyQt5, мы поговорим об операциях drag & drop. В графических интерфейсах, drag & drop – это действие клика на виртуальный объект и перетаскивания его в другое положение или в другой виртуальный объект.

Drag & drop – это часть графического пользовательского интерфейса. Операции перетаскивания позволяют пользователям делать сложные вещи интуитивно.

Обычно, мы можем перетаскивать две вещи: данные или некие графические объекты. Если мы перетаскиваем изображение из одного приложения в другое, мы перетаскиваем двоичные данные. Если мы перетаскиваем таблицу в Firefox и перемещаем её в другое место, мы перетаскиваем графический компонент.

Простое перетаскивание

В первом примере, у нас есть QLineEdit и QPushButton. Мы перетаскиваем текст из виджета строки редактирования и помещаем его в виджет кнопки. Метка кнопки изменится.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import (QPushButton, QWidget,
    QLineEdit, QApplication)

class Button(QPushButton):

    def __init__(self, title, parent):
        super().__init__(title, parent)

        self.setAcceptDrops(True)


    def dragEnterEvent(self, e):

        if e.mimeData().hasFormat('text/plain'):
            e.accept()
        else:
            e.ignore()

    def dropEvent(self, e):

        self.setText(e.mimeData().text())


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):

        edit = QLineEdit('', self)
        edit.setDragEnabled(True)
        edit.move(30, 65)

        button = Button("Button", self)
        button.move(190, 65)

        self.setWindowTitle('Simple drag & drop')
        self.setGeometry(300, 300, 300, 150)


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    app.exec_()

Пример представляет простую операцию перетаскивания.

class Button(QPushButton):

    def __init__(self, title, parent):
        super().__init__(title, parent)

        self.setAcceptDrops(True)

Для того, чтобы перетащить текст на виджет QPushButton, мы должны переопределить несколько методов. По этой причине, мы создаём собственный класс Button, который наследует класс QPushButton.

self.setAcceptDrops(True)

Мы разрешаем события перетаскивания для виджета.

def dragEnterEvent(self, e):

    if e.mimeData().hasFormat('text/plain'):
        e.accept()
    else:
        e.ignore()

Сначала мы переопределяем метод dragEnterEvent(). Мы сообщаем о типе данных, который мы допускаем. В нашем случае, это обычный текст.

def dropEvent(self, e):

    self.setText(e.mimeData().text())

Путём переопределения метода dropEvent, мы определяем, что мы должны делать после события перетаскивания. Здесь мы меняем текст виджета кнопки.

edit = QLineEdit('', self)
edit.setDragEnabled(True)

Виджет QLineEdit имеет встроенную поддержку операций перетаскивания. Все, что необходимо сделать – это вызвать метод setDragEnabled(), чтобы активировать её.

Drag & Drop

Перетаскивание виджета кнопки

В следующем примере, мы продемонстрируем, как перетаскивать виджет кнопки.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QPushButton, QWidget, QApplication
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag


class Button(QPushButton):

    def __init__(self, title, parent):
        super().__init__(title, parent)


    def mouseMoveEvent(self, e):

        if e.buttons() != Qt.RightButton:
            return

        mimeData = QMimeData()

        drag = QDrag(self)
        drag.setMimeData(mimeData)
        drag.setHotSpot(e.pos() - self.rect().topLeft())

        dropAction = drag.exec_(Qt.MoveAction)


    def mousePressEvent(self, e):

        QPushButton.mousePressEvent(self, e)

        if e.button() == Qt.LeftButton:
            print('press')


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):

        self.setAcceptDrops(True)

        self.button = Button('Button', self)
        self.button.move(100, 65)

        self.setWindowTitle('Click or Move')
        self.setGeometry(300, 300, 280, 150)


    def dragEnterEvent(self, e):

        e.accept()


    def dropEvent(self, e):

        position = e.pos()
        self.button.move(position)

        e.setDropAction(Qt.MoveAction)
        e.accept()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    app.exec_()

В нашем примере кода, мы имеем QPushButton в окне. Если мы нажимаем на кнопку левой кнопкой мыши, в консоли печатается "press". С помощью перемещения кнопки с помощью правой кнопки мыши, мы выполняем операцию перетаскивания на виджет кнопки.

class Button(QPushButton):

    def __init__(self, title, parent):
        super().__init__(title, parent)

Мы создаём класс Button, который наследуется от QPushButton. Мы также переопределяем два метода: mouseMoveEvent() и mousePressEvent(). Метод mouseMoveEvent() – это место, где начинается операция перетаскивания.

if e.buttons() != Qt.RightButton:
    return

Здесь мы решили, что мы можем выполнять перетаскивание только с помощью правой кнопки мыши. Левая кнопка мыши резервируется для нажатия на кнопку.

mimeData = QMimeData()

drag = QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(e.pos() - self.rect().topLeft())

Создаётся объект QDrag. Класс обеспечивает поддержку перетаскивания данных, основанную на MIME-типе.

dropAction = drag.exec_(Qt.MoveAction)

Метод start() начинает операцию drag & drop.

def mousePressEvent(self, e):

    QPushButton.mousePressEvent(self, e)

    if e.button() == Qt.LeftButton:
        print('press')

Мы печатаем "press" в консоли, если мы делаем клик левой кнопкой мыши на кнопку. Обратите внмание, что мы также вызываем метод mousePressEvent() в родителе. В противном случае, мы не увидим, что кнопка была нажата (можете попробовать поэкспериментировать).

position = e.pos()
self.button.move(position)

В методе dropEvent(), мы определили, что произойдёт после того, как мы отпустим кнопку мыши и завершим операцию перетаскивания. Мы выясняем текущее положение указателя мыши и перемещаем кнопку соответствующим образом.

e.setDropAction(Qt.MoveAction)
e.accept()

Мы указываем тип действия перетаскивания. В нашем случае, это действие перемещения.

Эта часть руководства PyQt5 была посвящена операциям drag & Drop.

Для вставки кода на Python в комментарий заключайте его в теги <pre><code class="python3">Ваш код</code></pre>
Опечатка в тексте:
Послать сообщение об ошибке автору?