Проблема с перетаскиванием, Drag-and-drop
Подскажите, как прописать условие, что если объект "бросается" в запретной зоне,
то он должен вернуться на свое место в исходном списке?
Ниже приведен полный код.
from PyQt6.QtCore import QMimeData, Qt, pyqtSignal
from PyQt6.QtGui import QDrag, QPixmap
from PyQt6.QtWidgets import (QApplication, QHBoxLayout,
QLabel, QMainWindow, QVBoxLayout, QWidget,)
class DragTargetIndicator(QLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.setContentsMargins(25, 5, 25, 5)
self.setStyleSheet(
"QLabel { background-color: #ccc; border: 1px solid black; }"
)
class DragItem(QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setContentsMargins(25, 5, 25, 5)
self.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.setStyleSheet("border: 1px solid black;")
# Store data separately from display label, but use label for default.
self.data = self.text()
def set_data(self, data):
self.data = data
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 DragWidget(QWidget):
orderChanged = pyqtSignal(list)
def __init__(self, *args, orientation=Qt.Orientation.Vertical, **kwargs):
super().__init__()
self.setAcceptDrops(True)
self.orientation = orientation
if self.orientation == Qt.Orientation.Vertical:
self.blayout = QVBoxLayout()
else:
self.blayout = QHBoxLayout()
self._drag_target_indicator = DragTargetIndicator()
self.blayout.addWidget(self._drag_target_indicator)
self._drag_target_indicator.hide()
self.setLayout(self.blayout)
def dragEnterEvent(self, e):
e.accept()
def dragLeaveEvent(self, e):
self._drag_target_indicator.hide()
e.accept()
def dragMoveEvent(self, e):
# Find the correct location of the drop target, so we can move it there.
index = self._find_drop_location(e)
if index is not None:
# Inserting moves the item if its alreaady in the layout.
self.blayout.insertWidget(index, self._drag_target_indicator)
# Hide the item being dragged.
e.source().hide()
# Show the target.
self._drag_target_indicator.show()
e.accept()
def dropEvent(self, e):
widget = e.source()
# Use drop target location for destination, then remove it.
self._drag_target_indicator.hide()
index = self.blayout.indexOf(self._drag_target_indicator)
if index is not None:
self.blayout.insertWidget(index, widget)
self.orderChanged.emit(self.get_item_data())
widget.show()
self.blayout.activate()
e.accept()
def _find_drop_location(self, e):
pos = e.position()
spacing = self.blayout.spacing() / 2
for n in range(self.blayout.count()):
# Get the widget at each index in turn.
w = self.blayout.itemAt(n).widget()
if self.orientation == Qt.Orientation.Vertical:
# Drag drop vertically.
drop_here = (
pos.y() >= w.y() - spacing
and pos.y() <= w.y() + w.size().height() + spacing
)
else:
# Drag drop horizontally.
drop_here = (
pos.x() >= w.x() - spacing
and pos.x() <= w.x() + w.size().width() + spacing
)
if drop_here:
# Drop over this target.
break
return n
def add_item(self, item):
self.blayout.addWidget(item)
def get_item_data(self):
data = []
for n in range(self.blayout.count()):
# Get the widget at each index in turn.
w = self.blayout.itemAt(n).widget()
if w != self._drag_target_indicator:
# The target indicator has no data.
data.append(w.data)
return data
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.drag = DragWidget(orientation=Qt.Orientation.Vertical)
for n, l in enumerate(["A", "B", "C", "D"]):
item = DragItem(l)
item.set_data(n) # Store the data.
self.drag.add_item(item)
self.drag.orderChanged.connect(print)
container = QWidget()
layout = QVBoxLayout()
layout.addStretch(1)
layout.addWidget(self.drag)
layout.addStretch(1)
container.setLayout(layout)
self.setCentralWidget(container)
app = QApplication([])
w = MainWindow()
w.show()
app.exec()