Qt QGraphicsScene 基于视频的绘图

需求:

基于视频进行 图形的绘制。

方案:

 上一篇文章分享了如何将视频实时渲染到QGraphicsScene 系统里,并简单讲述了如何进行绘图,但在实际使用时还是发现了一些技巧,现在总结一下。

Qt 基于海康相机 的视频标绘-CSDN博客文章浏览阅读652次,点赞6次,收藏6次。利用qml 基于opengl 进行渲染,可以达到任意图形的绘制,但是帧率 只有25帧左右。如今要开发光学测量仪,发现使用QGraphicsPixmapItem 进行图片的渲染,可以利用QGraphicsItem 进行任务图形的叠加绘制,并且帧率目测大概在25帧所有,满足需求。曾经搞在线教育时,尝试在视频上进行文字或者图形的绘制,但是发现利用Qt widget 传sdk 句柄的方式,只能使用窗口叠加的方式(同时将QGraphicsPixmapItem放到最底层,即可达到在上边绘制任意图形的目的。https://blog.csdn.net/weixin_38416696/article/details/135844799?spm=1001.2014.3001.55021,效果

直线:

矩形:

圆:

弧:

2,实现

定义一个控制点,用于旋转或者改变大小。开始也尝试了 通过hover事件 来改变鼠标形状,但是这样判断地方有点多,通过使用控制点的方式,实现起来相对简单,使用上也还可以。

#ifndef BPOINTITEM_H
#define BPOINTITEM_H

#include <QObject>
#include <QAbstractGraphicsShapeItem>
#include <QPointF>
#include <QPen>
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QCursor>
#include <QKeyEvent>
#include <QList>
#include <QMutex>
#include <QThread>

//控制点
class EPointItem :public QObject, public QGraphicsItem {
	Q_OBJECT

public:
    enum class PointType{
        MovePoint=0,
        RotatePoint=1
    };

    EPointItem(QGraphicsItem* parent, QPointF p,PointType type = PointType::MovePoint);

public:
	virtual QRectF boundingRect() const override;
	virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
	virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override;
	virtual void mousePressEvent(QGraphicsSceneMouseEvent* event) override;
	virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override;

signals:
    void posMove(QPointF curPos, QPointF lastPos);
    void posRotate(QPointF curPos,QPointF lastPos);
    void posRelease();

private:
    PointType m_type;

};
#endif // BPOINTITEM_H



#include "bpointitem.h"
#include <QDebug>
#include <QtMath>

EPointItem::EPointItem(QGraphicsItem* parent, QPointF p,PointType type)
    : QGraphicsItem(parent),m_type(type)
{
    this->setPos(p);
    this->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
    this->setCursor(Qt::PointingHandCursor);
}

QRectF EPointItem::boundingRect() const
{
    if(m_type==PointType::MovePoint){
        return QRectF(-4, -4, 8, 8);
    }else if(m_type==PointType::RotatePoint){
        return QRectF(-8, -8, 16, 16);
    }else{
        return QRectF(-4, -4, 8, 8);
    }
}

void EPointItem::mousePressEvent(QGraphicsSceneMouseEvent* event) {
    setSelected(true);
    QGraphicsItem::mousePressEvent(event);
}

void EPointItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
    emit posRelease();
    setSelected(false);
    QGraphicsItem::mouseReleaseEvent(event);
}

void EPointItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    if (isSelected()) {
        painter->setBrush(QBrush(Qt::green));
    }
    else {
        painter->setBrush(QBrush(Qt::white));
    }

    if(m_type==PointType::MovePoint){
        painter->setPen(QPen(Qt::gray, 1));
        painter->drawEllipse(-4, -4, 8, 8);
    }else if(m_type==PointType::RotatePoint){
        painter->setPen(QPen(Qt::gray, 2));
        painter->drawEllipse(-8, -8, 16, 16);
        painter->drawEllipse(-4, -4, 8, 8);
    }

}

void EPointItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
    if (event->buttons() == Qt::LeftButton) {
        if(m_type==PointType::MovePoint)
            emit posMove(event->scenePos(), event->lastScenePos());
        else if(m_type==PointType::RotatePoint){
            emit posRotate(event->scenePos(), event->lastScenePos());
        }
    }
}

 以直线为例,实现的时候 先画一个普通的矩形,然后添加两个控制点,用于改变大小和旋转,通过控制点中 推拽的信号 来改变矩形的大小和旋转的角度,并且在矩形的paint函数中,更新控制点的位置。

class EItemUtil{
public:
    void rotateItem(QGraphicsItem* item,QPointF &curPos, QPointF &lastPos){
        // 设置中心点为原点
        QPointF originPos = item->boundingRect().center();
        // 从原点延伸出去两条线
        QLineF p1 = QLineF(originPos, item->mapFromScene(lastPos));
        QLineF p2 = QLineF(originPos, item->mapFromScene(curPos));
        // 两条线的夹角 为旋转角度
        qreal dRotateAngle = p2.angleTo(p1);
        item->setTransformOriginPoint(originPos);
        qreal dCurAngle = item->rotation() + dRotateAngle;

        while (dCurAngle > 360.0) {
            dCurAngle -= 360.0;
        }
        item->setRotation(dCurAngle);
        item->update();
    }
};

//直线
class ELine : public QObject, public QGraphicsRectItem
{
    Q_OBJECT
public:
    ELine(const QRectF& rect, QGraphicsItem* parent = nullptr);

protected:
    void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;

private:
    EPointItem* resizeItem;
    EPointItem* rotateItem;

    EItemUtil util;

private:
   //改变大小后 旋转中心会改变,这里要处理一下
    void resetCenter();
};

ELine::ELine(const QRectF& rect, QGraphicsItem* parent)
    : QGraphicsRectItem(rect, parent)
{
    setFlag(QGraphicsItem::ItemIsMovable);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges);
    setAcceptHoverEvents(true);
    
    //定义一个旋转的控制点
    rotateItem = new EPointItem(this,QPointF(this->rect().x()+this->rect().width(), this->rect().y()),EPointItem::PointType::RotatePoint);
    connect(rotateItem, &EPointItem::posRotate, this, [this](QPointF curPos, QPointF lastPos) {
        util.rotateItem(this,curPos,lastPos);
    });

    //定义一个改变大小的控制点
    resizeItem = new  EPointItem(this, QPointF(this->rect().x(), this->rect().y()));
    resizeItem->setParentItem(this);
    connect(resizeItem, &EPointItem::posMove, this, [&](QPointF curPos, QPointF lastPos) {
        this->prepareGeometryChange();
        QRectF newRect = this->rect();

        qreal dMouseX = mapFromScene(curPos).x();
        qreal dMouseY = mapFromScene(curPos).y();

        double dRemainWidth = newRect.width();
        double dRemainHeight = newRect.height();

        dRemainWidth = newRect.right() - dMouseX;
        dRemainHeight = newRect.bottom() - dMouseY;
        if(dRemainHeight<0||dRemainWidth<0)
            return;

        newRect.setTopLeft(QPointF(newRect.right() - dRemainWidth, newRect.bottom() - dRemainHeight));
        this->setRect(newRect);
    });

    connect(resizeItem, &EPointItem::posRelease, this, [this]{
        resetCenter();
    });

}

void ELine::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
    QPen mPen = QPen(Qt::red, 1);
    painter->setPen(mPen);
    painter->drawRect(rect());

    //更新两个控制点的位置
    resizeItem->setPos(QPointF(this->rect().x(), this->rect().y()));
    rotateItem->setPos(QPointF(this->rect().x()+this->rect().width(), this->rect().y()));
}

void ELine::resetCenter()
{
    //处理旋转后,改变大小的导致的旋转中心位置偏差
    auto rr = this->boundingRect();
    auto angle = this->rotation()*PI/180;
    auto p1 = rr.center();
    auto origin = this->transformOriginPoint();
    QPointF p2 = QPointF(0, 0);

    p2.setX(origin.x() + qCos(angle) * (p1.x() - origin.x()) - qSin(angle) * (p1.y() - origin.y()));
    p2.setY(origin.y() + qSin(angle) * (p1.x() - origin.x()) + qCos(angle) * (p1.y() - origin.y()));

    auto diff = p1 - p2;

    this->setRect(rr.adjusted(-diff.x(), -diff.y(), -diff.x(), -diff.y()));
    this->setTransformOriginPoint(this->boundingRect().center());
    this->update();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/370704.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

人类的本性,逃不开党同伐异

近几年以来&#xff0c;不知道大家有没有感受到&#xff0c;网络上越来越充满戾气。 无论哪个网站&#xff0c;只要打开评论区&#xff0c;充斥在眼前的总是一片乌烟瘴气。 一言不合就「对线」&#xff0c;动不动一顶帽子扣过去&#xff0c;说话前先「站队」「找友军」&#xf…

博途PLC限幅器(SCL代码)

PLC限幅器详细介绍,可以参考下面文章: https://rxxw-control.blog.csdn.net/article/details/128701050https://rxxw-control.blog.csdn.net/article/details/128701050三菱PLC限幅器 https://rxxw-control.blog.csdn.net/article/details/135212965

C++入门的基础

幸福比傲慢更容易蒙住人的眼睛。 ——大仲马 C入门 1、属于C的关键字1、1、C从何而来1、2、C关键字(C98) 2、命名空间2、1、命名空间的定义2、2、命名空间使用 3、C输入和输出4、缺省参数4、1、缺省参数概念4、2、缺省参数分类 5、函数重载5、1、函数重载概念 6、引用6、1、引用…

PHP框架详解 - symfony框架

首先说一下为什么要写symfony框架&#xff0c;这个框架也属于PHP的一个框架&#xff0c;小编接触也是3年前&#xff0c;原因是小编接触Golang&#xff0c;发现symfony框架有PHP框架的东西也有Golang的东西&#xff0c;所以决定总结一下&#xff0c;有需要的同学可以参看小编的G…

yolov1到v8的变化

目录 1.YOLO介绍&#xff1a;1.变化&#xff1a;小结&#xff1a; 1.YOLO介绍&#xff1a; YOLO&#xff08;You Only Look Once&#xff09;是一种流行的目标检测算法&#xff0c;它的版本从YOLOv1到YOLOv8经历了多次改进。以下是YOLOv1到YOLOv8的一些不同之处和改变&#xf…

快来使用Portainer让测试环境搭建飞起来吧

Portainer是Docker的图形化管理工具&#xff0c;提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作&#xff08;包括上传下载镜像&#xff0c;创建容器等操作&#xff09;、事件日志显示、容器控制台操作、Swarm集群和服务等集中管理和操作、登录用户管理和控…

PM圆桌派:同事不愿意告诉你的职场套路有哪些?

职场是社会的缩影&#xff0c;想要崭露头角&#xff0c;获得更多升职加薪的机会&#xff0c;就不要做着和多数人一样的事情&#xff0c;却期待着不一样的结果。 职场上有很多潜在的规则&#xff0c;要会做事&#xff0c;也要会说话&#xff0c;更要会做人。如果不懂规则&#…

Python 数据可视化:配色方案

1、引言 在这篇文章中&#xff0c;我们将研究Python的一些配色方案&#xff0c;主要是Seaborn库。这将采用 Python Notebook 格式&#xff0c;其中包括绘图的代码。 2、实验数据 首先导入必要的库&#xff1a; import pandas as pd import seaborn as sns import matplotlib…

【Vue】3-2、组合式 API

一、setup 选项 <script> export default {/*** 1、setup 执行时机早于 beforeCreate* 2、setup 中无法获取 this* 3、数据和函数需要在 setup 最后 return&#xff0c;才能在模板中使用* 4、可以通过 setup 语法糖简化代码*/setup(){// console.log(setup function, thi…

Flink容错机制

目录 一&#xff0c;检查点&#xff1a; 二&#xff0c;保存点&#xff1a; ①版本管理和归档存储&#xff1a; ②更新Flink版本&#xff1a; ③更新应用程序&#xff1a; ④调整并行度&#xff1a; ⑤暂停应用程序&#xff1a; Flink容错机制 一&#xff0c;检查点&#xff…

RedHat8.4安装邮件服务器

一、配置发件服务器 1.1 根据现场IP&#xff0c;配置主机名 vim /etc/hosts 192.168.8.120 mail.test.com 将主机名更改为邮件服务器域名mail.test.com 1.2 关闭防火墙&#xff0c;禁止开机启动 systemctl stop firewalld systemctl disable firewalld 1.3 关闭selinux v…

java之ReentrantLock

在讲RentrantLock之前需要先讲一下AQS和LockSupport&#xff0c;因为rentrantLock底层是用AQS实现的&#xff0c;而AQS中获取阻塞和唤醒底使用LockSupport实现的。 1、LockSupport实现 下面代码中&#xff0c;LockSupport.park方法是当前线程等待&#xff0c;直到获得许可&am…

React Native学习记录

一、创建RN项目的时候是空文件夹的问题 1.使用npx react-native init RNDemos初始化项目的时候&#xff0c;会报错&#xff0c;模版错误&#xff0c;然后创建出来一个空的文件夹 2.如果出现这种情况&#xff0c;需要设置npm install -g react-native-cli 3.安装完成以后再次初…

Postgres与DynamoDB:选择哪个数据库

启动新项目时需要做出的决定之一是使用哪个数据库。如果您使用的是Django这样的包含电池的框架&#xff0c;那么没有理由再三考虑。选择一个受支持的数据库引擎&#xff0c;就可以了。另一方面&#xff0c;如果你使用像FastAPI或Flask这样的微框架&#xff0c;你需要自己做出这…

聚簇索引、回表与覆盖索引

聚簇索引一般指的是主键索引&#xff08;如果存在主键索引的话&#xff09;。 作为一个正常开发&#xff0c;建表时主键肯定是必须的。 而即使如果表中没有定义主键&#xff0c;InnoDB 会隐式选择一个唯一的非空索引代替。 所以我们就直接含糊点说&#xff1a; 聚簇索引就是…

crmebAI名片小程序全开源全端uniapp

应用介绍 AI名片小程序是一种基于人工智能技术的数字化智能名片&#xff0c;具有多种功能和特点。以下是AI名片小程序的简介&#xff1a; 智能化管理&#xff1a;AI名片小程序具备智能化的管理系统&#xff0c;用户可以方便地管理名片信息&#xff0c;包括个人信息、职位、公司…

jmeter-02切换中文,改为白色背景

文章目录 一、切换中文问题&#xff1a;jmeter设置中文后无法保存&#xff0c;下次启动还是英文 二、改为白色背景 一、切换中文 问题&#xff1a;jmeter设置中文后无法保存&#xff0c;下次启动还是英文 解决办法&#xff1a; 在jmeter路径下找到文件jmeter.bat开启编辑模式&…

如何在Shopee平台上进行手机类目选品?

在Shopee平台上进行手机类目的选品是一个关键而复杂的任务。卖家需要经过一系列的策略和步骤&#xff0c;以确保选品的成功和销售业绩的提升。下面将介绍一些有效的策略&#xff0c;帮助卖家在Shopee平台上进行手机类目选品。 先给大家推荐一款shopee知虾数据运营工具知虾免费…

指针的学习2

目录 数组名的理解 使用指针访问数组 一维数组传参的本质 冒泡排序 二级指针 指针数组 指针数组模拟二维数组 数组名的理解 数组名是数组首元素的地址 例外&#xff1a; sizeof(数组名),sizeof中单独放数组名&#xff0c;这里的数组名表示整个数组&#xff0c;计算的…