在滚动区域中添加拖放操作



我正在尝试创建一个滚动区域,允许在QtPy5中拖放。我希望能够将一个标签(标签1)拖动到另一个标签(标签3),并切换标签的位置。(1、2、3、4……变成3,2,1,4 .....)到目前为止,我可以得到滚动部分的工作,但我有麻烦得到拖放功能。我认为问题是我需要在某个地方使用setAcceptDrop(True),但我想不起来在哪里。任何帮助将不胜感激!谢谢。

from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QMainWindow, QGridLayout, QScrollArea, QHBoxLayout, QPushButton
from PyQt5.QtCore import Qt, QMimeData, pyqtSignal
from PyQt5.QtGui import QDrag, QPixmap
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()
self.setAcceptDrops(True)
def set_data(self, data):
self.data = data
def mouseMoveEvent(self, e):
if e.buttons() == Qt.LeftButton:
drag = QDrag(self)
mime = QMimeData()
drag.setMimeData(mime)
pixmap = QPixmap(self.size())
self.render(pixmap)
drag.setPixmap(pixmap)
drag.exec_(Qt.MoveAction)



class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.scrollArea = QScrollArea()
self.setCentralWidget(self.scrollArea)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setAcceptDrops(True)
self.setAcceptDrops(True)
self.contents = QWidget()
self.contents.setAcceptDrops(True)
self.scrollArea.setWidget(self.contents)
layout = QHBoxLayout(self.contents)
for row in range(20):
button = DragItem(str(row))
layout.addWidget(button)

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

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():
# We didn't drag past this widget.
# insert to the left of it.
self.blayout.insertWidget(n-1, widget)
break

e.accept()

if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_()) 

当前代码不允许您删除任何内容的原因是您没有实现dragEnterEventdragMoveEvent。它们都需要被小部件接受,以便它们接受掉落。此外,您在mainwindow中拥有的掉落事件实际上并没有做任何事情,因为它嵌套在__init__函数中。

你可以这样做:

我在主窗口上创建了一个带有两个参数的信号,用于标识您想要交换的小部件的索引。当发出信号时,它会触发一个函数来交换这些小部件。然后,我为每个标签实现了dragEnterdragMove事件,并调整了放置事件,使其在布局中的小部件位置发出MainWindow信号。

我调整了一些其他的东西,使它在运行时发生的事情更明显。

class DragItem(QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.original = int(self.text())
self.current = self.original
self.setText(f"Original: {self.original}n Current: {self.current}")
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.setAcceptDrops(True)
def set_data(self, number):
self.current = number
self.setText(f"Original: {self.original}n Current: {self.current}")
def dragEnterEvent(self, e):
if e.mimeData().hasImage(): e.accept()
else: e.ignore()
def dragMoveEvent(self, e):
if e.mimeData().hasImage(): e.accept()
else: e.ignore()
def dropEvent(self, e):
source_pos = e.source().current
current_pos = self.current
self.window().swap.emit(*sorted([source_pos, current_pos]))
# or instead you can extract the pixmap from the event mime data
# pixmap = e.mimeData().imageData()
# ... do something with pixmap
def mouseMoveEvent(self, e):
if e.buttons() == Qt.LeftButton:
drag = QDrag(self)
mime = QMimeData()
pixmap = QPixmap(self.size())
mime.setImageData(pixmap)
drag.setMimeData(mime)
self.render(pixmap)
drag.setPixmap(pixmap)
drag.exec_(Qt.MoveAction)
class MainWindow(QMainWindow):
swap = pyqtSignal([int,int])
def __init__(self):
super().__init__()
self.scrollArea = QScrollArea()
self.setCentralWidget(self.scrollArea)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setAcceptDrops(True)
self.setAcceptDrops(True)
self.contents = QWidget()
self.contents.setAcceptDrops(True)
self.scrollArea.setWidget(self.contents)
self.layout = QHBoxLayout(self.contents)
for row in range(20):
button = DragItem(str(row))
self.layout.addWidget(button)
self.swap.connect(self.swap_widgets)
def swap_widgets(self, pos1, pos2):
widget1 = self.layout.itemAt(pos1).widget()
widget2 = self.layout.itemAt(pos2).widget()
self.layout.removeWidget(widget2)
self.layout.removeWidget(widget1)
self.layout.insertWidget(pos1, widget2)
self.layout.insertWidget(pos2, widget1)
widget1.set_data(pos2)
widget2.set_data(pos1)

有关qt中拖放的更多详细信息,请查看官方文档。https://doc.qt.io/qt-5/dnd.html

最新更新