我正在Windows 10上的 Python 3.7 中工作,并将 PyQt5 用于GUI。在我的应用程序中,我得到了一个QScrollArea()
,其中包含一系列按钮。单击时,按钮必须移到该区域之外。我使用QPropertyAnimation()
来显示运动。
我已经创建了一个用于测试的小型应用程序。该应用程序显示一个小的QScrollArea()
,其中带有一堆按钮。当您单击按钮时,它将移至右侧:
代码如下:
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()
中。我需要把它放在一切之上:
感谢@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
请注意,一秒钟后,我在hide()
按钮上安装了单发计时器。这是因为只要此按钮的行为像“弹出”(由于self.setWindowFlags(Qt.Popup)
),Qt事件循环就被阻止。无论如何,单拍定时器对我来说足够好了。
很遗憾,我还剩下一期。当我单击第一个按钮 Foo 时,它几乎从最初的位置开始播放动画。如果我点击其他按钮之一(例如 Baz ),它会突然跳下约100像素并从此处开始动画。
我认为这与startpoint = self.mapToGlobal(self.pos())
函数以及这些按钮位于QScrollArea()
中有关。但是我不知道该如何解决。
我的目的是建立一个像这样的鼠标右键单击菜单:
当用户点击抓取并移动时,该按钮应从QScrollArea()
中消失,并迅速移向鼠标。当它到达鼠标指针时,该按钮应淡出,并且拖放操作可以开始。
注意:与该主题相关的以下问题是这个问题:
Qt: How to perform a drag-and-drop without holding down the mouse button?
答案 0 :(得分:2)
小部件的位置是相对于其父级的,所以您不应使用startpoint = self.mapToGlobal(self.pos())
,而应使用startpoint = self.mapToGlobal(QPoint())
,因为对于小部件,topLeft的位置是QPoint(0, 0)
。 / p>
因此,如果您想使用@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 pos已经存在,因此不必创建qproperty位置。
答案 1 :(得分:1)
使用mapToGlobal()
计算按钮的全局坐标。
使用Qt.Popup
和setWindowFlags()
show()
初始开始值和结束值与(1)中的全局坐标相关,并开始动画
ps在C ++上工作:)