1. Intro
我工作于Python 3.7在 Windows 10 上并使用PyQt5对于图形用户界面。在我的申请中,我得到了一个QScrollArea()
里面有一系列按钮。单击时,按钮必须移到该区域之外。我用一个QPropertyAnimation()
以显示运动。
2. 最小的、可重复的示例
我创建了一个用于测试的小应用程序。该应用程序显示一个小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_())
3.问题
当按钮移动时,它会停留在QScrollArea()
。我需要把它放在一切之上:
4.解决方案(差不多)
感谢@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()
一秒钟后按下按钮。这是因为只要此按钮表现为“弹出窗口”,Qt 事件循环就会被阻止(因为self.setWindowFlags(Qt.Popup)
)。无论如何,一次性计时器对我来说已经足够好了。
不幸的是我还剩下一个问题。当我点击第一个按钮时Foo,它(几乎)从最初所在的位置开始动画。如果我点击其他按钮之一 - 比如Baz- 它突然跳下约 100 像素并从那里开始动画。
我认为这与startpoint = self.mapToGlobal(self.pos())
功能以及这些按钮位于一个事实QScrollArea()
。但我不知道如何解决这个问题。
5. 目的
我的目的是建立一个像这样的鼠标右键菜单:
当用户点击抓取并移动,该按钮应该从QScrollArea()
并快速向鼠标移动。当它到达鼠标指针时,按钮应该淡出并且拖放可以开始操作。
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? https://stackoverflow.com/questions/56263595/qt-how-to-perform-a-drag-and-drop-without-holding-down-the-mouse-button