Qt贝塞尔曲线

目录

  • 引言
  • 核心代码
    • 基本表达
    • 绘制曲线
    • 使用QEasingCurve
  • 完整代码

引言

贝塞尔曲线客户端开发中常见的过渡效果,如界面的淡入淡出、数值变化、颜色变化等等。为了能够更深的了解地理解贝塞尔曲线,本文通过Demo将贝塞尔曲线绘制出来,如下所示:

在这里插入图片描述

核心代码

基本表达

一般来说贝塞尔曲线由起止点以及c1、c2点构成,如上图中,黄色为c1点,绿色为c2点,通过调整c1、c2点去调整整个曲线的变化快慢。

cubic-bezier(.42,0,.58,1)

而这两个点这么怎么去表达呢,如上所示,可以拆分为c1坐标(0.42,0)和c2坐标(0.58,1),而这个坐标则是一个相对坐标,范围是从(0,0)到(1,1),如下图所示:
在这里插入图片描述

绘制曲线

void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
Adds a cubic Bezier curve between the current position and the given endPoint using the control points specified by c1, and c2.
After the curve is added, the current position is updated to be at the end point of the curve.

绘制可以通过QPainterPath::cubicTo完成,需要注意的是其中使用的是这个绘制界面的绝对坐标,而不是相对坐标,因此需要增加两个转换函数方便后续的编码。

首先是百分比坐标转换为实际坐标:

QPoint CubicBezierWidget::PercentToPosition(const QPointF &percent)
{return valid_rect_.bottomLeft() + QPoint(valid_rect_.width() * percent.x(), -valid_rect_.height() * percent.y());
}

再者就是将实际坐标转换为百分比坐标:

QPointF CubicBezierWidget::PositionToPercent(const QPoint &position)
{double x_percent = position.x() - valid_rect_.bottomLeft().x();x_percent = x_percent / valid_rect_.width();double y_percent = valid_rect_.bottomLeft().y() - position.y();y_percent = y_percent / valid_rect_.height();return QPointF(x_percent, y_percent);
}

最后则是起止点以及c1、c2组装起来

    // 关键数据QPointF start_point = valid_rect_.bottomLeft();QPointF end_point = valid_rect_.topRight();QPoint c1_point = PercentToPosition(c1_);QPoint c2_point = PercentToPosition(c2_);QPainterPath path;path.moveTo(start_point);path.cubicTo(c1_point, c2_point, end_point);

使用QEasingCurve

QEasingCurve是Qt核心库的曲线函数,可以使用其作为动画函数的变化曲线,这也是贝塞尔曲线最多的应用场景,通过QAbstractAnimation::setEasingCurve设置。此处为了展示,从曲线中采样10个点,通过函数QEasingCurve::valueForProgress获取对应的y值进行绘制,如下所示:

    QEasingCurve easing_curve(QEasingCurve::BezierSpline);easing_curve.addCubicBezierSegment(QPointF(0.42, 0.0), QPointF(0.58, 1.0), QPointF(1.0, 1.0));QPainterPath path_bezier;int count = 10;for(int i=0; i <= count; i++){double progress = (double)i / count;QPointF target_point(PercentToPosition(QPointF(progress, easing_curve.valueForProgress(progress))));if(i){path_bezier.lineTo(target_point);}else{path_bezier.moveTo(target_point);}}pen.setColor(QColor(241, 148, 138));painter.save();painter.setPen(pen);painter.setBrush(Qt::NoBrush);painter.drawPath(path_bezier);painter.restore();

完整代码

class CubicBezierWidget : public QWidget
{Q_OBJECTQ_PROPERTY(QPointF c1 READ c1 WRITE setC1 NOTIFY c1Changed FINAL)Q_PROPERTY(QPointF c2 READ c2 WRITE setC2 NOTIFY c2Changed FINAL)public:explicit CubicBezierWidget(QWidget *parent = nullptr);enum MouseState {MouseNormal = 0,MouseActivatedC1,MouseActivatedC2,};public:QPointF c1() const;void setC1(QPointF c1);QPointF c2() const;void setC2(QPointF c2);signals:void c1Changed();void c2Changed();protected:void paintEvent(QPaintEvent *event) override;void resizeEvent(QResizeEvent *event) override;void mousePressEvent(QMouseEvent *event) override;void mouseReleaseEvent(QMouseEvent *event) override;void mouseMoveEvent(QMouseEvent *event) override;private:QPoint PercentToPosition(const QPointF &percent);QPointF PositionToPercent(const QPoint &position);MouseState StateByPosition(const QPoint &position);private slots:void toUpdate();private:int space_ = 20;int radius_ = 12;int inner_radius_ = 6;// 百分比QPointF c1_;QPointF c2_;QRect valid_rect_;// 鼠标标志MouseState mouse_type_ = MouseNormal;
};
#include <QDebug>
#include <QPainter>
#include <QPainterPath>
#include <QPaintEvent>
#include <QEasingCurve>CubicBezierWidget::CubicBezierWidget(QWidget *parent): QWidget{parent}
{connect(this, &CubicBezierWidget::c1Changed, this, &CubicBezierWidget::toUpdate);connect(this, &CubicBezierWidget::c2Changed, this, &CubicBezierWidget::toUpdate);
}void CubicBezierWidget::paintEvent(QPaintEvent *event)
{// 关键数据QPointF start_point = valid_rect_.bottomLeft();QPointF end_point = valid_rect_.topRight();QPoint c1_point = PercentToPosition(c1_);QPoint c2_point = PercentToPosition(c2_);QPainterPath path;path.moveTo(start_point);path.cubicTo(c1_point, c2_point, end_point);// 初始化画笔QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setPen(Qt::NoPen);painter.setBrush(Qt::NoBrush);// 背景painter.save();painter.setBrush(QColor(32, 32, 32));painter.drawRect(event->rect());painter.setBrush(QColor(42, 42, 42));painter.drawRect(valid_rect_);painter.restore();QPen pen;pen.setCapStyle(Qt::RoundCap);pen.setWidth(4);pen.setColor(QColor(100, 100, 100));// 连接线painter.save();painter.setPen(pen);painter.drawLine(start_point, c1_point);painter.drawLine(end_point, c2_point);painter.restore();pen.setWidth(6);pen.setColor("white");// 曲线painter.save();painter.setPen(pen);painter.drawPath(path);painter.restore();// 操作圆c1painter.save();painter.setBrush(QColor(247, 220, 111));painter.drawEllipse(c1_point, radius_, radius_);painter.setBrush(Qt::white);painter.drawEllipse(c1_point, inner_radius_, inner_radius_);painter.restore();// 操作圆c2painter.save();painter.setBrush(QColor(72, 201, 176));painter.drawEllipse(c2_point, radius_, radius_);painter.setBrush(Qt::white);painter.drawEllipse(c2_point, inner_radius_, inner_radius_);painter.restore();
}void CubicBezierWidget::resizeEvent(QResizeEvent *event)
{valid_rect_ = rect().adjusted(space_, space_, -space_, -space_);// 还原setC1(QPointF(0, 0));setC2(QPointF(1, 1));
}void CubicBezierWidget::mousePressEvent(QMouseEvent *event)
{mouse_type_ = StateByPosition(event->pos());
}void CubicBezierWidget::mouseReleaseEvent(QMouseEvent *event)
{mouse_type_ = MouseNormal;
}void CubicBezierWidget::mouseMoveEvent(QMouseEvent *event)
{if(mouse_type_ == MouseActivatedC1){QPointF percent = PositionToPercent(event->pos());setC1(percent);}else if(mouse_type_ == MouseActivatedC2){QPointF percent = PositionToPercent(event->pos());setC2(percent);}
}QPoint CubicBezierWidget::PercentToPosition(const QPointF &percent)
{return valid_rect_.bottomLeft() + QPoint(valid_rect_.width() * percent.x(), -valid_rect_.height() * percent.y());
}QPointF CubicBezierWidget::PositionToPercent(const QPoint &position)
{double x_percent = position.x() - valid_rect_.bottomLeft().x();x_percent = x_percent / valid_rect_.width();double y_percent = valid_rect_.bottomLeft().y() - position.y();y_percent = y_percent / valid_rect_.height();return QPointF(x_percent, y_percent);
}CubicBezierWidget::MouseState CubicBezierWidget::StateByPosition(const QPoint &position)
{QPoint c2_position = PercentToPosition(c2_);QRect c2_rect(c2_position.x() - radius_, c2_position.y() - radius_, 2 * radius_, 2* radius_);if(c2_rect.contains(position)){return MouseActivatedC2;}QPoint c1_position = PercentToPosition(c1_);QRect c1_rect(c1_position.x() - radius_, c1_position.y() - radius_, 2 * radius_, 2* radius_);if(c1_rect.contains(position)){return MouseActivatedC1;}return MouseNormal;
}void CubicBezierWidget::toUpdate()
{update();
}QPointF CubicBezierWidget::c1() const
{return c1_;
}void CubicBezierWidget::setC1(QPointF c1)
{c1.setX(qBound(0.0, c1.x(), 1.0));c1.setY(qBound(0.0, c1.y(), 1.0));if (c1_ == c1)return;c1_ = c1;emit c1Changed();
}QPointF CubicBezierWidget::c2() const
{return c2_;
}void CubicBezierWidget::setC2(QPointF c2)
{c2.setX(qBound(0.0, c2.x(), 1.0));c2.setY(qBound(0.0, c2.y(), 1.0));if (c2_ == c2)return;c2_ = c2;emit c2Changed();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/140035.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SSM的数据结构课程网络学习平台

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

力扣刷题第十七天--哈希表篇

前言 本题与「15. 三数之和」相似&#xff0c;解法也相似。 内容 一、四数之和 18.四数之和 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;…

Java,多线程,线程的同步机制

线程的安全问题与线程的同步机制 以火车站买票的问题来举例。假设火车站有100张票&#xff0c;分三个窗口售卖这一百张票。 分别用继承Thread类和实现Runnable接口的方式&#xff1a; 实现Runnable接口&#xff1a; public class WindowTest {public static void main(Stri…

Git系列之分支与标签的使用及应用场景模拟

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《Git实战开发》。&#x1f3af;&#x1f3af; &a…

Java学习_对象

对象在计算机中的执行原理 类和对象的一些注意事项 this关键字 构造器 构造器是一种特殊的方法 : 特殊之处在于&#xff0c;名字必须与所在类的名字一样&#xff0c;而且不能写返回值类型 封装 封装的设计规范&#xff1a;合理隐藏、合理暴露 实体类 成员变量和局部变量的区别 …

mybatisPlus update更新部分字段

第一种方式&#xff1a; 其中&#xff0c; lambdaUpdateWrapper.set 表示要更新的字段值。 .eq 则表示 WHERE 条件。 public void updateEntity() {// LambdaUpdateWrapper<TestEntity> lambdaUpdateWrapper new LambdaUpdateWrapper<>();//有些版本可能不兼容上…

微信聊天,收到二维码图片就自动帮你提取出来的方法

10-3 如果你是二维码收集的重度用户&#xff0c;那我非常推荐你好好阅读本文&#xff0c;也许可以帮你解决你的问题&#xff0c;比如做网推的人&#xff0c;需要常年混迹在各种微信群&#xff0c;那如何在各个微信群中收集到群友分享出来的二维码&#xff0c;并且要立即保存出…

SOLID 原则,程序设计五大原则,设计模式

SOLID 是让软件设计更易于理解、更加灵活和更易于维护的五个原则的简称。 单一职责(Single Responsibility Principle)&#xff1a;修改一个类的原因只能有一个。开闭原则(Open/Closed Principle)&#xff1a;对于扩展&#xff0c;类应该是“开放”的&#xff1b;对于修改&…

组件的设计原则

目录 插槽的基本概念 基础用法 具名插槽 使用场景 布局控制 嵌套组件 组件的灵活性 高级用法 作用域插槽 总结 前言 Vue 的 slot 是一项强大的特性&#xff0c;用于组件化开发中。它允许父组件向子组件传递内容&#xff0c;使得组件更加灵活和可复用。通过 slot&…

Python之函数进阶-nonlocal和LEGB

Python之函数进阶-nonlocal和LEGB nonlocal语句 nonlocal:将变量标记为不在本地作用域定义&#xff0c;而是在上级的某一级局部作用域中定义&#xff0c;但不能是全局作用域中定义。 函数的销毁 定义一个函数就是生成一个函数对象&#xff0c;函数名指向的就是函数对象。可…

在 React Router 中使用 JWT

在这篇文章中&#xff0c;我们将探讨 JWT 身份校验与 React 和 React-router 的无缝集成。 我们还将学习如何处理公共路由、受校验保护路由&#xff0c;以及如何利用 axios 库通过身份验证令牌&#xff08;token&#xff09;发出 API 请求。 创建一个 React 项目 使用下方的指…

华为云Ascend310服务器使用

使用华为云服务器 cpu: 16vCPUs Kunpeng 920 内存&#xff1a;16GiB gpu&#xff1a;4* HUAWEI Ascend 310 cann: 20.1.rc1 操作系统&#xff1a;Ubuntu aarch64目的 使用该服务器进行docker镜像编译&#xff0c;测试模型。 已知生产环境&#xff1a;mindx版本为3.0.rc3&a…

keep-alive缓存,三级路由不生效

此文章讲诉在vue中使用keep-alive缓存&#xff0c;三级路由缓存失败处理方案。 一二级路由缓存无任何问题&#xff0c;三级以上就会失败&#xff0c;因此我们在路由守卫中对matched做出如下优化 Router.beforeEach((to, from, next)>{if(to.matched && to.matched.l…

【机器学习】Kmeans聚类算法

一、聚类简介 Clustering (聚类)是常见的unsupervised learning (无监督学习)方法&#xff0c;简单地说就是把相似的数据样本分到一组&#xff08;簇&#xff09;&#xff0c;聚类的过程&#xff0c;我们并不清楚某一类是什么&#xff08;通常无标签信息&#xff09;&#xff0…

【PTE-day07 文件上传2】

1、常见的绕过方式 (1)畸形后缀名绕过 .php、.pht、.php3、.php4、.php5、.php2、.phtml、.pHp、.html、.Htm......(2)双写过滤字符绕过 (3).htaccess文件绕过 <FilesMatch "jpg"> SetHandler application/x-httpd-php

通义千问, 文心一言, ChatGLM, GPT-4, Llama2, DevOps 能力评测

引言 “克隆 dev 环境到 test 环境&#xff0c;等所有服务运行正常之后&#xff0c;把访问地址告诉我”&#xff0c;“检查所有项目&#xff0c;告诉我有哪些服务不正常&#xff0c;给出异常原因和修复建议”&#xff0c;在过去的工程师生涯中&#xff0c;也曾幻想过能够通过这…

目标检测工程化最佳实践:Python 并行条件下YOLOv8的模型推理,线程安全的模型推理!

文章大纲 YOLOv8模型的线程安全推理 背景简介Python 线程的一些理解共享模型实例的问题与危害!非线程安全的代码样例: 单个模型实例非线程安全的代码样例: 多个个模型实例YOLOv8 中线程安全的推理方式Thread-Safe Example 1Thread-Safe Example 2YOLOv8 主要开发人员的回复结论…

【FAQ】Gradle开发问题汇总

1. buildSrc依赖Spring Denpendency时报错 来自预编译脚本的插件请求不能包含版本号。请从有问题的请求中删除该版本&#xff0c;并确保包含所请求插件io.spring.dependency-management的模块是一个实现依赖项 解决方案 https://www.5axxw.com/questions/content/uqw0grhttps:/…

Flink之Catalog

Catalog Catalog概述Catalog分类 GenericInMemoryCatalogJdbcCatalog下载JAR包及使用重启操作创建Catalog查看与使用Catalog自动初始化catalog HiveCatalog下载JAR包及使用重启操作hive metastore服务创建Catalog查看与使用CatalogFlink与Hive中操作自动初始化catalog 用户自定…

基于springboot实现桥牌计分管理系统项目【项目源码】计算机毕业设计

基于springboot实现桥牌计分管理系统演示 JAVA简介 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&#…