Skip to content Skip to sidebar Skip to footer

PySide: Movable Labels Snap Back To Original Position When Released. Trying To Make Them Move Freely

The following code implements 5 movable labels. When I try to change the color of the labels while they are being moved, the labels snap back to the original position when the mous

Solution 1:

Whenever a widget is added to a layout, its size and position is decided by that layout (according to the available size and other widgets that are part of the layout).

While it is possible to change the geometry of a widget while it's part of a layout, any change to its contents will automatically be notified to the layout, that will then update itself accordingly.
Those changes include different size, different constraints (minimum/maximum size), the size policy (the ability to expand or shrink).

When a stylesheet is set to a widget, its content are immediately invalidated and computed again (even if the stylesheet is the same); the reason for which you can't move the widget is that as soon as the stylesheet is applied, the layout that contains it will force it again to its original position.

You can see what happens if you keep the commented lines, move a widget and then resize the window: the moved widget will be repositioned again as the layout requires.

If you want to be able to move a widget freely you need to create the movable widgets as children of the widget that will contain them, but not in a layout.
This obviously becomes a problem if you want to "lay out" them similarly to how a real QLayout would, but within the resizeEvent of the widget that contains them.

In this specific case:

class DragClass(QDialog):
    def __init__(self, parent=None):
        super(DragClass, self).__init__(parent)
        self.LabelGrid = Ui_Dialog()
        self.LabelGrid.setupUi(self)
        self.configureLabels() 

    def configureLabels(self):
        # Note that since the labels are not part of a layout anymore, now, I
        # had to use another way to "find them". Normally one would go with
        # findChildren, but that's not our case because of the way the DragButton
        # class is imported (it actually is snap.DragButton)
        self.labels = [child for child in self.children() if child.__class__.__name__ == 'DragButton']
        for label in self.labels:
            if (isinstance(label,DragButton)) :
                label.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Expanding)
                label.setStyleSheet("""
                    background-color: lightblue;
                    border-width: 2px;
                    border-style: solid;
                    border-color: black;
                    margin: 2px;
                """)

    def resizeEvent(self, event):
        margin = 5
        spacing = 4

        innerRect = self.rect().adjusted(margin, margin, -margin, -margin)

        count = len(self.labels)
        availableHeight = innerRect.height() - spacing * (count - 1)
        labelSize = availableHeight / count

        top = innerRect.y()
        left = innerRect.left()
        width = innerRect.width()
        for label in self.labels:
            rect = QtCore.QRect(left, top, width, labelSize)
            label.setGeometry(rect)
            top = rect.bottom() + spacing

In this way, the labels are aligned to a "virtual" layout when shown the first time, but now can be freely moved around. Obviously, with this simple implementation, as soon as the window is resized, they will be repositioned again, so it's up to you to decide what to do with labels that have been manually moved.

As a side note, you should not use a QDialog class for what you need. Dialogs are used as windows that appear over other existing windows. For your needs, a simple QWidget will suffice.


Post a Comment for "PySide: Movable Labels Snap Back To Original Position When Released. Trying To Make Them Move Freely"