简体   繁体   中英

Getting drag and drop target pyqt5

I am working on a drag and drop GUI in pyqt5 and am trying to get the target widget of the drag and drop operation but when i try the target() function of the QDrag object i returns < main .MainWindow object at 0x0000025FDAC09EE0> and I dont know how to use that. I want to access the index of the widget in a QGridLayout so that I can make the two widgets swap places.

Here is my code:

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()

The target of a drop event is always the widget that receives the drop action, so it's pretty obvious that if you intercept the event from the main window instance, you'll get the main window as target. If you need to find the widget at a specific position, you need to use QApplication.widgetAt(pos) .

In the following example, modified from the given code, I'm accepting the dragEnter/dragMove events only when the source is a Button instance, and the target is not the same. Then I switch those buttons using their position in the layout.

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()

Consider that this is a very simple implementation: you should also ensure that the widgets are actually children of the same window and are in the same layout.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM