Python PySide:释放时,可移动标签会弹回到原始位置。试图让他们自由移动

Python PySide:释放时,可移动标签会弹回到原始位置。试图让他们自由移动,python,pyqt,drag-and-drop,pyside,Python,Pyqt,Drag And Drop,Pyside,下面的代码实现了5个可移动标签。当我尝试在移动标签时更改标签的颜色时,松开鼠标按钮后,标签会恢复到原始位置。当您注释掉使用setStyleSheet的零件时,它起作用,标签可以自由移动和释放 import sys from PySide import QtGui from PySide import QtCore from PySide.QtGui import * from PySide.QtCore import * from Drag import Ui_Dialog class Mai

下面的代码实现了5个可移动标签。当我尝试在移动标签时更改标签的颜色时,松开鼠标按钮后,标签会恢复到原始位置。当您注释掉使用setStyleSheet的零件时,它起作用,标签可以自由移动和释放

import sys
from PySide import QtGui
from PySide import QtCore
from PySide.QtGui import *
from PySide.QtCore import *
from Drag import Ui_Dialog

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
    self.mainMenuWidget = MainStack(self)   
    self.setCentralWidget(self.mainMenuWidget)
        self.show()


class MainStack(QWidget):
    def __init__(self,parent=None):
        super(MainStack,self).__init__(parent)
        self.initUI()

    def initUI(self):
        layout = QVBoxLayout(self)
        self.stack = QStackedWidget(parent=self)
        self.dragPage = DragClass()
        #Add Pages to Stack
        self.stack.addWidget(self.dragPage)
        #Add Stack to Layout    
        self.stack.setCurrentWidget(self.dragPage)
        layout.addWidget(self.stack)

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):
        labels = (self.LabelGrid.verticalLayout.itemAt(i) for i in range(self.LabelGrid.verticalLayout.count()))
        for label in labels:
            label = label.widget()
            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;
                """)

#########DragButton Class#############
class DragButton(QLabel):

    def mousePressEvent(self, event):
        self.__mousePressPos = None
        self.__mouseMovePos = None
        if event.button() == QtCore.Qt.LeftButton:
            self.__mousePressPos = event.globalPos()
            self.__mouseMovePos = event.globalPos()
        self.start_move = 0
        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

    #If you Uncomment these blocks, the labels are no longer able to move freely. They snap back to their original position when released

            #if not self.start_move: 
                #self.setStyleSheet("background-color: red;")

            #if not self.start_move:
                #self.start_move = 1


        super(DragButton, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        if self.__mousePressPos is not None:


        #if self.start_move:
            #self.setStyleSheet("background-color: lightblue;")

            moved = event.globalPos() - self.__mousePressPos

            if moved.manhattanLength() > 3:
                event.ignore()
                return

        super(DragButton, self).mouseReleaseEvent(event)
##############################################


if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    ret = app.exec_()
    sys.exit( ret )
可拖动标签的UI类:

from PySide import QtCore, QtGui

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(400, 300)
        self.gridLayout = QtGui.QGridLayout(Dialog)
        self.gridLayout.setObjectName("gridLayout")
        self.verticalLayout = QtGui.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.option1 = DragButton(Dialog)
        font = QtGui.QFont()
        font.setWeight(75)
        font.setBold(True)
        self.option1.setFont(font)
        self.option1.setFrameShape(QtGui.QFrame.StyledPanel)
        self.option1.setFrameShadow(QtGui.QFrame.Raised)
        self.option1.setAlignment(QtCore.Qt.AlignCenter)
        self.option1.setObjectName("option1")
        self.verticalLayout.addWidget(self.option1)
        self.option2 = DragButton(Dialog)
        font = QtGui.QFont()
        font.setWeight(75)
        font.setBold(True)
        self.option2.setFont(font)
        self.option2.setFrameShape(QtGui.QFrame.StyledPanel)
        self.option2.setFrameShadow(QtGui.QFrame.Raised)
        self.option2.setAlignment(QtCore.Qt.AlignCenter)
        self.option2.setObjectName("option2")
        self.verticalLayout.addWidget(self.option2)
        self.Option3 = DragButton(Dialog)
        font = QtGui.QFont()
        font.setWeight(75)
        font.setBold(True)
        self.Option3.setFont(font)
        self.Option3.setFrameShape(QtGui.QFrame.StyledPanel)
        self.Option3.setFrameShadow(QtGui.QFrame.Raised)
        self.Option3.setAlignment(QtCore.Qt.AlignCenter)
        self.Option3.setObjectName("Option3")
        self.verticalLayout.addWidget(self.Option3)
        self.Option4 = DragButton(Dialog)
        font = QtGui.QFont()
        font.setWeight(75)
        font.setBold(True)
        self.Option4.setFont(font)
        self.Option4.setFrameShape(QtGui.QFrame.StyledPanel)
        self.Option4.setFrameShadow(QtGui.QFrame.Raised)
        self.Option4.setLineWidth(1)
        self.Option4.setMidLineWidth(0)
        self.Option4.setAlignment(QtCore.Qt.AlignCenter)
        self.Option4.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
        self.Option4.setObjectName("Option4")
        self.verticalLayout.addWidget(self.Option4)
        self.Option5 = DragButton(Dialog)
        font = QtGui.QFont()
        font.setWeight(75)
        font.setBold(True)
        self.Option5.setFont(font)
        self.Option5.setFrameShape(QtGui.QFrame.StyledPanel)
        self.Option5.setFrameShadow(QtGui.QFrame.Raised)
        self.Option5.setLineWidth(1)
        self.Option5.setMidLineWidth(0)
        self.Option5.setAlignment(QtCore.Qt.AlignCenter)
        self.Option5.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
        self.Option5.setObjectName("Option5")
        self.verticalLayout.addWidget(self.Option5)
        self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
        self.option1.setText(QtGui.QApplication.translate("Dialog", "Option 1", None, QtGui.QApplication.UnicodeUTF8))
        self.option2.setText(QtGui.QApplication.translate("Dialog", "Option 2", None, QtGui.QApplication.UnicodeUTF8))
        self.Option3.setText(QtGui.QApplication.translate("Dialog", "Option 3", None, QtGui.QApplication.UnicodeUTF8))
        self.Option4.setText(QtGui.QApplication.translate("Dialog", "Option 4", None, QtGui.QApplication.UnicodeUTF8))
        self.Option5.setText(QtGui.QApplication.translate("Dialog", "Option 5", None, QtGui.QApplication.UnicodeUTF8))

from app import DragButton

每当一个小部件被添加到一个布局中时,它的大小和位置由该布局决定(根据可用的大小和布局中的其他小部件)

虽然可以在小部件作为布局的一部分时更改其几何图形,但对其内容的任何更改都将自动通知布局,然后布局将相应地更新自身。
这些更改包括不同的大小、不同的约束(最小/最大大小)、大小策略(扩展或收缩的能力)

当一个样式表被设置为一个小部件时,它的内容立即失效并再次计算(即使样式表是相同的);无法移动小部件的原因是,一旦应用了样式表,包含它的布局将再次强制它回到原始位置

如果保留注释行,移动小部件,然后调整窗口大小,您可以看到会发生什么:移动的小部件将根据布局要求重新定位

如果您希望能够自由移动小部件,则需要将可移动小部件创建为包含它们的小部件的子部件,但不在布局中。
如果您希望以类似于真实QLayout的方式“布局”它们,但在包含它们的小部件的
resizeEvent
中,这显然会成为一个问题

在这种情况下:

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
这样,标签在第一次显示时就与“虚拟”布局对齐,但现在可以自由移动。显然,通过这个简单的实现,只要调整窗口的大小,它们就会再次被重新定位,因此由您决定如何处理手动移动的标签


作为旁注,您应该而不是使用QDialog类来满足您的需要。对话框用作显示在其他现有窗口上的窗口。对于您的需要,一个简单的QWidget就足够了。

您能告诉我您还更改了什么吗?你是怎么让它不是一个布局的?仅添加resizeEvent似乎不起作用。@Shock-o-lot如果您在designer中创建一个新的小部件,只需拖动其中的子部件,而不设置布局,否则右键单击任何子部件并从“布局”子菜单中选择“断开布局”。当从代码中添加小部件时,您只需添加父参数:在您的情况下,它将是
self.someButton=DragButton(self)
。谢谢。成功了。现在,我正在尝试使标签只能存在于特定的框或区域中,并且可以通过拖放对其重新排序。例如,如果将顶部的标签拖动到底部标签的上方,这两个标签将交换并捕捉到位。我该怎么做呢?@Shock-o-lot您需要检查mouseMoveEvent中的所有内容,并考虑所有“兄弟”标签,但问题太广泛,无法发表评论,与此问题无关。我建议您自己尝试一下,如果您找不到适合您需要的解决方案,请创建一个新问题。我已经问了这个问题,并包括了我的尝试:。我尝试移动小部件时遇到了一些问题。