【问题标题】:Getting drag and drop target pyqt5获取拖放目标pyqt5
【发布时间】:2020-08-31 18:30:32
【问题描述】:

我正在开发 pyqt5 中的拖放 GUI,并试图获取拖放操作的目标小部件,但是当我尝试 QDrag 对象的 target() 函数时,我返回 ma​​in.MainWindow 对象位于 0x0000025FDAC09EE0>,我不知道如何使用它。我想访问 QGridLayout 中小部件的索引,以便我可以使两个小部件交换位置。

这是我的代码:

import sys

from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QPushButton, QWidget, QApplication, QGridLayout, QScrollArea, QMainWindow, QSlider


class Stroj:
    
    def __init__(self, rok, naziv, trajanje):
        self.rok = rok
        self.naziv = naziv
        self.trajanje = trajanje
        

class Button(QPushButton):
    drag = 0

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

    def mouseMoveEvent(self, e):

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

        mimeData = QMimeData()
        mimeData.setText(self.text())

        self.drag = QDrag(self)
        self.drag.setMimeData(mimeData)
        self.drag.setPixmap(self.grab())
        self.drag.setHotSpot(self.rect().center())

        dropAction = self.drag.exec_(Qt.MoveAction)


class MainWindow(QMainWindow):
    layout = QGridLayout()
    btns = []
    snd = ""
    i = 0
    j = 0

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

        self.initUI()
        
    def initUI(self):

        self.setAcceptDrops(True)
        self.scroll = QScrollArea()
        self.widget = QWidget()
        self.drag = QDrag(self)
        
        SL = []
        for x in range(30):
            self.btns.append(x)
        
        for x in range(30):
            
            self.btns[x] = Button(str(x), self)
            self.btns[x].setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
            self.layout.addWidget(self.btns[x], self.i, self.j)
            if(self.j > 5):
                self.j = 0
                self.i += 1
            else:
                self.j += 1
        
        self.widget.setLayout(self.layout)
        self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll.setWidgetResizable(True)
        self.scroll.setWidget(self.widget)

        self.setCentralWidget(self.scroll)

        self.setWindowTitle('Raspored')
        self.setGeometry(350, 75, 950, 750)

    def dragEnterEvent(self, e):
        self.snd = e.mimeData().text()
        e.accept()
        
    def dragMoveEvent(self, e):
        e.accept()

    def dropEvent(self, e):
        sender = self.snd
        position = e.pos()
        position.setX(int(position.x() - self.btns[int(sender)].width() / 2))
        position.setY(int(position.y() - self.btns[int(sender)].height() / 2))
        self.btns[int(sender)].move(position)
        print(self.layout.indexOf(e.source()))
        print(e.source().drag.target())

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


def main():
    
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    app.exec_()


if __name__ == '__main__':
    main()

【问题讨论】:

    标签: python drag-and-drop pyqt5


    【解决方案1】:

    放置事件的目标始终是接收放置操作的小部件,因此很明显,如果您从主窗口实例中截取事件,您将获得主窗口作为目标。 如果需要在特定位置查找widget,需要使用QApplication.widgetAt(pos)。

    在下面的示例中,根据给定的代码进行了修改,仅当源是 Button 实例且目标不同时,我才接受 dragEnter/dragMove 事件。然后我使用它们在布局中的位置来切换这些按钮。

    class MainWindow(QMainWindow):
        # ...
        def dragEnterEvent(self, e):
            e.accept()
            
        def dragMoveEvent(self, e):
            source = e.source()
            target = QApplication.widgetAt(self.mapToGlobal(e.pos()))
            if (isinstance(e.source(), Button) and isinstance(target, Button) and target != source):
                e.accept()
            else:
                e.ignore()
    
        def dropEvent(self, e):
            source = e.source()
            target = QApplication.widgetAt(self.mapToGlobal(e.pos()))
            if (not isinstance(source, Button) or not isinstance(target, Button) 
                or target == source):
                    return
            layout = self.widget.layout()
    
            sourceIndex = layout.indexOf(source)
            sourcePos = layout.getItemPosition(sourceIndex)
    
            targetIndex = layout.indexOf(target)
            targetPos = layout.getItemPosition(targetIndex)
            
            layout.addWidget(source, *targetPos)
            layout.addWidget(target, *sourcePos)
    
            e.accept()
    

    认为这是一个非常简单的实现:您还应该确保小部件实际上是同一窗口的子窗口并且处于相同的布局中。

    【讨论】:

    • 非常感谢先生,它有效!我会看看我是否也能做到,这样如果你把它放在它放置的两个小部件之间。
    • @DominikGlavaš 考虑如果你想要一个“插入”模式,你需要改变整个布局结构,你需要考虑到这种改变必须让用户清楚,因为水平和垂直插入可以以不同的方式发生,用户必须意识到在在目标位置释放对象之前。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-27
    相关资源
    最近更新 更多