1.Цель
Моя цель - создать меню щелчка правой кнопкой мыши, например:
![enter image description here](https://i.stack.imgur.com/PzFPP.png)
Когда пользователь нажимает Хватайте и двигайте , кнопка должна исчезнуть с QScrollArea()
и быстро двигаться к мышке.Когда он достигнет указателя мыши, кнопка должна исчезнуть, и операция drag-and-drop может начаться.
2.Минимальный, воспроизводимый пример
У меня что-то работает, но оно еще не идеально.Скопируйте и вставьте приведенный ниже код и запустите его с Python 3.x (я использую Python 3.7) и PyQt5 .
Примечание. Чтобы сделатьстрока pixmap = QPixmap("my_pixmap.png")
работает правильно, пусть она ссылается на существующее png-изображение на вашем компьютере.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class MyButton(QPushButton):
'''
A special push button.
'''
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setFixedWidth(300)
self.setFixedHeight(30)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.showMenu)
return
def showMenu(self, pos):
'''
Show this popup menu when the user clicks with the right mouse button.
'''
menu = QMenu()
menuAction_01 = menu.addAction("action 01")
menuAction_02 = menu.addAction("action 02")
menuAction_03 = menu.addAction("action 03")
menuAction_04 = menu.addAction("action 04")
menuAction_grab = menu.addAction("grab")
action = menu.exec_(self.mapToGlobal(pos))
if action == menuAction_01:
print("clicked on action 01")
elif action == menuAction_02:
print("clicked on action 02")
elif action == menuAction_03:
print("clicked on action 03")
elif action == menuAction_04:
print("clicked on action 04")
elif action == menuAction_grab:
print("clicked on grab")
# 1. Start animation
# -> button moves to mouse pointer
self.animate()
# 2. After animation finishes (about 1 sec)
# -> start drag operation
QTimer.singleShot(1000, self.start_drag)
return
def animate(self):
'''
The button removes itself from the QScrollArea() and flies to the mouse cursor.
For more details, see the anser of @eyllanesc at
/8530564/kak-otobrazit-qpropertyanimation-poverh-qscrollarea
'''
startpoint = self.window().mapFromGlobal(self.mapToGlobal(QPoint()))
endpoint = self.window().mapFromGlobal(QCursor.pos())
self.setParent(self.window())
anim = QPropertyAnimation(
self,
b"pos",
self,
duration=1000,
startValue=startpoint,
endValue=endpoint,
finished=self.hide,
)
anim.start()
self.show()
return
def start_drag(self):
'''
Start the drag operation.
'''
drag = QDrag(self)
pixmap = QPixmap("my_pixmap.png")
pixmap = pixmap.scaledToWidth(100, Qt.SmoothTransformation)
drag.setPixmap(pixmap)
mimeData = QMimeData()
mimeData.setText("Foobar")
drag.setMimeData(mimeData)
dropAction = drag.exec(Qt.CopyAction | Qt.MoveAction)
return
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()
self.setAcceptDrops(True)
return
def dropEvent(self, event):
event.acceptProposedAction()
print("dropEvent at {0!s}".format(event))
return
def dragLeaveEvent(self, event):
event.accept()
return
def dragEnterEvent(self, event):
event.acceptProposedAction()
return
if __name__== '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
Запустите скрипт, и вы увидите небольшое окно с несколькими кнопками в QScrollArea()
:
ШАГ 1: Нажмите одну из кнопок правой кнопкой мыши.Вы должны увидеть всплывающее меню.Нажмите «захват».
ШАГ 2: Кнопка переместится на указатель мыши.Не перемещайте указатель мыши.
ШАГ 3: Как только указатель мыши окажется над кнопкой (не двигайте мышь, подождите, пока кнопка не появится), нажмитеи удерживайте кнопку мыши нажатой.
ШАГ 4: Теперь переместите мышь (удерживая кнопку мыши нажатой).Вы должны быть в состоянии drag-and-drop , с растровым изображением, привязанным к вашей мыши!
![enter image description here](https://i.stack.imgur.com/eg9hL.png)
Хорошо, этоработает, но есть несколько минусов.
3.Задача
В конце анимации летающая кнопка находится под указателем мыши.Но если вы слегка передвинете указатель мыши, кнопка исчезнет, и вы пропустите операцию drag-and-drop .
Другими словами, то, что я получил сейчас, не очень надежно.Пользователь может легко пропустить операцию drag-and-drop .
ПРИМЕЧАНИЕ. Очевидно, что описанная здесь проблема возникает только в Windows (не в Linux).Но я должен заставить эту штуку работать на Windows ...
4.Потенциальное решение
Я полагаю, что следующий подход будет лучше и все же интуитивно понятен для пользователя:
Как только кнопка появляется под указателем мыши (конец анимации), кнопкаисчезает.Операция drag-and-drop запускается автоматически, без необходимости нажимать и удерживать кнопку мыши.Перетаскивание продолжается, пока вы перемещаете указатель мыши, пока не щелкните где-нибудь.Это нажатие мыши - dropEvent()
.
Вы знаете, как это реализовать?Или, может быть, вы имеете в виду другой подход?
5.Примечания
Мой вопрос на самом деле является продолжением этого: Как отобразить QPropertyAnimation () поверх QScrollArea ()?
Спасибо @eyllanesc за решение этой проблемыодин ^ _ ^