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

如何在PyQt的QGridLayout单元格间拖拽QLineEdit组件

解决QGridLayout单元格间QLineEdit拖拽迁移问题

问题根源

  1. 空的MyWidget容器未初始化垂直布局,导致拖拽后无法将组件纳入布局管理
  2. dropEvent仅移除组件父对象,但未将其添加到目标容器的布局中,组件处于游离状态
  3. 未正确从原容器的布局中移除组件,导致原布局仍保留无效引用

修复方案

1. 确保所有容器自动拥有垂直布局

修改MyWidget类,自动为无布局的容器创建垂直布局,同时兼容PyQt Designer已生成的布局:

class MyWidget(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 仅当容器无布局时自动创建垂直布局
        if not self.layout():
            self.verticalLayout = QVBoxLayout(self)
            self.verticalLayout.setObjectName("verticalLayout")
    
    def dragEnterEvent(self, e):
        # 仅接受自定义标识的MyLineEdit拖拽
        if e.mimeData().hasFormat("application/x-mylineedit"):
            e.acceptProposedAction()
        else:
            e.ignore()
        
    def dropEvent(self, e):
        if not e.mimeData().hasFormat("application/x-mylineedit"):
            return
        
        widget = e.source()
        if not isinstance(widget, MyLineEdit):
            return
        
        # 从原布局中移除组件
        original_layout = widget.parent().layout()
        if original_layout:
            original_layout.removeWidget(widget)
        
        # 将组件添加到当前容器的布局
        widget.setParent(self)
        self.verticalLayout.addWidget(widget)
        widget.show()
        e.acceptProposedAction()

2. 优化拖拽逻辑,添加自定义标识

修改MyLineEdit的拖拽事件,添加专属mime数据标识,避免处理无关拖拽:

class MyLineEdit(QLineEdit):
    def mouseMoveEvent(self, e):
        if e.buttons() != Qt.LeftButton:
            return
        
        drag = QDrag(self)
        mime = QMimeData()
        # 添加自定义格式标识,用于识别MyLineEdit拖拽
        mime.setData("application/x-mylineedit", b"1")
        drag.setMimeData(mime)
        
        # 设置拖拽预览图
        pixmap = QPixmap(self.size())
        self.render(pixmap)
        drag.setPixmap(pixmap)
        
        # 执行移动拖拽动作
        result = drag.exec_(Qt.MoveAction)
        if result == Qt.MoveAction:
            self.hide()

3. 修复PyQt Designer生成代码的冗余问题

移除myui.py中Ui_Form类内多余的mouseMoveEvent方法,避免干扰组件拖拽;空容器的布局由MyWidget构造函数自动创建,无需手动添加。


完整修复代码

mywidgets.py

from PyQt5.QtWidgets import QLineEdit, QWidget, QScrollArea, QVBoxLayout
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag, QPixmap

class MyLineEdit(QLineEdit):
    def mouseMoveEvent(self, e):
        if e.buttons() != Qt.LeftButton:
            return
        
        drag = QDrag(self)
        mime = QMimeData()
        mime.setData("application/x-mylineedit", b"1")
        drag.setMimeData(mime)
        
        pixmap = QPixmap(self.size())
        self.render(pixmap)
        drag.setPixmap(pixmap)
        
        result = drag.exec_(Qt.MoveAction)
        if result == Qt.MoveAction:
            self.hide()
            
class MyWidget(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if not self.layout():
            self.verticalLayout = QVBoxLayout(self)
            self.verticalLayout.setObjectName("verticalLayout")
    
    def dragEnterEvent(self, e):
        if e.mimeData().hasFormat("application/x-mylineedit"):
            e.acceptProposedAction()
        else:
            e.ignore()
        
    def dropEvent(self, e):
        if not e.mimeData().hasFormat("application/x-mylineedit"):
            return
        
        widget = e.source()
        if not isinstance(widget, MyLineEdit):
            return
        
        original_layout = widget.parent().layout()
        if original_layout:
            original_layout.removeWidget(widget)
        
        widget.setParent(self)
        self.verticalLayout.addWidget(widget)
        widget.show()
        e.acceptProposedAction()
            
class MyScrollArea(QScrollArea):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setAcceptDrops(True)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.setWidgetResizable(True)

main.py

import sys
from PyQt5.QtWidgets import QApplication
from mywidgets import MyWidget
from myui import Ui_Form 

class MainWidget(MyWidget, Ui_Form):
    def __init__(self, *args, **kwargs):
        super().__init__()
        self.setupUi(self)
        self.show()
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWidget()
    sys.exit(app.exec_())

效果说明

  • 所有MyWidget容器自动拥有垂直布局,解决PyQt Designer中空容器无法添加布局的问题
  • 拖拽时仅处理MyLineEdit组件,避免误操作
  • 组件拖拽后会从原布局移除,并正确加入目标容器的布局,实现跨单元格迁移

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 13:00:55