Unsolved Drag & drop between widgets in a Layout
-
Hi everyone.
I have a project (using Qt.py) where I can drag and drop widgets between layouts (mainly VBox & HBox). All I have at the moment is the ability drag one widget onto another and it just reparents itself to the containing widget and adds it to the bottom.
What I'm looking for is the ability to drop between widgets, and for a visible line to indicate where it will drop, similar to a QListWidget (or if you were dragging widgets around inside Qt Designer.).
This is the basics of one of my classes that's just acting as a wrapper to a VBoxLayout:
from Qt import QtCore, QtWidgets, QtGui class DragDropPanel(QtWidgets.QWidget): def __init__(self, parent=None): super(DragDropPanel, self).__init__(parent=parent) self.setAcceptDrops(True) self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) self.setFrameStyle(QtWidgets.QFrame.StyledPanel) primaryLayout = QtWidgets.QVBoxLayout() primaryLayout.setAlignment(QtCore.Qt.AlignTop) self.setLayout(primaryLayout) def dragEnterEvent(self, event): source = event.source() if not source == self: event.accept() else: event.ignore() def dropEvent(self, event): source = event.source() if not source == self: # I have no idea what the correct way to add/remove widgets from one layout # to another is :( this seems to work though. source.setParent(self) # Is this needed? or the call to addWidget will do this for me? self.layout().addWidget(source) event.accept() else: event.ignore() def mouseMoveEvent(self, event): mimeData = QtCore.QMimeData() dragPixmap = self.grab() drag = QtGui.QDrag(self) drag.setMimeData(mimeData) drag.setPixmap(dragPixmap) drag.setHotSpot(event.pos()) self.setParent(None) drag.exec_(QtCore.Qt.CopyAction | QtCore.Qt.MoveAction)
So as you can see, if you added multiple of these to a window, you would be able to drag and drop them inside of each other, creating nested hierarchies or whatever you wanted. But it will always add it to the bottom as I'm just calling
addWidget
.Is there a way where I can determine which position I'm at and draw a line between each widget as I hover over the position? I've only thought of trivial solutions such as replacing the 'spacing' in the layout with invisible widgets where I can make use of their drag enter and drop events, but this just feels like a hack. I'm still fairly new to Qt :(
I don't mind what language help is posted in either :)
Thank you! (Also feel free to point out bad qt practices)
-
Hi Denni! I think you helped me out in my post on the python forums as well :) Thanks!
Awesome, this is definitely very similar to what I ended up implementing (checking the mouse position against the child widget positions inside the
dragMoveEvent
method).What about the way I'm removing widgets from one layout and putting them in another? Is just setting the parent to None during the drag operation and then reparenting it okay? If they drop the widget onto nothing then it disappears and has no parent. This is the behavior I want, but is that sufficient for the widget to be deleted from memory?
Thanks again.
-
Hm okay. At the moment it does disappear into the void but I haven't done testing to see if it's lingering around in memory anywhere. I'll experiment with it and see if I need to make a further call to delete it. Cheers.
-
@Denni-0 say you have a window where you can drag and drop widgets into different areas, and it will reparent it to the new area's layout, if you drag one of the widgets off the window and 'drop' it anywhere that doesn't accept drops, it gets deleted instead.
I'm not sure a call to
close()
would have an effect unless I set theWA_DeleteOnClose
flag on all of my widgets that have the potential to be deleted, in which case I may as well just manually delete it in this particular instance withdeleteLater
. -
I've gone with
deleteLater
. This seems to guarantee the behavior I'm after for my current usage:def mouseMoveEvent(self, event): mimeData = QMimeData() dragPixmap = self.grab() drag = QDrag(self) drag.setMimeData(mimeData) drag.setPixmap(dragPixmap) drag.setHotSpot(event.pos()) self.setParent(None) drag.exec_(Qt.MoveAction) if not self.parent(): self.deleteLater()
Using
destroy
seems to be rather dangerous, creating more potential problems than it solves. After reading both that thread you linked andQObject
's documentation, I would myself be very hesitant to usedestroy
. Unless you found yourself in a situation where you were absolutely forced to I guess.Thanks again for your help :)