实现效果:
import sys
import time
from PyQt5.Qt import *
from PyQt5.QtCore import *
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import *
# 自定义的item 继承自QListWidgetItem 为了在每一项里面装多个控件(或者说把每一项变为一个widget)
class customQListWidgetItem(QListWidgetItem):
"""
https://blog.csdn.net/Strengthennn/article/details/103747819
QT的:https://blog.csdn.net/HES_C/article/details/127429982
"""
def __init__(self, name, img):
super().__init__()
# 自定义item中的widget 用来显示自定义的内容 相当于个容器
self.widget = QWidget()
self.name = name # 把widget里面的每个控件所需要的参数放进实例属性里面 下面拖拽功能里需要用
self.img_path = img
# 用来显示name
self.nameLabel = QLabel()
self.nameLabel.setText(self.name)
# 用来显示avator(图像)
self.avatorLabel = QLabel()
# 设置图像源 和 图像大小
self.avatorLabel.setPixmap(QPixmap(self.img_path).scaled(50, 50))
self.avatorLabel.setScaledContents(True) # 自适应大小
# 设置布局用来对nameLabel和avatorLabel进行布局
self.hbox = QHBoxLayout()
self.hbox.addWidget(self.avatorLabel)
self.hbox.addWidget(self.nameLabel)
self.hbox.addStretch(1) # 添加 空白伸缩 为了让标签靠左
# 设置widget的布局
self.widget.setLayout(self.hbox)
# 设置自定义的QListWidgetItem的sizeHint,不然无法显示
self.setSizeHint(self.widget.sizeHint())
# 自定义QListWidget类 QListWidget 为了可以拖动Item
class MyListWidget(QListWidget):
"""
https://qa.1r1g.com/sf/ask/1835951981/ 重写类
https://blog.csdn.net/kenfan1647/article/details/119277723 模式选择
https://blog.csdn.net/mokemg/article/details/105286527 模式选择
https://www.thinbug.com/q/30291628 模式选择
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # 调用父类
self.setSelectionMode(QAbstractItemView.ExtendedSelection) # 设置选择模式 这个可以按住control多选
# 默认为同时支持拖和放 这个很重要,搜了很多都是 InternalMove 但是会导致拖动一个的时候出现混乱 下面会有判断如果选择多项的时候就改成这个InternalMove
self.setDragDropMode(QAbstractItemView.DragDrop)
# 以下都不用设置 默认就是的 https://blog.csdn.net/kenfan1647/article/details/119277723
# self.setDragEnabled(True) # 允许托动 默认就是True
# self.setAcceptDrops(True) # 允许放下 默认就是True
# self.setDragDropOverwriteMode(False) # 保存视图的拖放行为
# self.setDropIndicatorShown(True) # https://zhidao.baidu.com/question/2079442716339294708.html
# self.setAcceptDrops(True) # 设置接收释放
# self.setSelectionBehavior(QAbstractItemView.SelectRows) # 选择行或者列等
# self.setDefaultDropAction(Qt.TargetMoveAction) # 默认使用的放置操作。
# 重写释放时间
def dropEvent(self, event):
if event.source() == self: # 只接受自己的拖放
# 所有被选择的Item的行 集合
rows = set([mi.row() for mi in self.selectedIndexes()])
# 释放时 事件的鼠标位置所在的索引 的行
targetRow = self.indexAt(event.pos()).row()
# 防止释放所在的行也被误选择了 所以移除目标行 应该也可以用row.remove(targetRow)
rows.discard(targetRow)
rows = sorted(rows) # 排序
# 如果没有选择行或者说 自己拖放到自己身上那么忽略事件
if not rows:
event.ignore()
return
# 如果拖动到超出QListWidget范围了 那么放在最后
if targetRow == -1:
targetRow = self.count()
# 这步骤很重要,是否会导致拖下去然后下面增加了 上面删除了它附近项 会改变原来的QListWidget
# 如果拖动了一项 那么自己重写 如果选择了多项 那么重新赋值setDragDropMode为InternalMove就可以
if len(rows) > 1:
self.setDragDropMode(QAbstractItemView.InternalMove)
super(MyListWidget, self).dropEvent(event)
event.ignore()
self.setDragDropMode(QAbstractItemView.DragDrop) # 一定要改回去
return
row_n_item = self.item(rows[0]) # 获取拖动的Item
# 重新创建自定义的QListWidget对象 因为测试直接 调用上面获取的拖动Item 会显示空白
item = customQListWidgetItem(row_n_item.name, row_n_item.img_path)
# 删除QListWidget中的Item
# https://qa.1r1g.com/sf/ask/1668509321/
# https://blog.51cto.com/xiaohaiwa/5380507
self.takeItem(rows[0]) # 删除拖动行
self.insertItem(targetRow, item) # 在目标行位置 插入上面重新创建的ITem
self.setItemWidget(item, item.widget) # 设置插入的Item的widget
event.accept()
return
if __name__ == "__main__":
app = QApplication(sys.argv)
# 主窗口
w = QWidget()
w.setWindowTitle("QListWindow")
w.resize(300, 600)
# 创建自定义的QListWidget对象
listWidget = MyListWidget()
item1 = customQListWidgetItem("1", "res/Img/子弹.png")
item2 = customQListWidgetItem("2", "res/Img/小程序.png")
item3 = customQListWidgetItem("3", "res/Img/手机.png")
item4 = customQListWidgetItem("4", "res/Img/搜一搜.png")
item5 = customQListWidgetItem("5", "res/Img/搜索.png")
# 在listWidget中加入5个自定义的item
listWidget.addItem(item1)
listWidget.setItemWidget(item1, item1.widget)
listWidget.addItem(item2)
listWidget.setItemWidget(item2, item2.widget)
listWidget.addItem(item3)
listWidget.setItemWidget(item3, item3.widget)
listWidget.addItem(item4)
listWidget.setItemWidget(item4, item4.widget)
listWidget.addItem(item5)
listWidget.setItemWidget(item5, item5.widget)
# 设置垂直布局
layout = QVBoxLayout()
# 把自定义QListWidget添加到垂直布局
layout.addWidget(listWidget)
# 把布局添加到主QWidget上面
w.setLayout(layout)
# 绑定点击槽函数 点击显示对应item中的所在的行号
listWidget.itemClicked.connect(lambda item: print(listWidget.row(item)))
w.show()
sys.exit(app.exec_())