qt浏览文件支持惯性

cpp 复制代码
#include <QApplication>
#include <QListWidget>
#include <QScroller>
#include <QScrollerProperties>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 创建列表控件并添加示例项
    QListWidget listWidget;
    for (int i = 0; i < 100; ++i) {
        listWidget.addItem(QString("文件 %1").arg(i));
    }
    listWidget.resize(400, 300);
    listWidget.show();

    // 启用惯性滚动:触摸手势
    // QScroller::grabGesture(listWidget.viewport(), QScroller::TouchGesture);

    // 或者启用鼠标左键拖动惯性
    QScroller::grabGesture(listWidget.viewport(), QScroller::LeftMouseButtonGesture);

    // 获取Scroller对象并设置参数
    QScroller *scroller = QScroller::scroller(listWidget.viewport());
    QScrollerProperties properties = scroller->scrollerProperties();

    // 调整减速度因子(0~1,值越小惯性越长)
    properties.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.05);

    // 设置最小拖动触发距离(防止误触)
    properties.setScrollMetric(QScrollerProperties::DragStartDistance, 0.001);

    scroller->setScrollerProperties(properties);

    return app.exec();
}

示例二:

cpp 复制代码
// inertialscroller.h
#ifndef INERTIALSCROLLER_H
#define INERTIALSCROLLER_H

#include <QAbstractItemView>
#include <QEvent>
#include <QMouseEvent>
#include <QObject>
#include <QPointF>
#include <QScrollBar>
#include <QTableView>
#include <QTime>
#include <QTimer>
#include <QWheelEvent>


/**
 * @brief 惯性滚动管理器类
 *
 * 为QTableView或其他QAbstractItemView子类提供惯性滚动功能。
 * 追踪鼠标拖拽事件并在释放后应用速度衰减算法模拟惯性滚动效果。
 */
class InertialScroller : public QObject
{
    Q_OBJECT

public:
    /**
     * @brief 构造函数
     * @param view 需要添加惯性滚动功能的视图
     * @param parent 父对象
     */
    explicit InertialScroller(QAbstractItemView *view, QObject *parent = nullptr);

    /**
     * @brief 析构函数
     */
    ~InertialScroller();

    /**
     * @brief 设置衰减系数,控制惯性滚动的衰减速度
     * @param factor 衰减系数(0.0-1.0),越小衰减越快
     */
    void setDecelerationFactor(qreal factor);

    /**
     * @brief 设置惯性滚动的最大初始速度
     * @param speed 最大速度(像素/秒)
     */
    void setMaxSpeed(qreal speed);

    /**
     * @brief 设置停止惯性滚动的最小速度阈值
     * @param threshold 速度阈值(像素/秒)
     */
    void setStopThreshold(qreal threshold);

    /**
     * @brief 设置滚轮惯性滚动的强度系数
     * @param factor 强度系数,数值越大惯性效果越强
     */
    void setWheelSpeedFactor(qreal factor);

    /**
     * @brief 启用或禁用滚轮惯性滚动
     * @param enable 是否启用
     */
    void setWheelInertiaEnabled(bool enable);

protected:
    /**
     * @brief 事件过滤器
     * 拦截视图的鼠标事件处理惯性滚动
     */
    bool eventFilter(QObject *watched, QEvent *event) override;

private:
    QAbstractItemView *m_view;                    // 关联的视图
    QTimer             m_timer;                   // 惯性滚动计时器
    QTime              m_lastTime;                // 上次事件时间
    QPointF            m_lastPos;                 // 上次鼠标位置
    QPointF            m_velocity;                // 当前速度(x和y方向)
    bool               m_isPressed     = false;   // 鼠标是否按下
    bool               m_isScrolling   = false;   // 是否正在滚动
    qreal              m_deceleration  = 0.95;    // 衰减系数(0.0-1.0)
    qreal              m_maxSpeed      = 2000.0;  // 最大速度(像素/秒)
    qreal              m_stopThreshold = 10.0;    // 停止阈值(像素/秒)
    QVector<QPointF>   m_positions;               // 最近的鼠标位置记录
    QVector<qint64>    m_timestamps;              // 最近的鼠标位置时间戳
    qreal              m_wheelSpeedFactor = 15.0;  // 滚轮速度系数
    bool               m_wheelInertiaEnabled = true; // 是否启用滚轮惯性

private slots:
    /**
     * @brief 执行惯性滚动步骤
     */
    void scrollStep();

    /**
     * @brief 开始惯性滚动
     */
    void startScrolling(const QPointF &velocity);

    /**
     * @brief 停止惯性滚动
     */
    void stopScrolling();
};

#endif  // INERTIALSCROLLER_H
cpp 复制代码
// inertialscroller.cpp
#include <QDateTime>
#include <QDebug>
#include <QtMath>

#include "inertialscroller.h"


InertialScroller::InertialScroller(QAbstractItemView *view, QObject *parent) : QObject(parent), m_view(view)
{
    // 安装事件过滤器
    m_view->viewport()->installEventFilter(this);

    // 初始化计时器
    m_timer.setInterval(16);  // 约60FPS
    connect(&m_timer, &QTimer::timeout, this, &InertialScroller::scrollStep);

    // 初始化历史记录容器
    m_positions.reserve(10);
    m_timestamps.reserve(10);
}

InertialScroller::~InertialScroller()
{
    if(m_view && m_view->viewport())
    {
        m_view->viewport()->removeEventFilter(this);
    }
    m_timer.stop();
}

void InertialScroller::setDecelerationFactor(qreal factor)
{
    // 确保值在有效范围内
    m_deceleration = qBound(0.1, factor, 0.99);
}

void InertialScroller::setMaxSpeed(qreal speed)
{
    m_maxSpeed = qMax(1.0, speed);
}

void InertialScroller::setStopThreshold(qreal threshold)
{
    m_stopThreshold = qMax(1.0, threshold);
}

void InertialScroller::setWheelSpeedFactor(qreal factor)
{
    m_wheelSpeedFactor = qMax(1.0, factor);
}

void InertialScroller::setWheelInertiaEnabled(bool enable)
{
    m_wheelInertiaEnabled = enable;
}

bool InertialScroller::eventFilter(QObject *watched, QEvent *event)
{
    if(watched != m_view->viewport())
        return false;

    switch(event->type())
    {
        case QEvent::MouseButtonPress: {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
            if(mouseEvent->button() == Qt::LeftButton)
            {
                stopScrolling();
                m_isPressed = true;
                m_lastPos   = mouseEvent->pos();
                m_lastTime.start();

                // 清空历史记录
                m_positions.clear();
                m_timestamps.clear();

                // 记录初始位置和时间
                m_positions.append(mouseEvent->pos());
                m_timestamps.append(QDateTime::currentMSecsSinceEpoch());
            }
            break;
        }

        case QEvent::MouseMove: {
            if(m_isPressed)
            {
                QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
                QPointF      currentPos = mouseEvent->pos();

                // 计算移动距离
                QPointF delta = m_lastPos - currentPos;

                // 添加到历史记录
                m_positions.append(currentPos);
                m_timestamps.append(QDateTime::currentMSecsSinceEpoch());

                // 仅保留最近的5个记录点
                while(m_positions.size() > 5)
                {
                    m_positions.removeFirst();
                    m_timestamps.removeFirst();
                }

                // 滚动视图
                QScrollBar *vScrollBar = m_view->verticalScrollBar();
                QScrollBar *hScrollBar = m_view->horizontalScrollBar();

                if(vScrollBar && vScrollBar->isVisible())
                {
                    vScrollBar->setValue(vScrollBar->value() + delta.y());
                }

                if(hScrollBar && hScrollBar->isVisible())
                {
                    hScrollBar->setValue(hScrollBar->value() + delta.x());
                }

                m_lastPos = currentPos;
            }
            break;
        }

        case QEvent::MouseButtonRelease: {
            if(m_isPressed)
            {
                QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
                if(mouseEvent->button() == Qt::LeftButton)
                {
                    m_isPressed = false;

                    // 如果有足够的历史数据来计算速度
                    if(m_positions.size() >= 2)
                    {
                        // 使用最后几个点的平均速度
                        QPointF totalVelocity(0, 0);
                        int     count = 0;

                        for(int i = 1; i < m_positions.size(); ++i)
                        {
                            qint64 timeDelta = m_timestamps[i] - m_timestamps[i - 1];
                            if(timeDelta > 0)
                            {
                                QPointF posDelta = m_positions[i - 1] - m_positions[i];
                                // 速度 = 距离/时间,单位为像素/秒
                                QPointF velocity = posDelta * (1000.0 / timeDelta);
                                totalVelocity += velocity;
                                count++;
                            }
                        }

                        if(count > 0)
                        {
                            QPointF avgVelocity = totalVelocity / count;

                            // 限制最大速度
                            qreal speedX = qBound(-m_maxSpeed, avgVelocity.x(), m_maxSpeed);
                            qreal speedY = qBound(-m_maxSpeed, avgVelocity.y(), m_maxSpeed);

                            // 如果速度足够大,启动惯性滚动
                            QPointF finalVelocity(speedX, speedY);
                            qreal   speed = qSqrt(speedX * speedX + speedY * speedY);

                            if(speed > m_stopThreshold)
                            {
                                startScrolling(finalVelocity);
                            }
                        }
                    }
                }
            }
            break;
        }

        case QEvent::Wheel: {
            // 如果滚轮惯性被禁用,不拦截事件
            if(!m_wheelInertiaEnabled)
            {
                stopScrolling();
                return false;
            }

            // 处理鼠标滚轮事件产生惯性滚动
            QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);

            // 停止当前的惯性滚动
            stopScrolling();

            // 计算滚轮滚动的速度和方向
            QPoint pixelDelta = wheelEvent->pixelDelta();
            QPoint angleDelta = wheelEvent->angleDelta();

            // 优先使用像素增量,如果没有则使用角度增量
            QPointF scrollAmount;
            if(!pixelDelta.isNull())
            {
                scrollAmount = QPointF(pixelDelta);
            } else if(!angleDelta.isNull())
            {
                // 标准鼠标滚轮:角度转为像素(大约8度为一个标准滚动单位)
                scrollAmount = QPointF(angleDelta) / 8.0;
            }

            // 应用滚动方向(正数向下/右,负数向上/左)
            scrollAmount = -scrollAmount;

            // 缩放滚动量来创建合适的惯性效果
            QPointF velocity = scrollAmount * m_wheelSpeedFactor;

            // 如果速度足够大,开始惯性滚动
            qreal speed = qSqrt(velocity.x() * velocity.x() + velocity.y() * velocity.y());
            if(speed > m_stopThreshold)
            {
                startScrolling(velocity);

                // 先手动执行一次滚动,让响应更快
                QScrollBar *vScrollBar = m_view->verticalScrollBar();
                QScrollBar *hScrollBar = m_view->horizontalScrollBar();

                if(vScrollBar && vScrollBar->isVisible() && !angleDelta.isNull())
                {
                    vScrollBar->setValue(vScrollBar->value() + angleDelta.y() / 120 * vScrollBar->singleStep());
                }

                if(hScrollBar && hScrollBar->isVisible() && !angleDelta.isNull())
                {
                    hScrollBar->setValue(hScrollBar->value() + angleDelta.x() / 120 * hScrollBar->singleStep());
                }

                return true;  // 拦截滚轮事件,自己处理
            }
            break;
        }

        default:
            break;
    }

    // 继续传递事件,不拦截
    return false;
}

void InertialScroller::scrollStep()
{
    if(!m_isScrolling || m_isPressed)
        return;

    // 减速
    m_velocity *= m_deceleration;

    // 计算滚动距离
    qreal dx = m_velocity.x() * (m_timer.interval() / 1000.0);
    qreal dy = m_velocity.y() * (m_timer.interval() / 1000.0);

    // 应用滚动
    QScrollBar *vScrollBar = m_view->verticalScrollBar();
    QScrollBar *hScrollBar = m_view->horizontalScrollBar();

    if(vScrollBar && vScrollBar->isVisible())
    {
        vScrollBar->setValue(vScrollBar->value() + qRound(dy));
    }

    if(hScrollBar && hScrollBar->isVisible())
    {
        hScrollBar->setValue(hScrollBar->value() + qRound(dx));
    }

    // 如果速度足够小,停止滚动
    qreal speed = qSqrt(m_velocity.x() * m_velocity.x() + m_velocity.y() * m_velocity.y());

    if(speed < m_stopThreshold)
    {
        stopScrolling();
    }
}

void InertialScroller::startScrolling(const QPointF &velocity)
{
    if(m_isScrolling)
        return;

    m_velocity    = velocity;
    m_isScrolling = true;
    m_timer.start();
}

void InertialScroller::stopScrolling()
{
    if(!m_isScrolling)
        return;

    m_timer.stop();
    m_isScrolling = false;
    m_velocity    = QPointF(0, 0);
}
相关推荐
爱喝水的鱼丶31 分钟前
SAP-ABAP:ABAP异常处理与SAP现代技术融合—— 面向云原生、微服务与低代码场景的创新实践
开发语言·低代码·微服务·云原生·sap·abap
赵和范44 分钟前
C++:与7无关的数
开发语言·c++·算法
长勺1 小时前
Java线程池调优与实践经验
java·开发语言
灬0灬灬0灬1 小时前
掩膜合并代码
开发语言·python
愿做无知一猿1 小时前
【JAVA】中文我该怎么排序?
java·开发语言·python
一切顺势而行1 小时前
树的基础算法
java·开发语言·算法
ghost1431 小时前
C#学习第24天:程序集和部署
开发语言·学习·c#
雪山上的小灰熊1 小时前
如何使用Antv X6使用拖拽布局?
开发语言·前端·javascript
破刺不会编程2 小时前
Linux中进程控制(上)
linux·运维·服务器·开发语言·windows
EnigmaCoder2 小时前
Java异常处理全解析:从基础到自定义
java·开发语言