組件運行效果展示
組件概述
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;
};
數據結構的關鍵設計:
- 數值管理:
minValue
、maxValue
、value
三元組管理滑塊狀態 - 交互狀態:
isPressVertex
、isDrag
標志位控制拖拽邏輯 - 性能優化:
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 站視頻
感謝支持和關注,如果對項目感興趣,請點贊、收藏和轉發!