向滚动区域添加拖放功能

问题描述 投票:0回答:1

我正在尝试创建一个允许在 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_()) 
python pyqt5 qscrollarea
1个回答
1
投票

您当前的代码不允许您删除任何内容的原因是您尚未实现

dragEnterEvent
dragMoveEvent
。它们都需要被小部件接受才能接受掉落。此外,您
mainwindow
中的放置事件实际上并没有执行任何操作,因为它嵌套在您的
__init__
函数中。

这是您可以做到的一种方法:

我在主窗口上创建了一个信号,其中有两个参数标识要交换的小部件的索引。当信号发出时,它会触发一个函数来交换这些小部件。然后,我为每个标签实现了

dragEnter
dragMove
事件,并调整了放置事件以发出
MainWindow
信号以及布局中小部件的位置。

我调整了一些其他内容,以使运行它时发生的情况更加明显。


import sys

from PyQt5.QtWidgets import QApplication, QMainWindow, QScrollArea, QWidget, QHBoxLayout


from PyQt5.QtWidgets import QLabel

from PyQt5.QtGui import QDrag, QPixmap


from PyQt5.QtCore import Qt, pyqtSignal, QMimeData


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)
        
        
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    
    w.show()
    
    sys.exit(app.exec_())

有关拖放的更多详细信息,请查看官方文档。

https://doc.qt.io/qt-5/dnd.html

© www.soinside.com 2019 - 2024. All rights reserved.