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

PyQt5 QListWidget拖拽优化:仅控件区域启用文件拖拽

PyQt5 QListWidget 拖拽功能限定区域问题解决方案

我想给PyQt5的QListWidget实现拖拽功能,一开始在主窗口设置self.setAcceptDrops(True)时功能能生效,但拖拽范围是整个应用窗口,在窗口任意区域拖入文件都会加到QListWidget里。我的目标是只在QListWidget区域启用拖拽,折腾了半天没搞定,求解决办法。

初始代码

class okno_glowne(qtw.QWidget):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ui = Ui_main_new()
        self.ui.setupUi(self)
    
        #self.setAcceptDrops(True)

     def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
            print("Coś się udaje")
        else:
            event.ignore()

    def dropEvent(self, event):

    
        if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist'):
            if event.source() == self.ui.lista_excele_drag:
                event.acceptProposedAction()
        for url in event.mimeData().urls():
             file_path = url.toLocalFile()
             self.ui.lista_excele_drag.addItem(file_path)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
             self.drag_start_position = event.pos()

    def mouseMoveEvent(self, event):
        if not (event.buttons() & Qt.LeftButton):
             return

        if (event.pos() - self.drag_start_position).manhattanLength() <QApplication.startDragDistance():
        return

        drag = QDrag(self)
         mime_data = QMimeData()
        mime_data.setText(self.ui.lista_excele_drag.currentItem().text())
        drag.setMimeData(mime_data)
        drag.exec_(Qt.CopyAction)

if __name__ == '__main__':
app =qtw.QApplication([])
Widget = okno_glowne()
Widget = QtWidgets.QStackedWidget()
w = okno_glowne()
w.show()
app.exec_()

自定义QListWidget类的尝试

后来我把代码改成在单独文件mylistwidget.py里定义自定义类,知道可以通过提升部件来用,但功能有问题:dragEnterEvent能正常触发(有打印输出),但dropEvent和鼠标相关事件完全没反应。

修改后的代码:

from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag

class MyListWidget(QListWidget):

# def __init__(self, *args, **kwargs):
#     QListWidget().__init__(self, *args, **kwargs)
    def __init__(self, parent=None):
        QListWidget().__init__(parent)
        self.setAcceptDrops(True)
    

    def dragEnterEvent(self, event):
        print("dragEnterEvent")
        if event.mimeData().hasUrls():
             event.accept()
        else:
             event.ignore()

    def dropEvent(self, event):
        print("dropEvent")
        if event.mimeData().hasUrls():
            for url in event.mimeData().urls():
                path = url.toLocalFile()
                item = QListWidgetItem(path)
                self.addItem(item)
            event.accept()
        else:
            event.ignore()

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_start_position = event.pos()

    def mouseMoveEvent(self, event):
        if not (event.buttons() & Qt.LeftButton):
            return

        if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
            return

        drag = QDrag(self)
        mime_data = QMimeData()
        mime_data.setText(self.currentItem().text())
        drag.setMimeData(mime_data)
        drag.exec_(Qt.CopyAction)

问题修复方案

自定义类里有两个关键错误导致功能异常:

  1. 初始化方法调用错误:
    原代码中QListWidget().__init__(parent)是创建了一个新的QListWidget实例并调用其初始化方法,而非初始化当前的MyListWidget对象。正确写法应为super().__init__(parent)或QListWidget.__init__(self, parent)。

  2. 拖拽事件处理逻辑不规范:
    在dragEnterEvent中,应调用event.acceptProposedAction()而非event.accept(),这样才能正确响应拖拽动作;同时鼠标事件中需先调用父类方法,确保选中项的正常逻辑。

另外要确保在UI设计器中正确提升部件:将QListWidget提升为自定义的MyListWidget,并指定对应的模块文件(mylistwidget)。

修正后的MyListWidget代码

from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QApplication
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag

class MyListWidget(QListWidget):
    def __init__(self, parent=None):
        super().__init__(parent)  # 正确初始化父类
        self.setAcceptDrops(True)
        # 启用内部拖拽排序(按需开启)
        self.setDragEnabled(True)
        self.setDropIndicatorShown(True)
        self.setDefaultDropAction(Qt.CopyAction)

    def dragEnterEvent(self, event):
        print("dragEnterEvent triggered")
        # 区分外部文件拖拽与内部项拖拽
        if event.mimeData().hasUrls():
            event.acceptProposedAction()
        elif event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist'):
            super().dragEnterEvent(event)
        else:
            event.ignore()

    def dropEvent(self, event):
        print("dropEvent triggered")
        if event.mimeData().hasUrls():
            # 处理外部文件拖拽
            for url in event.mimeData().urls():
                file_path = url.toLocalFile()
                self.addItem(QListWidgetItem(file_path))
            event.acceptProposedAction()
        else:
            # 处理内部项拖拽,交给父类逻辑
            super().dropEvent(event)

    def mousePressEvent(self, event):
        # 先调用父类方法确保选中项正常工作
        super().mousePressEvent(event)
        if event.button() == Qt.LeftButton:
            self.drag_start_position = event.pos()

    def mouseMoveEvent(self, event):
        if not (event.buttons() & Qt.LeftButton):
            return
        # 检查拖拽距离是否达到系统阈值
        if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
            return
        # 获取当前选中项,避免空指针
        current_item = self.currentItem()
        if not current_item:
            return
        # 创建拖拽对象并设置数据
        drag = QDrag(self)
        mime_data = QMimeData()
        mime_data.setText(current_item.text())
        # 添加内部拖拽支持的格式
        mime_data.setData('application/x-qabstractitemmodeldatalist', b'')
        drag.setMimeData(mime_data)
        drag.exec_(Qt.CopyAction)

主窗口代码调整

主窗口无需再实现拖拽相关事件,删除原有的dragEnterEvent、dropEvent、mousePressEvent、mouseMoveEvent方法,且不要设置self.setAcceptDrops(True),确保拖拽仅在MyListWidget区域生效。

import qtw
from mylistwidget import MyListWidget  # 导入自定义类

class okno_glowne(qtw.QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ui = Ui_main_new()
        self.ui.setupUi(self)

if __name__ == '__main__':
    app = qtw.QApplication([])
    w = okno_glowne()
    w.show()
    app.exec_()

内容的提问来源于stack exchange,提问作者Sebastian Kasperski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:15:12