2012-08-31 23 views
7

Realmente estoy luchando por encontrar una manera de hacerlo. Digamos que implementar un botón de forma muy sencilla en una ventana de widgets:Arrastrando/moviendo un QPushButton en PyQt

self.button = QPushButton("Drag Me", self) 

puedo mover su punto de inicialización alrededor del área del widget padre usando self.button.move(x,y), y puedo conseguir eventos de ratón de mousePressEvent(self, e) través e.x() y e.y(), de modo que el botón se mueve a donde haga clic, pero parece que no puedo juntar todo esto en un marco de arrastrar y soltar.

Aclaración: Después de leer el significado 'verdadero' de Arrastrar/Soltar, eso no es lo que necesito. Solo quiero mover un widget con mi mouse, muy similar a la forma en que mueves los imanes en una nevera.

+1

@Eric hace un muy buen punto en su respuesta. ¿Podrían aclarar esta pregunta si quieren arrastrar y soltar verdaderos eventos ... o simplemente para poder mover el botón con el mouse – jdi

+1

en función de lo que pretenden hacer? Me gustaría ver el QGraphicsView marco de referencia. Lo que estás tratando de hacer (tablero de imán virtual) sería muy fácil de lograr con eso. –

Respuesta

10

Aquí es un ejemplo de un botón móvil de la que todavía es compatible con la señal haga clic en Normal correctamente:

from PyQt4 import QtCore, QtGui 


class DragButton(QtGui.QPushButton): 

    def mousePressEvent(self, event): 
     self.__mousePressPos = None 
     self.__mouseMovePos = None 
     if event.button() == QtCore.Qt.LeftButton: 
      self.__mousePressPos = event.globalPos() 
      self.__mouseMovePos = event.globalPos() 

     super(DragButton, self).mousePressEvent(event) 

    def mouseMoveEvent(self, event): 
     if event.buttons() == QtCore.Qt.LeftButton: 
      # adjust offset from clicked point to origin of widget 
      currPos = self.mapToGlobal(self.pos()) 
      globalPos = event.globalPos() 
      diff = globalPos - self.__mouseMovePos 
      newPos = self.mapFromGlobal(currPos + diff) 
      self.move(newPos) 

      self.__mouseMovePos = globalPos 

     super(DragButton, self).mouseMoveEvent(event) 

    def mouseReleaseEvent(self, event): 
     if self.__mousePressPos is not None: 
      moved = event.globalPos() - self.__mousePressPos 
      if moved.manhattanLength() > 3: 
       event.ignore() 
       return 

     super(DragButton, self).mouseReleaseEvent(event) 

def clicked(): 
    print "click as normal!" 

if __name__ == "__main__": 
    app = QtGui.QApplication([]) 
    w = QtGui.QWidget() 
    w.resize(800,600) 

    button = DragButton("Drag", w) 
    button.clicked.connect(clicked) 

    w.show() 
    app.exec_() 

En el mousePressEvent grabo tanto la posición de arranque inicial, y una posición que va a actualizarse a lo largo del arrastre.

En el mouseMoveEvent, obtengo el desplazamiento correcto del widget desde donde se hizo clic hasta donde está el origen real, para que el movimiento sea preciso.

En el mouseReleaseEvent, compruebo si el movimiento general fue mayor que al menos una pequeña cantidad. Si lo fue, entonces fue un arrastre e ignoramos el evento normal para no producir una señal de "clic". De lo contrario, permitimos que el controlador de eventos normal produzca el clic.

+0

This debería darme suficiente para masticar mientras tanto. ¡Gracias! – RodericDay

+0

¡De nada! ¡No olvides aceptar la respuesta si resolvió tu problema! – jdi

+0

Tenía dudas porque la respuesta que más me gustaba era usar QGraphicsScene, pero esta solución es una mejor respuesta a la pregunta exacta que hice. ¡Aqui tienes! – RodericDay

2

Depende de lo que está tratando de hacer. Si está intentando hacer un "Arrastrar & Drop" real, lo está haciendo mal. Lo que estás haciendo es simplemente mover el botón en su espacio de coordenadas X e Y dentro de su padre. En realidad, nunca invoca ningún evento de arrastrar/soltar, son completamente diferentes.

Usted debe leer a través de la documentación de arrastrar & aquí:

http://doc.qt.nokia.com/4.7-snapshot/dnd.html
http://qt-project.org/doc/qt-5.0/qdrag.html

En lugar de mover el botón dentro del mousePressEvent, que necesitará para crear un nuevo objeto QDrag y ejecutarlo. Puede hacer que se parezca a su botón tomando una instantánea de su botón utilizando el método QPixmap :: grabWidget y asígnelo a la instancia de QDrag utilizando el método QDrag :: setPixmap.

Evento si todo lo que intenta hacer es mover el widget en el espacio principal, le recomendaría usar este marco y solo aceptar el evento de colocar para su botón. Entonces no disparas un montón de redibujados innecesarios.

+0

alguna idea donde podría encontrar un buen ejemplo para seguir? No me importaría comenzar arrastrando un rectángulo de color o algo así. La idea de una instancia de "QDrag" me parece demasiado compleja por algún motivo, como si su propósito fuera arrastrar una carpeta a un directorio o algo así. – RodericDay

+0

Sí, ese es el punto principal de esto.Supongo que la pregunta es, ¿cuál es tu objetivo? –

+0

Mi objetivo final es poder simular un tablero de juego importando * .bmps A corto plazo, quiero poder mover objetos QLabel que contengan un Pixmap, como piezas de ajedrez en un tablero. Pero sin ninguna lógica o zonas de caída automática. Solo imágenes en una pantalla. La mejor analogía que puedo pensar es imanes de nevera. Quiero imanes de nevera virtuales. – RodericDay