title: Drag n Drop Button and Drop-down menu PyQt/Qt designer
body: <p>I would like to know the "best practice" to change the behavior of some buttons to do the following:</p>
  
  <p>I want with a click to appear a menu. Or when you drag this same button you could drop it in another and this will "draw" a line connecting them. </p>
  
  <p>Here is an Example:
  <img src="https://i.stack.imgur.com/fT3Ry.png" alt="enter image description here">
  The idea is to connect those "jack" buttons to any other "input" buttons.</p>
  
  <p>I was using the Qt designer and I realize that the buttons properties only the "acceptDrops" property is listed, but I can't make it work.
  Signals/Slots doesn't list something about dragging or dropping.</p>
  
  <p>So I think that the only way to do it is creating a "Custom Widget" or "reimplementing" a button by code. Maybe the same thing with Signals/Slots</p>
  
  <p>What is the best approach to do so if I don't want to modify the pyuic generated file?</p>
  
  <p><strong>UPDATE</strong>: The approach that I tried is by using Qt designer and the "Promoted widgets" option. That allows me to create separate class files and reimplement some elements. I already tested by promoting a PushButton to a "DragButton" and created a class for it:</p>
  
  <p>from PyQt4 import QtGui, QtCore</p>
  
  <p>class DragButton(QtGui.QPushButton):</p>
  
  <pre><code>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()
  </code></pre>
  
  <p>I got some hints and take snippets from this post: 
  <a href="https://stackoverflow.com/questions/14395799/pyqt4-drag-and-drop">PyQt4 - Drag and Drop</a></p>
  
  <p>At this point I'm able to drag this button, and drop it into another of the same type that have the "acceptDrops" property set true in Qt designer. <strike>However, I still want to restrict dragging of some buttons  (perhaps by setting at the main file with UpdateUi method) because some will be just for accepting drops </strike></p>
  
  <p><strong>UPDATE 2:</strong> Now I'm trying to write a class which paint lines or "wires" connecting those buttons.</p>
  
  <p>I'm trying to draw a line between two widgets (two pushbuttons) into a graphicsView with their positions as reference. But when I try, the line is drawn in a wrong place. I also tried using functions like mapToGlobal or mapToParent with different results, but still wrong.
  In the same class I have another method that draws lines with the mouse, and works ok. I was taking it like a reference or example, but it seems that the events position has a different coordinate system. Well, i don't know why is happening this.</p>
  
  <p>The buttons and the graphicsview are inside a Widget, which is also inside a Window.</p>
  
  <p>Here it is the class, the method that we are talking about is 
  from PyQt4 import QtGui, QtCore</p>
  
  <pre><code>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 )
  </code></pre>
  
  <p>If is there any better way to implement this, please tell me.</p>