需求:
基于视频进行 图形的绘制。
方案:
上一篇文章分享了如何将视频实时渲染到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();
}