基于QGraphicsView实现的图片编辑工具

最近工作当中需要实现图片编辑的功能,具体需求要求可在图片中绘制圆形,矩形及多边形,考虑到QGraphicsView对于一些图像绘制的便捷性,故而选择使用此框架作为实现基础。

继承QGraphicsView并重载鼠标事件

void ImageSequence::mousePressEvent(QMouseEvent * e)
{
	if (isLoading())
	{
		return;
	}
	if (_operateType != OperateTypeNone && e->button() == Qt::LeftButton)
	{
		_mousePrePos = e->pos();
		_mousePressed = true;
		return;
	}
	_curEllipseItem = dynamic_cast<CustomEllipseItem*>(scene()->itemAt(e->pos(), QTransform()));
	if (nullptr != _curEllipseItem)
	{
		if (e->button() == Qt::RightButton)
		{
			//右击弹出菜单
			showMenu(e->pos(), OperateTypeCycle, _curEllipseItem->id());
		}
		else if(e->button() == Qt::LeftButton)
		{
			//左键开始编辑或移动
			_mousePrePos = e->pos();
			_itemPrePos = _curEllipseItem->pos();
			_mouseEllipsePressed = true;
			initTextItemPos(_curEllipseItem->id());
			//新建编辑图元
			createEllipseEditItem();
		}
	}
	else
	{
		CustomEllipseEditItem *itemTmp = dynamic_cast<CustomEllipseEditItem*>(scene()->itemAt(e->pos(), QTransform()));
		if (nullptr == itemTmp)
		{
			clearEllipseEditItem(); //清除圆形编辑图元
		}
	}

	_curRectItem = dynamic_cast<CustomRectItem*>(scene()->itemAt(e->pos(), QTransform()));
	if (nullptr != _curRectItem)
	{
		if (e->button() == Qt::RightButton)
		{
			//右键弹出菜单
			showMenu(e->pos(), OperateTypeRectangle, _curRectItem->id());
		}
		else if (e->button() == Qt::LeftButton)
		{
			//左键开始编辑或移动
			_mousePrePos = e->pos();
			_itemPrePos = _curRectItem->pos();
			_mouseRectPressed = true;
			initTextItemPos(_curRectItem->id());
			//新建矩形编辑图元
			createRectangleEditItem();
		}
	}
	else
	{
		CustomEllipseEditItem *itemTmp = dynamic_cast<CustomEllipseEditItem*>(scene()->itemAt(e->pos(), QTransform()));
		if (nullptr == itemTmp)
		{
			clearRectangleEditItem(); //清除矩形编辑图元
		}
	}

	_curPolygonItem = dynamic_cast<CustomPolygonItem*>(scene()->itemAt(e->pos(), QTransform()));
	if (nullptr != _curPolygonItem)
	{
		if (e->button() == Qt::RightButton)
		{
			//右键弹出菜单
			showMenu(e->pos(), OperateTypePolygon, _curPolygonItem->id());
		}
		else if (e->button() == Qt::LeftButton)
		{
			//左键开始编辑或移动
			_mousePrePos = e->pos();
			_itemPrePos = _curPolygonItem->pos();
			_mousePolygonPressed = true;
			_polygonPrePos = _curPolygonItem->polygon();
			initTextItemPos(_curPolygonItem->id());
			//新建多边形编辑图元
			createPolygonEditItem();
		}
	}
	else
	{
		CustomEllipseEditItem *itemTmp = dynamic_cast<CustomEllipseEditItem*>(scene()->itemAt(e->pos(), QTransform()));
		if (nullptr == itemTmp)
		{
			clearPolygonEditItem(); //清除多边形编辑图元
		}
	}
	QGraphicsView::mousePressEvent(e);
}

void ImageSequence::mouseMoveEvent(QMouseEvent * e)
{
	if (_mousePressed)
	{
		switch (_operateType)
		{
		case OperateTypeCycle:
			drawEllipse(e->pos());
			break;
		case OperateTypeRectangle:
			drawRectangle(e->pos());
			break;
		default:
			break;
		}
	}
	//实时绘制多边形点
	if (OperateTypePolygon == _operateType && nullptr != _polygonLineItem)
	{
		updatePolygonLine(e->pos());
	}
	
	//移动圆形
	if (_mouseEllipsePressed)
	{
		if (nullptr != _curEllipseItem)
		{
			moveItem<CustomEllipseItem>(_curEllipseItem, _itemPrePos, e->pos());
			if (nullptr != _ellipseEditItem) //移动编辑点
			{
				_ellipseEditItem->moveItem(_curEllipseItem->pos(), _curEllipseItem->rect());
				_ellipseEditItem->setItemFlag(QGraphicsItem::ItemSendsGeometryChanges, false);
			}
		}
		moveItem<CustomTextItem>(_curTextItem, _textPrePos, e->pos());
	}

	//移动矩形
	if (_mouseRectPressed)
	{
		if (nullptr != _curRectItem)
		{
			moveItem<CustomRectItem>(_curRectItem, _itemPrePos, e->pos());
			if (nullptr != _rectangleEditItem) //移动编辑图元
			{
				_rectangleEditItem->moveItem(_curRectItem->pos(), _curRectItem->rect());
				_rectangleEditItem->setItemFlag(QGraphicsItem::ItemSendsGeometryChanges, false);
			}
		}
		moveItem<CustomTextItem>(_curTextItem, _textPrePos, e->pos());
	}

	//移动多边形
	if (_mousePolygonPressed)
	{
		if (nullptr != _curPolygonItem)
		{
			updatePolygonVertex(e->pos());
			if (nullptr != _polygonEditItem)
			{
				_polygonEditItem->moveItem(_curPolygonItem->polygon());
				_polygonEditItem->setItemFlag(QGraphicsItem::ItemSendsGeometryChanges, false);
			}
		}
		moveItem<CustomTextItem>(_curTextItem, _textPrePos, e->pos());
	}
	QGraphicsView::mouseMoveEvent(e);
}

void ImageSequence::mouseReleaseEvent(QMouseEvent * e)
{
	if (_operateType == OperateTypeCycle || _mouseEllipsePressed)
	{
		_mouseEllipsePressed = false;
		_operateType = OperateTypeNone;
		createEllipseEditItem();
		if (nullptr != _curEllipseItem && _itemPrePos != _curEllipseItem->pos()) //防止点击绘制区域但位置未发生变化重复计算
		{
			calcEllipseParam(_curEllipseItem);
			setLoadingVisible(true);
		}
	}
	else if (_operateType == OperateTypeRectangle || _mouseRectPressed)
	{
		_mouseRectPressed = false;
		_operateType = OperateTypeNone;
		if (nullptr != _curRectItem && _itemPrePos != _curRectItem->pos()) //防止点击绘制区域但位置未发生变化重复计算
		{
			calcRectangleParam(_curRectItem);
			setLoadingVisible(true);
		}
	}
	else if (OperateTypePolygon == _operateType)
	{
		drawPolygon(e->pos());
	}
	if (nullptr != _curPolygonItem)
	{
		_mousePolygonPressed = false;
		if(_polygonPrePos.size() > 0 && _polygonPrePos.at(0) != _curPolygonItem->polygon().at(0)) //移动多边形后重新计算参数,且防止点击绘制区域但位置未发生变化重复计算
		{
			calcPolygonParam(_curPolygonItem);
			setLoadingVisible(true);
		}
	}
	_mousePressed = false;

	QGraphicsView::mouseReleaseEvent(e);
}
void ImageSequence::mouseDoubleClickEvent(QMouseEvent * e)
{
	if (OperateTypePolygon == _operateType)
	{
		if (nullptr != _curPolygonItem)
		{
			QPolygonF polygonPos = _curPolygonItem->polygon();
			if (polygonPos.size() < 3)
			{
				delete _curPolygonItem;
				_curPolygonItem = nullptr;
			}
			else
			{
				_curItemId += 1;
				_curPolygonItem->setId(_curItemId);
				_mapPolygonItem.insert(_curItemId, _curPolygonItem);
				drawText(polygonPos.at(0) - TEXT_ITEM_POS_OFFSET);
			}
		}
		//计算参数
		_operateType = OperateTypeNone;
		calcPolygonParam(_curPolygonItem);
		setLoadingVisible(true);
		deletePolygonLine();
		update();
	}
}

该代码块主要实现鼠标的按下,移动,松开事件,在此方法中实现对应形状的绘制以及移动。

编辑圆形图元大小

#ifndef __ELLIPSEITEMEDITMGR_H__
#define __ELLIPSEITEMEDITMGR_H__

#include <QObject>
#include <QGraphicsEllipseItem>

class QGraphicsEllipseItem;
class QGraphicsScene;
class QSize;
class QPointF;
class QRectF;
class CustomEllipseItem;
class QMouseEvent;

class CustomEllipseEditItem : public QObject, public QGraphicsEllipseItem
{
	Q_OBJECT

public:
	CustomEllipseEditItem(QGraphicsItem *parent = nullptr);
	~CustomEllipseEditItem();

	void setItemType(int type);

Q_SIGNALS:
	void signalItemMoved(const QPoint &newPos);
	void signalMouseRelease();
	void signalItemMovedWithType(int itemType, const QPointF &newPos);

protected:
	QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant & value);
	void mousePressEvent(QGraphicsSceneMouseEvent *e);
	void mouseReleaseEvent(QGraphicsSceneMouseEvent *e);

private:
	int m_itemType;
};

class EllipseItemEditMgr : public QObject
{
	Q_OBJECT

public:
	EllipseItemEditMgr(QGraphicsScene *scene);
	~EllipseItemEditMgr();

	void createItem(int itemId, const QSize &size, const QPointF &pos, const QRectF &ellipseRect);
	void moveItem(const QPointF &pos, const QRectF &ellipseRect);
	void deleteItem();
	void setItemFlag(QGraphicsItem::GraphicsItemFlag flag, bool enabled = true);
	void refreshItemPos(int itemId, const QPointF &pos, const QRectF &ellipseRect);

Q_SIGNALS:
	void signalItemResize(int id, int w, int h);
	void signalItemResizeFinished(int id);

private:
	int m_ellipseItemId;
	QGraphicsScene *m_scene;
	CustomEllipseEditItem *m_ellipseBottomRightPotItem;
	QPointF m_posStart;
	QRectF m_preRect;
};

#endif // !__ELLIPSEITEMEDITMGR_H__

该代码块主要实现了对于圆形绘制区域的resize,实现思路是在圆形区域的右下角添加一个可以移动的QGraphicsEllipseItem,通过移动此图元,来计算鼠标的移动距离,从而重新设置圆形区域的pos和rect,达到resize的目的。

矩形区域编辑大小

矩形区域编辑大小与圆形区域类似,只不过编辑矩形区域的时候,需要根据矩形的四个顶点和四条边的中点坐标,创建可移动的QGraphicsEllipseItem,通过计算每一个item的移动距离,更新矩形区域的pos和rect,达到resize的目的。

功能演示

ROIVideo

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值