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

PyQt中如何替换QGraphicsPixmapItem选中区域的QPixmap

PyQt QGraphicsPixmapItem 区域替换与滤镜处理问题解决

一、修复右键「replace pic」无效果的问题

你的代码存在几个关键问题导致替换失效,以下是修正后的实现:

1. 修正RectangleScene核心逻辑

class RectangleScene(QGraphicsScene):
    def __init__(self, main_pic_item, parent=None):
        super().__init__(parent)
        self.main_pic_item = main_pic_item  # 保存目标图形项引用
        self.graphics_line = None
        self.start_point = None
        self.end_point = None

    def clean_scene(self):
        # 清除历史矩形框
        if self.graphics_line:
            self.removeItem(self.graphics_line)
            self.graphics_line = None

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.clean_scene()
            self.start_point = event.scenePos()
            self.end_point = self.start_point
            self.graphics_line = QGraphicsRectItem(QRectF(self.start_point, self.end_point))
            self.addItem(self.graphics_line)

    def mouseMoveEvent(self, event):
        if self.graphics_line and event.buttons() & Qt.LeftButton:
            self.end_point = event.scenePos()
            self.graphics_line.setRect(QRectF(self.start_point, self.end_point))

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.end_point = event.scenePos()
            self.graphics_line.setRect(QRectF(self.start_point, self.end_point))

    def contextMenuEvent(self, event):
        if not self.graphics_line:
            return
        menu = QtWidgets.QMenu()
        replace_action = menu.addAction("replace pic")
        action = menu.exec_(event.screenPos())  # 获取用户选中的菜单动作
        if action == replace_action:
            # 转换为整数矩形,避免浮点坐标误差
            rect = self.graphics_line.rect().toRect()
            # 确保选中区域在pixmap范围内
            pixmap_rect = self.main_pic_item.pixmap().rect()
            target_rect = rect.intersected(pixmap_rect)
            if target_rect.isEmpty():
                return

            # 复制原始pixmap,避免修改源数据
            new_pixmap = self.main_pic_item.pixmap().copy()
            # 准备替换的图像(按目标区域尺寸缩放)
            replace_pixmap = QPixmap.fromImage(qImg).scaled(target_rect.size())
            
            # 绘制替换区域
            painter = QPainter(new_pixmap)
            painter.drawPixmap(target_rect, replace_pixmap, replace_pixmap.rect())
            painter.end()  # 必须结束painter释放资源
            
            # 更新图形项并刷新界面
            self.main_pic_item.setPixmap(new_pixmap)
            self.update()

2. 修正DrawRectangle方法

创建场景时传入图形项引用,确保场景能直接操作目标元素:

def DrawRectangle(self):
    scene = RectangleScene(self.mainpic)
    scene.addItem(self.mainpic)
    self.graphicsViewScene1.setScene(scene)

关键修复点

  • 保存QGraphicsPixmapItem引用,确保场景可直接操作目标元素
  • 调用setPixmap()更新图形项,触发界面刷新(原代码仅修改pixmap未通知界面)
  • 正确获取右键菜单选中动作(原代码未定义action变量)
  • 复制pixmap避免修改原始数据,防止其他依赖逻辑出错
  • 处理边界交集,确保替换区域在pixmap有效范围内

二、实现选中区域提取、滤镜处理与放回

借助numpy和OpenCV可实现选中区域转ndarray、加滤镜再放回的流程:

1. 导入依赖

import numpy as np
import cv2
from PyQt5.QtGui import QImage

2. 替换右键菜单的滤镜处理逻辑

在RectangleScene的contextMenuEvent中替换为以下代码:

def contextMenuEvent(self, event):
    if not self.graphics_line:
        return
    menu = QtWidgets.QMenu()
    filter_action = menu.addAction("add filter to selected area")
    action = menu.exec_(event.screenPos())
    if action == filter_action:
        rect = self.graphics_line.rect().toRect()
        pixmap_rect = self.main_pic_item.pixmap().rect()
        target_rect = rect.intersected(pixmap_rect)
        if target_rect.isEmpty():
            return

        # 步骤1:QPixmap转ndarray
        pixmap = self.main_pic_item.pixmap()
        qimage = pixmap.toImage().convertToFormat(QImage.Format_RGB888)
        width, height = qimage.width(), qimage.height()
        ptr = qimage.bits()
        ptr.setsize(qimage.byteCount())
        img_array = np.array(ptr).reshape(height, width, 3)

        # 步骤2:提取选中区域
        x, y, w, h = target_rect.getRect()
        selected_area = img_array[y:y+h, x:x+w].copy()

        # 步骤3:添加滤镜(示例:高斯模糊)
        filtered_area = cv2.GaussianBlur(selected_area, (15,15), 0)

        # 步骤4:将处理后区域放回原数组
        img_array[y:y+h, x:x+w] = filtered_area

        # 步骤5:ndarray转回QPixmap并更新界面
        new_qimage = QImage(img_array.data, width, height, QImage.Format_RGB888)
        new_pixmap = QPixmap.fromImage(new_qimage)
        self.main_pic_item.setPixmap(new_pixmap)
        self.update()

关键说明

  • QImage转ndarray时需转换为RGB格式,避免默认ARGB通道导致颜色偏差
  • 提取区域用copy()避免修改原数组的视图
  • 滤镜效果可替换为边缘检测、灰度化等自定义逻辑
  • 转换回QPixmap后必须调用setPixmap()触发界面刷新

内容的提问来源于stack exchange,提问作者md.119

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 20:25:16