Qt基于QAbstractSlider打造的控件( 自定义slider控件)

效果图

功能

  • 支持鼠标左键按下并拖动
  • 支持移动到鼠标按下的位置。
  • 支持线显示当前的数值
  • 支持设置滑块等颜色

完整代码

LineProgressBar.h

#pragma once
#include <QAbstractSlider>
#include "WidgetPackSymbol.hpp"

class WIDGET_PACK_API LineProgressBar : public QAbstractSlider
{
	Q_OBJECT
public:
    explicit LineProgressBar(QWidget *parent = nullptr);
    virtual ~LineProgressBar();

	void setRange(qreal nMin, qreal nMax);

    qreal value()const;

	/// 设置填充部分的颜色
	void setFillValueColor(const QString& str);

	/// 设置没有填充部分的颜色
	void setUnfillValueColor(const QString& str);
	/// 
	/// @brief:	设置当前值显示的颜色,
	/// @param:	str  比如: #FF00FF 
	/// @return:	void 
	///				
	void setValueTextColor(const QString& str);

protected:
	virtual void paintEvent(QPaintEvent *event)override;
	virtual void mouseMoveEvent(QMouseEvent *event)override;
	virtual void mousePressEvent(QMouseEvent *event)override;
	virtual void mouseReleaseEvent(QMouseEvent *event)override;

private:
	void updateValue(qreal nCurValue);
	void initValue();
	void initUISetting();

signals:
    void sigValueChanged(qreal nValue);

public slots:
    void slotSetValue(qreal nValue);

private:
	struct stRange
	{
		qreal m_nMin{0.0};
		qreal m_nMax{100.0};
	}m_range;

	qreal m_nCurValue{0};
	qreal m_dbCurPercent{0.0};

	qreal m_dbRadius{10.0};
	bool m_bPressed{false};

	/// 文字的颜色, 比如: "#FF00FF"
	QString m_valueTextColor{"#111111"};

	/// 当前数值划过的数值滑块颜色
	QString		m_currentValueColor{"#39CE99"};
	/// 没有划过的数值滑块颜色
	QString		m_invalidValueColor{"#BBBBBB"};
};

LineProgressBar.cpp

#include "LineProgressBar.h"
#include <QPainter>
#include <QMouseEvent>
#include <QFontMetrics>
#include <QLinearGradient>
#include <QDebug>

LineProgressBar::LineProgressBar(QWidget* parent)
	: QAbstractSlider(parent)
{
	initUISetting();
	initValue();
	setWindowFlags(Qt::FramelessWindowHint);
	setAttribute(Qt::WA_TranslucentBackground);
}

LineProgressBar::~LineProgressBar()
{
}


void LineProgressBar::setRange(qreal nMin, qreal nMax)
{
	if (nMax < nMin)
	{
		nMax = nMin;
	}

	m_range.m_nMin = nMin;
	m_range.m_nMax = nMax;

	updateValue(0);
}

void LineProgressBar::slotSetValue(qreal nValue)
{
	updateValue(nValue);
}

qreal LineProgressBar::value() const
{
	return m_nCurValue;
}

/// 
/// @brief:	setFillRectColor 
/// @param:	lg 
/// @return:	void 
///				
void LineProgressBar::setFillValueColor(const QString& str)
{
	if (m_currentValueColor == str)
	{
		return;
	}

	m_currentValueColor = str;
	update();
}

/// 
/// @brief:	setUnfillValueColor 
/// @param:	str 
/// @return:	void 
///				
void LineProgressBar::setUnfillValueColor(const QString& str)
{
	if (str == m_invalidValueColor)
	{
		return;
	}
	m_invalidValueColor = str;
	update();
}

/// 
/// @brief:	设置当前值显示的颜色 
/// @param:	str 
/// @return:	void 
///				
void LineProgressBar::setValueTextColor(const QString& str)
{
	m_valueTextColor = str;
	update();
}

void LineProgressBar::paintEvent(QPaintEvent* event)
{
	Q_UNUSED(event);

	QPainter painter(this);
	painter.setPen(Qt::NoPen);
	painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
	painter.setBrush(Qt::NoBrush);

	QSizeF currentValueSize{ 50, height()*1.0 };

	// 绘制 Slider 轨道
	QRectF sliderRect(m_dbRadius, height() / 2 - 5, width() - m_dbRadius* 2 - currentValueSize.width(), m_dbRadius);

	{
		// Slider 轨道的尺寸
		qreal trackHeight = m_dbRadius;
		qreal cornerRadius = trackHeight / 2;
		QRectF sliderRectAA(m_dbRadius, (height() - trackHeight) / 2, sliderRect.width(), trackHeight);

		// 绘制未划过的区域(灰色)
		QPainterPath trackPath;
		trackPath.addRoundedRect(sliderRect, cornerRadius, cornerRadius);
		painter.setPen(Qt::NoPen);
		painter.setBrush(QBrush(QColor(m_invalidValueColor)));
		painter.drawPath(trackPath);

		// 绘制划过的区域(绿色)
		QPainterPath filledPath{};

		qreal sliderWidth = sliderRect.width() * m_dbCurPercent;
		QRectF knobRect(sliderRect.x(), sliderRect.y(), sliderWidth, trackHeight);

		filledPath.addRoundedRect(knobRect, m_dbRadius / 2, m_dbRadius / 2);
		painter.setBrush(QColor(m_currentValueColor));
		painter.drawPath(filledPath);

	}

	qreal sliderWidth = sliderRect.width() * 1.0;
	// 绘制滑块
	qreal x = sliderRect.x() + sliderWidth * m_dbCurPercent;
	QRectF knobRect(x - m_dbRadius, sliderRect.center().y() - m_dbRadius, 2 * m_dbRadius, 2 * m_dbRadius);

	//painter.setBrush(QColor("#40508D"));
	painter.setBrush(QColor(m_currentValueColor));
	painter.setPen(Qt::NoPen);
	// 绘制圆形滑块
	painter.drawEllipse(knobRect); 

	painter.setPen(QColor(m_valueTextColor));
	painter.setBrush(Qt::NoBrush);
	QFont ft = painter.font();
	ft.setPointSizeF(10.0);
	painter.setFont(ft);
	QRectF currentValueRect(QPointF(sliderRect.x() + m_dbRadius + sliderRect.width(), 0), currentValueSize);
	painter.drawText(currentValueRect, Qt::AlignLeft | Qt::AlignVCenter, QString::number(m_nCurValue, 'f', 1));
}

void LineProgressBar::mouseMoveEvent(QMouseEvent* event)
{
	if (m_bPressed)
	{
		QSizeF currentValueSize{ 50, height() * 1.0 };

		// 绘制 Slider 轨道
		QRectF sliderRect(m_dbRadius, height() / 2 - 5, width() - m_dbRadius * 2 - currentValueSize.width(), m_dbRadius);
		QPoint pressPoint = event->pos();

		if (true == sliderRect.contains(pressPoint))
		{
			m_dbCurPercent = ((event->pos().x() - sliderRect.x()) * 1.0) / sliderRect.width();
			m_nCurValue = (m_range.m_nMax - m_range.m_nMin) * m_dbCurPercent;
			update();
			emit sigValueChanged(m_nCurValue);
			
		}
		/// 在外面
		else
		{
			int pressXX = pressPoint.x();

			/// 移动到最右侧
			if (pressXX > (sliderRect.width() + sliderRect.x()))
			{
				m_dbCurPercent = 1.0;
				m_nCurValue = (m_range.m_nMax - m_range.m_nMin) * m_dbCurPercent;
				emit sigValueChanged(m_nCurValue);
				update();				
			}
			else if (pressXX < sliderRect.x())
			{
				m_dbCurPercent = 0.0;
				m_nCurValue = (m_range.m_nMax - m_range.m_nMin) * m_dbCurPercent;
				update();
				emit sigValueChanged(m_nCurValue);
			}
			else
			{
				m_dbCurPercent = ((event->pos().x() - sliderRect.x()) * 1.0) / sliderRect.width();
				m_nCurValue = (m_range.m_nMax - m_range.m_nMin) * m_dbCurPercent;
				update();
				emit sigValueChanged(m_nCurValue);
			}
		}
	}

	QWidget::mouseMoveEvent(event);
}

void LineProgressBar::mousePressEvent(QMouseEvent* event)
{ 
	if (event->button() == Qt::LeftButton)
	{
		/// 如果是鼠标左键单击
		if (QEvent::MouseButtonPress == event->type())
		{
			QSizeF currentValueSize{ 50, height() * 1.0 };

			// 绘制 Slider 轨道
			QRectF sliderRect(m_dbRadius, height() / 2 - 5, width() - m_dbRadius * 2 - currentValueSize.width(), m_dbRadius);
			QPoint pressPoint = event->pos();

			QRectF checkSliderRect(sliderRect);
			checkSliderRect.setX(sliderRect.x() - m_dbRadius);
			checkSliderRect.setY(sliderRect.y() - m_dbRadius);
			checkSliderRect.setWidth(sliderRect.width() + m_dbRadius * 2);
			checkSliderRect.setHeight(sliderRect.height() + m_dbRadius * 2);

			if (true == checkSliderRect.contains(pressPoint))
			{
				qreal tmpXX = pressPoint.x() * 1.0;
				if (false == sliderRect.contains(pressPoint))
				{
					if (tmpXX > (sliderRect.x() + sliderRect.width()))
					{
						tmpXX = (sliderRect.x() + sliderRect.width() ) * 1.0;
					}
					else if (tmpXX < sliderRect.x())
					{
						tmpXX = sliderRect.x() * 1.0;
					}
					else
					{

					}
				}

				m_bPressed = true;
				m_dbCurPercent = (tmpXX - sliderRect.x() * 1.0) / sliderRect.width();
				m_nCurValue = (m_range.m_nMax - m_range.m_nMin) * m_dbCurPercent;
				emit sigValueChanged(m_nCurValue);
				update();
			}
		}
	}
}

void LineProgressBar::mouseReleaseEvent(QMouseEvent* event)
{
	if (m_bPressed)
	{
		m_bPressed = false;
	}
	
	QWidget::mouseReleaseEvent(event);
}

void LineProgressBar::updateValue(qreal nCurValue)
{
	if (m_nCurValue == nCurValue)
	{
		return;
	}
	m_nCurValue = nCurValue;
	if (m_range.m_nMax == m_range.m_nMin)
		m_dbCurPercent = 0;
	else
		m_dbCurPercent = m_nCurValue * 1.0 / (m_range.m_nMax - m_range.m_nMin);
	update();
}

void LineProgressBar::initValue()
{
	m_range = { 0, 100 };
	m_nCurValue = 0;
	m_dbCurPercent = 0;
	m_bPressed = false;
}

void LineProgressBar::initUISetting()
{
	//setCursor(QCursor(Qt::PointingHandCursor));
	setMouseTracking(true);
	setFocusPolicy(Qt::StrongFocus);
}
posted @ 2025-04-24 20:10  mohist  阅读(55)  评论(0)    收藏  举报