最近工作当中需要实现图片编辑的功能,具体需求要求可在图片中绘制圆形,矩形及多边形,考虑到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