【Qt5】写一个更容易鼠标点击并带游标的QSlider的子类

2018.02.12更新

优化了游标的控件窗口属性,游标不再受任何组件的范围限制;优化了游标出现的位置;优化了鼠标点击得到的值,四舍五入得到。

2018.02.22更新

使用自定义信号sig_triggeredToValue,控件被触发时发射此信号,传递出滑块变化后的值,不再使用sliderMoved()信号。避免了键盘控制滑块移动不能产生sliderMoved()信号的问题。

2018.04.10更新

重写了控件的setVisible(), setEnable(), setDisable()方法,保证在控件不可见或无效状态下,不显示游标。


Qt自带的QSlider控件提供了一个滑块控制,但是鼠标点击时,只能步进,使用起来很不方便。

另外,没有能够方便的区分出用户触发和setValue()两种情况导致的值变化的信号。

所以本人继承QSlider,写了QtSliderEasyClick控件。本控件可以点哪到哪,同时自定义了信号sig_triggeredToValue,用于表示滑块被触发时导致的值变化,可以区分出用户触发导致的值变化,另外还增加了游标显示鼠标位置对应的值,效果如图(鼠标位置在游标68下面,截屏不显示鼠标哈):


废话不多说,上核心代码,完整工程链接https://gitee.com/pplxlee/QtSliderEasyClick

#include "qtslidereasyclick.h"
#include <QCursor>

QtSliderEasyClick::QtSliderEasyClick(QWidget *parent) :
    QSlider(parent),
    triggered_(false)
{
    // 连接触发导致的值改变时的相关信号槽
    connect(this, &QtSliderEasyClick::actionTriggered, this, &QtSliderEasyClick::__slot_actionTriggered);
    connect(this, &QtSliderEasyClick::valueChanged, this, &QtSliderEasyClick::__slot_valueChanged);

    label_ = new QLabel(this);
    // 使其显示的位置不受父控件限制,也不回有窗口框架和任务栏任务,也不会获得焦点
    label_->setWindowFlags(Qt::FramelessWindowHint|Qt::Tool);
    label_->setFocusPolicy(Qt::NoFocus);
    label_->setAttribute(Qt::WA_ShowWithoutActivating,true);

    // 设置标签字体大小
    QFont font;
    font.setPointSize(11);
    label_->setFont(font);
    // 允许在鼠标不点击时跟踪鼠标位置
    setMouseTracking(true);

    // 保证点击游标时,不会产生中间值,而是一次性跳跃到点击处
    this->setPageStep(0);
}

QtSliderEasyClick::~QtSliderEasyClick()
{
}

void QtSliderEasyClick::setVisible(bool visible)
{
    QSlider::setVisible(visible);
    // 保证设置不可见后,label_不可见
    if(!visible)
    {
        label_->hide();
    }
}

void QtSliderEasyClick::setDisabled(bool disabled)
{
    QSlider::setDisabled(disabled);
    // 保证设置无效后,label_不可见
    if(disabled)
    {
        label_->hide();
    }
}

void QtSliderEasyClick::setEnabled(bool enabled)
{
    QSlider::setEnabled(enabled);
    // 保证设置无效后,label_不可见
    if(!enabled)
    {
        label_->hide();
    }
}

void QtSliderEasyClick::__slot_actionTriggered(int)
{
    // 滑块空间被触发时,触发状态为真
    triggered_ = true;
}

void QtSliderEasyClick::__slot_valueChanged(int value)
{
    // 触发状态为真时,发射触发值信号,并还原触发状态
    if(triggered_)
    {
        sig_triggeredToValue(value);
        triggered_ = false;
    }
}

void QtSliderEasyClick::mousePressEvent(QMouseEvent *ev)
{
    // 注意应先调用父类的鼠标点击处理事件,否则无法拖动
    QSlider::mousePressEvent(ev);

    // 根据鼠标位置计算值并设置值
    int value = __calcuValueFromPos(ev->pos());
    setValue(value);
}

void QtSliderEasyClick::mouseMoveEvent(QMouseEvent *ev)
{
    QSlider::mouseMoveEvent(ev);

    if(!this->isEnabled())
    {
        return;
    }

    int posValue = __calcuValueFromPos(ev->pos());
    label_->setText(valueToText(posValue));
    label_->adjustSize();

    // 获取算本控件在屏幕中的位置
    QPoint this_pos = this->mapToGlobal(QPoint(0,0));
    // 计算得到标签位置范围
    int min_pos_x_lable = this_pos.x();
    int max_pos_x_lable = this_pos.x() + this->width() - label_->width();
    int pos_y_lable = this_pos.y() - label_->height() - 5;
    // 根据鼠标位置和标签范围,计算新的标签位置
    int pos_x_label = QCursor::pos().x() - (label_->width()>>1);
    pos_x_label = (pos_x_label<min_pos_x_lable)?min_pos_x_lable:pos_x_label;
    pos_x_label = (pos_x_label>max_pos_x_lable)?max_pos_x_lable:pos_x_label;
    // 移动标签到计算得到的位置
    label_->move(pos_x_label, pos_y_lable);
}

void QtSliderEasyClick::enterEvent(QEvent *ev)
{
    QSlider::enterEvent(ev);

    if(!this->isEnabled())
    {
        return;
    }

    label_->show();
}

void QtSliderEasyClick::leaveEvent(QEvent *ev)
{
    QSlider::leaveEvent(ev);
    label_->hide();
}

QString QtSliderEasyClick::valueToText(int value)
{
    return QString::number(value);
}

int QtSliderEasyClick::__calcuValueFromPos(const QPoint &pos)
{
    // 使用四舍五入
    int value = qRound((maximum() - minimum()) * pos.x() / (double)this->width() + minimum());
    // 限制返回值在取值范围内
    value = (value > maximum())?maximum():value;
    return (value<minimum())?minimum():value;
}



  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值