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
相关产品推荐
相关产品推荐

