如何在qscrolArea()上显示QPropertyAnimation()?

2024-09-29 02:17:06 发布

您现在位置:Python中文网/ 问答频道 /正文

1。简介

我正在windows10上使用python3.7并使用PyQt5作为GUI。在我的应用程序中,我得到了一个QScrollArea(),里面有一组按钮。单击时,按钮必须移动到区域之外。我使用QPropertyAnimation()来显示移动。你知道吗


2。最小、可复制的示例

我创建了一个用于测试的小应用程序。这个应用程序显示了一个小的QScrollArea(),里面有一堆按钮。单击按钮时,它将向右移动:

enter image description here

代码如下:

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys

class MyButton(QPushButton):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setFixedWidth(300)
        self.setFixedHeight(30)
        self.clicked.connect(self.animate)
        return

    def animate(self):
        self.anim = QPropertyAnimation(self, b'position')
        self.anim.setDuration(3000)
        self.anim.setStartValue(QPointF(self.pos().x(), self.pos().y()))
        self.anim.setEndValue(QPointF(self.pos().x() + 200, self.pos().y() - 20))
        self.anim.start()
        return

    def _set_pos_(self, pos):
        self.move(pos.x(), pos.y())
        return

    position = pyqtProperty(QPointF, fset=_set_pos_)


class CustomMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setGeometry(100, 100, 600, 300)
        self.setWindowTitle("ANIMATION TEST")

        # OUTER FRAME
        # ============
        self.frm = QFrame()
        self.frm.setStyleSheet("""
            QFrame {
                background: #d3d7cf;
                border: none;
            }
        """)
        self.lyt = QHBoxLayout()
        self.frm.setLayout(self.lyt)
        self.setCentralWidget(self.frm)

        # BUTTON FRAME
        # =============
        self.btn_frm = QFrame()
        self.btn_frm.setStyleSheet("""
            QFrame {
                background: #ffffff;
                border: none;
            }
        """)
        self.btn_frm.setFixedWidth(400)
        self.btn_frm.setFixedHeight(200)
        self.btn_lyt = QVBoxLayout()
        self.btn_lyt.setAlignment(Qt.AlignTop)
        self.btn_lyt.setSpacing(5)
        self.btn_frm.setLayout(self.btn_lyt)

        # SCROLL AREA
        # ============
        self.scrollArea = QScrollArea()
        self.scrollArea.setStyleSheet("""
            QScrollArea {
                border-style: solid;
                border-width: 1px;
            }
        """)
        self.scrollArea.setWidget(self.btn_frm)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setFixedWidth(400)
        self.scrollArea.setFixedHeight(150)
        self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.lyt.addWidget(self.scrollArea)

        # ADD BUTTONS TO BTN_LAYOUT
        # ==========================
        self.btn_lyt.addWidget(MyButton("Foo"))
        self.btn_lyt.addWidget(MyButton("Bar"))
        self.btn_lyt.addWidget(MyButton("Baz"))
        self.btn_lyt.addWidget(MyButton("Qux"))
        self.show()
        return

if __name__== '__main__':
    app = QApplication(sys.argv)
    QApplication.setStyle(QStyleFactory.create('Plastique'))
    myGUI = CustomMainWindow()
    sys.exit(app.exec_())


三。问题

当按钮移动时,它停留在QScrollArea()。我需要把它放在一切之上:

enter image description here


第四条。解决方案(几乎)

谢谢你@magrif为我指明了正确的方向。多亏了你的建议,我才有了工作。你知道吗

所以我将animate()函数改为:

    def animate(self):
        self.anim = QPropertyAnimation(self, b'position')
        self.anim.setDuration(3000)
        startpoint = self.mapToGlobal(self.pos())
        endpoint = self.mapToGlobal(QPoint(self.pos().x() + 200, self.pos().y() - 20))
        self.setWindowFlags(Qt.Popup)
        self.show()
        self.anim.setStartValue(QPointF(startpoint.x(), startpoint.y()))
        self.anim.setEndValue(QPointF(endpoint.x(), endpoint.y()))
        self.anim.start()
        QTimer.singleShot(1000, self.hide)
        return

请注意,我在按钮上安装了一个单次触发计时器。这是因为只要这个按钮表现为“弹出窗口”(因为self.setWindowFlags(Qt.Popup)),Qt eventloop就会被阻塞。不管怎样,一次性计时器对我来说已经足够好了。你知道吗

不幸的是我还有一个问题。当我单击第一个按钮Foo时,它从最初的位置开始(几乎)动画。如果我点击另一个按钮,比如Baz,它会突然跳下大约100个像素并从那里开始动画。你知道吗

我认为这与startpoint = self.mapToGlobal(self.pos())函数以及这些按钮位于QScrollArea()中的事实有关。但我不知道怎么解决这个问题。你知道吗


五。目标

我的目的是建立一个鼠标右键菜单,如下所示:

enter image description here

当用户单击“抓取并移动”时,按钮应该从QScrollArea()中消失,并快速移向鼠标。当它到达鼠标指针时,按钮应该淡出,并且可以开始拖放操作。你知道吗

注:以下与本主题相关的问题是这个问题:
Qt: How to perform a drag-and-drop without holding down the mouse button?


Tags: posselfreturndefqt按钮btnqpointf
2条回答

小部件的位置是相对于其父部件的,因此不应使用startpoint = self.mapToGlobal(self.pos()),而应使用startpoint = self.mapToGlobal(QPoint()),因为对于小部件,左上角的位置是QPoint(0, 0)。你知道吗

因此,如果要使用@magrif解决方案,应该将其更改为:

def animate(self):
    startpoint = self.mapToGlobal(QPoint())
    self.setWindowFlags(Qt.Popup)
    self.show()
    anim = QPropertyAnimation(
        self,
        b"pos",
        self,
        duration=3000,
        startValue=startpoint,
        endValue=startpoint + QPoint(200, -20),
        finished=self.deleteLater,
    )
    anim.start()

但缺点是,当动画运行时,不能与窗口交互。你知道吗

另一种解决方案是将QFrame的父级更改为窗口本身:

def animate(self):
    startpoint = self.window().mapFromGlobal(self.mapToGlobal(QPoint()))
    self.setParent(self.window())
    anim = QPropertyAnimation(
        self,
        b"pos",
        self,
        duration=3000,
        startValue=startpoint,
        endValue=startpoint + QPoint(200, -20),
        finished=self.deleteLater,
    )
    anim.start()
    self.show()

注意:不需要创建qproperty位置,因为qpropertypos已经存在。你知道吗

  1. 使用mapToGlobal()计算按钮的全局坐标。

  2. 使用setWindowFlags()show()设置标志Qt.Popup

  3. Init start和end值关联(1)中的全局坐标,并开始动画

< C++中的P>作品::

相关问题 更多 >