告别Qt Slider!用纯C++打造更轻量的TpSlider组件

组件运行效果展示

TpSlider

组件概述

TpSlider组件简介

TpSlider是PiXSingleGUI库中的可拖动滑块组件,支持水平和垂直两种方向的滑动操作。TpSlider.h:13-17该组件提供了完整的用户交互功能,包括鼠标拖拽、数值范围设置和实时反馈机制。

核心特性

  • 双向支持:支持水平(Horizon)和垂直(Vertical)两种滑动方向
  • 精确控制:提供数值范围设置和当前值获取功能TpSlider.h:24-37
  • 事件驱动:基于信号槽机制的松耦合通信TpSlider.h:40-48
  • 高性能渲染:优化的绘制流程,适用于嵌入式环境

核心架构设计

继承关系与基类依赖

TpSlider继承自tpChildWidget基类,这种设计遵循了PiXSingleGUI库的统一架构模式。TpSlider.h:10基类提供了以下核心能力:

  • 事件处理框架:统一的鼠标、键盘事件分发机制
  • 渲染管理:标准化的绘制接口和更新调度
  • 内存管理:自动的生命周期管理和资源释放

数据结构设计

组件采用PIMPL(PointertoImplementation)设计模式,将所有实现细节封装在TpSliderData结构中:TpSlider.cpp:7-25

struct TpSliderData
{int32_t maxValue = 100;int32_t minValue = 0;double value = 0;TpSlider::SliderDirect direct = TpSlider::Horizon;// 顶点矩形区域bool isPressVertex = false;ItpRect vertexRect;ItpPoint pressPoint;// 一个间隔值对应的像素double valuePx = 0;// 是否正在拖拽调整进度,拖拽过程不响应setValue事件bool isDrag = false;
};

数据结构的关键设计:

  • 数值管理:minValuemaxValuevalue三元组管理滑块状态
  • 交互状态:isPressVertexisDrag标志位控制拖拽逻辑
  • 性能优化:valuePx预计算像素比例,vertexRect缓存顶点区域

功能实现详解

数值范围管理

滑块的数值管理通过setRange()setValue()方法实现:TpSlider.cpp:48-68

void TpSlider::setRange(const int32_t &min, const int32_t &max)
{TpSliderData *sliderData = static_cast<TpSliderData *>(data_);sliderData->minValue = min;sliderData->maxValue = max;if (sliderData->maxValue < sliderData->minValue)sliderData->maxValue = sliderData->minValue + 1;if (sliderData->value < sliderData->minValue)sliderData->value = sliderData->minValue;else if (sliderData->value > sliderData->maxValue)sliderData->value = sliderData->maxValue;else{}rangeChanged.emit(sliderData->minValue, sliderData->maxValue);update();
}

边界处理逻辑:

  • 自动修正无效范围(最大值小于最小值)
  • 当前值超出范围时自动调整到边界值
  • 范围变化时触发rangeChanged信号通知

鼠标事件处理机制

按下事件处理

鼠标按下事件的核心是区域检测和状态初始化:TpSlider.cpp:104-129

bool TpSlider::onMousePressEvent(TpMouseEvent *event)
{TpSliderData *sliderData = static_cast<TpSliderData *>(data_);sliderData->isPressVertex = false;sliderData->isDrag = false;if (event->button() != BUTTON_LEFT)return true;ItpPoint mousePoint = event->pos();if (sliderData->vertexRect.contains(mousePoint)){if (sliderData->direct == TpSlider::Horizon)sliderData->valuePx = 1.0 * (sliderData->maxValue - sliderData->minValue) / width();elsesliderData->valuePx = 1.0 * (sliderData->maxValue - sliderData->minValue) / height();sliderData->pressPoint = mousePoint;sliderData->isPressVertex = true;sliderData->isDrag = true;}return true;
}

关键算法:

  • 只响应左键点击且点击在顶点区域内
  • 根据滑块方向预计算像素-数值转换比例
  • 设置拖拽状态标志,防止外部setValue干扰

移动事件处理

拖拽过程中的数值计算是组件的核心算法:TpSlider.cpp:131-183

bool TpSlider::onMouseMoveEvent(TpMouseEvent *event)
{TpSliderData *sliderData = static_cast<TpSliderData *>(data_);if (sliderData->isPressVertex){ItpPoint curMotionPoint = event->pos();int32_t offsetPx = 0;if (sliderData->direct == TpSlider::Horizon){offsetPx = curMotionPoint.x - sliderData->pressPoint.x;}else{offsetPx = sliderData->pressPoint.y - curMotionPoint.y;}// std::cout << " offsetPx  " << offsetPx << std::endl;if (std::abs(offsetPx) >= sliderData->valuePx){int32_t oldValue = sliderData->value;sliderData->value += (offsetPx * sliderData->valuePx);if (sliderData->value > sliderData->maxValue){sliderData->value = sliderData->maxValue;}else if (sliderData->value < sliderData->minValue){sliderData->value = sliderData->minValue;}else{}// std::cout << " sliderData->value  " << sliderData->value << std::endl;int32_t newValue = sliderData->value;if (newValue != oldValue){valueChanged.emit(newValue);}}sliderData->pressPoint = curMotionPoint;update();}return true;
}

算法特点:

  • 方向适配:水平方向计算X轴偏移,垂直方向计算Y轴偏移(注意Y轴反向)
  • 增量更新:基于像素偏移量计算数值变化
  • 防抖处理:只有数值真正改变时才触发valueChanged信号

信号槽通信机制

组件提供两个核心信号用于外部通信:TpSlider.h:41-48

public 
signals:
/// @brief 值变化信号
/// @param int 当前值
declare_signal(valueChanged, int32_t);/// @brief 范围变化信号
/// @param int 当前最小值
/// @param int 当前最大值
declare_signal(rangeChanged, int32_t, int32_t);

PiXSingleGUI的信号槽系统基于模板实现,提供类型安全的事件通信:TpSignalSlot.h:91-130

渲染系统实现

绘制流程

滑块的绘制分为三个层次:背景轨道、进度填充和拖拽顶点。TpSlider.cpp:192-228

TpSliderData *sliderData = static_cast<TpSliderData *>(data_);// tpChildWidget::onPaintEvent(event);
TpShared<TpCssData> curCssData = currentStatusCss();TpCanvas *painter = event->canvas();// 整体高度、宽度;分成4份。进度条1份,顶点2份,浅色顶点4份
uint32_t bgWidth = width();
uint32_t bgHeight = height();
uint32_t bgX = 0;
uint32_t bgY = 0;// 不能用父类绘制,绘制背景色
ItpRect rect = event->rect();
if (objectType() == TP_FLOAT_OBJECT)
{if ((curCssData->backgroundColor() & 0xff) != 0xff){painter->erase();}
}if (sliderData->direct == TpSlider::Horizon)
{bgHeight = height() / 4.0;bgY = (height() - bgHeight) / 2.0;painter->roundedBox(0, bgY, rect.w, bgY + bgHeight, roundCorners(), curCssData->backgroundColor());
}
else
{bgWidth = width() / 4.0;bgX = (width() - bgWidth) / 2.0;painter->roundedBox(bgX, 0, bgX + bgWidth, rect.h, roundCorners(), curCssData->backgroundColor());
}

方向适配渲染

水平方向渲染:TpSlider.cpp:240-272

if (sliderData->direct == TpSlider::Horizon)
{circleRadius = height() / 4.0 * 2.0 / 2.0;valueWidth = valuePercent * width();if (valueWidth != 0)painter->roundedBox(0, bgY, valueWidth, bgY + bgHeight, roundCorners(), curCssData->subColor());int32_t circleX = valueWidth;if (circleX == 0){circleX = circleRadius;}else if (circleX == width()){circleX = width() - circleRadius;}else{}// 绘制淡色圆形顶点painter->filledCircle(circleX, height() / 2.0, height() / 2.0, lightSubColor);// 绘制圆形顶点painter->filledCircle(circleX, height() / 2.0, circleRadius, subColor);// 记录顶点区域sliderData->vertexRect.x = circleX - circleRadius;sliderData->vertexRect.y = height() / 2.0 - circleRadius;sliderData->vertexRect.w = circleRadius * 2;sliderData->vertexRect.h = circleRadius * 2;
}
  • 轨道高度为组件高度的1/4
  • 顶点位置根据数值百分比计算
  • 边界处理确保顶点不超出范围

垂直方向渲染:TpSlider.cpp:274-307

else
{circleRadius = width() / 4.0 * 2.0 / 2.0;valueWidth = valuePercent * height();if (valueWidth != 0)painter->roundedBox(bgX, height() - valueWidth, bgX + bgWidth, height(), roundCorners(), subColor);int32_t circleY = height() - valueWidth;if (circleY == 0){circleY = circleRadius;}else if (circleY == height()){circleY = height() - circleRadius;}else{}// 绘制淡色圆形顶点painter->filledCircle(width() / 2.0, circleY, width() / 2.0, lightSubColor);// 绘制圆形顶点painter->filledCircle(width() / 2.0, circleY, circleRadius, subColor);// 记录顶点区域sliderData->vertexRect.x = width() / 2.0 - circleRadius;sliderData->vertexRect.y = circleY - circleRadius;sliderData->vertexRect.w = circleRadius * 2;sliderData->vertexRect.h = circleRadius * 2;
}
  • 轨道宽度为组件宽度的1/4
  • 从底部向上填充(符合用户直觉)
  • Y坐标计算考虑垂直方向特殊性

样式系统集成

TpSlider组件集成了PiXSingleGUI的样式系统,支持通过CSS配置外观:TpSlider.cpp:197

// tpChildWidget::onPaintEvent(event);
TpShared<TpCssData> curCssData = currentStatusCss();

可配置的样式属性:

  • backgroundColor:轨道背景色
  • subColor:进度填充色和顶点颜色
  • roundCorners:圆角半径设置

使用示例程序

#include "TpApp.h"
#include "TpFixScreen.h"
#include "TpLabel.h"
#include "TpSlider.h"
#include "TpFont.h"int32_t main(int32_t argc, char *argv[])
{TpApp app(argc, argv);TpFixScreen *vScreen = new TpFixScreen();vScreen->setBackGroundColor(_RGBA(128, 128, 128, 255));vScreen->setVisible(true); // vScreen setvisible will be update displayapp.bindVScreen(vScreen);TpLabel *valueText = new TpLabel(vScreen);valueText->setText(TpString::number(50));valueText->setAlign(tinyPiX::AlignCenter);valueText->font()->setFontColor(_RGB(255, 255, 255),_RGB(255, 255, 255));valueText->font()->setFontSize(128);valueText->setWidth(600);valueText->setHeight(400);valueText->move(20, 150);TpSlider *slider = new TpSlider(vScreen);slider->setValue(50);slider->setSize(500, 10);slider->move(20, 20);TpSlider *vSlider = new TpSlider(vScreen);vSlider->setDirection(TpSlider::Vertical);vSlider->setValue(50);vSlider->setSize(10, 500);vSlider->move(650, 20);connect(slider, valueChanged, [=](int32_t value){ valueText->setText(TpString::number(value));vSlider->setValue(value); });connect(vSlider, valueChanged, [=](int32_t value){ valueText->setText(TpString::number(value));slider->setValue(value); });vScreen->update();return app.run();
}

TinyPiXOS开发者联盟

源码级支持 + 真实项目:TinyPiXOS开发者联盟招募中​,国产自主轻量级嵌入式设备桌面操作系统交流社群。
获取开发资料
官网网站
B 站视频

感谢支持和关注,如果对项目感兴趣,请点赞、收藏和转发!

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

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

相关文章

sensitive-word 敏感词性能提升14倍优化全过程 v0.28.0

背景 有一天&#xff0c;群里收到小伙伴提的一个问题&#xff0c;为什么程序 sensitive-word 第一次执行这么慢? sensitive-word-131 初步验证 自己本地用 v0.27.1 验证了一下&#xff0c;确实很奇怪&#xff0c;第一次明显很慢。 为了排除一些干扰项&#xff0c;我们把一些…

4.6 多个光源

1.Include Files 2.The Second Light 3.Point Light1.Include Files 为了在着色器中实现多光源支持, 我们需要添加更多通道; 这些通道会包含几乎相同的代码, 为了避免代码重复, 我们将着色器代码移到一个包含文件中; 与光照着色器相同的文件夹中创建一个后缀为.cginc的文件, 将…

ANSYS HFSS的简单认识

HFSS&#xff08;High Frequency Structure Simulator&#xff09;是ANSYS公司开发的一款用于高频电磁场仿真的行业标准软件。它通过“计算”电磁波在各种结构中的行为&#xff0c;来帮助工程师设计天线、滤波器、微波电路、高速电子封装等。我用一个简单易懂的比喻来帮你理解整…

Codeforces Round 1046 (Div. 2) vp补题

只是签了三道题就燃尽了… 原题连接 A //不可能连续进三球 得分值差最多的只有00X00X00X00 bool jud(int a,int b){if(a!0&&b!0&&max(a,b)-2*(min(a,b)1)>1)return 0;if(a0||b0){if(abs(a-b)>3)return 0;}return 1; } void solve() {int a,b,c,d;cin…

水泵运行组态监控系统御控物联网解决方案

一、方案背景与需求分析随着工业4.0和智慧城市建设的推进&#xff0c;传统水泵监控方式存在数据孤岛、响应滞后、运维成本高等问题。本方案通过物联网&#xff08;IoT&#xff09;技术构建水泵运行组态监控系统&#xff0c;实现设备状态实时感知、故障预警、远程调控及能效优化…

海尔电视刷机

硬盘格式化只有ntfs和exfat怎么办&#xff0c;没有fat32 这台型号le32c31 连有线几天后突然卡系统启动中 电视系统崩溃了怎么办&#xff1f;一直显示启动中&#xff01;三分钟解决问题&#xff0c;只要五元搞定&#xff01;_哔哩哔哩_bilibili format H: /fs:FAT32 慢 disk…

Science Advances副主编:如何提高论文投稿接收率?

国际著名综合性学术期刊《Science Advances》每年可接到约20000份投稿&#xff0c;有高达90%的拒稿率&#xff0c;大部分稿件甚至没有进入评审阶段&#xff0c;作为该期刊的副主编之一&#xff0c;杜克大学的Warren Warren教授撰写了文章&#xff0c;给投稿人提出几点建议以提高…

少儿配音教育:广州声与色在线科技有限公司打造趣味课程,助力青少年语言能力提升

针对青少年语言表达能力培养需求&#xff0c;广州声与色在线科技有限公司推出 “少儿配音趣味课程”&#xff0c;通过动画、童话等青少年喜爱的形式&#xff0c;融合发声训练与兴趣培养&#xff0c;成为少儿素质教育的新选择。课程设计贴合 8-15 岁青少年认知特点&#xff1a;分…

【架构艺术】变更风险防控架构嵌入决策降噪模块的方法

在先前的文章中&#xff0c;我们聊到了一个变更观测任务可以通过什么样的方式对不同的变更防控能力做统一调度&#xff0c;达到优越的变更风险拦截效果。但是在实战当中&#xff0c;变更观测任务集成了很多能力&#xff0c;即便风险拦截率很高&#xff0c;但不同能力效果也有差…

LeetCode算法日记 - Day 33: 最长公共前缀、最长回文子串

目录 1. 最长公共前缀 1.1 题目解析 1.2 解法 1.3 代码实现 2. 最长回文子串 2.1 题目解析 2.2 解法 2.3 代码实现 1. 最长公共前缀 14. 最长公共前缀 - 力扣&#xff08;LeetCode&#xff09; 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&…

Python毕业设计推荐:基于Django的饮食计划推荐与交流分享平台 饮食健康系统 健康食谱计划系统

精彩专栏推荐订阅&#xff1a;在 下方专栏&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设木哥&#x1f525; &#x1f496; 文章目录 一、项目介绍二…

物联网双轴倾角传感器厂家全面解析

内容概要本文旨在全面解析物联网双轴倾角传感器厂家的核心竞争力&#xff0c;为进口设备代理商及工业物联网项目提供实用选型指南。我们将深入探讨行业领先制造商的研发实力和生产标准&#xff0c;重点分析产品特性如低功耗设计优势、0.2高精度测量特性&#xff0c;以及CAN/电流…

Docker学习笔记-网络类型

Docker 网络类型1、Docker四种网络模式 &#xff08;1&#xff09;docker四种网络模式如下&#xff1a; Bridge contauner 桥接式网络模式Host(open) container 开放式网络模式Container(join) container 联合挂载式网络模式&#xff0c;是host网络模式的延伸None(Close)…

SDRAM详细分析-08 数据手册解读

大家好,这里是大话硬件。 前面我们梳理了很多关于内存的内容,不知道有没有人好奇,为什么要花这么大的精力做这些内容? 在4月份的时候,三星宣布将在2025年逐步停产DDR4内存颗粒,随后海力士和镁光也跟着一起,都宣布逐步停产DDR4颗粒。这三家半导体厂商在内存方面顶了半边…

Windows 环境下部署 MinIO 集群

文章目录介绍软件特点下载多机分布式集群部署1.前提准备2. 新建minio工作目录3. 编写运行命令4. 启动、测试5. nginx配置介绍 MinIO 是一款高性能、开源、云原生的分布式对象存储系统&#xff0c;专为私有云、公有云和边缘计算场景设计&#xff0c;完全兼容 Amazon S3 API&…

鸿蒙libxm2交叉编译

一开始先使用了lycium,但是没有编译通过 改为使用源码自带的配置文件编译 我使用的源码是libxml2-2.9.10.tar.gz 解压后进行下面的配置: root@ubuntu:/home/lw/libxml2-2.9.10# export OHOS_SDK=/home/lw/ohos-sdk/linuxroot@ubuntu:/home/lw/libxml2-2.9.10# export AS=…

MCAP :机器人数据容器的全面实践指南

Outline: MCAP 已形成完整工具链生态&#xff1a; Foxglove Studio&#xff1a;可视化分析工具mcap-cli&#xff1a;跨平台命令行工具AWS RoboMaker&#xff1a;原生云存储支持 随着 IEEE 正在制定的 P3196 机器人数据标准&#xff0c;MCAP 正在演进为行业基础架构的重要组成…

【Bluedroid】A2dp Source播放流程源码分析(7):蓝牙音频流启动流程深度解析(btif_av_stream_start)

本文深入分析Android Bluetooth协议栈中A2DP音频流启动的完整流程,从应用层调用btif_av_stream_start()开始,穿越BTIF、BTA、AVDTP多层架构,最终通过L2CAP发送AVDTP启动命令。揭示状态机驱动、异步消息传递、流控制等核心机制。并通过代码与日志结合的方式,揭示蓝牙音频流从…

Miniconda安装与VSCode搭建远程Python、Jupyter开发环境

前言 数据科学和机器学习工作流程中&#xff0c;当本地计算机无法满足计算任务的需求时&#xff0c;往往需要一个更强大计算能力的远程环境。另一方面&#xff0c;VSCode由于其轻便和易用性&#xff0c;以及丰富的插件生态系统&#xff0c;一直是远程开发的首选编辑器。本文介绍…

vue3前端开发的基础教程——快速上手

【前言】这里使用的技术栈&#xff1a;fastapivue3pycharm一、创建vue3项目在项目的文件夹使用下面命令创建vue3前端框架代码npm create vitelatest frontend选择框中选择&#xff1a; Framework: VueVariant: JavaScript 或 TypeScript cd frontend npm install启动本地开发np…