Qt 自定义尺子控件

废话不多说,先上效果图,下面贴出全部代码,有需求自行修改。

截图

1. RulerSlider.h文件

#ifndef RULERSLIDER_H

#define RULERSLIDER_H

#define SMALL_Y 5 //小刻度

#define MIDDLE_Y 7 //中刻度

#define BIG_Y 10  //大刻度

#define DIFFERVALUE 1 //最小差值(最大值与最小值的差)

#define SIDEDISTANCE 10 //尺子距离左边的距离

#define SIDEDISTANCER 50 //尺子距离右边的距离

#define HANDLEWIDTH 10 //滑块的宽度

#define HANDLEHEIGHT 15 //滑块的高度

#define MININTERVSL 10 //最小刻度之间距离

#define MAXINTERVSL 20 //最大刻度之间距离

#define DISTANCEMOUSE 20 //鼠标悬停距离箭头的位置

class RulerSlider : public QSlider

{

public:

    RulerSlider(QWidget *parent = nullptr);

    ~RulerSlider();

    //刻度之间的距离

    qreal sliderInterval = 1;

    //显示刻度值

    QLabel *valueLabel;

    //滑块

    QLabel *handleLabel;

    //鼠标是否点击

    bool mouseIsClick = false;

    //设置控件的范围

    void setRulerSliderRange(int min,int max);

    //设置当前值

    void setRulerSliderValue(int value);

private:

    //绘制尺子背景

    void drawRulerBackgroud(QPainter *painter);

    //绘制刻度线与值

    void drawSliderMark(QPainter *painter);

    //根据坐标位置计算当前值

    void eventPosGetValue(QMouseEvent *ev);

    //鼠标事件

    void mouseFilterEvent(QMouseEvent *event);

protected:

    //重新绘制

    void paintEvent(QPaintEvent *);

    //过滤器

    bool eventFilter(QObject *watched,QEvent *event);

};

#endif // RULERSLIDER_H

2.RulerSlider.cpp 文件

#include "RulerSlider.h"

#include "QPainter"

#include "math.h"

RulerSlider::RulerSlider(QWidget *parent):QSlider(parent)

{

    //注册过滤器

    installEventFilter(this);

    //鼠标事件追踪

    setMouseTracking(true);

    //显示当前值label

    valueLabel = new QLabel(this);

    valueLabel->setStyleSheet("background-color: rgb(0, 0, 0);font-size:14px;color:white;border:1px solid black");

    valueLabel->setAlignment(Qt::AlignCenter);

    valueLabel->hide();

    //滑块

    handleLabel = new QLabel(this);

    handleLabel->setFixedSize(20,15);

    handleLabel->raise();

    QImage image,result;

    image.load(":/images/handle.png");//temppath为图片的路径

    result = image.scaled(handleLabel->width(), handleLabel->height(),Qt::IgnoreAspectRatio, Qt::SmoothTransformation);//放缩图片,以固定大小显示

    handleLabel->setPixmap(QPixmap::fromImage(result));//在Label控件上显示图片

}

RulerSlider::~RulerSlider()

{

}

/**

* 绘制

* @brief RulerSlider::paintEvent

*/

void RulerSlider::paintEvent(QPaintEvent *)

{

    QPainter painter;

    painter.begin(this);

    //抗锯齿

    painter.setRenderHint(QPainter::Antialiasing);

    drawRulerBackgroud(&painter);

    drawSliderMark(&painter);

    painter.end();

}

/**

* 绘制尺子背景

* @brief RulerSlider::drawRulerBackgroud

* @param painter

*/

void RulerSlider::drawRulerBackgroud(QPainter *painter)

{

    QPointF topLeftPot3(0,0);

    QPointF bottomRightPot3(width(),height());

    painter->setPen(Qt::white);

    painter->setBrush(QColor(61,84,110));

    painter->drawRect(QRectF(topLeftPot3,bottomRightPot3));

}

/**

* 绘制刻度与值

* @brief RulerSlider::drawSliderMark

* @param painter

*/

void RulerSlider::drawSliderMark(QPainter *painter)

{

    painter->setPen(Qt::gray);

    painter->setBrush(QColor(128,128,128));

    //计算刻度之间的间隔最小为10

    int dis = maximum()-minimum();

    //绘制横坐标开始起点

    qreal startX = SIDEDISTANCE;

    for (uint16_t i=minimum();i<=(minimum()+dis/singleStep());i++) {

        //偶数

        if(i%2 == 0 && i%5 == 0){

            painter->drawRect(QRectF(startX,0,0.1,BIG_Y));

            //绘制刻度值

            float value = round(minimum()+((i-minimum())*singleStep()));

            QString strValue = tr("%1").arg(value);

            int fontWidth = fontMetrics().width(strValue)/2;

            painter->drawText(QPointF(startX-fontWidth,BIG_Y+15),strValue);

        }else {

            if(i%5 == 0){

                painter->drawRect(QRectF(startX,0,0.1,MIDDLE_Y));

            }else{

                painter->drawRect(QRectF(startX,0,0.1,SMALL_Y));

            }

        }

        startX+=sliderInterval;

    }

}

/**

* 根据鼠标位置获取当前值

* @brief RulerSlider::eventPosGetValue

* @param ev

*/

void RulerSlider::eventPosGetValue(QMouseEvent *ev)

{

    //光标所在矩形范围

    double posWidth = (double)width()-SIDEDISTANCER-SIDEDISTANCE;

    //鼠标光标位置

    double xpos = ev->pos().x();

    if(xpos >=SIDEDISTANCE&&xpos<=width()-SIDEDISTANCER){

        //根据光标计算

        double posValue = (xpos-SIDEDISTANCE)/posWidth;

        int value =round(posValue * (maximum()-minimum()) + minimum());

        handleLabel->move(xpos-handleLabel->width()/2,0);

        qDebug()<<"结果值"<<value;

        setValue(value);

    }

}

/**

* 设置slider范围

* @brief RulerSlider::setRulerSliderRange

* @param min

* @param max

*/

void RulerSlider::setRulerSliderRange(int min, int max)

{

    //当设置的最小值为负数或0时,设置最小值为0

    int minNum = min;

    int maxNum = max;

    if(min<=0){

        minNum = 0;

    }

    //当最大值与最小值之间差值

    if(max-min <= DIFFERVALUE){

        maxNum = min+DIFFERVALUE;

    }

    qDebug()<<"滑动条"<<value()<<minNum<<maxNum;

    setMaximum(maxNum);

    setMinimum(minNum);

    if(value()>minNum){

        int dis = maxNum-minNum;

        sliderInterval = (float)(width()-SIDEDISTANCE-SIDEDISTANCER)/(dis/singleStep());

        double pos = ((value()-minNum)/singleStep())*sliderInterval+SIDEDISTANCE;

        handleLabel->move(pos-handleLabel->width()/2,0);

    }else {

        handleLabel->move(SIDEDISTANCE-handleLabel->width()/2,0);

    }

    update();

}

/**

* 设置当前值

* @brief RulerSlider::setRulerSliderValue

* @param value

*/

void RulerSlider::setRulerSliderValue(int value)

{

    int dis = maximum()-minimum();

    sliderInterval = (float)(width()-SIDEDISTANCE-SIDEDISTANCER)/(dis/singleStep());

    double pos = ((value-minimum())/singleStep())*sliderInterval+SIDEDISTANCE;

    handleLabel->move(pos-handleLabel->width()/2,0);

    setValue(value);

}

/**

* 鼠标移动事件

* 当mouseIsClick为true时拖动false悬停

* @brief RulerSlider::mouseFilterEvent

* @param event

*/

void RulerSlider::mouseFilterEvent(QMouseEvent *event)

{

    //鼠标光标位置

    double xpos = event->pos().x();

    if(xpos >=SIDEDISTANCE&&xpos<=width()-SIDEDISTANCER){

        if(mouseIsClick){

            handleLabel->move(xpos-handleLabel->width()/2,0);

            if(valueLabel->isVisible()){

                valueLabel->hide();

            }

        }else {

            //光标所在矩形范围

            double posWidth = (double)width()-SIDEDISTANCER-SIDEDISTANCE;

            //根据光标计算

            double posValue = (xpos-SIDEDISTANCE)/posWidth;

            int value =round(posValue * (maximum()-minimum()) + minimum());

            if(valueLabel->isHidden()){

                valueLabel->show();

            }

            if(value >= minimum() && value <= maximum()){

                valueLabel->setText(QString::number(value));

                valueLabel->adjustSize();

                valueLabel->move(xpos+DISTANCEMOUSE,height()-valueLabel->height());

                valueLabel->raise();

            }

        }

    }

}

/**

* 事件过滤

* @brief RulerSlider::eventFilter

* @param watched

* @param event

* @return

*/

bool RulerSlider::eventFilter(QObject *watched, QEvent *event)

{

    //鼠标事件

    QMouseEvent *mouseEvent = static_cast(event);

    //鼠标离开

    if(QEvent::HoverLeave == event->type()&&!valueLabel->isHidden()){

        valueLabel->hide();

    }

    //鼠标悬停与拖动

    if(QEvent::HoverMove == event->type()){

        mouseFilterEvent(mouseEvent);

    }

    //鼠标点击事件

    if(QEvent::MouseButtonPress == event->type()){

        if(mouseEvent->button() & Qt::LeftButton){

            mouseIsClick = true;

        }

    }

    //鼠标释放事件

    if(QEvent::MouseButtonRelease == event->type()){

        //注意应先调用父类的鼠标点击处理事件,这样可以不影响拖动的情况

        QSlider::mouseReleaseEvent(mouseEvent);

        eventPosGetValue(mouseEvent);

        mouseIsClick = false;

    }

    return QSlider::eventFilter(watched,event);

}

3. 图片文件


handle.png


4. 使用

RulerSlider *slider = new RulerSlider(this);

    slider->setFixedSize(this->width(),50);

    slider->setSingleStep(1);

    slider->setRulerSliderRange(0,100);

    slider->setRulerSliderValue(50);

QObject::connect(slider,SIGNAL(valueChanged(int)),this,SLOT(sliderValueChanged(int)));

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,548评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,497评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,990评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,618评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,618评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,246评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,819评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,725评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,268评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,356评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,488评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,181评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,862评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,331评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,445评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,897评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,500评论 2 359