PyQt6:如何避免拖放事件中列表项重叠并下移已有项
PyQt6列表项拖拽重叠问题解决
问题说明
使用PyQt6开发跨组件拖拽列表项的GUI时,拖放目标位置的列表项会重叠。期望效果是拖放时目标位置及下方的已有列表项自动下移,为新项腾出空间。尝试过统计目标组件现有项数量、对比插入索引来手动下移,但插入行索引仅在拖放完成后才生成,无法提前处理。
当前代码
import sys from PyQt6.QtWidgets import QApplication, QWidget, QListView, QAbstractItemView, QHBoxLayout, QVBoxLayout, QLabel from PyQt6.QtCore import Qt, QSize from PyQt6.QtGui import QStandardItemModel, QIcon, QStandardItem class Draggable_ListView(QListView): def __init__(self, parent=None): super().__init__(parent) self.setAcceptDrops(True) self.setIconSize(QSize(150, 150)) self.setDragDropMode(QAbstractItemView.DragDropMode.DragDrop) self.setResizeMode(QListView.ResizeMode.Adjust) self.setViewMode(QListView.ViewMode.ListMode) def dropEvent(self, event): super().dropEvent(event) event.setDropAction(Qt.DropAction.MoveAction) class ListView_Left(QWidget): def __init__(self, parent=None): super().__init__(parent) self.parent = parent self.subtitle = 'List 1' layout = QVBoxLayout() self.setLayout(layout) subtitle_label = QLabel(self.subtitle) layout.addWidget(subtitle_label, alignment=Qt.AlignmentFlag.AlignCenter) self.list_view = Draggable_ListView(self) layout.addWidget(self.list_view) def set_model(self, model): self.list_view.setModel(model) def get_model(self): return self.list_view.model() class ListView_Right(QWidget): def __init__(self,parent=None): super().__init__(parent) self.subtitle = 'List 2' layout = QVBoxLayout() self.setLayout(layout) subtitle_label = QLabel(self.subtitle) layout.addWidget(subtitle_label, alignment=Qt.AlignmentFlag.AlignCenter) self.list_view = Draggable_ListView(self) layout.addWidget(self.list_view) def set_model(self, model): self.list_view.setModel(model) def get_model(self): return self.list_view.model() class MyApp(QWidget): def __init__(self): super().__init__() self.window_width, self.window_height = 1400, 500 self.setMinimumSize(self.window_width, self.window_height) self.resize(self.window_width, self.window_height) layout = QHBoxLayout() self.setLayout(layout) self.listViewLeft = ListView_Left(self) layout.addWidget(self.listViewLeft) self.listViewRight = ListView_Right(self) layout.addWidget(self.listViewRight) icon = QStandardItem() icon.setIcon(QIcon(r"path to JPG image")) icon_2 = QStandardItem() icon_2.setIcon(QIcon(r"path to JPG image_1")) icon_3 = QStandardItem() icon_3.setIcon(QIcon(r"path to JPG image_2")) self.listViewLeft.set_model(QStandardItemModel(self)) self.listViewLeft.get_model().appendRow(icon) self.listViewLeft.get_model().appendRow(icon_2) self.listViewLeft.get_model().appendRow(icon_3) self.listViewRight.set_model(QStandardItemModel(self)) if __name__ == '__main__': app = QApplication(sys.argv) myApp = MyApp() myApp.show() sys.exit(app.exec())
解决方案
核心问题是默认拖放逻辑未正确触发目标位置的项位移,通过重写拖拽相关事件、配置视图属性,结合手动处理跨模型拖拽数据迁移,可实现预期效果:
修改后的Draggable_ListView类
class Draggable_ListView(QListView): def __init__(self, parent=None): super().__init__(parent) self.setAcceptDrops(True) self.setIconSize(QSize(150, 150)) # 配置拖拽模式为支持跨组件拖拽和内部移动 self.setDragDropMode(QAbstractItemView.DragDropMode.DragDrop) self.setDefaultDropAction(Qt.DropAction.MoveAction) self.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) self.setResizeMode(QListView.ResizeMode.Adjust) self.setViewMode(QListView.ViewMode.ListMode) # 显示插入位置指示器,明确提示用户拖放位置 self.setDropIndicatorShown(True) def dragMoveEvent(self, event): # 允许跨组件或内部的移动拖拽动作 if event.source() != self or event.possibleActions() & Qt.DropAction.MoveAction: event.setDropAction(Qt.DropAction.MoveAction) event.accept() else: event.ignore() def dropEvent(self, event): # 处理跨不同模型的拖拽 if event.source() != self: mime_data = event.mimeData() if mime_data.hasFormat('application/x-qabstractitemmodeldatalist'): target_model = self.model() # 获取插入位置:拖到空白区域则插入到末尾,否则插入到目标索引上方 index = self.indexAt(event.pos()) insert_row = target_model.rowCount() if not index.isValid() else index.row() # 从源模型复制数据到目标模型,并删除源项 source_model = event.source().model() for idx in event.source().selectedIndexes(): item = source_model.itemFromIndex(idx) target_model.insertRow(insert_row, item.clone()) source_model.removeRow(idx.row()) event.setDropAction(Qt.DropAction.MoveAction) event.accept() else: # 内部拖拽使用默认处理逻辑 super().dropEvent(event)
关键说明
setDropIndicatorShown(True):开启插入位置指示器,拖拽时显示一条线提示插入位置,提升用户体验。- 跨模型拖拽手动处理数据:默认拖放逻辑在跨不同
QStandardItemModel时无法自动完成插入下移,因此需要手动获取源项数据、插入到目标位置并删除源项。 dragMoveEvent中明确允许移动动作,确保拖拽过程中视图正确响应并显示插入提示。
内容的提问来源于stack exchange,提问作者Shubham
相关产品推荐
相关产品推荐

