【QT Graphics/View】自定义动态同心弧DyConcentricArc

2024-02-06 19:10

本文主要是介绍【QT Graphics/View】自定义动态同心弧DyConcentricArc,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、功能

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

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

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

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

二、效果图

 

 

三、实现原理

数据结构表达:由2个半径 + 起始角度 + 终点角度组成

    qreal m_radius = 0;        /**< 第一个半径 */qreal m_another_radius = 0; /**< 第二个半径 */qreal m_startAngle = 0;    /**< 起始角度 */qreal m_endAngle = 0;      /**< 终点角度 */

整个图元组成:由3个圆弧+2个直线+箭头组成

绘图在paint()函数中实现

绘制圆弧,根据起始角度和角度范围确定

    painter->drawArc(rect1, m_startAngle * 16, spanAngle * 16);painter->drawArc(rect2, m_startAngle * 16, spanAngle * 16);

 绘制直线,需要根据原点坐标、半径大小、角度计算直线位置

    QLineF maxEndLine;maxEndLine.setP1(QPointF(0, 0.0001));maxEndLine.setLength(qMax(m_radius, m_another_radius));maxEndLine.setAngle(m_endAngle);
painter->drawLine(startLine);

判断鼠标是否再圆弧边上,通过鼠标位置到圆心的距离和半径的大小是否接近判断

bool DyConcentricArc::judgeInAnotherArc(QPointF pos)
{QLineF line(QPointF(0, 0), pos);if(abs(line.length() - m_another_radius) < this->pen().widthF()){return true;}return false;
}

判断鼠标是否在起始直线上,先判断是否在鼠标到圆心的距离是否在2个半径之间,再判断鼠标到圆心直线的角度是否接近开始角度

bool DyConcentricArc::judgeInStartLine(QPointF pos)
{QLineF line(QPointF(0, 0), pos);if(line.length() < qMax(m_radius, m_another_radius) &&line.length() > qMin(m_radius, m_another_radius)){if(qAbs(line.angle() - m_startAngle) < 2 ){return true;}}return false;
}

 再重写鼠标事件,完成对半径、角度的改变

四、关键代码

DyConcentricArc.h

#ifndef DYCONCENTRICARC_H
#define DYCONCENTRICARC_H#include "BaseGraphicsItem.h"
#include "GraphicsMemory.h"class GRAPHICSLIBSHARED_EXPORT DyConcentricArc : public BaseGraphicsItem
{Q_OBJECTpublic:enum E_STATE_FLAG{DEFAULT_FLAG = 0,MOV_ARC_RADIUS,            /**< 第一个弧半径 */MOV_ANOTHER_ARC_RADIUS,    /**< 第二个弧半径 */MOV_START_ANGLE,           /**< 开始角度 */MOV_END_ANGLE              /**< 结束角度 */};DyConcentricArc(qreal radius1, qreal radius2,qreal startAngle, qreal endAngle,E_ItemType type = BaseGraphicsItem::E_ItemType::Dy_ConcentricArc);double radiusInner() const;double radiusOuter() const;double startAngle() const;double endAngle() const;void setConfig(const T_DyArcConfig &config);T_DyArcConfig config() const;protected:virtual QRectF boundingRect() const override;virtual void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget) override;virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) 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 judgeInArc 是否在第一个圆弧上* @param pos* @return*/bool judgeInArc(QPointF pos);/*** @brief judgeInAnotherArc 是否在第二个圆弧上* @param pos* @return*/bool judgeInAnotherArc(QPointF pos);/*** @brief judgeInStartLine 是否在开始边上* @param pos* @return*/bool judgeInStartLine(QPointF pos);/*** @brief judgeInEndLine 是否在结束边上* @param pos* @return*/bool judgeInEndLine(QPointF pos);private:qreal m_radius = 0;        /**< 第一个半径 */qreal m_another_radius = 0; /**< 第二个半径 */qreal m_startAngle = 0;    /**< 起始角度 */qreal m_endAngle = 0;      /**< 终点角度 */E_STATE_FLAG m_stateFlag = DEFAULT_FLAG;T_DyArcConfig m_dyArcConfig;
};
#endif // DYCONCENTRICARC_H

 DyConcentricArc.cpp

#include "DyConcentricArc.h"
#include <QCheckBox>
#include <QComboBox>
#include <QDebug>
#include <QMenu>
#include <QSpinBox>
#include <QWidgetAction>
#include <QtMath>DyConcentricArc::DyConcentricArc(qreal radius1, qreal radius2,qreal startAngle, qreal endAngle, BaseGraphicsItem::E_ItemType type): BaseGraphicsItem(type), m_radius(radius1), m_another_radius(radius2),m_startAngle(startAngle), m_endAngle(endAngle)
{setAcceptHoverEvents(true);this->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);setConfig(GraphicsMemory::getInstance()->dyArcConfig());
}double DyConcentricArc::radiusInner() const
{return qMin(m_radius, m_another_radius);
}double DyConcentricArc::radiusOuter() const
{return qMax(m_radius, m_another_radius);
}double DyConcentricArc::startAngle() const
{return m_startAngle;
}double DyConcentricArc::endAngle() const
{return m_endAngle;
}void DyConcentricArc::setConfig(const T_DyArcConfig &config)
{m_dyArcConfig = config;
}T_DyArcConfig DyConcentricArc::config() const
{return m_dyArcConfig;
}QRectF DyConcentricArc::boundingRect() const
{qreal maxRadius = qMax(m_radius, m_another_radius);return QRectF(0 - maxRadius, 0 - maxRadius, maxRadius * 2, maxRadius * 2);
}void DyConcentricArc::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);  /* 线段保持原来的线宽 */QBrush brush(pen.color(), Qt::Dense1Pattern);painter->setPen(pen);painter->setBrush(brush);painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);/* 画2个边界弧 */int spanAngle = m_endAngle - m_startAngle ;if(spanAngle < 0){spanAngle = 360 + spanAngle;}QRectF rect1(0 - m_radius, 0 - m_radius, m_radius * 2, m_radius * 2);QRectF rect2(0 - m_another_radius, 0 - m_another_radius, m_another_radius * 2, m_another_radius * 2);painter->drawArc(rect1, m_startAngle * 16, spanAngle * 16);painter->drawArc(rect2, m_startAngle * 16, spanAngle * 16);/* 画中间弧 */pen.setStyle(Qt::DashDotLine);painter->setPen(pen);double r3 = (m_another_radius + m_radius) / 2;QRectF rect3(0 - r3, 0 - r3, r3 * 2, r3 * 2);painter->drawArc(rect3, m_startAngle * 16, spanAngle * 16);/* 画2边界直线-虚线 */pen.setStyle(Qt::DashDotLine);painter->setPen(pen);QLineF minStartLine;minStartLine.setP1(QPointF(0, 0.0001));minStartLine.setLength(qMin(m_radius, m_another_radius));minStartLine.setAngle(m_startAngle);
//    painter->drawLine(minStartLine);QLineF minEndLine;minEndLine.setP1(QPointF(0, 0.0001));minEndLine.setLength(qMin(m_radius, m_another_radius));minEndLine.setAngle(m_endAngle);
//    painter->drawLine(minEndLine);/* 画2边界直线-实线*/pen.setStyle(Qt::SolidLine);painter->setPen(pen);QLineF maxStartLine;maxStartLine.setP1(QPointF(0, 0.0001));maxStartLine.setLength(qMax(m_radius, m_another_radius));maxStartLine.setAngle(m_startAngle);QLineF maxEndLine;maxEndLine.setP1(QPointF(0, 0.0001));maxEndLine.setLength(qMax(m_radius, m_another_radius));maxEndLine.setAngle(m_endAngle);QLineF startLine(minStartLine.p2(), maxStartLine.p2());painter->drawLine(startLine);QLineF endLine(minEndLine.p2(), maxEndLine.p2());painter->drawLine(endLine);/* 画方向-带箭头 */if(0 == m_dyArcConfig.direction){/* 向圆心方向 */double angle = std::atan2(-startLine.dy(), startLine.dx());qreal arrowSize = 10;QPointF arrowP1 = startLine.p1() + QPointF(sin(angle + M_PI / 3) * arrowSize,cos(angle + M_PI / 3) * arrowSize);QPointF arrowP2 = startLine.p1() + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,cos(angle + M_PI - M_PI / 3) * arrowSize);painter->drawLine(startLine.p1(), arrowP1);painter->drawLine(startLine.p1(), arrowP2);}else{/* 远离圆心方向 */double angle = std::atan2(-startLine.dy(), startLine.dx());qreal arrowSize = 10;QPointF arrowP1 = startLine.p2() - QPointF(sin(angle + M_PI / 3) * arrowSize,cos(angle + M_PI / 3) * arrowSize);QPointF arrowP2 = startLine.p2() - QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,cos(angle + M_PI - M_PI / 3) * arrowSize);painter->drawLine(startLine.p2(), arrowP1);painter->drawLine(startLine.p2(), arrowP2);}/* 画点 */pen.setColor(QColor(0, 128, 0));painter->setPen(pen);painter->drawPoint(QPointF(0, 0));
}void DyConcentricArc::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
//    if ( !this->isSelected() )
//    {
//        return;
//    }QMenu *menu = new QMenu();menu->setStyleSheet("QMenu { background-color:rgb(255,255,255); border: 5px solid rgb(235,110,36); }");QCheckBox *modeSelectCheckBox =  new QCheckBox(menu);modeSelectCheckBox->setStyleSheet("QComboBox { background-color:rgb(255,255,255);}");modeSelectCheckBox->setText(tr("Fuzzy edge algorithm"));bool ischecked = (0 == m_dyArcConfig.modeSelect) ? false : true;modeSelectCheckBox->setChecked(ischecked);connect(modeSelectCheckBox, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged), [ = ](int index){Q_UNUSED(index)m_dyArcConfig.modeSelect = modeSelectCheckBox->isChecked() ? 1 : 0;emit stateChanged(this);});QComboBox *darkAndLightDirComboBox =  new QComboBox(menu);darkAndLightDirComboBox->setStyleSheet("QComboBox { background-color:rgb(255,255,255);}");darkAndLightDirComboBox->addItem(tr("Auto"));darkAndLightDirComboBox->addItem(tr("From dark to light"));darkAndLightDirComboBox->addItem(tr("From light to dark"));darkAndLightDirComboBox->setCurrentIndex(m_dyArcConfig.darkAndLightDir);connect(darkAndLightDirComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [ = ](int index){m_dyArcConfig.darkAndLightDir = index;emit stateChanged(this);});QWidgetAction *widgetAction1 = new QWidgetAction(menu);widgetAction1->setDefaultWidget(modeSelectCheckBox);menu->addAction(widgetAction1);QWidgetAction *widgetAction2 = new QWidgetAction(menu);widgetAction2->setDefaultWidget(darkAndLightDirComboBox);menu->addAction(widgetAction2);menu->exec(QCursor::pos() + QPoint(radiusOuter(), 0));delete menu;QGraphicsItem::contextMenuEvent(event);
}void DyConcentricArc::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{if(judgeInArc(event->pos())){setCursor(Qt::CrossCursor);}else if (judgeInAnotherArc(event->pos())){setCursor(Qt::CrossCursor);}else if (judgeInStartLine(event->pos())){setCursor(Qt::CrossCursor);}else if (judgeInEndLine(event->pos())){setCursor(Qt::CrossCursor);}else{setCursor(Qt::ArrowCursor);}event->accept();
}void DyConcentricArc::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{if(judgeInArc(event->pos())){setCursor(Qt::CrossCursor);}else if (judgeInAnotherArc(event->pos())){setCursor(Qt::CrossCursor);}else if (judgeInStartLine(event->pos())){setCursor(Qt::CrossCursor);}else if (judgeInEndLine(event->pos())){setCursor(Qt::CrossCursor);}else{setCursor(Qt::ArrowCursor);}event->accept();
}void DyConcentricArc::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{Q_UNUSED(event)setCursor(Qt::ArrowCursor);event->accept();
}void DyConcentricArc::mousePressEvent(QGraphicsSceneMouseEvent *event)
{if(event->button() == Qt::LeftButton){if(judgeInArc(event->pos())){m_stateFlag = MOV_ARC_RADIUS;}else if(judgeInAnotherArc(event->pos())){m_stateFlag = MOV_ANOTHER_ARC_RADIUS;}else if(judgeInStartLine(event->pos())){m_stateFlag = MOV_START_ANGLE;}else if(judgeInEndLine(event->pos())){m_stateFlag = MOV_END_ANGLE;}}else if(event->button() == Qt::RightButton){}else if(event->button() == Qt::MidButton){/* 中键切换方向 */if(0 == m_dyArcConfig.direction){m_dyArcConfig.direction = 1;}else{m_dyArcConfig.direction = 0;}emit stateChanged(this);}QGraphicsItem::mousePressEvent(event);
}void DyConcentricArc::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{switch (static_cast<int>(m_stateFlag)){case MOV_ARC_RADIUS:{/* 必须为pos,非scenePos*/QLineF line(QPointF(0, 0), event->pos());m_radius = line.length();break;}case MOV_ANOTHER_ARC_RADIUS:{QLineF line(QPointF(0, 0), event->pos());m_another_radius = line.length();break;}case MOV_START_ANGLE:{QLineF line(QPointF(0, 0), event->pos());m_startAngle = line.angle();break;}case MOV_END_ANGLE:{QLineF line(QPointF(0, 0), event->pos());m_endAngle = line.angle();break;}default:{QGraphicsItem::mouseMoveEvent(event);break;}}
}void DyConcentricArc::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{m_stateFlag = DEFAULT_FLAG;if(event->button() == Qt::LeftButton){emit stateChanged(this);}QGraphicsItem::mouseReleaseEvent(event);
}void DyConcentricArc::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{if(event->button() == Qt::LeftButton){GraphicsMemory::getInstance()->setDyArcConfig(config());emit selectCompleted(this);}else{QGraphicsItem::mouseDoubleClickEvent(event);}
}bool DyConcentricArc::judgeInArc(QPointF pos)
{QLineF line(QPointF(0, 0), pos);if(abs(line.length() - m_radius) < this->pen().widthF()){return true;}return false;
}bool DyConcentricArc::judgeInAnotherArc(QPointF pos)
{QLineF line(QPointF(0, 0), pos);if(abs(line.length() - m_another_radius) < this->pen().widthF()){return true;}return false;
}bool DyConcentricArc::judgeInStartLine(QPointF pos)
{QLineF line(QPointF(0, 0), pos);if(line.length() < qMax(m_radius, m_another_radius) &&line.length() > qMin(m_radius, m_another_radius)){if(qAbs(line.angle() - m_startAngle) < 2 ){return true;}}return false;
}bool DyConcentricArc::judgeInEndLine(QPointF pos)
{QLineF line(QPointF(0, 0), pos);if(line.length() < qMax(m_radius, m_another_radius) &&line.length() > qMin(m_radius, m_another_radius)){if(qAbs(line.angle() - m_endAngle) < 2 ){return true;}}return false;
}

更多文章

【QT】自定义旋转矩形DyRotatedRectangle_俊俊的博客-CSDN博客

【QT】自定义动态同心圆DyConcentricCircle_俊俊的博客-CSDN博客

这篇关于【QT Graphics/View】自定义动态同心弧DyConcentricArc的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/685272

相关文章

dubbo3 filter(过滤器)如何自定义过滤器

《dubbo3filter(过滤器)如何自定义过滤器》dubbo3filter(过滤器)类似于javaweb中的filter和springmvc中的intercaptor,用于在请求发送前或到达前进... 目录dubbo3 filter(过滤器)简介dubbo 过滤器运行时机自定义 filter第一种 @A

Vue中动态权限到按钮的完整实现方案详解

《Vue中动态权限到按钮的完整实现方案详解》这篇文章主要为大家详细介绍了Vue如何在现有方案的基础上加入对路由的增、删、改、查权限控制,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、数据库设计扩展1.1 修改路由表(routes)1.2 修改角色与路由权限表(role_routes)二、后端接口设计

Qt实现发送HTTP请求的示例详解

《Qt实现发送HTTP请求的示例详解》这篇文章主要为大家详细介绍了如何通过Qt实现发送HTTP请求,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、添加network模块2、包含改头文件3、创建网络访问管理器4、创建接口5、创建网络请求对象6、创建一个回复对

前端 CSS 动态设置样式::class、:style 等技巧(推荐)

《前端CSS动态设置样式::class、:style等技巧(推荐)》:本文主要介绍了Vue.js中动态绑定类名和内联样式的两种方法:对象语法和数组语法,通过对象语法,可以根据条件动态切换类名或样式;通过数组语法,可以同时绑定多个类名或样式,此外,还可以结合计算属性来生成复杂的类名或样式对象,详细内容请阅读本文,希望能对你有所帮助...

Nginx实现动态封禁IP的步骤指南

《Nginx实现动态封禁IP的步骤指南》在日常的生产环境中,网站可能会遭遇恶意请求、DDoS攻击或其他有害的访问行为,为了应对这些情况,动态封禁IP是一项十分重要的安全策略,本篇博客将介绍如何通过NG... 目录1、简述2、实现方式3、使用 fail2ban 动态封禁3.1 安装 fail2ban3.2 配

Vue3中的动态组件详解

《Vue3中的动态组件详解》本文介绍了Vue3中的动态组件,通过`component:is=动态组件名或组件对象/component`来实现根据条件动态渲染不同的组件,此外,还提到了使用`markRa... 目录vue3动态组件动态组件的基本使用第一种写法第二种写法性能优化解决方法总结Vue3动态组件动态

Qt 中集成mqtt协议的使用方法

《Qt中集成mqtt协议的使用方法》文章介绍了如何在工程中引入qmqtt库,并通过声明一个单例类来暴露订阅到的主题数据,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一,引入qmqtt 库二,使用一,引入qmqtt 库我是将整个头文件/源文件都添加到了工程中进行编译,这样 跨平台

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...