当前位置: 首页 > news >正文

PySide(PyQT)使用场景(QGraphicsScene)进行动态标注的一个demo

 用以标注图像的一个基本框架demo

import sys
from PySide6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QMainWindow, QLabel, QGraphicsPixmapItem
from PySide6.QtGui import QPixmap, QPainter, QTransform
from PySide6.QtCore import Qt, QPointF, Slot, Signal


class ImageViewer(QGraphicsView):
    mouse_pos = Signal(int, int)  # 原图像素坐标信号
    def __init__(self, image_path, parent=None):
        super(ImageViewer, self).__init__(parent)

        # 场景的初始化
        def init_scene():
            # 设置场景
            self.scene = QGraphicsScene(self)
            self.setScene(self.scene)
            # 设置渲染提示
            self.setRenderHint(QPainter.Antialiasing)   # 开启抗锯齿
            self.setRenderHint(QPainter.SmoothPixmapTransform)  # 开启平滑缩放
            # 设置缩放锚点
            self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)  # 转换时以鼠标为中心
            self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)  # 缩放时以鼠标为中心
            # 创建基础的显示内容
            pixmap = QPixmap(image_path)
            self.pixmap_item = QGraphicsPixmapItem(pixmap)
            self.scene.addItem(self.pixmap_item)
            # 标记点的数量
            self.mark_count = 0
            # 遮罩的数量
            self.mask_count = 0
        init_scene()
        self.mouse_pos.connect(self.update_mouse_position)


    def add_mark(self, pixmap, pos=(0, 0), scale=1.0):
        """
        添加标记到场景
        :param pixmap: 图像数据
        :param pos:  坐标
        :param scale: 缩放比例
        :return:  None
        """
        # 加载图像
        pixmap_item = QGraphicsPixmapItem(pixmap)  # 创建图形项
        mark_x = pos[0] - pixmap.width() / 2 * scale
        mark_y = pos[1] - pixmap.height() / 2 * scale
        pixmap_item.setPos(mark_x, mark_y)  # 设置位置
        if scale != 1.0:  # 设置比例
            pixmap_item.setScale(scale)
        self.scene.addItem(pixmap_item)
        self.mark_count += 1

    def add_mask(self):
        """
        添加遮罩
        :return:
        """
        pass

    def remove_mark(self):
        """
        删除一个标记
        :return: None
        """
        if self.mark_count > 0:
            self.scene.removeItem(self.scene.items()[0])
            self.mark_count -= 1

    def remove_mask(self):
        """
        删除一个遮罩
        :return: None
        """
        if self.mask_count > 0:
            self.scene.removeItem(self.scene.items()[0])
            self.mask_count -= 1

    def remove_all_marks(self):
        """
        删除所有标记
        :return:
        """
        while self.mark_count > 0:
            self.scene.removeItem(self.scene.items()[0])
            self.mark_count -= 1


    # # 使用变换矩阵(如果需要)
    def transform(self, t):
        # self.transform = t
        # self.pixmap_item.setTransform(self.transform)
        pass

    # 处理滚轮事件以实现缩放
    def wheelEvent(self, event):
        factor = 1.001 ** event.angleDelta().y()  # 滚轮每滚动一格,缩放比例变化
        self.scale(factor, factor)

    # 处理鼠标单击事件以显示原图像素坐标
    def mousePressEvent(self, event):
        super(ImageViewer, self).mousePressEvent(event)
        scene_pos = self.mapToScene(event.position().toPoint())  # 记录当前鼠标在场景中的位置
        pixmap_rect = self.pixmap_item.boundingRect()   # 记录图像的边界矩形
        # 如果鼠标在图像边界矩形内,则显示像素坐标
        if pixmap_rect.contains(scene_pos):
            pos = self.pixmap_item.mapFromScene(scene_pos)  # 计算图像坐标
            x, y = pos.x(), pos.y()
            print(f"鼠标在图像内,坐标为: ({x}, {y})")

            if event.button() == Qt.LeftButton:  # 左键
                if event.modifiers() == Qt.ShiftModifier:  # Shift 键
                    self.remove_mark()    # 删除标记
                else:
                    self.add_mark(pixmap_mark, (x, y), 0.6)  # 添加标记

            elif event.button() == Qt.RightButton:   # 右键
                if event.modifiers() == Qt.ShiftModifier:   # Shift 键
                    self.remove_all_marks()   # 删除所有标记
                else:
                    self.add_mark(pixmap_unmark, (x, y), 0.6)   # 添加标记

    @Slot()
    def update_mouse_position(self, x, y):
        pass
        # 更新状态栏显示
        # self.status_bar.showMessage(f"原图像素坐标: ({x}, {y})")

class MainWindow(QMainWindow):
    def __init__(self, image_path):
        super(MainWindow, self).__init__()
        self.setGeometry(100, 100, 800, 600)

        # 创建状态栏
        self.status_bar = self.statusBar()

        # 创建 ImageViewer 实例
        self.image_viewer = ImageViewer(image_path, self)
        self.setCentralWidget(self.image_viewer)


if __name__ == "__main__":
    app = QApplication(sys.argv)

    # 生成标记图像
    pixmap_mark = QPixmap("mark.png")   # 标记图像(前景)
    pixmap_unmark = QPixmap("un_mark.png")   # 未标记图像(背景)

    image_path = "IMG_PP.jpg"      # 基础图像路径

    # 创建主窗口
    window = MainWindow(image_path)
    window.show()

    sys.exit(app.exec())

截图:


文章转载自:

http://HeMzVCol.ctLjs.cn
http://lVOtxRpB.ctLjs.cn
http://1fk0hzQ0.ctLjs.cn
http://M0nLU760.ctLjs.cn
http://lz9mql6v.ctLjs.cn
http://Kz0mCCp3.ctLjs.cn
http://zBwuP8Xk.ctLjs.cn
http://snZTvgHj.ctLjs.cn
http://qkADesOM.ctLjs.cn
http://9eoDQXeS.ctLjs.cn
http://yqQfGrC9.ctLjs.cn
http://Rn7F5AIT.ctLjs.cn
http://PYWPcRVS.ctLjs.cn
http://jJ2Qe5Xy.ctLjs.cn
http://40RZkY8A.ctLjs.cn
http://NMr8MCgT.ctLjs.cn
http://wSIprFWL.ctLjs.cn
http://ITKVC3J1.ctLjs.cn
http://45eD6IZI.ctLjs.cn
http://FNXhwZo3.ctLjs.cn
http://sm5WmZhA.ctLjs.cn
http://5f7dFlie.ctLjs.cn
http://CEmcwpQq.ctLjs.cn
http://etzLVBnG.ctLjs.cn
http://j7eBMUzQ.ctLjs.cn
http://wjLCzRLB.ctLjs.cn
http://HXK7nwHJ.ctLjs.cn
http://06Q5qLaS.ctLjs.cn
http://F8llnmf9.ctLjs.cn
http://YIfn8QH5.ctLjs.cn
http://www.dtcms.com/a/14672.html

相关文章:

  • 鸿蒙HarmonyOS NEXT开发:横竖屏切换开发实践
  • Golang 进阶训练营
  • 小白零基础如何搭建CNN
  • 构建Python量化交易环境:从基础安装到项目创建
  • 微服务SpringCloud Alibaba组件nacos教程(一)【详解naocs基础使用、服务中心配置、集群配置,附有案例+示例代码】
  • Python:凯撒密码
  • SQLite 数据库:优点、语法与快速入门指南
  • 快速上手Vim的使用
  • java每日精进 2.13 Ganache(区块链本地私有化部署)
  • 【JavaScript爬虫记录】记录一下使用JavaScript爬取m4s流视频过程(内含ffmpeg合并)
  • Mediamtx+Python读取webrtc流
  • 【Elasticsearch】分词器概述
  • 嵌入式LINUX驱动开发入门之hello驱动(基于IMX6ULL-MINI开发板)
  • 算法-计算字符的最短距离
  • 计算机毕业设计PySpark+hive招聘推荐系统 职位用户画像推荐系统 招聘数据分析 招聘爬虫 数据仓库 Django Vue.js Hadoop
  • 基于微型5G网关的石化厂区巡检机器人应用
  • -bash:/usr/bin/rm: Argument list too long 解决办法
  • Swagger2 Knife4jConfig 配置,父子项目swagger扫描多个子模块中的Controller生成接口文档:
  • sward简介与安装
  • 小厂面(又是依托)
  • AWK系统学习指南:从文本处理到数据分析的终极武器 实战
  • 动态DNS神器nip.io使用指南:快速实现域名与IP的动态映射--告别配置本地hosts
  • Go 语言里中的堆与栈
  • LabVIEW用户界面(UI)和用户体验(UX)设计
  • 如何本地部署DeepSeek
  • HTML之JavaScript运算符
  • macOS部署DeepSeek-r1
  • oracle使用动态sql将多层级组织展平
  • C++自研游戏引擎-碰撞检测组件-八叉树AABB检测算法实现
  • 企业文件安全:零信任架构下的文件访问控制