如何在 QScrollArea() 之上显示 QPropertyAnimation()? [英] How display a QPropertyAnimation() on top of the QScrollArea()?

查看:76
本文介绍了如何在 QScrollArea() 之上显示 QPropertyAnimation()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Windows 10 上使用 Python 3.7 并使用 PyQt5 作为 GUI.在我的应用程序中,我得到了一个 QScrollArea() ,里面有一组按钮.单击时,按钮必须移出该区域.我使用 QPropertyAnimation() 来显示运动.

I'm working in Python 3.7 on Windows 10 and use PyQt5 for the GUI. In my application, I got a QScrollArea() with an array of buttons inside. When clicked, a button has to move outside the area. I use a QPropertyAnimation() to show the movement.

 

我创建了一个用于测试的小应用程序.该应用程序显示了一个小的 QScrollArea(),里面有一堆按钮.当你点击一个按钮时,它会向右移动:

I've created a small application for testing. The application shows a small QScrollArea() with a bunch of buttons inside. When you click on a button, it will move to the right:

代码如下:

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().我需要把它放在一切之上:

When the button moves, it stays in the QScrollArea(). I would need to get it on top of everything:

 

感谢@magrif 为我指明了正确的方向.感谢您的建议,我得到了一些工作.

Thank you @magrif for pointing me in the right direction. Thanks to your suggestions, I got something working.

所以我把 animate() 函数改成了这样:

So I changed the animate() function into this:

    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

请注意,我安装了一个单次定时器以在一秒后hide() 按钮.这是因为只要此按钮表现为弹出窗口"(因为 self.setWindowFlags(Qt.Popup)),Qt 事件循环就会被阻止.不管怎样,一次性定时器对我来说已经足够了.

Note that I install a single-shot timer to hide() the button after one second. That's because the Qt eventloop is blocked as long as this button behaves as a "popup" (because of self.setWindowFlags(Qt.Popup)). Anyway, the one-shot timer works good enough for me.

不幸的是,我还剩下一个问题.当我点击第一个按钮 Foo 时,它的动画(几乎)从它最初坐的位置开始.如果我点击其他按钮之一 - 例如 Baz - 它会突然跳下大约 100 像素并从那里开始动画.

Unfortunately I got one issue left. When I click on the first button Foo, it starts its animation (almost) from where it was sitting initially. If I click on one of the other buttons - like Baz - it suddenly jumps down about 100 pixels and starts its animation from there.

我认为这与 startpoint = self.mapToGlobal(self.pos()) 函数以及这些按钮位于 QScrollArea() 中的事实有关代码>.但我不知道如何解决这个问题.

I think this has something to do with the startpoint = self.mapToGlobal(self.pos()) function and the fact that those buttons are sitting in a QScrollArea(). But I don't know how to fix this.

 

我的目的是构建一个像这样的鼠标右键菜单:

My purpose is to build a rightmouse-click-menu like this:

当用户点击抓取并移动时,按钮应该从QScrollArea()消失并快速移向鼠标.当它到达鼠标指针时,按钮应该淡出并且拖放操作可以开始.

When the user clicks on Grab and move, the button should disappear from the QScrollArea() and move quickly towards the mouse. When it arrives at the mouse pointer, the button should fade out and the drag-and-drop operation can start.

注意:以下与此主题相关的问题是:
Qt:如何在不按住鼠标按钮的情况下执行拖放?

Note: The following question related to this topic is this one:
Qt: How to perform a drag-and-drop without holding down the mouse button?

推荐答案

widget 的位置是相对于它的父级的,所以你不应该使用 startpoint = self.mapToGlobal(self.pos())code>,但 startpoint = self.mapToGlobal(QPoint()),因为对于小部件,topLeft 的位置是 QPoint(0, 0).

The position of a widget is relative to its parent, so you should not use startpoint = self.mapToGlobal(self.pos()), but startpoint = self.mapToGlobal(QPoint()), since for the widget the position of the topLeft is the QPoint(0, 0).

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

So if you want to use the @magrif solution you should change it to:

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()

但缺点是动画运行时不能与窗口交互.

But the drawback is that while the animation is running you can not interact with the window.

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

Another solution is to change the parent of the QFrame to the window itself:

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 位置,因为 qproperty pos 已经存在.

Note: it is not necessary to create the qproperty position since the qproperty pos already exists.

这篇关于如何在 QScrollArea() 之上显示 QPropertyAnimation()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆