【QT Graphics/View】自定义动态同心圆DyConcentricCircle

本文介绍了如何使用Qt库实现在图形界面中绘制两个同心圆,并允许用户通过鼠标平移、缩放圆的大小,以及通过鼠标形状变化判断位置。代码展示了圆心坐标获取、半径操作以及鼠标事件处理的详细过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、功能

1、任意平移、改变内圆、外圆大小

2、鼠标中键切换箭头方向

3、获取圆心坐标,获取大小半径

4、鼠标移动到圆的边缘上改变鼠标形状

 

二、效果图

37858d5c350455735bf0e29b582a1821.png

 a4fcf71e5e8168d48991f5f01bc4749f.png

 

三、实现原理

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;
}

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jason~shen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值