import sys
from PyQt5.QtWidgets import QHBoxLayout, QLabel, QMainWindow, QVBoxLayout, QWidget, QApplication
from PyQt5.QtCore import QMimeData, Qt
from PyQt5.QtGui import QDrag, QPainter, QPixmap, QColor
class DragBaseLayout(QWidget):
def __init__(self, parent=None):
super(DragBaseLayout, self).__init__(parent)
self.setAcceptDrops(True) # 必须开启接受拖拽
self.setLayoutType(QVBoxLayout) # 设置布局类型并且设定好布局
self.createComponent() # 生成控件
self.assembleComponent() # 组装控件
# 设定控件的layout类型
def setLayoutType(self, Alayout):
self.layout = Alayout()
self.setLayout(self.layout)
# 初始化内部的控件
def createComponent(self):
self.lb1 = QLabel()
self.lb1.setPixmap(QPixmap("A.png"))
self.lb2 = QLabel()
self.lb2.setPixmap(QPixmap("../qss学习/B.png"))
self.lb3 = QLabel()
self.lb3.setPixmap(QPixmap("C.png"))
self.line = LineLable(self)
def assembleComponent(self):
self.layout.addWidget(self.lb1)
self.layout.addWidget(self.lb2)
self.layout.addWidget(self.lb3)
# 自定义插入控件的函数
def addWidget(self, widget):
self.layout.addWidget(widget)
# 设定鼠标按下事件
def mousePressEvent(self, event):
item = self.childAt(event.pos()) # 通过位置获得控件
if item == None: return # 如果为空则直接跳过
index = self.layout.indexOf(item) # 获得当前控件所处的布局中的索引位置
self.drag = QDrag(item) # 创建QDrag对象
mimedata = QMimeData() # 然后必须要有mimeData对象,用于传递拖拽控件的原始index信息
mimedata.setText(str(index)) # 携带索引位置信息
self.drag.setMimeData(mimedata)
pixmap = QPixmap(item.size())
pixmap.fill(QColor(255, 255, 255, 0.5)) # 绘制为透明度为0.5的白板
painter = QPainter(pixmap)
painter.setOpacity(0.5) # painter透明度为0.5
painter.drawPixmap(item.rect(), item.grab()) # 这个很有用,自动绘制整个控件
painter.end()
self.drag.setPixmap(pixmap)
self.drag.setHotSpot(event.pos() - item.pos())
self.drag.exec_(Qt.MoveAction) # 这个作为drag对象必须执行
# 拖拽移动事件(通过实时监测鼠标的位置,并根据位置决定标志线是否显示)
def dragMoveEvent(self, event):
point = event.pos()
currentItem = self.childAt(point)
if type(currentItem) == QLabel:
geometry = currentItem.geometry()
if type(self.layout) == QHBoxLayout:
self.Hline(point, geometry, currentItem)
elif type(self.layout) == QVBoxLayout:
self.Vline(point, geometry, currentItem)
else:
self.line.hide()
# 拖拽放下事件
def dropEvent(self, event):
point = event.pos() # 获得落点的坐标
otherItem = self.childAt(point) # 获得当前落点上的控件
if type(otherItem) == QLabel:
if type(self.layout) == QHBoxLayout:
self.HInsert(point, otherItem) # 改变指定控件的位置(适用于水平布局)
elif type(self.layout) == QVBoxLayout:
self.VInsert(point, otherItem) # 改变指定控件的位置(适用于垂直布局)
# 鼠标拖拽接受事件
def dragEnterEvent(self, event):
event.setDropAction(Qt.MoveAction)
event.accept()
# 判定模式(横向判断)
def Hline(self, point, geometry, item):
x = geometry.x()
x2 = geometry.x() + geometry.width()
y = geometry.y()
if point.x() <= x + 15: # 横向模式,鼠标处于控件左边
self.line.resize(5, item.height())
self.line.move(x, y) # 将原本设定好的标示线(QLabel)移动到指定位置
self.line.raise_() # 设置为最顶层
self.line.show() # 显示
elif x2 - 15 <= point.x(): # 横向模式,鼠标处于控件右边
self.line.resize(5, item.height())
self.line.move(x2 - 5, y)
self.line.raise_()
self.line.show()
else:
self.line.hide()
# 判定模式(竖向判断)
def Vline(self, point, geometry, item):
y = geometry.y()
y2 = geometry.y() + geometry.height()
x = geometry.x()
if point.y() <= y + 15:
self.line.resize(item.width(), 5)
self.line.move(x, y)
self.line.raise_()
self.line.show()
elif y2 - 15 <= point.y():
self.line.resize(item.width(), 5)
self.line.move(x, y2 - 5)
self.line.raise_()
self.line.show()
else:
self.line.hide()
# 插入模式(横向判断)
def HInsert(self, point, otherItem):
geometry = otherItem.geometry()
x = geometry.x()
x2 = geometry.x() + geometry.width()
y = geometry.y()
oldIndex = int(self.drag.mimeData().text()) # 获得原先存储在mimeData中的索引信息
if oldIndex == self.layout.indexOf(otherItem): return # 如果otherItem和oldItem实属同一个控件,则不做改变
if point.x() <= x + 15: # 插入在控件的左边
oldItem = self.layout.takeAt(oldIndex).widget()
index = self.layout.indexOf(otherItem) # 重新获得当前控件所处的布局中的索引位置
self.layout.insertWidget(index, oldItem)
elif x2 - 15 <= point.x(): # 插入在控件的右边
oldItem = self.layout.takeAt(oldIndex).widget()
index = self.layout.indexOf(otherItem) # 重新获得当前控件所处的布局中的索引位置
self.layout.insertWidget(index + 1, oldItem)
else:
pass
self.line.hide()
# 插入模式(竖向判断)
def VInsert(self, point, otherItem):
geometry = otherItem.geometry()
y = geometry.y()
y2 = geometry.y() + geometry.height()
x = geometry.x()
oldIndex = int(self.drag.mimeData().text()) # 获得原先存储在mimeData中的索引信息
if oldIndex == self.layout.indexOf(otherItem): return # 如果otherItem和oldItem实属同一个控件,则不做改变
if point.y() <= y + 15: # 插入在控件的上边
oldItem = self.layout.takeAt(oldIndex).widget()
index = self.layout.indexOf(otherItem) # 重新获得当前控件所处的布局中的索引位置
self.layout.insertWidget(index, oldItem)
elif y2 - 15 <= point.y(): # 插入在控件的下边
oldItem = self.layout.takeAt(oldIndex).widget()
index = self.layout.indexOf(otherItem) # 重新获得当前控件所处的布局中的索引位置
self.layout.insertWidget(index + 1, oldItem)
else:
pass
self.line.hide()
# 指示条纹(实际是个QLabel)
class LineLable(QLabel):
def __init__(self, parent=None):
super(LineLable, self).__init__(parent)
self.setStyleSheet("background-color:red;")
self.hide() # 一开始隐藏
class mainWidget(QMainWindow):
def __init__(self, parent=None):
super(mainWidget, self).__init__(parent)
self.centerWidget = DragBaseLayout()
self.setCentralWidget(self.centerWidget)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = mainWidget()
win.show()
sys.exit(app.exec_())
11-28
1万+