Este artículo muestra específicamente cómo llevar a cabo el mecanismo de «Drag and Drop» (arrastrar y soltar) entre dos objetos de la misma aplicación. Se basa en el documento «Drag and Drop» de la documentación oficial de Qt 4.8.
El código presenta dos partes. La primera es el widget que será arrastrado hacia un determinado lugar. El segundo es dicho lugar, otro widget el cual recibirá al primero. Para esto se han creado dos clases: DropBox
y DraggableLabel
, respectivamente. El widget que será arrastrado es una etiqueta, la clase QLabel
, que puede trasladarse entre las dos «cajas» (DropBox
) o en la que se encuentre en el momento.
#!/usr/bin/env python # -*- coding: utf-8 -*- # # dnd.py # # Copyright 2013 Recursos Python - www.recursospython.com # from PyQt4.QtCore import Qt, QMimeData from PyQt4.QtGui import QApplication, QDrag, QFrame, QMainWindow, QLabel class DropBox(QFrame): def __init__(self, parent): QFrame.__init__(self, parent) self.setAcceptDrops(True) # Aceptar objetos self.setStyleSheet("background-color: #E6E6E6;") def dragEnterEvent(self, event): # Ignorar objetos arrastrados sin información if event.mimeData().hasText(): event.acceptProposedAction() def dropEvent(self, event): # Establecer el widget en una nueva posición pos = event.pos() self.label = event.source() self.label.setParent(self) self.label.setGeometry(pos.x(), pos.y(), 150, 20) self.label.show() event.acceptProposedAction() class DraggableLabel(QLabel): def __init__(self, parent): QLabel.__init__(self, parent) self.setStyleSheet("background-color: white;") def mousePressEvent(self, event): # Inicializar el arrastre con el botón derecho if event.button() == Qt.LeftButton: self.drag_start_position = event.pos() def mouseMoveEvent(self, event): # Chequear que se esté presionando el botón derecho if not (event.buttons() and Qt.LeftButton): return # Verificar que sea una posición válida if ((event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance()): return drag = QDrag(self) mime_data = QMimeData() # Establecer el contenido del widget como dato mime_data.setText(self.text()) drag.setMimeData(mime_data) # Ejecutar la acción self.drop_action = drag.exec_(Qt.CopyAction | Qt.MoveAction) class Window(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.setWindowTitle("Drag and Drop") self.resize(600, 300) self.dropbox1 = DropBox(self) self.dropbox1.setGeometry(10, 10, 580, 130) self.dropbox2 = DropBox(self) self.dropbox2.setGeometry(10, 150, 580, 130) self.label = DraggableLabel(self.dropbox1) self.label.setGeometry(10, 10, 150, 20) self.label.setText("Hazme click y mueveme") if __name__ == "__main__": app = QApplication([]) window = Window() window.show() app.exec_()
La etiqueta comienza el proceso de arrastrado con el evento mousePressEvent
, en el cual verifica que el botón empleado sea el derecho. El evento mouseMoveEvent
crea el objeto QDrag
y añade información que luego será utilizada por el receptor. En este ejemplo únicamente se establece como dato el texto del widget, a través de QMimeData
, pero ésta, además de texto plano, puede incluír imágenes, URLs, código HTML, entre otras (véase la documentación de la clase). En el otro extremo, el receptor (la clase DropBox
) acepta el objeto arrastrado una vez llamado al evento dragEnterEvent
, realizando una verificación para no aceptar objetos vacíos. Por último, una vez soltado el ratón y arrastrado el objeto, el evento dropEvent
es llamado y se establece el nuevo widget padre para el objeto junto con su nueva posición. La función event.source()
retorna el widget que se ha pasado como primer argumento al crear una instancia de la clase QDrag
en el objeto arrastrado, por lo tanto se realiza una copia idéntica y únicamente se cambia el widget padre o parent (desde dropbox1
hacia dropbox2
y viceversa) y se establece una nueva posición.
Implementar el mecanismo D&D en Qt no es una tarea dificultosa, tomando como base este código puedes arrastrar y soltar objetos más complejos; recuerda que puedes añadir todo tipo de dato a través de la clase QMimeData
. Por ejemplo, puedes añadir una imágen al objeto antes de ser arrastrado utilizando mime_data.setImageData()
y luego recibirla en la clase receptora (en el evento dragEnterEvent
) realizando event.mimeData().imageData()
.
Versión
Python 2.7
Curso online 👨💻
¡Ya lanzamos el curso oficial de Recursos Python en Udemy!
Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter
en 2024.
Consultoría 💡
Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.