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

如何在PyQt6中实现可拖拽裁剪矩形,隐藏画布外部内容

解决PyQt6战争迷雾(拖拽矩形外内容隐藏)问题

问题描述

需求:在整个画布上添加可拖拽矩形,隐藏矩形外所有内容,实现类似游戏“战争迷雾(fog-of-war)”的效果。
问题:尝试的方法无法隐藏画布外的内容,现有代码可拖拽矩形但裁剪未生效,仍能看到全部画布内容。

原代码问题分析

  • ClipRectItem的paint方法中设置的裁剪路径仅对自身绘制生效,不会影响场景内其他元素的显示。
  • drawForeground方法重复渲染整个场景,导致画面叠加,且裁剪逻辑未正确作用于视图的整体渲染流程。

修正方案

通过在视图的paintEvent中绘制半透明遮罩实现迷雾效果:先正常渲染场景内容,再绘制覆盖整个视口的黑色遮罩,仅在拖拽矩形区域保留透明区域。同时监听矩形的移动事件,实时更新遮罩位置。

修正后代码

import sys
from PyQt6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsRectItem
from PyQt6.QtGui import QPainter, QPainterPath, QPen, QBrush, QColor
from PyQt6.QtCore import QRectF, Qt, QPointF

class ClipRectItem(QGraphicsRectItem):
    def __init__(self, rect, view):
        super().__init__(rect)
        self.view = view
        # 设置可拖拽、可选中
        self.setFlag(QGraphicsRectItem.GraphicsItemFlag.ItemIsMovable)
        self.setFlag(QGraphicsRectItem.GraphicsItemFlag.ItemIsSelectable)
        self.setBrush(QBrush(Qt.GlobalColor.transparent))
        self.setPen(QPen(Qt.GlobalColor.red, 2, Qt.PenStyle.DashLine))

    def itemChange(self, change, value):
        # 当矩形位置变化时触发视图重绘
        if change == QGraphicsRectItem.GraphicsItemChange.ItemPositionChange or change == QGraphicsRectItem.GraphicsItemChange.ItemPositionHasChanged:
            self.view.update()
        return super().itemChange(change, value)

class Example(QGraphicsView):
    def __init__(self):
        super().__init__()
        self.scene = QGraphicsScene()
        self.setScene(self.scene)
        self.initUI()

    def initUI(self):
        # 添加示例元素
        for i in range(5):
            rect_item = QGraphicsRectItem(i * 100, i * 50, 80, 40)
            rect_item.setBrush(QBrush(Qt.GlobalColor.green))
            self.scene.addItem(rect_item)

        # 创建可拖拽裁剪矩形,传入视图引用
        self.clip_rect = ClipRectItem(QRectF(50, 50, 200, 150), self)
        self.scene.addItem(self.clip_rect)

        # 视图设置
        self.setGeometry(300, 300, 600, 400)
        self.setWindowTitle('战争迷雾效果示例')

    def paintEvent(self, event):
        # 先正常渲染场景内容
        super().paintEvent(event)

        # 创建画笔绘制迷雾遮罩
        painter = QPainter(self.viewport())
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)

        # 将裁剪矩形从场景坐标转换为视图坐标
        scene_rect = self.clip_rect.rect()
        top_left = self.mapFromScene(scene_rect.topLeft())
        bottom_right = self.mapFromScene(scene_rect.bottomRight())
        view_clip_rect = QRectF(top_left, bottom_right)

        # 创建遮罩路径:覆盖整个视口,减去裁剪矩形区域
        fog_path = QPainterPath()
        fog_path.addRect(QRectF(self.viewport().rect()))
        fog_path.addRect(view_clip_rect)
        # 使用奇偶填充规则实现区域减法
        fog_path.setFillRule(Qt.FillRule.OddEvenFill)

        # 填充半透明黑色(最后一个参数是透明度,0-255)
        painter.fillPath(fog_path, QBrush(QColor(0, 0, 0, 200)))

        painter.end()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    view = Example()
    view.show()
    sys.exit(app.exec())

关键改动说明

  1. 遮罩绘制逻辑:通过paintEvent在场景渲染完成后,绘制一个覆盖整个视口的半透明黑色遮罩,仅在拖拽矩形位置留出空白,实现迷雾效果。
  2. 坐标转换:将裁剪矩形的场景坐标转换为视图坐标,确保遮罩的空白区域与矩形位置完全匹配。
  3. 实时更新:监听ClipRectItem的位置变化事件,触发视图重绘,保证迷雾区域随矩形拖拽实时更新。

内容的提问来源于stack exchange,提问作者Drew S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 21:57:34