HOME/Articles/

pyqt example DragDrop (snippet)

Article Outline

Python pyqt (gui) example 'DragDrop'

DragDrop

Python pyqt example: DragDrop

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Created on 2018年9月14日
@author: Irony
@site: https://pyqt5.com , https://github.com/892768447
@email: [email protected]
@file: DragListWidget
@description: 
"""
from PyQt5.QtCore import Qt, QSize, QRect, QPoint
from PyQt5.QtGui import QColor, QPixmap, QDrag, QPainter, QCursor
from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QLabel, QRubberBand


__Author__ = """By: Irony
QQ: 892768447
Email: [email protected]"""
__Copyright__ = "Copyright (c) 2018 Irony"
__Version__ = "Version 1.0"


class DropListWidget(QListWidget):
    # 可以拖进来的QListWidget

    def __init__(self, *args, **kwargs):
        super(DropListWidget, self).__init__(*args, **kwargs)
        self.resize(400, 400)
        self.setAcceptDrops(True)
        # 设置从左到右、自动换行、依次排列
        self.setFlow(self.LeftToRight)
        self.setWrapping(True)
        self.setResizeMode(self.Adjust)
        # item的间隔
        self.setSpacing(5)

    def makeItem(self, size, cname):
        item = QListWidgetItem(self)
        item.setData(Qt.UserRole + 1, cname)  # 把颜色放进自定义的data里面
        item.setSizeHint(size)
        label = QLabel(self)  # 自定义控件
        label.setMargin(2)  # 往内缩进2
        label.resize(size)
        pixmap = QPixmap(size.scaled(96, 96, Qt.IgnoreAspectRatio))  # 调整尺寸
        pixmap.fill(QColor(cname))
        label.setPixmap(pixmap)
        self.setItemWidget(item, label)

    def dragEnterEvent(self, event):
        mimeData = event.mimeData()
        if not mimeData.property('myItems'):
            event.ignore()
        else:
            event.acceptProposedAction()

    def dropEvent(self, event):
        # 获取拖放的items
        items = event.mimeData().property('myItems')
        event.accept()
        for item in items:
            # 取出item里的data并生成item
            self.makeItem(QSize(100, 100), item.data(Qt.UserRole + 1))


class DragListWidget(QListWidget):
    # 可以往外拖的QListWidget

    def __init__(self, *args, **kwargs):
        super(DragListWidget, self).__init__(*args, **kwargs)
        self.resize(400, 400)
        # 隐藏横向滚动条
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        # 不能编辑
        self.setEditTriggers(self.NoEditTriggers)
        # 开启拖功能
        self.setDragEnabled(True)
        # 只能往外拖
        self.setDragDropMode(self.DragOnly)
        # 忽略放
        self.setDefaultDropAction(Qt.IgnoreAction)
        # ****重要的一句(作用是可以单选,多选。Ctrl、Shift多选,可从空白位置框选)****
        # ****不能用ExtendedSelection,因为它可以在选中item后继续框选会和拖拽冲突****
        self.setSelectionMode(self.ContiguousSelection)
        # 设置从左到右、自动换行、依次排列
        self.setFlow(self.LeftToRight)
        self.setWrapping(True)
        self.setResizeMode(self.Adjust)
        # item的间隔
        self.setSpacing(5)
        # 橡皮筋(用于框选效果)
        self._rubberPos = None
        self._rubberBand = QRubberBand(QRubberBand.Rectangle, self)

        self.initItems()

    # 实现拖拽的时候预览效果图
    # 这里演示拼接所有的item截图(也可以自己写算法实现堆叠效果)
    def startDrag(self, supportedActions):
        items = self.selectedItems()
        drag = QDrag(self)
        mimeData = self.mimeData(items)
        # 由于QMimeData只能设置image、urls、str、bytes等等不方便
        # 这里添加一个额外的属性直接把item放进去,后面可以根据item取出数据
        mimeData.setProperty('myItems', items)
        drag.setMimeData(mimeData)
        pixmap = QPixmap(self.viewport().visibleRegion().boundingRect().size())
        pixmap.fill(Qt.transparent)
        painter = QPainter()
        painter.begin(pixmap)
        for item in items:
            rect = self.visualRect(self.indexFromItem(item))
            painter.drawPixmap(rect, self.viewport().grab(rect))
        painter.end()
        drag.setPixmap(pixmap)
        drag.setHotSpot(self.viewport().mapFromGlobal(QCursor.pos()))
        drag.exec_(supportedActions)

    def mousePressEvent(self, event):
        # 列表框点击事件,用于设置框选工具的开始位置
        super(DragListWidget, self).mousePressEvent(event)
        if event.buttons() != Qt.LeftButton or self.itemAt(event.pos()):
            return
        self._rubberPos = event.pos()
        self._rubberBand.setGeometry(QRect(self._rubberPos, QSize()))
        self._rubberBand.show()

    def mouseReleaseEvent(self, event):
        # 列表框点击释放事件,用于隐藏框选工具
        super(DragListWidget, self).mouseReleaseEvent(event)
        self._rubberPos = None
        self._rubberBand.hide()

    def mouseMoveEvent(self, event):
        # 列表框鼠标移动事件,用于设置框选工具的矩形范围
        super(DragListWidget, self).mouseMoveEvent(event)
        if self._rubberPos:
            pos = event.pos()
            lx, ly = self._rubberPos.x(), self._rubberPos.y()
            rx, ry = pos.x(), pos.y()
            size = QSize(abs(rx - lx), abs(ry - ly))
            self._rubberBand.setGeometry(
                QRect(QPoint(min(lx, rx), min(ly, ry)), size))

    def makeItem(self, size, cname):
        item = QListWidgetItem(self)
        item.setData(Qt.UserRole + 1, cname)  # 把颜色放进自定义的data里面
        item.setSizeHint(size)
        label = QLabel(self)  # 自定义控件
        label.setMargin(2)  # 往内缩进2
        label.resize(size)
        pixmap = QPixmap(size.scaled(96, 96, Qt.IgnoreAspectRatio))  # 调整尺寸
        pixmap.fill(QColor(cname))
        label.setPixmap(pixmap)
        self.setItemWidget(item, label)

    def initItems(self):
        size = QSize(100, 100)
        for cname in QColor.colorNames():
            self.makeItem(size, cname)


if __name__ == '__main__':
    import sys
    from PyQt5.QtWidgets import QApplication
    app = QApplication(sys.argv)
    app.setStyleSheet("""QListWidget {
        outline: 0px;
        background-color: transparent;
    }
    QListWidget::item:selected {
        border-radius: 2px;
        border: 1px solid rgb(0, 170, 255);
    }
    QListWidget::item:selected:!active {
        border-radius: 2px;
        border: 1px solid transparent;
    }
    QListWidget::item:selected:active {
        border-radius: 2px;
        border: 1px solid rgb(0, 170, 255);
    }
    QListWidget::item:hover {
        border-radius: 2px;
        border: 1px solid rgb(0, 170, 255);
    }"""
                      )
    wa = DragListWidget()
    wa.show()
    wo = DropListWidget()
    wo.show()
    sys.exit(app.exec_())