qt QGraphicsView 绘制多种图形

入门

先看一个简单的例子

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include 
#include 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    init();
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::init()
{
    QGraphicsScene *pScene = new QGraphicsScene();
    pScene->addText("Hello, world!");
    QGraphicsView *pView = new QGraphicsView(pScene, this);

}

效果:
qt QGraphicsView 绘制多种图形_第1张图片

  • QGraphicsView
  • QCustomQGraphicsScene
  • QGraphicsItem

实时绘制

qt QGraphicsView 绘制多种图形_第2张图片
需求:至少有两种图形需要绘制

画一个图元的逻辑

在界面上(LabelGraphicsView类)需要有一个成员变量(m_graphicsBaseItem )负责当前图元的绘画。这个成员变量可以画多种类型的图元。

设计一个基类 GraphicsBaseItem (继承自QGraphicsItem),再扩展两个子类:

  1. MyPolylineItem (继承自GraphicsBaseItem ): 绘制多边形,自定义paint函数
  2. MyRectangleItem(继承自GraphicsBaseItem ):绘制长方形,自定义paint函数. (调用update后,会调用paint函数)

需要画矩形时,就把MyRectangleItem 赋值到GraphicsBaseItem ,调用paint函数绘制。需要画多边形时,使用MyRectangleItem
每个图元类自己内部维护一个画图状态(m_drawFinished)。如画图未完成,则可以继续加点,可以更新实时动态等。

画多个图元的逻辑

成员变量(m_graphicsBaseItem )负责当前图元的绘画,画图完成之后,更新画图状态(m_drawFinished),并重新new一个相同的图元,添加当场景中。清空成员变量(m_graphicsBaseItem )的值,等待下一个图元的绘制

GraphicsBaseItem 类

至少需要考虑一下场景:

  1. 画矩形框:鼠标拖动实时显示矩形的形状。那么就涉及到:
    (1)矩形框的起始位置
    (2)当前鼠标位置
    (3)矩形最终位置
    所以鼠标按下时,添加第一个点,拖动时更新鼠标当前位置m_mousePt,并绘图。鼠标松开时,加入最后一个点。所以还需要判断当前是否在绘图(bool m_drawFinished;)
  2. 画多边形:
    (1)点是否有重复
    (2)需要一个闭合函数:比如最后一个点自动连接到第一个点( int GetNearestVertex(const QPointF& pt);)
  3. 图行点击时高亮:QColor m_color; QColor m_lineHighlightColor;
#ifndef GRAPHICSBASEITEM_H
#define GRAPHICSBASEITEM_H
#include
#include "globaldefine.h"
class GraphicsBaseItem : public QGraphicsItem
{
public:
    GraphicsBaseItem(const QPolygonF& pts,
                     bool drawFinished = false,
                     QGraphicsItem *parent = Q_NULLPTR);
    // 查看图形类型,比如矩形,多边形
    virtual ShapeType GetShapeType() = 0;
    // 查看是否包含这个点
    virtual bool Contains(const QPointF& pt) = 0;
    //是否完成了绘图(鼠标拖动场景下使用,或者多边形绘制)
    virtual void FinishDrawing();
	//添加点,获取点
    void AddPoint(const QPointF& point);
    int GetPointCounts();
    bool GetPoint(int id, QPointF& pt);
    QPolygonF GetPoints();
    bool RemoveLastPoint();
    int GetNearestVertex(const QPointF& pt);
   //设置当前鼠标位置
   void SetMousePt(const QPointF& mousePt);
   //重置
   void Reset();
   //是否被选中
   void SetSelected(bool isSelected);
   virtual QRectF boundingRect() const override;

protected:
    QPolygonF m_pts;//闭合曲线首尾点无需重复存储
    QPointF m_mousePt;//画图时鼠标当前位置点
    bool    m_selected;//是否被选中
    bool    m_drawFinished;//是否完成绘图
};

#endif // GRAPHICSBASEITEM_H

MyRectangleItem类

主要实现该类的绘图功能 paint

#ifndef MYRECTANGLEITEM_H
#define MYRECTANGLEITEM_H
#include "globaldefine.h"
#include"mygraphicsbaseitem.h"
class MyRectangleItem : public GraphicsBaseItem
{
public:
    MyRectangleItem(const QPolygonF& pts,bool drawFinished = false,GraphicsBaseItem *parent = Q_NULLPTR);


    virtual ShapeType GetShapeType();
    //绘图 在updata后调用 MyPolylineItem
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    virtual bool Contains(const QPointF& pt);
    virtual void FinishDrawing();//完成绘制并检查点的位置是否是左上点和右下点,如果不是就更新
};

#endif // MYRECTANGLEITEM_H

具体实现

#include "myrectangleitem.h"
#include
#include 
namespace
{
    bool IsGreaterThan(double d1, double d2)//比较d1是否大于d2
    {
        double eps = 1.0e-6;
        double delta = d1 - d2;
        if (delta > eps)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    QRectF makeRect(const QPointF& pt1, const QPointF& pt2)
    {
        double topLeftX = IsGreaterThan(pt1.x(), pt2.x()) ? pt2.x() : pt1.x();
        double topLeftY = IsGreaterThan(pt1.y(), pt2.y()) ? pt2.y() : pt1.y();
        double width = qAbs(pt1.x() - pt2.x());
        double height = qAbs(pt1.y() - pt2.y());
        return QRectF(topLeftX, topLeftY, width, height);
    }
}


MyRectangleItem::MyRectangleItem(const QPolygonF& pts,bool drawFinished ,GraphicsBaseItem *parent )
    :GraphicsBaseItem(pts, drawFinished, parent)
{

}



ShapeType MyRectangleItem::GetShapeType()
{
    return RECTANGLE;
}

void MyRectangleItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
{
    if (m_pts.isEmpty())
    {
        return;
    }

    painter->setPen(Qt::blue);
    painter->setBrush(Qt::NoBrush);
    painter->setOpacity(1.0);

    if (m_drawFinished)
    {
        if (m_pts.size() == 2)
        {
            QRectF rect = makeRect(m_pts[0], m_pts[1]);
            painter->drawRect(rect);
            painter->setBrush(Qt::blue);
			qDebug() <<"finish "<< m_pts[0] <<" , "<< m_pts[1];
        }
    }
    else
    {
        QRectF rect = makeRect(m_pts[0], m_mousePt);//m_mousePt用于追踪鼠标绘制
        painter->drawRect(rect);
		qDebug() << "unfinish " << m_pts[0] << " , " << m_mousePt;
    }
}

bool MyRectangleItem::Contains(const QPointF & pt)
{
    if (m_pts.count() != 2)
    {
        return false;
    }

    QRectF rect(m_pts[0],m_pts[1]);
    bool ret = rect.contains(pt);
    return ret;
}

void MyRectangleItem::FinishDrawing()
{
	GraphicsBaseItem::FinishDrawing();

    if (m_pts.count() == 2)
    {
        QRectF rect = makeRect(m_pts[0], m_pts[1]);
        QPointF topLeft = rect.topLeft();
        QPointF bottomRight = rect.bottomRight();
        if (topLeft != m_pts[0] || bottomRight != m_pts[1])
        {
            QPolygonF pts;
            pts.push_back(topLeft);
            pts.push_back(bottomRight);
            m_pts.swap(pts);
        }
    }
}

MyPolylineItem 类

多边形类
MyPolylineItem
多边形由一系列点组成,起始点和终止点比较重要
需要设置终止方案。

#ifndef MYPOLYLINEITEM_H
#define MYPOLYLINEITEM_H
#include"mygraphicsbaseitem.h"

class MyPolylineItem : public GraphicsBaseItem
{
public:
    MyPolylineItem(const QPolygonF& pts,bool drawFinished = false,GraphicsBaseItem *parent = Q_NULLPTR);
    virtual ShapeType GetShapeType();
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    virtual QPainterPath shape() const override;
    virtual bool Contains(const QPointF& pt);
};

#endif // MYPOLYLINEITEM_H

绘图的逻辑

思路

纵向是三个鼠标事件

  1. virtual void mousePressEvent(QMouseEvent *event) override; virtual

  2. void mouseReleaseEvent(QMouseEvent *event) override; virtual void

  3. mouseMoveEvent(QMouseEvent *event) override;

横向是两种图元

  1. 矩形
  2. 多边形

矩形画法

按下鼠标左键,不放开拖动到目标位置,放开。于是按照时间顺序依次触发

  1. mousePressEvent 添加第一个点

  2. mouseMoveEvent 界面实时更新鼠标位置 画矩形

  3. mouseReleaseEvent 如果释放的点不是第一个点的位置,则认为是一个完整的矩形,添加最后一个点完成绘制。否则清空所有点

多边形画法

问题:

  1. 什么事件加点?mousePressEvent or mouseReleaseEvent ? 考虑到有人会在按下鼠标时后悔,再拖动鼠标到正确位置释放,所以选择在mouseReleaseEvent 事件中加点
  2. 什么时候标记第一个点:判断当前没有图形绘制时,也没有其他操作时,且触发了mouseReleaseEvent
  3. 什么情况完成绘图:最后一个点和第一个点重合时,结束绘画

所以在界面类中,我们需要标记状态

  1. 绘图
  2. 移动
  3. 默认

界面LabelGraphicsView

#include "LabelGraphicsView.h"
#include "myrectangleitem.h"
#include
 
LabelGraphicsView::LabelGraphicsView()
{
	init();
	QVector<QPointF> pts;
	m_GraphicsBaseItem = new MyRectangleItem(pts, false);
}
LabelGraphicsView::~LabelGraphicsView()
{
}
void LabelGraphicsView::init()
{
	setBackgroundBrush(QColor(255, 255, 255));
	setScene(&m_graphicsScene);
	setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
	setOptimizationFlags(QGraphicsView::DontSavePainterState);
	setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
	m_graphicsScene.setBackgroundBrush(Qt::darkGray);
}

void LabelGraphicsView::mousePressEvent(QMouseEvent *event)
{
	//TODO:鼠标左键,点击绘制图形;
	if (event->button() == Qt::LeftButton)
	{
		QPoint pos = event->pos();
		QPointF leftButtonPressedPos = mapToScene(pos);
		m_GraphicsBaseItem->AddPoint(leftButtonPressedPos);
		viewport()->update();
	}
	QGraphicsView::mousePressEvent(event);
}
void LabelGraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
	//TODO:鼠标释放之后操作
	QPoint pos = event->pos();
	QPointF ReleasePos = mapToScene(pos);
	m_GraphicsBaseItem->AddPoint(ReleasePos);
	m_GraphicsBaseItem->FinishDrawing();
	viewport()->update();
	ShapeType shapeType = m_GraphicsBaseItem->GetShapeType();
	QPolygonF pts = m_GraphicsBaseItem->GetPoints();
	auto newItem = new MyRectangleItem(pts, true);
	m_graphicsScene.addItem(newItem);
	m_GraphicsBaseItem->Reset();
}

//实时获取鼠标最新位置并绘图 
void LabelGraphicsView::mouseMoveEvent(QMouseEvent *event)
{
	//TODO:鼠标移动时,如果存在有效图形类型,进行图形绘制
	if (m_GraphicsBaseItem->GetShapeType() == RECTANGLE)
	{
		QPoint pos = event->pos();
		QPointF movePos = mapToScene(pos);
		m_GraphicsBaseItem->SetMousePt(movePos);
		viewport()->update();
	}
	QGraphicsView::mouseMoveEvent(event);
}

未完待续

参考文献

Qt中QGraphicsView架构下实时鼠标绘制图形

源码

main.cpp

#include "LabelImg.h"
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    LabelImg w;
    w.show();
    return a.exec();
}

ui 界面
LabelImg.h

#pragma once
#include "LabelGraphicsView.h"
#include 
#include "ui_LabelImg.h"

class LabelImg : public QMainWindow
{
    Q_OBJECT

public:
    LabelImg(QWidget *parent = Q_NULLPTR);
private:
	void init();

public slots:
	void SlotRectangleButtonClicked();
	void SlotPolylineButtonClicked();
private:
    Ui::LabelImgClass ui;
	LabelGraphicsView * m_graphicsView =nullptr;
};

#include "LabelImg.h"
#include
LabelImg::LabelImg(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
	init();
	connect(ui.rectangleButton, SIGNAL(clicked()),this, SLOT(SlotRectangleButtonClicked()));
	connect(ui.polylineButton, SIGNAL(clicked()), this, SLOT(SlotPolylineButtonClicked()));
}

void LabelImg::init()
{
	QVBoxLayout *vBoxLayout = new QVBoxLayout;
	ui.widget->setLayout(vBoxLayout);
	vBoxLayout->setMargin(2);
	vBoxLayout->setSpacing(2);
	m_graphicsView = new LabelGraphicsView( );
	m_graphicsView->setFrameShape(QFrame::NoFrame);
	m_graphicsView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
	m_graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
	m_graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
	vBoxLayout->addWidget(m_graphicsView);
}

void LabelImg::SlotRectangleButtonClicked()
{
	m_graphicsView->setDrawRectangleModule();
	
}
void LabelImg::SlotPolylineButtonClicked()
{
	m_graphicsView->setDrawPolylineModule();
}

LabelGraphicsView.h

 #pragma once
#include 
#include "mygraphicsbaseitem.h"
#include "myrectangleitem.h"
#include "mypolylineitem.h"

enum ProcessMode
{
	DEFAULT = 0, // 默认模式
	DRAW, //绘图模式
	MOVE  //移动模式
};

class LabelGraphicsView :
	public QGraphicsView
{
public:
	LabelGraphicsView();
	~LabelGraphicsView();
	void setDrawRectangleModule();
	void setDrawPolylineModule();
	virtual void mousePressEvent(QMouseEvent *event) override;
	virtual void mouseReleaseEvent(QMouseEvent *event) override;
	virtual void mouseMoveEvent(QMouseEvent *event) override;
private:
	void init();
	bool isSamePoint(QPointF p1, QPointF p2);
private:
	QGraphicsScene			m_graphicsScene;
	GraphicsBaseItem*  m_GraphicsBaseItem = nullptr;
	MyRectangleItem *m_RectangleItem = nullptr;
	MyPolylineItem *	m_PolylineItem = nullptr;
	ProcessMode m_processMode= DEFAULT;
};


 #include "LabelGraphicsView.h"
#include

LabelGraphicsView::LabelGraphicsView()
{
	init();
}

LabelGraphicsView::~LabelGraphicsView()
{
}
void LabelGraphicsView::init()
{
	setBackgroundBrush(QColor(255, 255, 255));
	setScene(&m_graphicsScene);
	setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
	setOptimizationFlags(QGraphicsView::DontSavePainterState);
	setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
	m_graphicsScene.setBackgroundBrush(Qt::darkGray);
	setSceneRect(-300, -300, 600, 600);


	QVector<QPointF> pts;
	m_PolylineItem = new MyPolylineItem(pts, false);

	QVector<QPointF> rpts;
	m_RectangleItem = new MyRectangleItem(rpts, false);
	m_graphicsScene.addItem(m_GraphicsBaseItem);

}

void LabelGraphicsView::setDrawRectangleModule()
{
	if (m_GraphicsBaseItem)
	{
		m_GraphicsBaseItem->Reset();
		m_GraphicsBaseItem = nullptr;
	}
	if (!m_RectangleItem)
	{
		QVector<QPointF> rpts;
		m_RectangleItem = new MyRectangleItem(rpts, false);
	}
	m_GraphicsBaseItem =m_RectangleItem;
	m_graphicsScene.addItem(m_GraphicsBaseItem);
}
void LabelGraphicsView::setDrawPolylineModule()
{
	if (m_GraphicsBaseItem)
	{
		m_GraphicsBaseItem->Reset();
		m_GraphicsBaseItem = nullptr;
	}
	if (!m_PolylineItem)
	{
		QVector<QPointF> pts;
		m_PolylineItem = new MyPolylineItem(pts, false);
	}
	m_GraphicsBaseItem = m_PolylineItem;
	m_graphicsScene.addItem(m_GraphicsBaseItem);
}

void LabelGraphicsView::mousePressEvent(QMouseEvent *event)
{
	//TODO:鼠标左键,点击绘制图形;
	if (event->button() == Qt::LeftButton && m_GraphicsBaseItem)
	{
		QPoint pos = event->pos();
		QPointF leftButtonPressedPos = mapToScene(pos);
		ShapeType shapeType = m_GraphicsBaseItem->GetShapeType();
		switch (m_processMode)
		{
			case DEFAULT:
			{
				// 如果时默认模式,现在点击了鼠标,则进入绘图模式
				m_processMode = DRAW;
				if (shapeType == RECTANGLE)
				{
					m_GraphicsBaseItem->AddPoint(leftButtonPressedPos);
					//m_graphicsScene.addItem((MyRectangleItem*)m_GraphicsBaseItem);
				}
				//POLYGON在mouseReleaseEvent加点
			}
			break;
			case DRAW:
			{
			}
			break;
			case MOVE:
			{

			}
			break;
		}
		viewport()->update();
	}
	//QGraphicsView::mousePressEvent(event);
}
void LabelGraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
	//TODO:鼠标释放之后操作
	QPoint pos = event->pos();
	QPointF ReleasePos = mapToScene(pos);
	if (m_GraphicsBaseItem)
	{
		ShapeType shapeType = m_GraphicsBaseItem->GetShapeType();

		switch (m_processMode)
		{
		case DRAW:
		{
			if (shapeType == RECTANGLE)
			{
				//结束矩形的绘制
				m_GraphicsBaseItem->AddPoint(ReleasePos);
				m_GraphicsBaseItem->FinishDrawing();
				m_processMode = DEFAULT;

				//将矩形加到场景中保存,将绘图的指针reset
				QPolygonF pts = m_GraphicsBaseItem->GetPoints();
				MyRectangleItem* newItem = new MyRectangleItem(pts, true);
				m_graphicsScene.addItem(newItem);
				//m_graphicsScene.removeItem(m_GraphicsBaseItem);
				m_GraphicsBaseItem->Reset();
			}
			else if (shapeType == POLYGON)
			{
				QPointF firstPt;
				if (!m_GraphicsBaseItem->GetPoint(0, firstPt))
				{
					m_GraphicsBaseItem->AddPoint(ReleasePos);
				}

				if (m_GraphicsBaseItem->GetPointCounts() > 1 && isSamePoint(firstPt, ReleasePos))
				{
					//结束多边形的绘制
					m_GraphicsBaseItem->FinishDrawing();
					m_processMode = DEFAULT;
					//将矩形加到场景中保存,将绘图的指针reset
					QPolygonF pts = m_GraphicsBaseItem->GetPoints();
					auto newItem = new MyPolylineItem(pts, true);
					m_graphicsScene.addItem(newItem);
					//m_graphicsScene.removeItem(m_GraphicsBaseItem);
					m_GraphicsBaseItem->Reset();
				}
				else
				{
					m_GraphicsBaseItem->AddPoint(ReleasePos);
				}
			}
		}
		break;
		case MOVE:
		{

		}
		break;
		}

		viewport()->update();
	}

	QGraphicsView::mouseReleaseEvent(event);//不写这行图元会错位
}

//实时获取鼠标最新位置并绘图 
void LabelGraphicsView::mouseMoveEvent(QMouseEvent *event)
{
	//TODO:鼠标移动时,如果存在有效图形类型,进行图形绘制
	switch (m_processMode)
	{
		case DRAW:
		{
			QPoint pos = event->pos();
			QPointF movePos = mapToScene(pos);
			m_GraphicsBaseItem->SetMousePt(movePos);
		}
		break;
		case MOVE:
		{

		}
		break;
	}
	viewport()->update();
 
	QGraphicsView::mouseMoveEvent(event);
}


bool LabelGraphicsView::isSamePoint(QPointF p1, QPointF p2)
{
	QPointF dif = p1 - p2;
	float distance = dif.manhattanLength();
	//方圆5个像素点内
	if (distance <= 5)
	{
		return true;
	}
	else
	{
		return false;
	}
}

图元
mygraphicsbaseitem.h

#ifndef GRAPHICSBASEITEM_H
#define GRAPHICSBASEITEM_H
#include
#include "globaldefine.h"
class GraphicsBaseItem : public QGraphicsItem
{
public:
    GraphicsBaseItem(const QPolygonF& pts,
                     bool drawFinished = false,
                     QGraphicsItem *parent = Q_NULLPTR);
	~GraphicsBaseItem();
    // 查看图形类型,比如矩形,多边形
    virtual ShapeType GetShapeType() = 0;
    // 查看是否包含这个点
    virtual bool Contains(const QPointF& pt) = 0;
    //是否完成了绘图(鼠标拖动场景下使用,或者多边形绘制)
    virtual void FinishDrawing();
	virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) =0;
	//添加点,获取点
    void AddPoint(const QPointF& point);
    int GetPointCounts();
    bool GetPoint(int id, QPointF& pt);
    QPolygonF GetPoints();
    bool RemoveLastPoint();
    int GetNearestVertex(const QPointF& pt);
   //设置当前鼠标位置
   void SetMousePt(const QPointF& mousePt);
   //重置
   void Reset();
   //是否被选中
   void SetSelected(bool isSelected);
   virtual QRectF boundingRect() const override;

protected:
    QPolygonF m_pts;//闭合曲线首尾点无需重复存储
    QPointF m_mousePt;//画图时鼠标当前位置点
    bool    m_selected;//是否被选中
    bool    m_drawFinished;//是否完成绘图
};

#endif // GRAPHICSBASEITEM_H

#include "mygraphicsbaseitem.h"
#include 
GraphicsBaseItem::GraphicsBaseItem(
    const QPolygonF& pts,
    bool drawFinished/* = false*/,
    QGraphicsItem *parent/* = Q_NULLPTR*/)
    : QGraphicsItem(parent), m_pts(pts), m_drawFinished(drawFinished)
{
    if (m_pts.count() > 0)
    {
        m_mousePt = m_pts[0];
    }
}

GraphicsBaseItem::~GraphicsBaseItem()
{

}

void GraphicsBaseItem::FinishDrawing()
{
    m_drawFinished = true;
}

void GraphicsBaseItem::AddPoint(const QPointF & point)
{
    if (m_drawFinished)
    {
        return;
    }

    if (m_pts.count() > 0)
    {
        bool hasPt = m_pts.contains(point);
        if (hasPt)
        {
            return;
        }
    }

    m_pts.push_back(point);
}

int GraphicsBaseItem::GetPointCounts()
{
    return m_pts.count();
}

bool GraphicsBaseItem::GetPoint(int id, QPointF& pt)
{
    if (id >= 0 && id < m_pts.size())
    {
        pt = m_pts[id];
        return true;
    }
    return false;
}

QPolygonF GraphicsBaseItem::GetPoints()
{
    return m_pts;
}

bool GraphicsBaseItem::RemoveLastPoint()
{
    if (!m_pts.isEmpty())
    {
        m_pts.removeLast();
        return true;
    }
    return false;
}


int GraphicsBaseItem::GetNearestVertex(const QPointF & pt)
{
    int id = -1;
    double minDistance = 999999.99;
    double eps = 2.0;
    for (int ii = 0; ii < m_pts.size(); ++ii)
    {
        QPointF ptII = m_pts[ii];
        QPointF p = ptII - pt;
        double dis = std::sqrt(p.x()*p.x() + p.y()*p.y());
        if (dis < eps && dis < minDistance)
        {
            minDistance = dis;
            id = ii;
        }
    }
    return id;
}


void GraphicsBaseItem::SetMousePt(const QPointF & mousePt)
{
    m_mousePt = mousePt;
}

void GraphicsBaseItem::Reset()
{
    m_pts.clear();
    m_drawFinished = false;
}

void GraphicsBaseItem::SetSelected(bool isSelected)
{
    m_selected = isSelected;
}

QRectF GraphicsBaseItem::boundingRect() const
{
	if (m_pts.size() > 1)
	{
		QPointF leftTop = m_pts[0];
		QPointF rightBottom = m_pts[0];
		for (int ii = 1; ii < m_pts.size(); ++ii)
		{
			QPointF pt = m_pts[ii];
			if (pt.x() < leftTop.x())
			{
				leftTop.setX(pt.x());
			}

			if (pt.y() < leftTop.y())
			{
				leftTop.setY(pt.y());
			}

			if (pt.x() > rightBottom.x())
			{
				rightBottom.setX(pt.x());
			}

			if (pt.y() > rightBottom.y())
			{
				rightBottom.setY(pt.y());
			}
		}
		return QRectF(leftTop, rightBottom);
	}
	//这行不写,m_graphicsScene.addItem(newItem)时,m_pts为空会直接崩掉
	return QRectF();
	
}

矩形
myrectangleitem.h

#ifndef MYRECTANGLEITEM_H
#define MYRECTANGLEITEM_H
#include "globaldefine.h"
#include"mygraphicsbaseitem.h"
class MyRectangleItem : public GraphicsBaseItem
{
public:
    MyRectangleItem(const QPolygonF& pts,bool drawFinished = false,GraphicsBaseItem *parent = Q_NULLPTR);


    virtual ShapeType GetShapeType();
    //绘图 在updata后调用 paint
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    virtual bool Contains(const QPointF& pt);
    virtual void FinishDrawing();//完成绘制并检查点的位置是否是左上点和右下点,如果不是就更新
};

#endif // MYRECTANGLEITEM_H

#include "myrectangleitem.h"
#include
#include 
namespace
{
    bool IsGreaterThan(double d1, double d2)//比较d1是否大于d2
    {
        double eps = 1.0e-6;
        double delta = d1 - d2;
        if (delta > eps)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    QRectF makeRect(const QPointF& pt1, const QPointF& pt2)
    {
        double topLeftX = IsGreaterThan(pt1.x(), pt2.x()) ? pt2.x() : pt1.x();
        double topLeftY = IsGreaterThan(pt1.y(), pt2.y()) ? pt2.y() : pt1.y();
        double width = qAbs(pt1.x() - pt2.x());
        double height = qAbs(pt1.y() - pt2.y());
        return QRectF(topLeftX, topLeftY, width, height);
    }
}


MyRectangleItem::MyRectangleItem(const QPolygonF& pts,bool drawFinished ,GraphicsBaseItem *parent )
    :GraphicsBaseItem(pts, drawFinished, parent)
{

}



ShapeType MyRectangleItem::GetShapeType()
{
    return RECTANGLE;
}

void MyRectangleItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
{
    if (m_pts.isEmpty())
    {
        return;
    }

    painter->setPen(Qt::blue);
    painter->setBrush(Qt::NoBrush);
    painter->setOpacity(1.0);

    if (m_drawFinished)
    {
        if (m_pts.size() == 2)
        {
            QRectF rect = makeRect(m_pts[0], m_pts[1]);
            painter->drawRect(rect);
            painter->setBrush(Qt::blue);
			for (int i = 0; i < m_pts.size(); i++)
			{
				painter->drawEllipse(m_pts.at(i), 0.5, 0.5);
			}
        }
    }
    else
    {
        QRectF rect = makeRect(m_pts[0], m_mousePt);//m_mousePt用于追踪鼠标绘制
        painter->drawRect(rect);
		qDebug() << "unfinish " << m_pts[0] << " , " << m_mousePt;
    }
}

bool MyRectangleItem::Contains(const QPointF & pt)
{
    if (m_pts.count() != 2)
    {
        return false;
    }

    QRectF rect(m_pts[0],m_pts[1]);
    bool ret = rect.contains(pt);
    return ret;
}

void MyRectangleItem::FinishDrawing()
{
	GraphicsBaseItem::FinishDrawing();

    if (m_pts.count() == 2)
    {
        QRectF rect = makeRect(m_pts[0], m_pts[1]);
        QPointF topLeft = rect.topLeft();
        QPointF bottomRight = rect.bottomRight();
        if (topLeft != m_pts[0] || bottomRight != m_pts[1])
        {
            QPolygonF pts;
            pts.push_back(topLeft);
            pts.push_back(bottomRight);
            m_pts.swap(pts);
        }
    }
}

多边形
mypolylineitem.h

#ifndef MYPOLYLINEITEM_H
#define MYPOLYLINEITEM_H
#include"mygraphicsbaseitem.h"

class MyPolylineItem : public GraphicsBaseItem
{
public:
    MyPolylineItem(const QPolygonF& pts,bool drawFinished = false,GraphicsBaseItem *parent = Q_NULLPTR);
    virtual ShapeType GetShapeType();
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    virtual QPainterPath shape() const override;
    virtual bool Contains(const QPointF& pt);
};

#endif // MYPOLYLINEITEM_H

#include "mypolylineitem.h"
#include
MyPolylineItem::MyPolylineItem(const QPolygonF& pts,bool drawFinished ,GraphicsBaseItem *parent )
  :GraphicsBaseItem(pts, drawFinished, parent)
{

}

ShapeType MyPolylineItem::GetShapeType()
{
    return POLYGON;
}

void MyPolylineItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
{
    if (m_pts.isEmpty())
    {
        return;
    }

    painter->setPen(Qt::blue);
    painter->setBrush(Qt::NoBrush);
    painter->setOpacity(1.0);

    if (m_drawFinished)
    {

        painter->drawPolygon(m_pts);
    }
    else
    {
        painter->drawPolyline(m_pts);
        painter->drawLine(m_pts.last(), m_mousePt);
    }
	for (int i = 0; i < m_pts.size(); i++)
	{
		painter->drawEllipse(m_pts.at(i), 2, 2);
	}
}

QPainterPath MyPolylineItem::shape() const
{
    QPainterPath path;

    if (m_drawFinished)
    {
        path.addPolygon(m_pts);
    }
    else
    {
        for (auto &pt : m_pts)
        {
            path.lineTo(pt);
        }
    }

    return path;
}

bool MyPolylineItem::Contains(const QPointF & pt)
{
    if (m_pts.count() < 3)
    {
        return false;
    }

    bool ret = m_pts.containsPoint(pt, Qt::OddEvenFill);
    return ret;
}


头文件 globaldefine.h

#ifndef GLOBALDEFINE_H
#define GLOBALDEFINE_H

enum ShapeType
{
    UNDEFINED = 0,
    CIRCLE,	            // 圆形
    ELLIPSE,	        // 椭圆形
    RECTANGLE,	        // 矩形
    POLYGON     //多边形
};

#endif // GLOBALDEFINE_H

你可能感兴趣的:(qt,C++,qt,ui,c++)