2013-01-18 13 views
8

Hey, ich hatte diese tutorial für das Verständnis von Drag & Drop-Methoden in PyQt4 durchlaufen. Allerdings kann ich die folgenden Punkte nicht verstehen. Es wäre schön, wenn jemand es mir klarer machen könnte.PyQt4 - Drag and Drop

def mouseMoveEvent(self, e): //class Button 


    mimeData = QtCore.QMimeData() 

    drag = QtGui.QDrag(self) 
    drag.setMimeData(mimeData) 
    drag.setHotSpot(e.pos() - self.rect().topLeft()) 

    dropAction = drag.start(QtCore.Qt.MoveAction) 

def dropEvent(self, e): //class Example 

    position = e.pos() 
    self.button.move(position) 

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

Warum gibt es eine separate self.button.move sind() und e.setDropAction() Doesnt self.button.move() tatsächlich auf die Schaltfläche selbst bewegen? Und könnte jemand erklären, was drag.setHotSpot und drag.start() tun? Vielen Dank.

Antwort

15

Dieses Tutorial ist ernsthaft veraltet. QDrag.start ist seit Qt 4.3 veraltet. QDrag.exec_ sollte stattdessen verwendet werden.

Wie Sie in der Dokumentation für exec sehen können, hat es einen Rückgabewert. setDropAction in dropEvent bestimmt diesen Wert. Es führt den Umzug nicht durch. Deshalb brauchen Sie eine self.button.move(), um die tatsächliche Bewegung zu tun. Also, was ist der Sinn einer setDropAction? Sie müssen möglicherweise wissen, welche Art von Ziehen Sie ausgeführt haben. Stellen Sie sich vor, Sie implementieren Drag-Drop zwischen zwei Listen-Widgets. Wenn Sie eine Verschiebungsoperation ausgeführt haben, müssen Sie das Objekt aus dem Quellwidget entfernen und im Ziel erstellen. Wenn es sich um einen Kopiervorgang handelt, können Sie das Original belassen und lediglich eine Kopie im Ziel erstellen.

setHotSpot/hotSpot ist verwandt mit der einer QDrag. Sie können eine QPixmap anzeigen, während Sie das Element ziehen. hotSpot bestimmt die Positionierung der Pixmap. Die Pixmap wird so positioniert, dass der Cursor bei hotSpot relativ zur oberen linken Ecke der Pixmap steht. Im Fall dieses Tutorials ist es also ziemlich sinnlos, da kein Pixmap angezeigt werden muss.

Hier ist ein bisschen modifizierte und aktualisierte Version dieses Tutorials. Hoffentlich habe ich genug Kommentare aufgenommen. Sie können mit Right-Click oder Kopie mit Shift + Right-Click bewegen:

#!/usr/bin/python 
# -*- coding: utf-8 -*- 

import sys 
from PyQt4 import QtGui, QtCore 


class Button(QtGui.QPushButton): 
    def mouseMoveEvent(self, e): 
     if e.buttons() != QtCore.Qt.RightButton: 
      return 

     # write the relative cursor position to mime data 
     mimeData = QtCore.QMimeData() 
     # simple string with 'x,y' 
     mimeData.setText('%d,%d' % (e.x(), e.y())) 

     # let's make it fancy. we'll show a "ghost" of the button as we drag 
     # grab the button to a pixmap 
     pixmap = QtGui.QPixmap.grabWidget(self) 

     # below makes the pixmap half transparent 
     painter = QtGui.QPainter(pixmap) 
     painter.setCompositionMode(painter.CompositionMode_DestinationIn) 
     painter.fillRect(pixmap.rect(), QtGui.QColor(0, 0, 0, 127)) 
     painter.end() 

     # make a QDrag 
     drag = QtGui.QDrag(self) 
     # put our MimeData 
     drag.setMimeData(mimeData) 
     # set its Pixmap 
     drag.setPixmap(pixmap) 
     # shift the Pixmap so that it coincides with the cursor position 
     drag.setHotSpot(e.pos()) 

     # start the drag operation 
     # exec_ will return the accepted action from dropEvent 
     if drag.exec_(QtCore.Qt.CopyAction | QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction: 
      print 'moved' 
     else: 
      print 'copied' 


    def mousePressEvent(self, e): 
     QtGui.QPushButton.mousePressEvent(self, e) 
     if e.button() == QtCore.Qt.LeftButton: 
      print 'press' 



class Example(QtGui.QWidget): 
    def __init__(self): 
     super(Example, self).__init__() 
     self.initUI() 


    def initUI(self): 
     self.setAcceptDrops(True) 

     button = Button('Button', self) 
     button.move(100, 65) 

     self.buttons = [button] 

     self.setWindowTitle('Copy or Move') 
     self.setGeometry(300, 300, 280, 150) 


    def dragEnterEvent(self, e): 
     e.accept() 


    def dropEvent(self, e): 
     # get the relative position from the mime data 
     mime = e.mimeData().text() 
     x, y = map(int, mime.split(',')) 

     if e.keyboardModifiers() & QtCore.Qt.ShiftModifier: 
      # copy 
      # so create a new button 
      button = Button('Button', self) 
      # move it to the position adjusted with the cursor position at drag 
      button.move(e.pos()-QtCore.QPoint(x, y)) 
      # show it 
      button.show() 
      # store it 
      self.buttons.append(button) 
      # set the drop action as Copy 
      e.setDropAction(QtCore.Qt.CopyAction) 
     else: 
      # move 
      # so move the dragged button (i.e. event.source()) 
      e.source().move(e.pos()-QtCore.QPoint(x, y)) 
      # set the drop action as Move 
      e.setDropAction(QtCore.Qt.MoveAction) 
     # tell the QDrag we accepted it 
     e.accept() 



if __name__ == '__main__': 
    app = QtGui.QApplication(sys.argv) 
    ex = Example() 
    ex.show() 
    app.exec_() 
+0

Vielen Dank für die wunderbare Antwort. Allerdings habe ich noch einen Zweifel, warum ich den Knopf auf e.pos() - QtCore.QPoint (x, y) verschiebe. Gibt e.pos() selbst nicht die Position an, an die es fallen soll? . Und nicht mimeData gibt die Position des Cursors, weil Sie es auf ex() und ey() gesetzt haben, wobei e die Position der Maus ist, und wenn ich sie ablege, werden nicht sowohl e.pos() als auch QtCore.QPoint (x, y). Entschuldigung für mein Noobness. – Manoj

+1

@Manoj: 'x' und' y' aus dem 'Mime' sind _lokale Position_ des Cursors in Bezug auf die Schaltfläche. 'e.pos()' ist die aktuelle Position des Cursors auf dem Widget ('Beispiel'). Wenn Sie auf die Schaltfläche zu 'e.pos()' wechseln, wird die obere linke Ecke der Schaltfläche an diese Position verschoben. Zum Beispiel haben Sie den Knopf in der Mitte des Knopfes gewählt. Und wenn Sie mit nur 'e.pos()' zur neuen Cursorposition gehen, wird die Schaltfläche nicht so platziert, wie Sie sie ausgewählt haben, sondern verschoben. 'x, y' korrigieren Sie diese Verschiebung, indem Sie den oberen linken Teil der Taste so einstellen, dass der Cursor nach dem Ablegen an der gleichen Position ist. – Avaris

+1

@Manoj: Ich denke, Ihre Verwirrung kommt von 'e.x()/e.y()' vs 'e.pos()' im Code. Der Teil, in dem ich 'e.x()/e.y()' hole, ist das 'mouseMoveEvent' des' Buttons'. Sie sind also relativ zu der Schaltfläche ("0,0" ist die obere linke). Wo als 'e.pos()' im 'dropEvent' von' Example'. Die Position, die es geben wird, ist die relative Position des Cursors in Bezug auf "Beispiel". – Avaris

1

Avaris Antwort angepasst für PyQt5 und Python 3.

#!/usr/bin/python 
# -*- coding: utf-8 -*- 

# Adapted for PyQt5 and Python 3 from Avaris' answer to 
# https://stackoverflow.com/questions/14395799/pyqt4-drag-and-drop 

import sys 
from PyQt5.QtWidgets import QPushButton, QWidget, QApplication 
from PyQt5.QtCore import Qt, QMimeData, QPoint 
from PyQt5.QtGui import QDrag, QPixmap, QPainter, QColor 


class Button(QPushButton): 
    def mouseMoveEvent(self, e): 
     if e.buttons() != Qt.RightButton: 
      return 

     # write the relative cursor position to mime data 
     mimeData = QMimeData() 
     # simple string with 'x,y' 
     mimeData.setText('%d,%d' % (e.x(), e.y())) 

     # let's make it fancy. we'll show a "ghost" of the button as we drag 
     # grab the button to a pixmap 
     pixmap = QWidget.grab(self) 

     # below makes the pixmap half transparent 
     painter = QPainter(pixmap) 
     painter.setCompositionMode(painter.CompositionMode_DestinationIn) 
     painter.fillRect(pixmap.rect(), QColor(0, 0, 0, 127)) 
     painter.end() 

     # make a QDrag 
     drag = QDrag(self) 
     # put our MimeData 
     drag.setMimeData(mimeData) 
     # set its Pixmap 
     drag.setPixmap(pixmap) 
     # shift the Pixmap so that it coincides with the cursor position 
     drag.setHotSpot(e.pos()) 

     # start the drag operation 
     # exec_ will return the accepted action from dropEvent 
     if drag.exec_(Qt.CopyAction | Qt.MoveAction) == Qt.MoveAction: 
      print('moved') 
     else: 
      print('copied') 


    def mousePressEvent(self, e): 
     QPushButton.mousePressEvent(self, e) 
     if e.button() == Qt.LeftButton: 
      print('press') 



class Example(QWidget): 
    def __init__(self): 
     super(Example, self).__init__() 
     self.initUI() 


    def initUI(self): 
     self.setAcceptDrops(True) 

     button = Button('Button', self) 
     button.move(100, 65) 

     self.buttons = [button] 

     self.setWindowTitle('Copy or Move') 
     self.setGeometry(300, 300, 280, 150) 


    def dragEnterEvent(self, e): 
     e.accept() 


    def dropEvent(self, e): 
     # get the relative position from the mime data 
     mime = e.mimeData().text() 
     x, y = map(int, mime.split(',')) 

     if e.keyboardModifiers() & Qt.ShiftModifier: 
      # copy 
      # so create a new button 
      button = Button('Button', self) 
      # move it to the position adjusted with the cursor position at drag 
      button.move(e.pos()-QPoint(x, y)) 
      # show it 
      button.show() 
      # store it 
      self.buttons.append(button) 
      # set the drop action as Copy 
      e.setDropAction(Qt.CopyAction) 
     else: 
      # move 
      # so move the dragged button (i.e. event.source()) 
      e.source().move(e.pos()-QPoint(x, y)) 
      # set the drop action as Move 
      e.setDropAction(Qt.MoveAction) 
     # tell the QDrag we accepted it 
     e.accept() 



if __name__ == '__main__': 
    app = QApplication(sys.argv) 
    ex = Example() 
    ex.show() 
    app.exec_()