自制Slider
需求:
1.绘制一个滑动条,在其上方绘制三角形,
2.当鼠标进入(移动)到三角形区域时显示对应的标注值
3.鼠标双击时三角形时,滑块移动到该位置
4.鼠标单击滑块槽时,滑块也会移动到对应位置//待实现
技术点:
1.实现鼠标单/双击
2.绘制三角形
3.判断某点是否位于三角形区域内
.h文件
#ifndef SLIPERNEW_H
#define SLIPERNEW_H
#include <QSlider>
#include <QStyle>
#include <QStylePainter>
#include <QStyleOptionSlider>
#include <QEvent>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QVector2D>
#include <QVector>
#include <QMap>
#include <QTimer>
#include <QString>
#include <QToolTip>
#define cout qDebug() << "[" << __FILE__ << __LINE__ << "]:"
#define TXT(sr) QStringLiteral(sr)
#define TXT1(sr) QString(sr)
/*需求:
* 1.绘制一个滑动条,在其上方绘制三角形,
* 2.当鼠标进入(移动)到三角形区域时显示对应的标注值
* 3.鼠标双击时三角形时,滑块移动到该位置
* 4.鼠标单击滑块槽时,滑块也会移动到对应位置//待实现
*/
struct TimerInfo
{
int m_iTimer;//时刻
bool m_boolIsDraw;//是否绘制
QString m_strComment;//标注信息
QVector<QVector2D> m_vecCoordinate;//坐标
TimerInfo(int timer, bool isDraw, QString comment)
{
m_iTimer = timer;
m_boolIsDraw = isDraw;
m_strComment = comment;
}
TimerInfo()
{
m_iTimer = 0;
m_boolIsDraw = false;
m_strComment = "";
}
};
class SliperNew:public QSlider
{
public:
SliperNew(QWidget *pParent = nullptr);
protected:
virtual void paintEvent(QPaintEvent *ev) override;
void mouseMoveEvent(QMouseEvent *event) override;
//以下三个函数重载用于实现单双击
void mouseReleaseEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *ev) override;
void mouseDoubleClickEvent(QMouseEvent *ev) override;
public slots:
void TimeOut();
private:
void Init();
void Draw();
void DrawTriangle(int timer, int x, int y);//绘制timer对应的三角形图标
bool PointInTriangle(QVector2D A,QVector2D B,QVector2D C,QVector2D P);//判断P点是否在三角形区域内
int GetTimer(QVector2D P);//如果P点位于某三角形区域内,返回该区域对应时刻
QPoint GetCurrenXY(int timer);//计算timer时刻对应的坐标系
private:
QMap<int, TimerInfo> m_TimerInfo;
int m_intMintimer, m_intMaxtimer;//最小,最大
//以下变量主要用于实现单双击
QTimer *m_timer;
bool m_bLIsLPressed;
bool m_blIShow;
};
#endif // SLIPERNEW_H
.cpp 文件
#include "SliperNew.h"
#include <QSlider>
#include <QDebug>
#include <QMessageBox>
#include <QFile>
SliperNew::SliperNew(QWidget *pParent):QSlider(pParent)
{
setOrientation(Qt::Horizontal);
setFocusPolicy(Qt::NoFocus);
setRange(0, 100);
//add szl
//自定义窗口背景
QPalette pal(this->palette());
pal.setColor(QPalette::Background, Qt::blue);
this->setAutoFillBackground(true);
this->setPalette(pal);
setOrientation(Qt::Horizontal);//设置进度条方向
// setFixedSize(Length, Height);//设置进度条大小
setFixedHeight(50);
// setMinimum(Min);//进度条最小数值
// setMaximum(Max);//进度条最大数值
setSingleStep(1);//单步大小
// setTickInterval(Tick);//刻度个数
setTickPosition(QSlider::TicksAbove);//刻度曲线位置位置
//设置样式
//QSlider::groove:horizontal 背景样式
//QSlider::handle:horizontal 滑块样式
//QSlider::sub-page:horizontal 划过区域的样式
setStyleSheet("QSlider::groove:horizontal{height:12px; left:0px; right:0px; border:0px; border-radius:6px; background:rgba(0,0,0,50);}\
QSlider::handle:horizontal{width:24px; background:#1644B0; border-radius:12px; margin:-6px 0px;}\
QSlider::sub-page:horizontal{background:#4C85FB; border:0px; border-radius:6px;}");
this->setMouseTracking(true);//设置鼠标跟踪
m_bLIsLPressed = false;
m_blIShow = true;
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, [=](){
TimeOut();
});
m_TimerInfo.clear();
Init();
}
QPoint SliperNew::GetCurrenXY(int timer)
{
QStyleOptionSlider opt;
initStyleOption(&opt);
// 获取滑块的大小
QRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
int x = 0;
int y = 0;
x = round((double)((double)((double)(timer - minimum()) / (double)(maximum() - minimum())) * (double)(width() - handle.width()) + (double)(handle.width() / 2.0)));
if (tickPosition() == TicksBothSides || tickPosition() == TicksAbove)
{
y = handle.top();
}
if (tickPosition() == TicksBothSides || tickPosition() == TicksBelow)
{
// int y = handle.bottom() + 5;
// DrawTriangle(x, y);
// to do
}
return QPoint(x, y);
}
//绘制函数
void SliperNew::Draw()
{
if (!m_blIShow || m_intMaxtimer <= 0)
{
return;
}
for (auto bg = m_TimerInfo.begin(); bg != m_TimerInfo.end(); ++bg)
{
if (!bg->m_boolIsDraw)
{
continue;
}
#if 0
// draw tick marks
// do this manually because they are very badly behaved with style sheets
int interval = tickInterval();
if (interval == 0)
{
interval = pageStep();
}
#endif
auto timer = bg->m_iTimer;
if (tickPosition() != NoTicks)
{
auto XY = GetCurrenXY(timer);
DrawTriangle(timer, XY.x(), XY.y());
}
}
}
void SliperNew::paintEvent(QPaintEvent *ev)
{
Draw();
QSlider::paintEvent(ev);
}
void SliperNew::DrawTriangle(int timer, int x, int y)
{
int x1 = x-5;
int y1 = y-5;
int x2 = x;
int y2 = y;
int x3 = x + 5;
int y3 = y - 5;
QVector2D v2dA(QPoint(x1, y1));
QVector2D v2dB(QPoint(x2, y2));
QVector2D v2dC(QPoint(x3, y3));
QVector<QVector2D> vc;
vc.append(v2dA);
vc.append(v2dB);
vc.append(v2dC);
m_TimerInfo[timer].m_vecCoordinate = vc;
//绘制三角图标
QPainter painter(this);
painter.setPen(QColor("#999999"));
painter.setBrush(QColor("#999999"));
QPolygon triangle;
triangle.setPoints(3, x1, y1, x2, y2, x3, y3);//三点坐标
painter.drawPolygon(triangle);
//填充颜色
QPainterPath path;
path.addPolygon(triangle);
painter.fillPath(path, Qt::red);
#if 0
//在对应刻度绘制字体,至少现在不需要
QStylePainter p(this);
QStyleOptionSlider opt;
initStyleOption(&opt);
// 因为刻度间隔比较密集,所以设置文本大小要小上几号,否则会重叠
QFont f = font();
f.setPointSize(f.pointSize() - 2);
QFontMetrics metrics(f);
p.setFont(f);
if (isEnabled()) p.setPen(Qt::blue);
else p.setPen(QColor("#a5a294"));
// 计算刻度文本数值的大小
int tw = metrics.width(QString(" %1 ").arg(maximum()));
int th = metrics.height();
QRect rt = QRect(x - tw / 2, y - 4 - th, tw, th);
// p.drawText(rt, QString::number(i / 10.0f, 'f', 1));
p.drawText(rt, QString::number(3));//绘制文本
//绘制滑动槽
// draw the slider (this is basically copy/pasted from QSlider::paintEvent)
opt.subControls = QStyle::SC_SliderGroove;
p.drawComplexControl(QStyle::CC_Slider, opt);
// draw the slider handle
opt.subControls = QStyle::SC_SliderHandle;
p.drawComplexControl(QStyle::CC_Slider, opt);
#endif
}
bool SliperNew::PointInTriangle(QVector2D A,QVector2D B,QVector2D C,QVector2D P)//P鼠标当前的点位置
{
QVector2D v0 = C - A;
QVector2D v1 = B - A;
QVector2D v2 = P - A;
float dot00 = QVector2D::dotProduct(v0, v0);
float dot01 = QVector2D::dotProduct(v0, v1);
float dot02 = QVector2D::dotProduct(v0, v2);
float dot11 = QVector2D::dotProduct(v1, v1);
float dot12 = QVector2D::dotProduct(v1, v2);
float inverDeno = 1 / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * inverDeno ;
if (u < 0 || u > 1)
{
return false;
}
float v = (dot00 * dot12 - dot01 * dot02) * inverDeno ;
if (v < 0 || v > 1)
{
return false;
}
return u + v <= 1;
}
int SliperNew::GetTimer(QVector2D P)
{
for (auto bg = m_TimerInfo.constBegin(); bg != m_TimerInfo.constEnd(); ++bg)
{
auto v3point = bg.value().m_vecCoordinate;
QVector2D A;
QVector2D B;
QVector2D C;
for (auto bv = v3point.begin(); bv != v3point.end(); ++bv)
{
if (A.isNull())
{
A = *bv;
}
else if(B.isNull())
{
B = *bv;
}
else if(C.isNull())
{
C = *bv;
}
}
bool isIn = PointInTriangle(A,B,C,P);
if (isIn)
{
return bg.key();
}
}
return -1;
}
void SliperNew::mouseMoveEvent(QMouseEvent *event)
{
float P_X = event->x();
float P_Y = event->y();
cout << "P_X" << P_X;
QVector2D v2d;
v2d.setX(P_X);
v2d.setY(P_Y);
int timer = GetTimer(v2d);
if (-1 == timer)
{
//此时应该把提示信息置空,不过不置空也不影响
//QToolTip::showText(QPoint(QCursor().pos().x(),QCursor().pos().y()),"", this);
QToolTip::hideText();
return;
}
cout << m_TimerInfo[timer].m_strComment;
QToolTip::showText(QPoint(QCursor().pos().x(),QCursor().pos().y()),m_TimerInfo[timer].m_strComment, this);
QSlider::mouseMoveEvent(event);
}
//实现鼠标单击,双击,右击功能
void SliperNew::mousePressEvent(QMouseEvent *ev)
{
if (ev->button() == Qt::LeftButton)
{
m_bLIsLPressed = true;
}
}
void SliperNew::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
if (m_bLIsLPressed)//如果已经点击过一次
{
m_timer->start(180);
}
m_bLIsLPressed = false;
}
else if (event->button() == Qt::RightButton)
{
cout << QStringLiteral("鼠标右键");
QSlider::mouseReleaseEvent(event);
}
}
void SliperNew::mouseDoubleClickEvent(QMouseEvent *ev)
{
if (ev->button() == Qt::LeftButton)
{
m_timer->stop();
//这里
QVector2D vc2(ev->x(),ev->y());
int timer = GetTimer(vc2);
if (-1 == timer) return;
this->setValue(timer);
}
}
void SliperNew::TimeOut()
{
m_timer->stop();
cout << QString("鼠标左键单击");
//计算鼠标当前值,并设置
//to do
this->setValue(0);
}
void SliperNew::Init()
{
TimerInfo test1;
test1.m_iTimer = 20;
test1.m_boolIsDraw = true;
test1.m_strComment = TXT1("标注20");
m_TimerInfo.insert(20, test1);
TimerInfo test2;
test2.m_iTimer = 50;
test2.m_boolIsDraw = true;
test2.m_strComment = TXT1("标注50");
m_TimerInfo.insert(50, test2);
TimerInfo test3;
test3.m_iTimer = 90;
test3.m_boolIsDraw = true;
test3.m_strComment = TXT1("标注90");
m_TimerInfo.insert(90, test3);
TimerInfo test4;
test4.m_iTimer = 100;
test4.m_boolIsDraw = true;
test4.m_strComment = TXT1("标注100");
m_TimerInfo.insert(100, test4);
}
参考链接:
原文实在找不到了,如有侵权请联系一下,谢谢-