1. 程式人生 > >【Qt5】寫一個更容易滑鼠點選並帶遊標的QSlider的子類

【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下面,截圖不顯示滑鼠哈):


#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;
}