自定义控件,基于QWidget 实现数值选择器;
1.支持水平、垂直两种方向;
2. 支持设置文字颜色,选中文字颜色,背景色,选中背景色;
3.支持设置后缀显示如 ℃,KG 等
4.可设置选中字体大小;
5.支持设置自定义文字,纯数字时支持设置显示位数如(01, 001)
6.支持拖动后动画效果
//设置数字选项,
//设置文字为选项
使用示例:
CValueSelector * valSelector = new CValueSelector(this, Qt::Vertical);
valSelector ->setShowNum(2);
valSelector ->SetFontSize(30);
valSelector ->SetRange(0, 23);
valSelector ->SetValue(0);
valSelector ->SetColors(QColor("#FFFFFF"), QColor("#FFFFFF"), QColor("#1B1C1E"), QColor(0xEA, 0x55, 0x32, 0x90));
valSelector ->setFixedSize(300, 400);
//信号
connect(valSelector , &CValueSelector::ValueChanged, this, ...);
上代码:
#pragma once
#include <QMouseEvent>
#include <QColor>
#include <QPainter>
#include <QTime>
#include <QDate>
#include <QWidget>
#include <QPropertyAnimation>
//数值选择器
class CValueSelector : public QWidget {
Q_OBJECT
Q_PROPERTY(int32_t deviation READ ReadDeviation WRITE SetDeviation)
public:
explicit CValueSelector(QWidget *parent = nullptr, Qt::Orientation ori = Qt::Horizontal);
virtual ~CValueSelector();
//初始化
bool Init();
//设置值显示个数 推荐为 5 默认
void SetValueCount(uint8_t valNum);
//设置值范围
void SetRange(int32_t min, int32_t max);
//设置值
void SetValue(int32_t val);
//获取当前值
int32_t Value();
//设置字体大小
void SetFontSize(uint8_t fontPix);
//设置文字
void SetTextList(const QStringList & tests);
//设置Color
void SetColors(const QColor & textCol, const QColor & textHCol, const QColor & bkgCol = Qt::NoBrush, const QColor & selectCol = Qt::NoBrush);
//设置值 后缀 如℃,KG, S
void SetValueSuffix(const QString & suffix);
//设置数字的最小显示位数
void setShowNum(uint8_t num);
protected:
virtual bool event(QEvent *event);
virtual void ProcMousePress(QMouseEvent * ev);
virtual void ProcMouseMove(QMouseEvent * ev);
virtual void ProcMouseRelease(QMouseEvent * ev);
virtual void paintEvent(QPaintEvent * ev);
protected:
//绘制背景
virtual void PainterBackgorund(QPainter &painter);
//绘制选中背景
virtual void PainterSelectBackgorund(QPainter &painter);
private:
//绘制值
void PainterVal(QPainter &painter, int32_t num, int32_t deviation);
//更正动画开始
void CorrectionAnimationStart();
//读取偏移量
int32_t ReadDeviation();
//设置偏移量
void SetDeviation(int32_t n);
//获取当前值索引的文字
const QString GetCurrentValText(int32_t num);
signals:
//值更新信号
void ValueChanged(int32_t value);
private:
Qt::Orientation m_orientation; //水平 / 垂直
QPalette m_palette; //调色板
QPropertyAnimation * m_pAnimation;
uint8_t m_valCount;
uint8_t m_showNum; //显示位数
int32_t m_minVal; //最小值
int32_t m_maxVal; //最大值
int32_t m_value; //当前值
int32_t m_valueBak; //当前值备份
bool m_bDragValid; //拖拽有效
int32_t m_pressPos; //鼠标按下POS
int32_t m_deviation; //移动偏移量
uint8_t m_fontSize; //字体大小
QStringList m_textList; //值对应的文字 不设置默认为数字
QString m_valSuffix; //值后缀
};
#include "valueSelector.h"
#include <QDebug>
#define MAX_DISP_VAL_NUM 5 //显示几个数值 默认
//数值选择器实现
CValueSelector::CValueSelector(QWidget *parent, Qt::Orientation ori)
: QWidget(parent), m_orientation(ori), m_palette(), m_pAnimation(nullptr), m_valCount(MAX_DISP_VAL_NUM), m_minVal(0), m_maxVal(100), m_value(0),
m_valueBak(5), m_bDragValid(false), m_pressPos(0), m_deviation(0), m_fontSize(24), m_textList(), m_showNum(0) {
Init();
}
CValueSelector::~CValueSelector() {
}
//初始化
bool CValueSelector::Init() {
setContentsMargins(0, 0, 0, 0);
setWindowFlags(Qt::FramelessWindowHint);
//setAttribute(Qt::WA_TranslucentBackground);
m_pAnimation = new(std::nothrow) QPropertyAnimation(this, "deviation");
if (nullptr == m_pAnimation)
return false;
m_pAnimation->setDuration(250);
m_pAnimation->setEasingCurve(QEasingCurve::OutQuad);
m_palette.setBrush(QPalette::Window, QColor(0x1B1C1E)); //背景默认无
m_palette.setColor(QPalette::Highlight, QColor(0xEA5200)); //高亮背景
m_palette.setColor(QPalette::Text, Qt::white); //文字颜色
m_palette.setColor(QPalette::HighlightedText, Qt::white); //高亮文字白色 (当前值)
return true;
}
//设置值个数 显示个数
void CValueSelector::SetValueCount(uint8_t valNum) {
if (0 != valNum)
m_valCount = valNum;
}
//设置范围
void CValueSelector::SetRange(int32_t min, int32_t max) {
m_minVal = min <= max ? min : max;
m_maxVal = min <= max ? max : min;
if (m_value < m_minVal)
m_value = m_minVal;
if (m_value > m_maxVal)
m_value = m_maxVal;
update();
}
//设置值
void CValueSelector::SetValue(int32_t val) {
m_value = val;
if (m_value < m_minVal)
m_value = m_minVal;
if (m_value > m_maxVal)
m_value = m_maxVal;
update();
}
//获取当前值
int32_t CValueSelector::Value() {
return m_value;
}
//设置字体大小
void CValueSelector::SetFontSize(uint8_t fontPix) {
if (fontPix < 10)
fontPix = 10;
m_fontSize = fontPix;
update();
}
//设置文字 字体最小10
void CValueSelector::SetTextList(const QStringList & tests) {
m_textList = tests;
update();
}
//设置Color
void CValueSelector::SetColors(const QColor & textCol, const QColor & textHCol, const QColor & bkgCol, const QColor & selectCol) {
m_palette.setBrush(QPalette::Window, bkgCol); //背景默认无
m_palette.setColor(QPalette::Highlight, selectCol); //高亮背景
m_palette.setColor(QPalette::Text, textCol); //文字颜色
m_palette.setColor(QPalette::HighlightedText, textHCol); //高亮文字 (当前值)
}
//设置值 后缀 如℃,KG, S
void CValueSelector::SetValueSuffix(const QString & suffix) {
m_valSuffix = suffix;
}
void CValueSelector::setShowNum(uint8_t num) {
m_showNum = num;
}
bool CValueSelector::event(QEvent *event) {
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent * mouseEnv = static_cast<QMouseEvent *>(event);
if (Qt::LeftButton == mouseEnv->button()) {
ProcMousePress(mouseEnv);
//return true;
}
} else if (event->type() == QEvent::MouseMove) {
QMouseEvent * mouseEnv = static_cast<QMouseEvent *>(event);
ProcMouseMove(mouseEnv);
//return true;
} else if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent * mouseEnv = static_cast<QMouseEvent *>(event);
if (Qt::LeftButton == mouseEnv->button()) {
//QPoint point = mouseEnv->pos();
//if (-65535 >= point.x() || -65535 >= point.y())
// return true;
ProcMouseRelease(mouseEnv);
//return true;
}
}
return QWidget::event(event);
}
void CValueSelector::ProcMousePress(QMouseEvent * e) {
m_pAnimation->stop();
m_bDragValid = true;
if (Qt::Horizontal == m_orientation)
m_pressPos = e->pos().x();
else
m_pressPos = e->pos().y();
}
void CValueSelector::ProcMouseMove(QMouseEvent * e) {
if (!m_bDragValid)
return;
if (Qt::Horizontal == m_orientation) {
//数值到边界时,阻止继续往对应方向移动
if ((m_value == m_minVal && e->pos().x() >= m_pressPos) ||
(m_value == m_maxVal && e->pos().x() <= m_pressPos))
return;
m_deviation = e->pos().x() - m_pressPos;
//限制一次最多一格
if (m_deviation > width() / m_valCount)
m_deviation = width() / m_valCount;
else if (m_deviation < -width() / m_valCount)
m_deviation = -(width()) / m_valCount;
} else {
//数值到边界时,阻止继续往对应方向移动
if ((m_value == m_minVal && e->pos().y() >= m_pressPos) ||
(m_value == m_maxVal && e->pos().y() <= m_pressPos))
return;
m_deviation = e->pos().y() - m_pressPos;
//限制一次最多一格
if (m_deviation > height() / m_valCount)
m_deviation = height() / m_valCount;
else if (m_deviation < -height() / m_valCount)
m_deviation = -(height()) / m_valCount;
}
update();
}
void CValueSelector::ProcMouseRelease(QMouseEvent * e) {
if (m_bDragValid) {
m_bDragValid = false;
if (m_deviation) //有偏移才启动动画纠正
CorrectionAnimationStart();
}
if (m_valueBak != m_value) { //值更改了 发信号
emit ValueChanged(m_value);
m_valueBak = m_value;
}
}
//重绘事件
void CValueSelector::paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
PainterBackgorund(painter); //绘制背景
PainterSelectBackgorund(painter); //绘制高亮背景
int32_t lenth = width();
if (Qt::Vertical == m_orientation)
lenth = height();
if (m_deviation >= lenth / m_valCount && m_value > m_minVal) {
m_pressPos += lenth / m_valCount;
m_deviation -= lenth / m_valCount;
--m_value;
}
if (m_deviation <= -lenth / m_valCount && m_value < m_maxVal) {
m_pressPos -= lenth / m_valCount;
m_deviation += lenth / m_valCount;
++m_value;
}
//绘制数值
for (int16_t i = -(m_valCount / 2); i <= m_valCount / 2; ++i) {
if (m_value + i < m_minVal || m_value + i > m_maxVal)
continue;
PainterVal(painter, m_value + i, m_deviation + lenth / m_valCount * i);
}
}
//绘制值
void CValueSelector::PainterVal(QPainter &painter, int32_t num, int32_t deviation) {
int32_t lenth = width();
if (Qt::Vertical == m_orientation)
lenth = height();
//qreal scale = qreal(lenth * 2 - qAbs(deviation)) / (lenth * 2);
double size = (qAbs(deviation) / (lenth / 2.0) * 0.5);
if (size > 0.5)
size = 0.5;
QColor col;
QString text = GetCurrentValText(num);
if (m_value == num) {
text += m_valSuffix;
col = m_palette.color(QPalette::HighlightedText);
} else
col = m_palette.color(QPalette::Text);
col.setAlpha(col.alpha() - col.alpha() * size);
QFont font;
font.setPixelSize(m_fontSize - m_fontSize * size);
painter.setFont(font);
painter.setPen(col);
int32_t offset = lenth / 2 + deviation - lenth / m_valCount / 2;
if (Qt::Vertical == m_orientation)
painter.drawText(QRectF(0, offset, width(), height() / m_valCount), Qt::AlignCenter, text);
else
painter.drawText(QRectF(offset, 0, width() / m_valCount, height()), Qt::AlignCenter, text);
}
//更正动画开始
void CValueSelector::CorrectionAnimationStart() {
int32_t lenth = width();
if (Qt::Vertical == m_orientation)
lenth = height();
//将文字矫正到中心
if (m_deviation > lenth / m_valCount / 2) {
m_pAnimation->setStartValue(-(lenth / m_valCount - m_deviation));
m_pAnimation->setEndValue(0);
m_value--;
} else if (m_deviation < -lenth / m_valCount / 2) {
m_pAnimation->setStartValue(lenth / m_valCount + m_deviation);
m_pAnimation->setEndValue(0);
m_value++;
} else {
m_pAnimation->setStartValue(m_deviation);
m_pAnimation->setEndValue(0);
}
m_pAnimation->start();
}
//获取偏移量
int32_t CValueSelector::ReadDeviation() {
return m_deviation;
}
//设置偏移量
void CValueSelector::SetDeviation(int32_t n) {
m_deviation = n;
update();
}
//获取当前值索引的文字
const QString CValueSelector::GetCurrentValText(int32_t num) {
int32_t valIdx = num - m_minVal;
if (valIdx >= m_textList.size())
return m_showNum != 0 ? QString("%1").arg(valIdx, m_showNum, 10, QLatin1Char('0')) : QString("%1").arg(num);
return m_textList.at(valIdx);
}
//绘制背景
void CValueSelector::PainterBackgorund(QPainter & painter) {
const QColor & bkgCol = m_palette.color(QPalette::Window);
if (bkgCol == Qt::NoBrush)
return;
painter.save();
painter.setPen(Qt::NoPen);
painter.setBrush(bkgCol);
painter.drawRect(rect());
painter.restore();
}
//绘制选中背景
void CValueSelector::PainterSelectBackgorund(QPainter & painter) {
const QColor & bkgCol = m_palette.color(QPalette::Highlight);
if (bkgCol == Qt::NoBrush)
return;
painter.save();
painter.setPen(Qt::NoPen);
QRect selectRect;
//QLinearGradient linearGradient; //渐变支持
if (Qt::Horizontal == m_orientation) {
selectRect.setRect(width() / m_valCount * (m_valCount / 2), 0, width() / m_valCount, height());
//linearGradient = QLinearGradient(selectRect.topLeft(), selectRect.bottomLeft());
} else {
selectRect.setRect(0, height() / m_valCount * (m_valCount / 2), width(), height() / m_valCount);
//linearGradient = QLinearGradient(selectRect.topLeft(), selectRect.topRight());
}
/*QColor col = bkgCol;
col.setAlpha(0);
linearGradient.setColorAt(0.0, col);
linearGradient.setColorAt(0.3, bkgCol);
linearGradient.setColorAt(0.7, bkgCol);
linearGradient.setColorAt(1.0, col);
painter.setBrush(linearGradient);*/
painter.setBrush(bkgCol);
painter.drawRect(selectRect);
painter.restore();
}