You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySide6自定义QWidget拖拽时QListWidget项重叠消失及禁止复制问题求解

PySide6 QListWidget自定义项拖拽问题修复

问题表现

  • 同一QListWidget内部拖拽项到其他项上方时,会出现项重叠堆叠、隐藏、显示为空白项的问题,异常效果如下:
    堆叠或丢失的QListWidget项
  • 拖拽时按住CTRL键会触发复制操作,鼠标光标变为+号,列表中会新增重复项,需要完全禁用该复制功能,保证两个QListWidget中所有项唯一,无重复。

问题原因

  1. 拖拽移动项的过程中,原有项的自定义Widget没有被正确清理,残留的Widget和新插入项生成的Widget堆叠,导致显示异常。
  2. 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()

修复说明

  1. 新增handleRowsAboutToBeRemoved方法,在项被移除前主动清理对应的自定义Widget,避免旧Widget残留导致的重叠、空白问题。
  2. 重写startDrag方法,启动拖拽时仅允许移动操作,从源头禁用复制逻辑。
  3. 重写dropEvent方法,放置项时强制设置为移动行为,即使按住CTRL键也不会触发复制。
  4. 新增setDragDropOverwriteMode(False)配置,避免拖拽覆盖原有项的问题。

内容的提问来源于stack exchange,提问作者Sina S.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 16:54:03