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

如何在PyQt5中实现基于QGraphicsScene的图形拖拽选择面板

问题解答

一、可用于自定义拖拽面板的组件

  • QListWidget/QListView:以列表项形式展示图形预览,支持拖拽功能,适合快速排列多种图形选项。
  • QToolBox:分栏式面板,可按类别分组存放图形预览,适合图形类型较多的场景。
  • 自定义QPushButton:给按钮设置图形预览图标,重写鼠标事件实现拖拽,样式灵活可控。
  • 小型QGraphicsView:用独立的小视图展示单个图形的精确预览,适合需要高保真预览的需求。
  • QGridLayout+自定义Widget:通过网格布局排列自定义预览部件,完全自定义样式和拖拽逻辑,灵活性最高。

二、完整实现示例(基于QListWidget的拖拽面板)

以下是修改后的代码,替换原按钮面板为可拖拽的图形预览列表,实现从面板拖拽图形到画布的功能:

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys

# 自定义可拖拽的列表项
class GraphicsListItem(QListWidgetItem):
    def __init__(self, type_name, pixmap, parent=None):
        super().__init__(parent)
        self.type_name = type_name
        self.setIcon(QIcon(pixmap))
        self.setText(type_name)
        self.setFlags(self.flags() | Qt.ItemIsDragEnabled)

# 主窗口类
class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        wid = QWidget()
        self.setCentralWidget(wid)
        self.setWindowTitle("Paint with PyQt5")
        self.setGeometry(100, 100, 800, 600)

        # 主绘图场景
        self.scene = QGraphicsScene(0, 0, 400, 200)
        view = QGraphicsView(self.scene)
        view.setRenderHint(QPainter.Antialiasing)
        view.setAcceptDrops(True)
        view.dropEvent = self.on_view_drop

        # 拖拽面板:QListWidget
        self.drag_panel = QListWidget()
        self.drag_panel.setFixedWidth(150)
        self.drag_panel.setViewMode(QListWidget.IconMode)
        self.drag_panel.setIconSize(QSize(80, 60))
        self.drag_panel.setDragEnabled(True)

        # 添加图形预览项
        self.add_drag_item("矩形", self.create_rect_pixmap())
        self.add_drag_item("椭圆", self.create_ellipse_pixmap())

        # 布局
        hbox = QHBoxLayout()
        hbox.addWidget(self.drag_panel)
        hbox.addWidget(view)
        wid.setLayout(hbox)

    def add_drag_item(self, type_name, pixmap):
        item = GraphicsListItem(type_name, pixmap)
        self.drag_panel.addItem(item)

    # 生成矩形预览图
    def create_rect_pixmap(self):
        pixmap = QPixmap(80, 60)
        pixmap.fill(Qt.transparent)
        painter = QPainter(pixmap)
        painter.setPen(QPen(Qt.black, 2))
        painter.drawRect(10, 10, 60, 40)
        painter.end()
        return pixmap

    # 生成椭圆预览图
    def create_ellipse_pixmap(self):
        pixmap = QPixmap(80, 60)
        pixmap.fill(Qt.transparent)
        painter = QPainter(pixmap)
        painter.setPen(QPen(Qt.black, 2))
        painter.drawEllipse(10, 10, 60, 40)
        painter.end()
        return pixmap

    # 处理画布的拖拽释放事件
    def on_view_drop(self, event):
        mime_data = event.mimeData()
        if not mime_data.hasFormat("application/x-qabstractitemmodeldatalist"):
            return

        # 解析拖拽的图形类型
        data_stream = QDataStream(mime_data.data("application/x-qabstractitemmodeldatalist"), QIODevice.ReadOnly)
        while not data_stream.atEnd():
            row = data_stream.readInt32()
            column = data_stream.readInt32()
            map_items = data_stream.readInt32()
            for _ in range(map_items):
                key = data_stream.readInt32()
                value = QVariant()
                data_stream >> value
                if key == Qt.DisplayRole:
                    type_name = value.toString()
                    view_pos = event.pos()
                    view = self.centralWidget().layout().itemAt(1).widget()
                    scene_pos = view.mapToScene(view_pos)
                    # 根据类型创建图形
                    if type_name == "矩形":
                        rect = QGraphicsRectItem(0, 0, 200, 50)
                        rect.setPos(scene_pos)
                        rect.setFlag(QGraphicsItem.ItemIsMovable)
                        rect.setFlag(QGraphicsItem.ItemIsSelectable)
                        self.scene.addItem(rect)
                    elif type_name == "椭圆":
                        ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
                        ellipse.setPos(scene_pos)
                        ellipse.setFlag(QGraphicsItem.ItemIsMovable)
                        ellipse.setFlag(QGraphicsItem.ItemIsSelectable)
                        self.scene.addItem(ellipse)
                    break
        event.accept()

if __name__ == "__main__":
    App = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(App.exec())

示例说明

  1. 用QListWidget搭建拖拽面板,每个列表项包含图形预览图标和名称,启用拖拽功能。
  2. 重写主视图的dropEvent方法,解析拖拽数据,将视图坐标转换为场景坐标,在拖拽位置创建对应图形。
  3. 若要使用自定义按钮实现拖拽,可重写QPushButton的mousePressEvent和mouseMoveEvent,手动启动拖拽流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:42:51