如何根据场景形状裁剪像素图图像

问题描述 投票:0回答:1

我正在尝试使用 QGraphicsScene 的给定边界来剪辑 QGraphicsPixmapItem。但是,我认为我并不真正理解剪切的工作原理,据我所知,当您在 QGraphicsPixmapItem 的绘制方法中将 setClipRect 应用于图像时,它应该剪切图像。

我有这个类,它充当查看器:

class ImageViewer(QGraphicsView):
    def __init__(self, parent, tab):
        super().__init__(parent)
        self.setScene(QGraphicsScene(self))

    def displayImage(self, image):
        rgb_image = image.convert('RGB')
        data = rgb_image.tobytes()
        qim = QImage(data, rgb_image.size[0], rgb_image.size[1], QImage.Format_RGB888)
        pixmap = QPixmap(qim)
        self.scene().clear()
        scene_rect = QRectF(0, 0, pixmap.width(), pixmap.height())
        self.setSceneRect(scene_rect)
        pixmapItem = ImagePixmapItem(pixmap, self.sceneRect())
        self.scene().addItem(pixmapItem)
        self.fitInView(self.sceneRect(), Qt.KeepAspectRatio)
        self.drawBorder()
        pixmapItem.rotate()

在这里,我有我的图像、QPixmap 并定义了 pixmapItem,它是继承自 QGraphicsPixmapItem 的自定义类。我还画了一个边框只是为了看看我的场景的边界在哪里。

class ImagePixmapItem(QGraphicsPixmapItem):
    def __init__(self, pixmap, scene_rect):
        super().__init__(pixmap)
        self.scene_rect = scene_rect

    def paint(self, painter, option, widget):
        painter.setClipRect(self.scene_rect)
        super().paint(painter, option, widget)
        
    def rotate(self):
        self.setTransformOriginPoint(self.boundingRect().center())
        rotation_angle = 45
        self.setRotation(rotation_angle)

现在,我的图像尺寸为 1000x1000。启动程序后场景的分辨率也为 1000x1000。当我使用旋转方法旋转图像时,理论上,方形图像应该占用比 1000x1000 更多的空间。一旦整个程序启动以及我在场景中滚动或移动,绘图方法就会被调用。我预计 setClipRect 会剪辑我的图像。场景之外的部分不应显示。然而,它根本不起作用。

我被困在这里,不知道问题是什么。

python pyqt5 clipping
1个回答
0
投票

paint()
中收到的QPainter已经处于转换项目的上下文中,包括其父级和视图以及其自身的转换。

如果此时调用

painter.drawRect()
,它最终会显示一个 rotated 矩形。这就是为什么使用
setClipRect()
是没有意义的,因为矩形与已经变换的矩阵正交。

一个可能的解决方案是获取实际场景 mapped 到项目变换,并使用

setClipPath()
和包含该映射矩形的 QPainterPath(实际上是 QPolygonF,因为变换也可以具有透视)。

    def paint(self, painter, option, widget):
        map = self.mapFromScene(self.scene_rect)
        path = QPainterPath()
        path.addPolygon(map)
        painter.setClipPath(path)
        super().paint(painter, option, widget)

另一种可能性,正如 ekhumoro 所建议的那样,是添加一个顶级 QGraphicsRectItem 充当剪辑蒙版(通过设置 ItemClipsChildrenToShape

 标志),并将所有需要剪辑的元素添加为该项目的子项。

最后,只要需要剪切

所有项目,您就可以覆盖drawForeground()

并在所有内容上方绘制一个空心QRegion。这样就根本不需要实现裁剪了。

class ImageViewer(QGraphicsView): ... def drawForeground(self, qp, rect): viewRect = self.mapToScene(self.viewport().rect()).boundingRect().toRect() mask = QRegion(viewRect) - QRegion(self.sceneRect().toRect()) qp.setClipRegion(mask) qp.fillRect(viewRect, self.palette().base())
    
© www.soinside.com 2019 - 2024. All rights reserved.