GUI 编程 —— QT 的 QSlider 鼠标点击定位问题

时间:2021-04-20 09:26:03

这几天打算封装 libvlc 实现一个简单的播放器操作类,用 QT 写 UI 测试程序的时候,发现播放进度的显示控件 QSlider 在处理鼠标点击时,并不能直接定位到鼠标按下的位置。为解决这一问题,我在网上看了几篇博文提供的解决办法,但实现的效果并不理想,主要的问题是:鼠标点击定位时,出现位置偏差。鉴于这,我提供了如下的解决办法。

1. 为 QSlider 控件设置事件过滤

在 QSlider 控件父窗口初始化的时候设置(比如我的代码中就在 Widget 构造函数中设置):

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    ......
    ui->hSlider_duration->installEventFilter(this);
    ......
    ui->hSlider_volume->installEventFilter(this);
    ......
}

2. 重载 eventFilter() 接口

对 QSlider 控件的父级窗口重载 eventFilter() 接口,然后针对 鼠标左键的按下事件 进行过滤判断操作:

bool Widget::eventFilter(QObject * watched, QEvent * event)
{
    if ((event->type() == QEvent::MouseButtonPress) &&
        ((watched == ui->hSlider_duration) || (watched == ui->hSlider_volume)))
    {
        on_slider_mouseLButtonPress(watched, event);
    }

    return QWidget::eventFilter(watched, event);
}

3. 计算点击定位的 value

void Widget::on_slider_mouseLButtonPress(QObject * slider, QEvent * event)
{
    do
    {
        //======================================
        // 只处理 鼠标左键 的按下事件

        QSlider     * sliderCtrl = static_cast< QSlider     * >(slider);
        QMouseEvent * mouseEvent = static_cast< QMouseEvent * >(event );
        if (Qt::LeftButton != mouseEvent->button())
        {
            break;
        }

        //======================================
        // 确定控件操作的基本参数

        int cxctl = 0;  // 滑块宽度
        int cxwnd = 0;  // 滑槽长度
        int mxpos = 0;  // 鼠标按下的位置

        if (Qt::Horizontal == sliderCtrl->orientation())
        {
            // 水平样式的 slider
            cxctl = sliderCtrl->minimumSizeHint().width();
            cxwnd = sliderCtrl->width();
            if (sliderCtrl->invertedAppearance())
                mxpos = cxwnd - mouseEvent->x();
            else
                mxpos = mouseEvent->x();
        }
        else
        {
            // 垂直样式的 slider
            cxctl = sliderCtrl->minimumSizeHint().height();
            cxwnd = sliderCtrl->height();
            if (sliderCtrl->invertedAppearance())
                mxpos = mouseEvent->y();
            else
                mxpos = cxwnd - mouseEvent->y();
        }

        if (cxwnd <= cxctl)
        {
            break;
        }

        //======================================
        // 计算结果,并设置新计算得到的 value 值

        int value = sliderCtrl->minimum() +
                    (int)((sliderCtrl->maximum() - sliderCtrl->minimum()) *
                          ((mxpos - cxctl / 2.0) / (cxwnd - cxctl)));

        if (sliderCtrl->value() == value)
        {
            break;
        }

        sliderCtrl->setValue(value);

        //======================================

    } while (0);
}

4. 头文件中 Widget 类的接口声明如下:

class Widget : public QWidget
{
    ......

    // overrides
protected:
    virtual bool eventFilter(QObject * watched, QEvent * event);

    // inner invoking
protected:
    void on_slider_mouseLButtonPress(QObject * slider, QEvent * event);

    ......
};

另外,我的 libvlc 播放器封装类快完成了,到时在另外一篇文章中介绍。