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

QGraphicsLayoutItem子项拖放事件未触发问题求助

问题解决:QGraphicsItem布局内拖放事件不触发

核心问题诊断

你的代码存在三个关键问题导致拖放事件失效:

  1. 事件判断逻辑错误:自定义Scene的dropEvent中直接访问item.setAcceptDrops属性判断是否接收拖放,这是错误的,应该调用item.acceptDrops()方法。
  2. 事件传递被阻断:RectGroup设置了setHandlesChildEvents(False),加上QGraphicsLayoutItem的层级关系,导致子项无法接收到拖放事件。
  3. 子项未发起拖拽:CustomRect没有实现拖拽启动逻辑,无法触发完整的拖放流程。

修改后的完整代码

from PySide6 import QtCore
from PySide6.QtWidgets import *
from PySide6.QtGui import QDrag, QMouseEvent


class CustomRect(QGraphicsRectItem):
    def __init__(self, parent=None):
        super(CustomRect, self).__init__(parent)
        self.setRect(0.0, 0.0, 80.0, 80.0)
        self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemSendsGeometryChanges)
        self.setAcceptDrops(True)

    def mousePressEvent(self, event):
        # 启动拖拽流程
        if event.button() == QtCore.Qt.LeftButton:
            drag = QDrag(self)
            mime_data = QtCore.QMimeData()
            mime_data.setText(self.parentItem().childItems()[1].widget().text())
            drag.setMimeData(mime_data)
            drag.exec(QtCore.Qt.MoveAction)
        super().mousePressEvent(event)

    def dragEnterEvent(self, e):
        if e.mimeData().hasText():
            e.acceptProposedAction()

    def dropEvent(self, e):
        # 此处可添加拖放后的位置交换逻辑
        source_text = e.mimeData().text()
        target_text = self.parentItem().childItems()[1].widget().text()
        print(f"拖放动作:{source_text} → {target_text}")
        e.acceptProposedAction()


class MyProxy(QGraphicsProxyWidget):
    def dragEnterEvent(self, e):
        if e.mimeData().hasText():
            e.acceptProposedAction()

    def dropEvent(self, e):
        # 将事件转发到父组处理
        self.parentItem().dropEvent(e)
        e.acceptProposedAction()

    def dragMoveEvent(self, e):
        if e.mimeData().hasText():
            e.acceptProposedAction()


class RectGroup(QGraphicsItemGroup):
    def __init__(self, text, parent=None):
        super(RectGroup, self).__init__(parent)
        # 移除事件阻断,让子项能正常接收事件
        # self.setHandlesChildEvents(False)
        self.addToGroup(CustomRect())
        label = QLabel(text)
        self.proxy = MyProxy()
        self.proxy.setAcceptDrops(True)
        self.proxy.setWidget(label)
        self.proxy.setPos(30, 30)
        self.addToGroup(self.proxy)
        self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemSendsGeometryChanges)
        self.setAcceptDrops(True)

    def dropEvent(self, e):
        # 将事件转发到鼠标位置对应的子项
        child = self.itemAt(e.pos())
        if child:
            child.dropEvent(e)
        e.acceptProposedAction()

    def dragEnterEvent(self, e):
        if e.mimeData().hasText():
            e.acceptProposedAction()

    def dragMoveEvent(self, e):
        if e.mimeData().hasText():
            e.acceptProposedAction()


class Item(QGraphicsLayoutItem):
    def __init__(self, graphics_group, parent=None):
        super(Item, self).__init__(parent)
        self.gg = graphics_group
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    def sizeHint(self, hint, size=QtCore.QSizeF()):
        return self.gg.boundingRect().size()

    def setGeometry(self, rect):
        self.gg.setPos(rect.topLeft())
        super().setGeometry(rect)


class Container(QGraphicsWidget):
    def __init__(self, parent=None):
        super(Container, self).__init__(parent)
        self.setAcceptDrops(True)
        self.layout = QGraphicsGridLayout()
        self.setLayout(self.layout)

    def add_item(self, item, row, col):
        self.layout.addItem(item, row, col)


class MyScene(QGraphicsScene):
    def dragEnterEvent(self, e):
        if e.mimeData().hasText():
            e.acceptProposedAction()

    def dropEvent(self, e):
        item = self.itemAt(e.scenePos())
        # 正确判断目标项是否接受拖放
        if item and item.acceptDrops():
            try:
                item.dropEvent(e)
            except RuntimeError:
                pass

    def dragMoveEvent(self, e):
        if e.mimeData().hasText():
            e.acceptProposedAction()


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.central_widget = QWidget(self)
        central_layout = QHBoxLayout()
        self.central_widget.setLayout(central_layout)
        self.setCentralWidget(self.central_widget)
        self.resize(500, 500)

        view = QGraphicsView()
        scene = MyScene()
        view.setScene(scene)

        container = Container()
        scene.addItem(container)
        for i in range(3):
            rg = RectGroup(f"foo_{i}", container)
            f = Item(rg)
            container.add_item(f, i, 0)
        central_layout.addWidget(view)


app = QApplication([])
main = MainWindow()
main.show()
app.exec()

关键修改说明

  • CustomRect添加拖拽启动逻辑:重写mousePressEvent,创建QDrag对象并传递MIME数据,触发拖放流程。
  • 修正Scene事件判断:用item.acceptDrops()替代属性访问,正确判断目标项是否接受拖放。
  • 调整事件传递路径:移除RectGroup的setHandlesChildEvents(False),让子项能接收事件;MyProxy的dropEvent转发到父组,父组再转发到对应子项。
  • 完善Item类:添加sizePolicy,确保布局能正确管理项的大小。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 00:15:08