一、功能
1、任意平移、改变内圆、外圆大小
2、鼠标中键切换箭头方向
3、获取圆心坐标,获取大小半径
4、鼠标移动到圆的边缘上改变鼠标形状
二、效果图
三、实现原理
1、图元组成及对应接口
2个圆圈
painter->drawEllipse(rect1);
painter->drawEllipse(rect2);
2条直线
painter->drawLine(m_center.x() - m_radius, m_center.y(), m_center.x() - m_another_radius, m_center.y());
painter->drawLine(m_center.x() + m_radius, m_center.y(), m_center.x() + m_another_radius, m_center.y());
2个箭头(一个箭头由2条直线组成)
2、判断鼠标在圆上
diff = 鼠标点与圆心的距离 - 圆的半径
如果diff接近于0则认为鼠标在圆上
bool DyConcentricCircle::judgeInCircle(QPointF pos)
{
QLineF line(m_center, pos);
if(abs(line.length() - m_radius) < this->pen().widthF())
{
return true;
}
return false;
}
3、获取半径
并没有规定哪个半径为外半径,哪个半径为内半径,而是通过最大值、最小值确认的
外圆半径 = max(r1, r2)
内圆半径 = min(r1,r2)
double DyConcentricCircle::radiusInner() const
{
return qMin(m_radius, m_another_radius);
}
double DyConcentricCircle::radiusOuter() const
{
return qMax(m_radius, m_another_radius);
}
四、关键代码
.h
#ifndef DYCONCENTRICCIRCLE_H
#define DYCONCENTRICCIRCLE_H
#include "BaseGraphicsItem.h"
class DyConcentricCircle : public BaseGraphicsItem
{
Q_OBJECT
public:
DyConcentricCircle(QPointF center, qreal radius1, qreal radius2,
E_ItemType type = BaseGraphicsItem::E_ItemType::Dy_ConcentricCircle);
double radiusInner() const;
double radiusOuter() const;
public:
enum E_STATE_FLAG
{
DEFAULT_FLAG = 0,
MOV_RADIUS, /**< 第一个圆半径 */
MOV_ANOTHER_RADIUS, /**< 第二个圆半径 */
};
protected:
virtual QRectF boundingRect() const override;
virtual void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget) override;
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
private:
/**
* @brief judgeInCircle 是否在第一个圆上
* @param pos
* @return
*/
bool judgeInCircle(QPointF pos);
/**
* @brief judgeInAnotherArc 是否在第二个圆上
* @param pos
* @return
*/
bool judgeInAnotherCircle(QPointF pos);
protected:
qreal m_radius = 0; /**< 第一个半径 */
qreal m_another_radius = 0; /**< 第二个半径 */
E_STATE_FLAG m_stateFlag = DEFAULT_FLAG;
};
#endif // DYCONCENTRICCIRCLE_H
.cpp
#include "DyConcentricCircle.h"
#include <QMenu>
#include <QSpinBox>
#include <QWidgetAction>
#include <QDebug>
#include <QComboBox>
#include <QCheckBox>
#include <QtMath>
DyConcentricCircle::DyConcentricCircle(QPointF center, qreal radius1, qreal radius2, E_ItemType type)
: BaseGraphicsItem(center, type), m_radius(radius1), m_another_radius(radius2)
{
m_pointList.append(new DyPointItem(this, m_center, DyPointItem::Center));
m_pointList.setRandColor();
setAcceptHoverEvents(true);
this->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
}
double DyConcentricCircle::radiusInner() const
{
return qMin(m_radius, m_another_radius);
}
double DyConcentricCircle::radiusOuter() const
{
return qMax(m_radius, m_another_radius);
}
QRectF DyConcentricCircle::boundingRect() const
{
qreal maxRadius = qMax(m_radius, m_another_radius);
return QRectF(m_center.x() - maxRadius, m_center.y() - maxRadius, maxRadius * 2, maxRadius * 2);
}
void DyConcentricCircle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
QPen pen = this->pen();
double scaleFactor = painter->matrix().m11();
pen.setWidthF(pen.widthF() / scaleFactor + 1); /* 线段保持原来的线宽 */
painter->setPen(pen);
// QBrush brush(pen.color(), Qt::Dense1Pattern);
// painter->setBrush(brush);
painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
/* 画圆 */
QRectF rect1(m_center.x() - m_radius, m_center.y() - m_radius,
m_radius * 2, m_radius * 2);
QRectF rect2(m_center.x() - m_another_radius, m_center.y() - m_another_radius,
m_another_radius * 2, m_another_radius * 2);
painter->drawEllipse(rect1);
painter->drawEllipse(rect2);
/* 直线 */
pen.setStyle(Qt::SolidLine);
painter->setPen(pen);
painter->drawLine(m_center.x() - m_radius, m_center.y(), m_center.x() - m_another_radius, m_center.y());
painter->drawLine(m_center.x() + m_radius, m_center.y(), m_center.x() + m_another_radius, m_center.y());
/* 箭头 */
double arrowSize = 10;
double arrowAngle = M_PI / 6;
if(0 == m_direction)
{
qreal radiusIn = radiusInner();
/* 左箭头 */
QPointF leftP(m_center.x() - radiusIn, m_center.y());
QPointF leftArrowP1;
leftArrowP1.setX(m_center.x() - radiusIn - arrowSize * cos(arrowAngle));
leftArrowP1.setY(m_center.y() - arrowSize * sin(arrowAngle));
QPointF leftArrowP2;
leftArrowP2.setX(m_center.x() - radiusIn - arrowSize * cos(arrowAngle));
leftArrowP2.setY(m_center.y() + arrowSize * sin(arrowAngle));
painter->drawLine(leftArrowP1, leftP);
painter->drawLine(leftArrowP2, leftP);
/* 右箭头 */
QPointF rightP(m_center.x() + radiusIn, m_center.y());
QPointF rightArrowP1;
rightArrowP1.setX(m_center.x() + radiusIn + arrowSize * cos(arrowAngle));
rightArrowP1.setY(m_center.y() - arrowSize * sin(arrowAngle));
QPointF rightArrowP2;
rightArrowP2.setX(m_center.x() + radiusIn + arrowSize * cos(arrowAngle));
rightArrowP2.setY(m_center.y() + arrowSize * sin(arrowAngle));
painter->drawLine(rightArrowP1, rightP);
painter->drawLine(rightArrowP2, rightP);
}
else
{
qreal radiusOut = radiusOuter();
/* 左箭头 */
QPointF leftP(m_center.x() - radiusOut, m_center.y());
QPointF leftArrowP1;
leftArrowP1.setX(m_center.x() - radiusOut + arrowSize * cos(arrowAngle));
leftArrowP1.setY(m_center.y() - arrowSize * sin(arrowAngle));
QPointF leftArrowP2;
leftArrowP2.setX(m_center.x() - radiusOut + arrowSize * cos(arrowAngle));
leftArrowP2.setY(m_center.y() + arrowSize * sin(arrowAngle));
painter->drawLine(leftArrowP1, leftP);
painter->drawLine(leftArrowP2, leftP);
/* 右箭头 */
QPointF rightP(m_center.x() + radiusOut, m_center.y());
QPointF rightArrowP1;
rightArrowP1.setX(m_center.x() + radiusOut - arrowSize * cos(arrowAngle));
rightArrowP1.setY(m_center.y() - arrowSize * sin(arrowAngle));
QPointF rightArrowP2;
rightArrowP2.setX(m_center.x() + radiusOut - arrowSize * cos(arrowAngle));
rightArrowP2.setY(m_center.y() + arrowSize * sin(arrowAngle));
painter->drawLine(rightArrowP1, rightP);
painter->drawLine(rightArrowP2, rightP);
}
}
void DyConcentricCircle::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
if(judgeInCircle(event->pos()))
{
setCursor(Qt::CrossCursor);
}
else if (judgeInAnotherCircle(event->pos()))
{
setCursor(Qt::CrossCursor);
}
else
{
setCursor(Qt::ArrowCursor);
}
event->accept();
}
void DyConcentricCircle::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
if(judgeInCircle(event->pos()))
{
setCursor(Qt::CrossCursor);
}
else if (judgeInAnotherCircle(event->pos()))
{
setCursor(Qt::CrossCursor);
}
else
{
setCursor(Qt::ArrowCursor);
}
event->accept();
}
void DyConcentricCircle::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
setCursor(Qt::ArrowCursor);
event->accept();
}
void DyConcentricCircle::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
if(judgeInCircle(event->pos()))
{
m_stateFlag = MOV_RADIUS;
}
else if(judgeInAnotherCircle(event->pos()))
{
m_stateFlag = MOV_ANOTHER_RADIUS;
}
}
else if(event->button() == Qt::RightButton)
{
}
else if(event->button() == Qt::MiddleButton)
{
/* 中键切换方向 */
if(0 == m_direction)
{
m_direction = 1;
}
else
{
m_direction = 0;
}
emit stateChanged(this);
}
QGraphicsItem::mousePressEvent(event);
}
void DyConcentricCircle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
switch (static_cast<int>(m_stateFlag))
{
case MOV_RADIUS:
{
/* 必须为pos,非scenePos*/
QLineF line(m_center, event->pos());
m_radius = line.length();
break;
}
case MOV_ANOTHER_RADIUS:
{
QLineF line(m_center, event->pos());
m_another_radius = line.length();
break;
}
default:
{
QGraphicsItem::mouseMoveEvent(event);
break;
}
}
}
void DyConcentricCircle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
m_stateFlag = DEFAULT_FLAG;
if(event->button() == Qt::LeftButton)
{
emit stateChanged(this);
}
QGraphicsItem::mouseReleaseEvent(event);
}
void DyConcentricCircle::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
emit selectCompleted(this);
}
QGraphicsItem::mouseDoubleClickEvent(event);
}
bool DyConcentricCircle::judgeInCircle(QPointF pos)
{
QLineF line(m_center, pos);
if(abs(line.length() - m_radius) < this->pen().widthF())
{
return true;
}
return false;
}
bool DyConcentricCircle::judgeInAnotherCircle(QPointF pos)
{
QLineF line(m_center, pos);
if(abs(line.length() - m_another_radius) < this->pen().widthF())
{
return true;
}
return false;
}