PySide6自定义QWidget拖拽时QListWidget项重叠消失及禁止复制问题求解
PySide6 QListWidget自定义项拖拽问题修复
问题表现
- 同一QListWidget内部拖拽项到其他项上方时,会出现项重叠堆叠、隐藏、显示为空白项的问题,异常效果如下:

- 拖拽时按住CTRL键会触发复制操作,鼠标光标变为+号,列表中会新增重复项,需要完全禁用该复制功能,保证两个QListWidget中所有项唯一,无重复。
问题原因
- 拖拽移动项的过程中,原有项的自定义Widget没有被正确清理,残留的Widget和新插入项生成的Widget堆叠,导致显示异常。
- QListWidget默认的拖拽策略允许CopyAction,即使设置了默认移动行为,按住CTRL键仍然会触发复制逻辑。
修复后的完整代码
from PySide6 import QtGui, QtCore, QtWidgets import sys, os, pathlib class ThumbListWidget(QtWidgets.QListWidget): def __init__(self, type=None, parent=None): super(ThumbListWidget, self).__init__(parent) self.setIconSize(QtCore.QSize(124, 124)) self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop) self.setDefaultDropAction(QtCore.Qt.MoveAction) self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.setAcceptDrops(True) self.setDragDropOverwriteMode(False) # 绑定行移除前的清理信号 self.model().rowsAboutToBeRemoved.connect( self.handleRowsAboutToBeRemoved, QtCore.Qt.QueuedConnection) self.model().rowsInserted.connect( self.handleRowsInserted, QtCore.Qt.QueuedConnection) def handleRowsAboutToBeRemoved(self, parent, first, last): # 移除行前先清理对应的自定义Widget,避免残留 for index in range(first, last + 1): item = self.item(index) if item is not None: self.removeItemWidget(item) def handleRowsInserted(self, parent, first, last): for index in range(first, last + 1): item = self.item(index) if item is not None and self.itemWidget(item) is None: index, name, icon = item.data(QtCore.Qt.UserRole) widget = QCustomQWidget() widget.setTextUp(index) widget.setTextDown(name) widget.setIcon(icon) item.setSizeHint(widget.sizeHint()) self.setItemWidget(item, widget) def startDrag(self, actions): # 启动拖拽时只允许移动行为,禁用复制 super().startDrag(QtCore.Qt.MoveAction) def dropEvent(self, event): # 放置时强制设置为移动行为,忽略CTRL键触发的复制请求 if event.mimeData().hasFormat(self.mimeType()): event.setDropAction(QtCore.Qt.MoveAction) super().dropEvent(event) class Dialog_01(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.listItems = {} myQWidget = QtWidgets.QWidget() myBoxLayout = QtWidgets.QHBoxLayout() myQWidget.setLayout(myBoxLayout) self.setCentralWidget(myQWidget) self.myQListWidget = ThumbListWidget(self) myBoxLayout.addWidget(self.myQListWidget) images_files_dir = pathlib.Path(__file__).parent.absolute().joinpath("custom_qlistwidget") for data in [ ('No.1', 'Meyoko', pathlib.Path(images_files_dir).joinpath('among-us-small-green.png')), ('No.2', 'Nyaruko', pathlib.Path(images_files_dir).joinpath('among-us-small-yellow.png')), ('No.3', 'Louise', pathlib.Path(images_files_dir).joinpath('among-us-small-red.png'))]: myQListWidgetItem = QtWidgets.QListWidgetItem(self.myQListWidget) # 存储创建自定义Widget所需的全部数据 myQListWidgetItem.setData(QtCore.Qt.UserRole, data) self.myQListWidget.addItem(myQListWidgetItem) self.listWidgetB = ThumbListWidget(self) myBoxLayout.addWidget(self.listWidgetB) class QCustomQWidget(QtWidgets.QWidget): def __init__(self, parent=None): super().__init__(parent) self.textQVBoxLayout = QtWidgets.QVBoxLayout() self.textUpQLabel = QtWidgets.QLabel() self.textDownQLabel = QtWidgets.QLabel() self.textQVBoxLayout.addWidget(self.textUpQLabel) self.textQVBoxLayout.addWidget(self.textDownQLabel) self.allQHBoxLayout = QtWidgets.QHBoxLayout() self.iconQLabel = QtWidgets.QLabel() self.allQHBoxLayout.addWidget(self.iconQLabel, 0) self.allQHBoxLayout.addLayout(self.textQVBoxLayout, 1) self.setLayout(self.allQHBoxLayout) # 样式设置 self.textUpQLabel.setStyleSheet(''' color: rgb(0, 0, 255); ''') self.textDownQLabel.setStyleSheet(''' color: rgb(255, 0, 0); ''') def setTextUp(self, text): self.textUpQLabel.setText(text) def setTextDown(self, text): self.textDownQLabel.setText(text) def setIcon(self, imagePath): self.iconQLabel.setPixmap(QtGui.QPixmap(imagePath)) if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) dialog_1 = Dialog_01() dialog_1.show() dialog_1.resize(480, 320) app.exec()
修复说明
- 新增
handleRowsAboutToBeRemoved方法,在项被移除前主动清理对应的自定义Widget,避免旧Widget残留导致的重叠、空白问题。 - 重写
startDrag方法,启动拖拽时仅允许移动操作,从源头禁用复制逻辑。 - 重写
dropEvent方法,放置项时强制设置为移动行为,即使按住CTRL键也不会触发复制。 - 新增
setDragDropOverwriteMode(False)配置,避免拖拽覆盖原有项的问题。
内容的提问来源于stack exchange,提问作者Sina S.
相关产品推荐
相关产品推荐

