Drag n Drop Button und Dropdown-Menü PyQt / Qt designer

Ich möchte die "Best Practice" kennen, mit der das Verhalten einiger Schaltflächen folgendermaßen geändert werden kann:

Ich möchte mit einem Klick ein Menü erscheinen lassen. Wenn Sie dieselbe Schaltfläche ziehen, können Sie sie auch in eine andere ziehen, um eine Linie zu zeichnen, die sie verbindet.

Hier ist ein Beispiel: Die Idee ist, diese "Jack" -Tasten mit anderen "Input" -Tasten zu verbinden.

Ich habe den Qt-Designer verwendet und festgestellt, dass in den Schaltflächeneigenschaften nur die Eigenschaft "acceptDrops" aufgeführt ist, aber ich kann sie nicht zum Laufen bringen. Signale / Slots enthalten keine Informationen zum Ziehen oder Ablegen.

So denke ich, dass der einzige Weg, dies zu tun, das Erstellen eines "benutzerdefinierten Widgets" oder das "Neuimplementieren" einer Schaltfläche durch Code ist. Vielleicht dasselbe mit Signals / Slots

Was ist die beste Vorgehensweise, wenn ich die von pyuic generierte Datei nicht ändern möchte?

AKTUALISIERE: Der Ansatz, den ich ausprobiert habe, ist die Verwendung von Qt Designer und der Option "Heraufgestufte Widgets". Dadurch kann ich separate Klassendateien erstellen und einige Elemente neu implementieren. Ich habe bereits getestet, indem ich einen PushButton zu einem "DragButton" hochgestuft habe und eine Klasse dafür erstellt habe:

von PyQt4 importieren QtGui, QtCore

class DragButton (QtGui.QPushButton):

def __init__(self, parent):
     super(DragButton,  self).__init__(parent)
     self.allowDrag = True

def setAllowDrag(self, allowDrag):
    if type(allowDrag) == bool:
       self.allowDrag = allowDrag
    else:
        raise TypeError("You have to set a boolean type")

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

    if self.allowDrag == True:
        # write the relative cursor position to mime data
        mimeData = QtCore.QMimeData()
        # simple string with 'x,y'
        mimeData.setText('%d,%d' % (e.x(), e.y()))
        print mimeData.text()

        # 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.LinkAction | QtCore.Qt.MoveAction) == QtCore.Qt.LinkAction:
            print 'linked'
        else:
            print 'moved'

def mousePressEvent(self, e):
    QtGui.QPushButton.mousePressEvent(self, e)
    if e.button() == QtCore.Qt.LeftButton:
        print 'press'
        #AQUI DEBO IMPLEMENTAR EL MENU CONTEXTUAL

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

        # move
        # so move the dragged button (i.e. event.source())
    print e.pos()
        #e.source().move(e.pos()-QtCore.QPoint(x, y))
        # set the drop action as LinkAction
    e.setDropAction(QtCore.Qt.LinkAction)
    # tell the QDrag we accepted it
    e.accept()

Ich habe einige Tipps und Schnipsel aus diesem Beitrag: PyQt4 - Ziehen und Ablegen

An diesem Punkt kann ich diese Schaltfläche ziehen und in eine andere vom gleichen Typ ablegen, für die die Eigenschaft "acceptDrops" im Qt-Designer auf "true" gesetzt ist.Ich möchte jedoch das Ziehen einiger Schaltflächen einschränken (möglicherweise durch Einstellen der Hauptdatei mit der UpdateUi-Methode), da einige nur zum Akzeptieren von Ablagen dienen.

UPDATE 2: Jetzt versuche ich, eine Klasse zu schreiben, die Linien oder "Drähte" malt, die diese Knöpfe verbinden.

Ich versuche, eine Linie zwischen zwei Widgets (zwei Drucktasten) in einer Grafikansicht mit ihren Positionen als Referenz zu zeichnen. Aber wenn ich es versuche, wird die Linie an einer falschen Stelle gezogen. Ich habe auch versucht, Funktionen wie mapToGlobal oder mapToParent mit unterschiedlichen Ergebnissen zu verwenden, aber immer noch falsch. In der gleichen Klasse habe ich eine andere Methode, die Linien mit der Maus zeichnet und ok funktioniert. Ich habe es als Referenz oder Beispiel genommen, aber es scheint, dass die Ereignisposition ein anderes Koordinatensystem hat. Nun, ich weiß nicht, warum das passiert.

Die Schaltflächen und die Grafikansicht befinden sich in einem Widget, das sich auch in einem Fenster befindet.

Hier ist es die Klasse, die Methode, über die wir sprechen, stammt von PyQt4-Import QtGui, QtCore

class WiringGraphicsView(QtGui.QGraphicsView):

    def __init__(self, parent):
        QtGui.QGraphicsView.__init__(self, parent)
        self.setScene(QtGui.QGraphicsScene(self))
        self.setSceneRect(QtCore.QRectF(self.viewport().rect()))

    def mousePressEvent(self, event):
        self._start = event.pos()

    def mouseReleaseEvent(self, event):
        start = QtCore.QPointF(self.mapToScene(self._start))
        end = QtCore.QPointF(self.mapToScene(event.pos()))
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0) )
        pen = QtGui.QPen(brush, 2)
        line = QtGui.QGraphicsLineItem(QtCore.QLineF(start, end))
        line.setPen(pen)
        self.scene().addItem( line )

    def paintWire(self, start_widget,  end_widget):
        start_position = QtCore.QPointF(self.mapToScene(start_widget.pos()))
        end_position = QtCore.QPointF(self.mapToScene(end_widget.pos()))
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0) )
        pen = QtGui.QPen(brush, 2)
        line = QtGui.QGraphicsLineItem(QtCore.QLineF(start_position, end_position))
        line.setPen(pen)
        self.scene().addItem( line )

Wenn es einen besseren Weg gibt, dies umzusetzen, sagen Sie es mir bitte.

Antworten auf die Frage(8)

Ihre Antwort auf die Frage