PyQt5 自定义可拖拽列表组件:实现动态排序、上下移动与删除功能
在本篇教程中,将实现一个基于 PyQt5 的自定义列表组件,这个组件支持拖拽排序、点击按钮上下移动、以及点击删除的功能。
实现效果:
为什么不用Qt自带的QListWidget
和 QListView
?
QListWidget
支持拖拽,但仅限于默认的文字图标样式的item,不支持item上显示自定义widget(拖拽之后会出现空白)- 自定义拖拽图像:默认的
QListWidget
直接拖动列表项的文字或图标,而我们希望通过截图方式,让拖拽过程中显示小部件的实际样式。 - 控制拖拽行为:在自定义组件中,可以细粒度地控制拖拽行为,甚至动态调整拖拽图像、拖拽位置、以及拖拽结果的处理逻辑。例如,我们可以在拖拽时调整列表项的大小,或者设置复杂的拖拽区域判断。
:::
环境准备
首先,请确保你的环境中安装了 PyQt5,可以使用以下命令安装:
pip install PyQt5
项目需求分析
实现一个自定义列表组件,要求如下:
- 每个列表项由一个标签和三个操作按钮组成(上移、下移、删除)。
- 支持拖拽排序,使用户可以自由地重新排列列表项的顺序。
- 能够通过按钮快速将项上下移动或删除。
代码详解
1. 项目结构
首先我们创建以下几个类:
CustomWidget
: 定义列表项样式,包括标签和按钮。FileInfo
: 存储每个列表项的信息。FileItem
: 继承QStandardItem
,保存自定义数据。FileListView
: 主列表视图类,实现拖拽、排序和按钮操作的核心逻辑。
2. 自定义列表项 CustomWidget
CustomWidget
负责定义每个列表项的外观,包括:
- 标签显示项的名称。
↑
按钮(上移)、↓
按钮(下移)、以及删除
按钮。- 鼠标悬停效果。
代码实现如下:
class CustomWidget(QWidget):"""自定义列表项,带有标签和删除按钮"""def __init__(self, text):super(CustomWidget, self).__init__()self.is_selected = Falseself.label = QLabel(os.path.basename(text))self.btn_up = QPushButton("↑")self.btn_down = QPushButton("↓")self.btn_delete = QPushButton("删除")# 创建布局layout = QHBoxLayout()layout.addWidget(self.label)spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)layout.addItem(spacerItem)layout.addWidget(self.btn_up)layout.addWidget(self.btn_down)layout.addWidget(self.btn_delete)layout.setContentsMargins(0, 0, 10, 0)self.setLayout(layout)self.setStyleSheet("QWidget,QLabel{background: rgb(238,244,249);}")
通过 QLabel
显示标签信息,QPushButton
添加操作按钮,并设置 QHBoxLayout
布局,使标签和按钮水平排列。
3. 数据结构 FileInfo
和 FileItem
FileInfo
类保存每个列表项的具体信息,如文件名、路径和排序所需的页码。
class FileInfo:def __init__(self, file, index):self.filename = os.path.basename(file)self.filepath = fileself.index = indexself.selected = True
FileItem
继承自 QStandardItem
,在每项中存储 FileInfo
数据。
class FileItem(QStandardItem):def __init__(self, fileinfo):super(FileItem, self).__init__()self.setData(fileinfo, Qt.UserRole) # 存储自定义文本数据
4. 列表视图 FileListView
FileListView
是组件的核心部分,定义了以下功能:
- 拖拽排序:通过
mousePressEvent
和startDrag
实现拖拽效果。 - 列表操作:调用
add_item
添加列表项,通过remove_item
删除项。
关键功能实现
- 拖拽:
在 FileListView
的 startDrag
方法中,生成自定义小部件的截图作为拖拽图像,并设置拖拽热点,确保拖拽开始时图像与鼠标对齐。
def startDrag(self, supportedActions):index = self.currentIndex()if index.isValid():item_widget = self.indexWidget(index)if item_widget:pixmap = item_widget.grab() drag = QDrag(self)drag.setPixmap(pixmap)drag.setHotSpot(self.start_drag_pos - item_widget.pos())drag.exec_(supportedActions)
- 上下移动:
按钮 ↑
和 ↓
实现上下移动功能。通过获取目标行号和源行号,进行插入和删除操作。
def move_item_up(self, item):row = item.row()if row > 0:new_item = FileItem(item.data(Qt.UserRole).copy())self.model.removeRow(item.row())self.model.insertRow(row - 1, new_item)self.set_item_widget(new_item)
- 删除:
当点击删除按钮时,调用 remove_item
方法,将指定项从模型中移除。
def remove_item(self, item):self.model.removeRow(item.row())
5. 初始化组件
在 main
函数中,我们创建一个 FileListView
实例,并添加几个示例项,展示组件的完整效果。
if __name__ == '__main__':app = QApplication(sys.argv)view = FileListView()for i in range(5):view.add_item(f'Item {i + 1}', i)view.show()sys.exit(app.exec_())
效果预览
运行后,窗口会显示一个列表,每个项可以通过拖拽排序、点击按钮上移或下移,或者删除,方便用户进行自由操作。
总结
通过本教程,我们实现了一个功能丰富的 PyQt5 自定义列表组件,主要包括以下要点:
- 自定义小部件,实现项目内容的布局和交互。
- 使用
QStandardItem
和QStandardItemModel
维护列表数据。 - 通过自定义拖拽和按钮操作,实现上下移动、删除和排序功能。
本组件适用于需要动态排序的项目管理类应用,如文件列表、任务管理等。
完整代码
https://github.com/LC044/pyqt_component_library