Qt基于QGraphicsView 实现四个教具之一直尺(一)

前言

Graphics View提供了一个surface,用于管理大量定制的2D图形项并与之交互,还提供了一个View小部件,用于可视化项目,并支持缩放和旋转.

该框架包含一个事件传播框架,该架构允许对场景中的项目提供精确的双精度交互功能.项目可以处理按键事件,鼠标按下,移动,释放和双击事件,还可以跟踪鼠标移动。

Graphics View使用BSP(二进制空间划分)树来提供非常快速的项目发现,因此,它可以实时地可视化大型场景,甚至是数百万个项目.

如何实现教具直尺

  • 需要先设计所有教具的基类
  • 继承基类设计直尺教具

(AbsTeachingTool.h)基类设计

#ifndef ABSTEAHINGTOOL_H
#define ABSTEAHINGTOOL_H

#include <QObject>
#include <QApplication>
#include <QColor>
#include <QRectF>
#include <functional>

class QGraphicsSvgItem;
class QGraphicsView;
class QGraphicsScene;

// 传入当前的场景返回视图
typedef std::function<QGraphicsView*(QGraphicsScene*)> CurrentViewFunc;

class AbsTeachingTool : public QObject
{
    Q_OBJECT
    Q_ENUMS(TEAHINGTOOLSTATE)
public:

    enum TEAHINGTOOLSTATE {
        TEAHINGTOOL_STATE_NONE,             // 教具状态(无状态)
        TEAHINGTOOL_STATE_MOVE,             // 教具状态(移动)
        TEAHINGTOOL_STATE_RESIZE_ONE,       // 教具状态(改变大小-1)
        TEAHINGTOOL_STATE_RESIZE_TWO,       // 教具状态(改变大小-2)
        TEAHINGTOOL_STATE_RESIZE_THREE,     // 教具状态(改变大小-3)
        TEANINGTOOL_STATE_RESIZE_FOUR,      // 教具状态(改变大小-4)
        TEAHINGTOOL_STATE_ROTATE,           // 教具状态(旋转)
        TEAHINGTOOL_STATE_CLOSE,            // 教具状态(关闭)
        TEAHINGTOOL_STATE_REST,             // 教具状态(复位)
        TEAHINGTOOL_STATE_PEN,              // 教具状态(画图状态,画线)
        TEAHINGTOOL_STATE_DORMANCY          // 启动该状态为不响应任何鼠标事件
    };

    AbsTeachingTool() {
        m_yState = TEAHINGTOOL_STATE_NONE;
        SetRect(QRectF(0,0,0,0));
        SetMinRect(QRectF(0,0,0,0));
        SetMaxRect(QRectF(0,0,0,0));
        PenWidthF = 1;
        CurrentView = nullptr;
    }

    ~AbsTeachingTool() {}

    inline QString CurrentPath( void ) const {
        return QCoreApplication::applicationDirPath();
    }

    inline void SetRect(QRectF rect) {
        m_yRectF = rect;
    }

    inline QRectF Rect( void ) const {
        return m_yRectF;
    }

    inline void SetMinRect(QRectF rect) {
        m_yMinRectF = rect;
    }

    inline QRectF MinRect( void ) const {
        return m_yMinRectF;
    }

    inline void SetMaxRect(QRectF rect) {
        m_yMaxRectF = rect;
    }

    inline QRectF MaxRect( void ) const {
        return m_yMaxRectF;
    }

    inline void SetPressPos(QPointF pos) {
        m_yPressPos = pos;
    }

    inline QPointF PressPos( void ) const {
        return m_yPressPos;
    }

    inline void SetOriginPos(QPointF pos) {
        m_yOriginPos = pos;
    }

    inline QPointF OriginPos( void ) const {
        return m_yOriginPos;
    }

    inline void SetPenState(qreal penwidth) {
        PenWidthF = penwidth;
        m_yState = TEAHINGTOOL_STATE_PEN;
    }

    inline void SetPenState(int penwidth) {
        PenWidthF = static_cast<qreal>(penwidth);
        m_yState = TEAHINGTOOL_STATE_PEN;
    }

    inline void SetState( TEAHINGTOOLSTATE state ) {
        m_yState = state;
    }

    inline TEAHINGTOOLSTATE GetState( void ) const {
        return m_yState;
    }

    inline void SetCurrentView( CurrentViewFunc func )
    {
        CurrentView = func;
    }

    inline void SetPenWidth(qreal penwidth) {
        PenWidthF = penwidth;
    }

    inline qreal PenWidth( void ) const {
        return PenWidthF;
    }

protected:
    virtual void InitProperty( void ) = 0;
    virtual void UpdateResizeCursor( void ) = 0;
    virtual void SetCursor(QCursor cursor) = 0;

    CurrentViewFunc CurrentView;

signals:
    // 控件关闭信号
    void Closeed();
    // 鼠标移入信号
    void HoverEntered();
    // 鼠标移出信号
    void HoverLeaveed();
    // 画线起始点
    void LineStartPos(QPointF);
    // 画线更新结束点
    void LineUpdatePos(QPointF);
    // 退出画线
    void LineEnd();
    // 画圆起始点
    void ArcStartPos(QPointF);
    // 画圆更新结束点
    void ArcUpdatePos(QPointF);
    // 退出画圆
    void ArcEndPos();
    /**
     * @brief ArcStart 开始画弧
     * @param QRectF
     *
     *      QRectF(const QRect &rectangle)
     *      QRectF(qreal x, qreal y, qreal width, qreal height)
     *      QRectF(const QPointF &topLeft, const QPointF &bottomRight)
     *      QRectF(const QPointF &topLeft, const QSizeF &size)
     *
     *      本接口与drawArc一样
     * @param startAngle
     *        范围: 0 --- 360
     */
    void ArcStart(QRectF, qreal startAngle);

    /**
     * @brief ArcUpdate 更新圆弧的角度
     * @param updateAngle
     *        范围: -720 --- 720
     *        startAngle+updateAngle 为最终旋转后的角度
     */
    void ArcUpdate(qreal updateAngle);
    /**
     * @brief ArcEnd 退出画圆弧
     */
    void ArcEnd();

private:
    TEAHINGTOOLSTATE m_yState;

    QRectF m_yRectF;
    QRectF m_yMinRectF;
    QRectF m_yMaxRectF;

    QPointF m_yPressPos;
    QPointF m_yOriginPos;   // 原点坐标

    qreal PenWidthF;




};

#endif // ABSTEAHINGTOOL_H

直尺类设计 (TeachingToolRuler.h)

#ifndef TEAHINGTOOLRULER_H
#define TEAHINGTOOLRULER_H

#include "AbsTeachingTool.h"
#include <QGraphicsItem>

class TeachingToolRuler : public AbsTeachingTool, public QGraphicsItem
{
    Q_OBJECT
    Q_INTERFACES(QGraphicsItem)
public:
    TeachingToolRuler();
    ~TeachingToolRuler();

    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget);
    QPainterPath shape() const;

    void SetRulerBorderRadius(int radius);
    void SetRulerBackgroundColor(QColor color);
    void SetRulerBorderColor(QColor color);
    void SetRulerScaleTextColor(QColor color);
    void SetRulerScaleColor(QColor color);
    void SetRulerRotateColor(QColor color);

    void SetCloseButtonSvg(QString svg);
    void SetResizeButtonSvg(QString svg);
    void SetRotateButtonSvg(QString svg);

private:
	// 传入画板的画线状态
    enum DRAWLINE {
        TOP,
        BOTTOM
    };

    void InitProperty();

    void UpdateResizeCursor();

    void SetCursor(QCursor cursor);

    QRectF CloseButtonRect() const;
    QRectF ResizeButtonRect() const;
    QRectF RotateButtonRect() const;
	
	// 绘制本体
    void PaintBody(QPainter *painter);
    // 绘制按钮
    void PaintButton( void );
    // 刻度绘制
    void PaintScale(QPainter *painter);
    // 绘制旋转状态中的弧线
    void PaintRotate(QPainter *painter);

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
    void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
    void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
    void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);

private:
    // svg文件
    QString CloseButtonSvg;
    QString ResizeButtonSvg;
    QString RotateButtonSvg;

    QGraphicsSvgItem *m_pCloseButton = nullptr;           // 关闭按钮
    QGraphicsSvgItem *m_pResizeButton = nullptr;          // 改变大小按钮
    QGraphicsSvgItem *m_pRotateButton = nullptr;          // 旋转按钮

    // 颜色样式
    int RulerBorderRadius;
    QColor RulerBackgroundColor;
    QColor RulerBorderColor;
    QColor RulerScaleTextColor;
    QColor RulerScaleColor;
    QColor RulerRotateColor;

    QCursor RotateCursor;           // 旋转时鼠标显示的图标
    QCursor DrawLineRulerCursor;    // 与白板交互时绘制功能,鼠标显示图标
    QCursor ResizeCursor;           // 改变大小显示的图标
    QCursor MoveCursor;             // 移动时显示的图标
    QCursor CloseCursor;            // 关闭鼠标的图标


    DRAWLINE DrawLineState;

};

#endif // TEAHINGTOOLRULER_H

直尺类实现 (TeachingToolRuler.cpp)

#include "TeachingToolRuler.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsSvgItem>
#include <QPainter>
#include <QGraphicsSceneHoverEvent>
#include <QCursor>
#include <QDebug>

#include "CatMath.h"

TeachingToolRuler::TeachingToolRuler()
{
    InitProperty();
}

TeachingToolRuler::~TeachingToolRuler()
{

}

QRectF TeachingToolRuler::boundingRect() const
{
    return Rect();
}

void TeachingToolRuler::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option)
    Q_UNUSED(widget)
    // 打开抗锯齿
    painter->setRenderHint(QPainter::Antialiasing, true);
    // 平滑的像素图转换算法
    painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
    // 指示引擎应尽可能消除文本的别名
    painter->setRenderHint(QPainter::TextAntialiasing, true);
    painter->save();
    // [0] 绘制直尺本体
    PaintBody(painter);
    // [1] 按钮绘制
    PaintButton();
    // [3] 刻度绘制
    PaintScale(painter);
    // [4] 旋转状态绘制
    if(GetState() == TEAHINGTOOL_STATE_ROTATE)
    {
        PaintRotate(painter);
    }

    painter->restore();
}

// 以局部坐标的QPainterPath返回该项的形状。这个形状被用于很多方面,包括碰撞检测,碰撞测试,以及QGraphicsScene::items()函数。
QPainterPath TeachingToolRuler::shape() const
{
    QPainterPath path;
    path.addRoundedRect(Rect(), RulerBorderRadius, RulerBorderRadius);
    return path;
}

void TeachingToolRuler::SetRulerBorderRadius(int radius)
{
    RulerBorderRadius = radius;
}

void TeachingToolRuler::SetRulerBackgroundColor(QColor color)
{
    RulerBackgroundColor = color;
}

void TeachingToolRuler::SetRulerBorderColor(QColor color)
{
    RulerBorderColor = color;
}

void TeachingToolRuler::SetRulerScaleTextColor(QColor color)
{
    RulerScaleTextColor = color;
}

void TeachingToolRuler::SetRulerScaleColor(QColor color)
{
    RulerScaleColor = color;
}

void TeachingToolRuler::SetRulerRotateColor(QColor color)
{
    RulerRotateColor = color;
}

void TeachingToolRuler::SetCloseButtonSvg(QString svg)
{
    if(CloseButtonSvg != svg)
    {
        if(m_pCloseButton != nullptr)
        {
            m_pCloseButton->deleteLater();
            m_pCloseButton = nullptr;
        }
        CloseButtonSvg = svg;
        m_pCloseButton = new QGraphicsSvgItem(svg, this);
        prepareGeometryChange();
    }
}

void TeachingToolRuler::SetResizeButtonSvg(QString svg)
{
    if(ResizeButtonSvg != svg)
    {
        if(m_pResizeButton != nullptr)
        {
            m_pResizeButton->deleteLater();
            m_pResizeButton = nullptr;
        }
        ResizeButtonSvg = svg;
        m_pResizeButton = new QGraphicsSvgItem(svg, this);
        prepareGeometryChange();
    }
}

void TeachingToolRuler::SetRotateButtonSvg(QString svg)
{
    if(RotateButtonSvg != svg)
    {
        if(m_pRotateButton != nullptr)
        {
            m_pRotateButton->deleteLater();
            m_pRotateButton = nullptr;
        }
        RotateButtonSvg = svg;
        m_pRotateButton = new QGraphicsSvgItem(svg, this);
        prepareGeometryChange();
    }
}

void TeachingToolRuler::InitProperty()
{
	// 初始化参数
    RotateCursor = QCursor(QPixmap(":/TeachingTool/png/rotate.png"), 16, 16);
    DrawLineRulerCursor = QCursor(QPixmap(":/TeachingTool/png/drawRulerLine.png"), 5, 16);
    ResizeCursor = QCursor(QPixmap(":/TeachingTool/png/resize.png"), 16, 16);
    MoveCursor = QCursor(Qt::SizeAllCursor);
    CloseCursor = QCursor(Qt::ArrowCursor);

    SetRect(QRectF(0, 0, CatMath::CmtoPx(21), 96));
    SetMinRect(QRectF(0, 0, CatMath::CmtoPx(5), 96));
    SetMaxRect(QRectF(0, 0, CatMath::CmtoPx(1010), 96));

    SetRulerBorderRadius(6);
    SetRulerBackgroundColor(QColor(255, 255, 255, 204));
    SetRulerBorderColor(QColor(102, 102, 102));
    SetRulerScaleTextColor(RulerBorderColor);
    SetRulerScaleColor(RulerScaleTextColor);
    SetRulerRotateColor(RulerScaleColor);
    //qDebug() << CurrentPath();

    setAcceptHoverEvents(true);

    SetResizeButtonSvg(":/TeachingTool/svg/resizeRuler.svg");
    m_pResizeButton->setVisible(false);
    SetCloseButtonSvg(":/TeachingTool/svg/closeTool.svg");
    m_pCloseButton->setVisible(false);
    SetRotateButtonSvg(":/TeachingTool/svg/rotateTool.svg");
    m_pRotateButton->setVisible(false);
	
	// 设置直尺原点坐标,鼠标选择后旋转的坐标
    SetOriginPos(QPointF(10,0));
    setTransformOriginPoint(OriginPos());
    UpdateResizeCursor();
}

void TeachingToolRuler::UpdateResizeCursor()
{
    QPixmap pix(":/TeachingTool/png/resize.png");
    QTransform tr;
    // 设置改变直尺大小鼠标图标的旋转角度
    tr.rotate(this->rotation());
    ResizeCursor  = QCursor(pix.transformed(tr, Qt::SmoothTransformation), pix.width() / 2,  pix.height() / 2);
}

// 这里是一个关键, 鼠标样式可以影响范围,如果我们直接setCursor是改变了整个软件的鼠标样式,如果我们获取当前视图下的setCursor则鼠标样式改变的影响只在当前的view视图中。
void TeachingToolRuler::SetCursor(QCursor cursor)
{
    if(CurrentView != nullptr)
    {
        QGraphicsView *view = CurrentView(this->scene());
        view->setCursor(cursor);
    } else {
        setCursor(cursor);
    }

}

QRectF TeachingToolRuler::CloseButtonRect() const
{
    QPixmap pixmap(CloseButtonSvg);
    QSizeF size(pixmap.rect().width(),
                pixmap.rect().height());
    QPointF topleft(size.width()/2,
                    (Rect().height() - size.height())/2);
    return QRectF(topleft, size);
}

QRectF TeachingToolRuler::ResizeButtonRect() const
{
    QPixmap pixmap(ResizeButtonSvg);
    QSizeF size(pixmap.rect().width(),
                pixmap.rect().height());
    QPointF topleft(Rect().width() - size.width(),
                    0);
    return QRectF(topleft, size);
}

QRectF TeachingToolRuler::RotateButtonRect() const
{
    QPixmap pixmap(RotateButtonSvg);
    QSizeF size(pixmap.rect().width(),
                pixmap.rect().height());
    QPointF topleft(ResizeButtonRect().x() - size.width() - size.width()/2,
                    (Rect().height() - size.height())/2);
    return QRectF(topleft, size);
}

void TeachingToolRuler::PaintBody(QPainter *painter)
{
    QPen pen = painter->pen();
    pen.setColor(RulerBorderColor);
    painter->setPen(pen);
    painter->setBrush(QBrush(RulerBackgroundColor));
    painter->drawRoundedRect(QRectF(QPointF(0,0), Rect().size()), RulerBorderRadius, RulerBorderRadius);
}

void TeachingToolRuler::PaintButton()
{
    // [1] resize按钮
    m_pResizeButton->setPos(ResizeButtonRect().x(), ResizeButtonRect().y());

    // [2] close按钮
    m_pCloseButton->setPos(CloseButtonRect().x(), CloseButtonRect().y());

    // [3] rotate按钮
    m_pRotateButton->setPos(RotateButtonRect().x(), RotateButtonRect().y());
}

void TeachingToolRuler::PaintScale(QPainter *painter)
{
    QFont font = painter->font();
    font.setPixelSize(14);
    QPen pen = painter->pen();
    pen.setColor(RulerScaleColor);
    painter->setPen(pen);
    painter->setFont(font);
    int scalevalue = 0;
    int value = 0;
    qreal scalelocation = 10;
    qreal longscaleheight = CatMath::CmtoPx(1)/2;
    qreal scaleheight = longscaleheight*(2.0/3.0);
    qreal shortscaleheight = scaleheight/2;

    QFontMetrics fm(font);
    while(scalelocation < Rect().width() - 10)
    {
        if(scalevalue == 0)
        {
            int pixelsWide = fm.horizontalAdvance(QString::number(value));
            painter->drawLine(QLineF(scalelocation, 0, scalelocation, longscaleheight));

            // 绘制刻度文字
            painter->drawText(QPointF(scalelocation - pixelsWide/2 , longscaleheight + 14 + 1),
                              QString::number(value));

            painter->drawLine(QLineF(scalelocation, Rect().height(),
                                     scalelocation, Rect().height() - longscaleheight));

            // 绘制刻度文字
            painter->drawText(QPointF(scalelocation - pixelsWide/2 ,
                                      Rect().height() - longscaleheight - 5),
                              QString::number(value));

        } else if(scalevalue == 5)
        {
            painter->drawLine(QLineF(scalelocation, 0, scalelocation, scaleheight));

            painter->drawLine(QLineF(scalelocation, Rect().height(), scalelocation,
                                     Rect().height() - scaleheight));
        } else {
            painter->drawLine(QLineF(scalelocation, 0, scalelocation, shortscaleheight));

            painter->drawLine(QLineF(scalelocation, Rect().height(), scalelocation,
                                     Rect().height() - shortscaleheight));
        }

        scalevalue += 1;
        if(scalevalue == 10)
        {
            value++;
            scalevalue = 0;
        }

        scalelocation += CatMath::CmtoPx(0.1);
    }
}

void TeachingToolRuler::PaintRotate(QPainter *painter)
{
    QPen pen = painter->pen();
    pen.setColor(RulerRotateColor);
    painter->setPen(pen);
    qreal longscaleheight = CatMath::CmtoPx(1);
    painter->drawArc(QRectF(-(longscaleheight/2) + 10, -(longscaleheight/2), longscaleheight, longscaleheight), 0 * 16, -90 * 16);
}

void TeachingToolRuler::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
	// 如果鼠标是左键按下,并且教具处于活动状态下
    if(event->button() == Qt::LeftButton && GetState() != TEAHINGTOOL_STATE_DORMANCY)
    {
    	// 如果教具不处于画线状态下
        if(GetState() != TEAHINGTOOL_STATE_PEN)
        {
        	
            if(ResizeButtonRect().contains(event->pos()))
            {
            	// 如果是改变大小按钮按下
                SetState(TEAHINGTOOL_STATE_RESIZE_ONE);
                m_pRotateButton->setVisible(false);
                m_pCloseButton->setVisible(false);
                SetPressPos(event->pos());
            } else if(RotateButtonRect().contains(event->pos()))
            {
            	// 如果旋转按钮被按下
                SetState(TEAHINGTOOL_STATE_ROTATE);
                m_pResizeButton->setVisible(false);
                m_pCloseButton->setVisible(false);
                SetPressPos(event->pos());
            } else if(CloseButtonRect().contains(event->pos()))
            {
            	// 如果关闭按钮被按下
                SetState(TEAHINGTOOL_STATE_CLOSE);
                m_pResizeButton->setVisible(false);
                m_pRotateButton->setVisible(false);
            } else if(boundingRect().contains(event->pos()))
            {
            	// 如果本体被按下
                SetState(TEAHINGTOOL_STATE_MOVE);
                m_pCloseButton->setVisible(false);
                m_pResizeButton->setVisible(false);
                m_pRotateButton->setVisible(false);
                SetPressPos(event->scenePos());
            }
        } else {
            // 先获取scentPos,当前在画线状态下
            QPointF pos = event->pos();
            QPointF sceneTopPenPos = CatMath::PointCoordinatesOnACircle(this->scenePos(), PenWidth()/2, this->rotation() + 270);
            QPointF sceneBottompos = CatMath::PointCoordinatesOnACircle(this->scenePos(), Rect().height() + PenWidth()/2, this->rotation() + 90);
            QPointF scentTopMinPos = CatMath::PointCoordinatesOnACircle(sceneTopPenPos, OriginPos().x(), this->rotation());
            QPointF scentTopMaxPos = CatMath::PointCoordinatesOnACircle(sceneTopPenPos, Rect().width() - 10, this->rotation());
            QPointF scentBottomMinPos = CatMath::PointCoordinatesOnACircle(sceneBottompos, OriginPos().x(), this->rotation());
            QPointF scentBottomMaxPos = CatMath::PointCoordinatesOnACircle(sceneBottompos, Rect().width() - 10, this->rotation());

            QPointF StartPos;
            if(pos.ry() > Rect().height()/2.0)
            {
                DrawLineState = BOTTOM;
            } else {
                DrawLineState = TOP;
            }

            switch (DrawLineState) {
                case BOTTOM:{
                    //qDebug() << "BOTTOM";
                    if(pos.rx() < 10.0)
                    {
                        StartPos = scentBottomMinPos;
                    } else if(pos.rx() > Rect().width() - 10.0)
                    {
                        StartPos = scentBottomMaxPos;
                    } else {
                        QPointF scentMousePos = CatMath::PointCoordinatesOnACircle(sceneBottompos,
                                                                                   pos.x(), this->rotation());
                        StartPos = scentMousePos;
                    }
                    break;
                }
                case TOP:{
                    //qDebug() << "TOP";
                    if(pos.rx() < 10.0)
                    {
                        StartPos = scentTopMinPos;
                    } else if(pos.rx() > Rect().width() - 10.0)
                    {
                        StartPos = scentTopMaxPos;
                    } else {
                        QPointF scentMousePos = CatMath::PointCoordinatesOnACircle(sceneTopPenPos,
                                                                                   pos.x(), this->rotation());
                        StartPos = scentMousePos;
                    }
                    break;
                }
            }
            emit LineStartPos(StartPos);
        }
    }
}

void TeachingToolRuler::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if(GetState() != TEAHINGTOOL_STATE_DORMANCY)
    {
        switch(GetState())
        {
            case TEAHINGTOOL_STATE_MOVE:{
                QPointF pos = event->scenePos();
                QPointF move = pos - PressPos();
                this->moveBy(move.rx(), move.ry());
                SetPressPos(pos);
                break;
            }
            case TEAHINGTOOL_STATE_RESIZE_ONE:{
                QPointF pos = event->pos();
                QPointF size = pos - PressPos();
                qreal width = Rect().width() + size.rx();
                if(width < MinRect().width())
                {
                     SetRect(MinRect());
                } else if(width >MaxRect().width())
                {
                    SetRect(MaxRect());
                } else {
                    SetRect(QRectF(Rect().x(), Rect().y(), width, Rect().height()));
                }
                SetPressPos(pos);
                break;
            }
            case TEAHINGTOOL_STATE_ROTATE:{
                QPointF pos = event->pos();
                QVector2D Start = QVector2D(PressPos() - QPointF(10.0, 0.0));
                QVector2D End = QVector2D(pos - QPointF(10.0, 0.0));

                qreal angle = 0.0;
                qreal angleEnd = CatMath::DegreeAngle(End);
                qreal angleStart = CatMath::DegreeAngle(Start);
                angle = angleEnd - angleStart + rotation();

                angle = CatMath::AngleJudge(angle);

                setRotation(angle);
                UpdateResizeCursor();
                break;
            }
            case TEAHINGTOOL_STATE_PEN:{
                QPointF pos = event->pos();
                QPointF sceneTopPenPos = CatMath::PointCoordinatesOnACircle(this->scenePos(), PenWidth()/2, this->rotation() + 270);
                QPointF sceneBottompos = CatMath::PointCoordinatesOnACircle(this->scenePos(), Rect().height() + PenWidth()/2, this->rotation() + 90);
                QPointF scentTopMinPos = CatMath::PointCoordinatesOnACircle(sceneTopPenPos, OriginPos().x(), this->rotation());
                QPointF scentTopMaxPos = CatMath::PointCoordinatesOnACircle(sceneTopPenPos, Rect().width() - 10, this->rotation());
                QPointF scentBottomMinPos = CatMath::PointCoordinatesOnACircle(sceneBottompos, OriginPos().x(), this->rotation());
                QPointF scentBottomMaxPos = CatMath::PointCoordinatesOnACircle(sceneBottompos, Rect().width() - 10, this->rotation());

                QPointF UpdatePos;

                switch (DrawLineState) {
                    case BOTTOM:{
                        if(pos.rx() < 10.0)
                        {
                            UpdatePos = scentBottomMinPos;
                        } else if(pos.rx() > Rect().width() - 10.0)
                        {
                            UpdatePos = scentBottomMaxPos;
                        } else {
                            QPointF scentMousePos = CatMath::PointCoordinatesOnACircle(sceneBottompos,
                                                                                       pos.x(), this->rotation());
                            UpdatePos = scentMousePos;
                        }
                        break;
                    }
                    case TOP:{
                        if(pos.rx() < 10.0)
                        {
                            UpdatePos = scentTopMinPos;
                        } else if(pos.rx() > Rect().width() - 10.0)
                        {
                            UpdatePos = scentTopMaxPos;
                        } else {
                            QPointF scentMousePos = CatMath::PointCoordinatesOnACircle(sceneTopPenPos,
                                                                                       pos.x(), this->rotation());
                            UpdatePos = scentMousePos;
                        }
                        break;
                    }

                }
                emit LineUpdatePos(UpdatePos);

                break;
            }
            default: {
                break;
            }
        }

        prepareGeometryChange();
    }
}

void TeachingToolRuler::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event)
    if(GetState() != TEAHINGTOOL_STATE_DORMANCY)
    {
        if(GetState() != TEAHINGTOOL_STATE_PEN)
        {
            if(GetState() == TEAHINGTOOL_STATE_CLOSE)
            {
                SetState(TEAHINGTOOL_STATE_NONE);
                emit Closeed();
                this->deleteLater();
            } else {
                SetState(TEAHINGTOOL_STATE_NONE);
                m_pResizeButton->setVisible(true);
                m_pCloseButton->setVisible(true);
                m_pRotateButton->setVisible(true);
                prepareGeometryChange();
            }
        } else {
            emit LineEnd();
        }
    }
}

void TeachingToolRuler::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
    Q_UNUSED(event)
    if(GetState() != TEAHINGTOOL_STATE_DORMANCY)
    {
        if(GetState() != TEAHINGTOOL_STATE_PEN)
        {
            if(ResizeButtonRect().contains(event->pos()))
            {
                SetCursor(ResizeCursor);
            } else if(RotateButtonRect().contains(event->pos()))
            {
                SetCursor(RotateCursor);
            } else if(CloseButtonRect().contains(event->pos()))
            {
                SetCursor(CloseCursor);
            } else {
                SetCursor(MoveCursor);
            }
        } else {
            SetCursor(DrawLineRulerCursor);
        }
    }
}

void TeachingToolRuler::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    Q_UNUSED(event)
    if(GetState() != TEAHINGTOOL_STATE_DORMANCY)
    {
        emit HoverEntered();
        if(GetState() != TEAHINGTOOL_STATE_PEN)
        {
            SetCursor(MoveCursor);
            m_pResizeButton->setVisible(true);
            m_pCloseButton->setVisible(true);
            m_pRotateButton->setVisible(true);
        } else {
            SetCursor(DrawLineRulerCursor);
        }
    }
}

void TeachingToolRuler::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    Q_UNUSED(event)

    if(GetState() != TEAHINGTOOL_STATE_DORMANCY)
    {
        emit HoverLeaveed();
        if(GetState() != TEAHINGTOOL_STATE_PEN)
        {
            SetState(TEAHINGTOOL_STATE_NONE);
            m_pResizeButton->setVisible(false);
            m_pCloseButton->setVisible(false);
            m_pRotateButton->setVisible(false);
        }
        SetCursor(Qt::ArrowCursor);
    }
}

需要用到数学函数 (CatMath.h)

CatMath.h

#ifndef CATMATH_H
#define CATMATH_H

#include <QtGlobal>
#include <QVector2D>

namespace CatMath {
    qreal CmtoPx(qreal cm);
    qreal PxtoCm(qreal px);
    qreal DegreeAngle(QVector2D vector2d);

    // 已知圆心,半径,角度,求圆上的点坐标
    QPointF PointCoordinatesOnACircle(QPointF centerpoint, qreal r, qreal angle);

    // 旋转角度转换,将大于360,与小于0的角度进行极值判断
    qreal AngleJudge(qreal angle);

    // 勾股定理
    qreal PythagoreanTheorem(qreal a, qreal b);

    // 计算 靠近A点 角度方法
    /*
                B____C
                |  /
                |_/
                |/
                A
    */
    // 根据tan算出角度
    qreal TanToAngle(qreal bc, qreal ab);
    // 根据cos算出角度
    qreal CosToAngle(qreal ab, qreal ac);
    // 根据sin算出角度
    qreal SinToAngle(qreal bc, qreal ac);

    QPointF PosMove(QPointF last, QPointF pos);
}



#endif // CATMATH_H

CatMath.cpp

#include "CatMath.h"
#include <QApplication>
#include <QScreen>
#include <QtMath>
#include <math.h>
#include <QDebug>

// 求弧度 180度/π是1弧度对应多少度!
const qreal AnglePerPI = 180.0 / M_PI;

qreal GetDpi()
{
   //QGuiApplication::highDpiScaleFactorRoundingPolicy();
   return QApplication::primaryScreen()->logicalDotsPerInch();
}

qreal CatMath::CmtoPx(qreal cm)
{
    //获取主屏幕分辨率
    QScreen *screen = QApplication::primaryScreen();
    // 1 英寸的像素
    //qreal inchToPx = qreal(1)/GetDpi();
    qreal inchToPx = qreal(1.0)/130.0;
    // 屏幕宽度
    qreal width = static_cast<qreal>(screen->geometry().width());
    // 1 英寸=2.54 厘米 WidthToCm为当前屏幕宽度所对应的厘米尺寸
    qreal WidthToCm = width * inchToPx * qreal(2.54);
    // 换算cm 转 px
    qreal px = (cm * width)/WidthToCm;
    return px;
}

qreal CatMath::PxtoCm(qreal px)
{
    //获取主屏幕分辨率
    QScreen *screen = QApplication::primaryScreen();
    // 1 英寸的像素
    qreal inchToPx = qreal(1)/GetDpi();
    // 屏幕宽度
    qreal width = static_cast<qreal>(screen->geometry().width());
    // 1 英寸=2.54 厘米 WidthToCm为当前屏幕宽度所对应的厘米尺寸
    qreal WidthToCm = width * inchToPx * qreal(2.54);
    // 换算px 转 cm
    qreal cm = (px * WidthToCm)/width;
    return cm;

}

qreal CatMath::DegreeAngle(QVector2D vector2d)
{
    // qAtan2 返回的是弧度 - 官方文档有误
    // qAtan2 * AnglePerPI + 360.0  用度表示反正切值
    // fmod 求余
    return fmod((qAtan2(static_cast<qreal>(vector2d.y()), static_cast<qreal>(vector2d.x())) * AnglePerPI + 360.0), 360.0);
}

QPointF CatMath::PointCoordinatesOnACircle(QPointF centerpoint, qreal r, qreal angle)
{
    QPointF point;
    qreal rotate = angle * M_PI / 180.0;
    point.setX(centerpoint.rx() + r * qCos(rotate));
    point.setY(centerpoint.ry() + r * qSin(rotate));
    return point;
}

qreal CatMath::AngleJudge(qreal angle)
{
    qreal temp = angle;
    if (temp > 360.0)
    {
        while(1)
        {
            temp -= 360.0;
            if (temp < 360.0) break;
        }
    }
    else if (temp < 0.0)
    {
        while(1)
        {
            temp += 360.0;
            if (temp > 0.0) break;
        }
    }
    return temp;
}

qreal CatMath::PythagoreanTheorem(qreal a, qreal b)
{
    qreal c = 0;
    c = qSqrt(qPow(a, 2) + qPow(b, 2));
    return c;
}

qreal CatMath::TanToAngle(qreal bc, qreal ab)
{
    return qAtan(bc/ab) * 180 / M_PI;
}

qreal CatMath::CosToAngle(qreal ab, qreal ac)
{
    return qAcos(ab/ac) * 180 / M_PI;
}

qreal CatMath::SinToAngle(qreal bc, qreal ac)
{
    return qAsin(bc/ac) * 180 / M_PI;
}

QPointF CatMath::PosMove(QPointF last, QPointF pos)
{
   return QPointF(last.rx() + pos.rx(), last.ry() + pos.ry());
}

演示效果

在这里插入图片描述

项目地址

gitee

codechina

github

猜你喜欢

转载自blog.csdn.net/qq_32312307/article/details/115058417