自制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);
}

参考链接:
原文实在找不到了,如有侵权请联系一下,谢谢-

posted @ 2022-08-07 16:52  test369  阅读(114)  评论(0编辑  收藏  举报