嗨在PyQt5(PyQt:v 5.15.7)中得到了这段代码,基本上我正在尝试跟踪拖到QMainwindow之外的小部件:
#!/usr/bin/env python3
from PyQt5.QtWidgets import (QApplication, QWidget, QMainWindow,
QVBoxLayout, QPushButton, QGridLayout
)
from PyQt5.QtCore import Qt, QMimeData, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QDrag, QPixmap, QCursor
class DragWidget(QWidget):
def __init__(self, *args, name , **kwargs):
super().__init__(*args, **kwargs)
def mouseMoveEvent(self, e):
if e.buttons() == Qt.LeftButton:
drag = QDrag(self)
mime = QMimeData()
drag.setMimeData(mime)
pixmap = QPixmap(self.size())
self.render(pixmap)
drag.setPixmap(pixmap)
drag.exec_(Qt.MoveAction)
class MainWindow(QMainWindow):
whereDropped = pyqtSignal(int,int)
dragleaves = pyqtSignal()
def __init__(self):
super().__init__()
self.setGeometry(200,200, 400, 500)
self.container = QWidget()
self.layout = QGridLayout()
self.container.setLayout(self.layout)
self.setCentralWidget(self.container)
for i in range(4):
self.x = DragWidget(self, name = 'widget_'+str(i))
self.layout.addWidget( self.x)
self.x.setObjectName('widget_'+str(i))
self.b = QPushButton('widget___'+str(i))
self.layout_2 = QVBoxLayout()
self.x.setLayout(self.layout_2)
self.layout_2.addWidget(self.b)
self.x.show()
self.installEventFilter(self)
self.setAcceptDrops(True)
self.whereDropped.connect(self.attdeta)
self.dragleaves.connect(self.removeParent)
@pyqtSlot()
def removeParent(self) :
print("\n\nself.dragleaves.connect(self.removeParent))")
pass
@pyqtSlot(int, int)
def attdeta(self, tupx, tupy) :
print("\n\nself.whereDropped.connect(self.attdeta)")
print('tupxy : ', tupx , tupy)
def dropEvent(self, e):
pos = e.pos()
widget = e.source()
print('\n\ndropEvent _________')
print('widget = e.source() :' , widget)
print('e.pos() : ' , pos ,'\n\n')
self.whereDropped.emit(e.pos().x(), e.pos().y())
e.accept()
def dragEnterEvent(self, e):
print('dragEnterEvent _________' , e.source(), '\n')
print('dragEnterEvent _________event-source-name :' ,e.source().objectName())
print('e.pos() : ' , e.pos().x())
print('e.pos() : ' , e.pos().y() ,'\n\n')
e.accept()
def dragLeaveEvent(self, event):
print('\n\nDragLeaveEvent event : ', event)
# print(event.sender()) ### AttributeError: 'QDragLeaveEvent' object has no attribute 'sender'
# print(event.source()) ### AttributeError: 'QDragLeaveEvent' object has no attribute 'source'
print("Drag left at: " + str(self.mapFromGlobal(QCursor.pos())))
print('self.dragleaves.emit()')
self.whereDropped.emit(self.mapFromGlobal(QCursor.pos()).x(), self.mapFromGlobal(QCursor.pos()).y()) ## see [https://stackoverflow.com/questions/50022465/how-do-i-get-the-exit-point-from-a-qdragleaveevent][1]
self.dragleaves.emit()
event.accept()
app = QApplication([])
w = MainWindow()
w.show()
app.exec_()
有机会获得
dragLeaveEvent
的发件人/来源/小部件,例如
dragEnterEvent
??? :
def dragEnterEvent(self, e):
print('dragEnterEvent _________' , e.source(), '\n')
print('dragEnterEvent _________event-source-name :' ,e.source().objectName())
print('e.pos() : ' , e.pos().x())
print('e.pos() : ' , e.pos().y() ,'\n\n')
e.accept()
由于
QDragLeaveEvent
前面始终带有 QDragEnterEvent
,因此您可以将源小部件存储在 dragEnterEvent()
中并在 dragLeaveEvent()
中使用它。
所以,就这样做吧。
...
def dragEnterEvent(self, e):
self.drag_source = e.source()
...
def dragLeaveEvent(self, event):
print('dragLeaveEvent, drag_source:', self.drag_source)
...
...
所以我添加这段代码来完成上面的答案,因为只使用:
...
def dragEnterEvent(self, e):
self.drag_source = e.source()
...
def dragLeaveEvent(self, event):
print('dragLeaveEvent, drag_source:', self.drag_source)
...
当我需要处理多个子 Widget 并需要能够将其放回原来的布局时,这是不够的。我需要在字典中存储一些小部件信息。实际上,我需要存储拖动的 Widget 的父级,以便能够将其重新添加到其原始父级,并在重新设置其父级时将其添加到其原始布局。
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR
print("Qt: v", QT_VERSION_STR, "\tPyQt: v", PYQT_VERSION_STR)
from PyQt5.QtWidgets import (QApplication, QWidget, QMainWindow,
QVBoxLayout, QPushButton)
from PyQt5.QtCore import Qt, QMimeData, QEvent, QObject
from PyQt5.QtGui import QDrag, QPixmap
class DragWidget(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle(self.objectName())
self.installEventFilter(self)
self.drag_source = drag_source
self.mouseMoveEvent = self.mouseMoveEventNEW
def eventFilter(self, source, event):
if type(source) == DragWidget :
if event.type() == QEvent.Close:
source.setParent(self.drag_source[source])
name_wid = self.objectName()
lay_inter = self.parent().parent().findChildren( QVBoxLayout , 'lay_'+name_wid.rsplit('_')[-1])
source.mouseMoveEvent = self.mouseMoveEventNEW
lay_inter[0].addWidget(self)
self.drag_source.pop(source)
event.ignore()
return True
else:
return QObject.eventFilter(self, source, event)
else:
return False
def mouseMoveEventPASS(self):
pass
def mouseMoveEventNEW(self, e):
if e.buttons() == Qt.LeftButton:
drag = QDrag(self)
mime = QMimeData()
drag.setMimeData(mime)
pixmap = QPixmap(self.size())
self.render(pixmap)
drag.setPixmap(pixmap)
drag.exec_(Qt.MoveAction)
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__()
self.setGeometry(600,200, 400, 500)
self.container = QWidget()
self.container.setObjectName('pippopippo')
self.layout = QVBoxLayout()
self.layout.setObjectName('pippopippopippo')
self.container.setLayout(self.layout)
self.setCentralWidget(self.container)
for i in range(4):
self.layout2 = QVBoxLayout()
self.layout2.setObjectName('lay_'+str(i))
self.widg = QWidget()
self.widg.setStyleSheet("""
background-color: yellow;
border: 5px dashed black;
""")
self.widg.setLayout(self.layout2)
self.widg.setObjectName('bigger_widg_'+str(i))
self.layout.addWidget(self.widg)
self.x = DragWidget(self, objectName = 'widget_'+str(i))
self.layout2.addWidget( self.x)
self.b = QPushButton(self.x.objectName())
self.b.setStyleSheet("""
QPushButton {
background-color: red;
border: 5px solid black;
}
QPushButton:hover {
color: yellow;
background-color : green;
}
""")
self.layout_2 = QVBoxLayout()
self.x.setLayout(self.layout_2)
self.layout_2.addWidget(self.b)
self.setAcceptDrops(True)
self.drag_source = drag_source
def dropEvent(self, e):
e.accept()
def dragEnterEvent(self, e):
self.drag_source[e.source()] = e.source().parent()
e.accept()
def dragLeaveEvent(self, event):
list(self.drag_source)[-1].setParent(None)
list(self.drag_source)[-1].show()
list(self.drag_source)[-1].setGeometry(50,50, 250,250)
event.accept()
list(self.drag_source)[-1].mouseMoveEvent = DragWidget.mouseMoveEventPASS
def closeEvent(self, event):
for window in QApplication.topLevelWidgets():
window.close()
if __name__ == "__main__":
drag_source = {}
app = QApplication([])
w = MainWindow()
w.setObjectName('Name_Main-window')
w.setWindowTitle(w.objectName())
w.show()
app.exec_()
我还不喜欢
PyQt QDockWidget
(https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QDockWidget.html);我很快就会看看它们。
这里有一张图片,描述了我的代码的输出: