You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Qt::Popup导致QScroller动态滚动方向异常问题

Qt Popup窗口中QTableView触摸滚动方向反转的解决方法

你遇到的这个问题是Qt中Qt::Popup窗口的一个特殊行为——当QTableView放在Popup窗口里时,触摸滚动方向会和预期相反,单独使用时却完全正常。这是因为Popup窗口的触摸事件坐标系统或事件处理逻辑和普通窗口存在差异,导致QScroller识别的滑动方向出现了反转。

快速修复方案

我们可以通过调整QScroller的垂直滚动因子,在Popup环境下手动反转滚动方向,让它和手指滑动方向匹配。修改你的configure_scoller_for_item_view函数即可:

void configure_scoller_for_item_view(QAbstractItemView *view) {
    QScroller *scroller = QScroller::scroller(view);
    view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
    view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);

    QScrollerProperties properties = scroller->scrollerProperties();
    QVariant overshootPolicy = QVariant::fromValue<QScrollerProperties::OvershootPolicy>(
        QScrollerProperties::OvershootAlwaysOff);
    properties.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, overshootPolicy);
    properties.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, overshootPolicy);

    // 新增:判断当前视图是否在Popup窗口中,若是则反转垂直滚动方向
    if (view->parentWidget() && (view->parentWidget()->windowFlags() & Qt::Popup)) {
        double originalFactor = properties.scrollMetric(QScrollerProperties::VerticalScrollFactor).toDouble();
        properties.setScrollMetric(QScrollerProperties::VerticalScrollFactor, -originalFactor);
    }

    scroller->setScrollerProperties(properties);

    if (is_touch_screen_avaible())
        scroller->grabGesture(view, QScroller::TouchGesture);
    else
        scroller->grabGesture(view, QScroller::LeftMouseButtonGesture);
}

原理说明

Qt::Popup窗口的触摸事件Y轴坐标可能和普通窗口的方向相反(比如Popup的Y轴向上为正,而普通窗口向下为正),QScroller基于这个坐标计算的滚动方向就会和手指滑动方向颠倒。通过把VerticalScrollFactor设置为原有值的负数,我们相当于把滚动方向反转回来,让手指从下向上滑时内容向下滚动,从上向下滑时内容向上滚动,恢复正常的触摸体验。

另一种可选方案:重写Popup窗口的事件处理

如果上面的方法不生效,你也可以尝试重写Popup窗口的event函数,直接调整触摸事件的坐标:

class PopupWidget : public QWidget {
    Q_OBJECT
public:
    PopupWidget(QWidget *parent = nullptr) : QWidget(parent) {
        setWindowFlags(Qt::Popup);
    }

protected:
    bool event(QEvent *event) override {
        if (event->type() == QEvent::TouchBegin || event->type() == QEvent::TouchUpdate || event->type() == QEvent::TouchEnd) {
            QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
            QList<QTouchEvent::TouchPoint> points = touchEvent->touchPoints();
            for (auto &point : points) {
                // 反转Y坐标,匹配普通窗口的坐标系统
                QPointF pos = point.pos();
                pos.setY(this->height() - pos.y());
                point.setPos(pos);
            }
            // 更新事件的触摸点信息
            *static_cast<QTouchEvent*>(event) = QTouchEvent(touchEvent->type(), touchEvent->device(), touchEvent->modifiers(), touchEvent->touchPointStates(), points);
        }
        return QWidget::event(event);
    }
};

这种方法直接修正了触摸事件的坐标,让QScroller能正确识别滑动方向,不过需要你自定义一个Popup子类来实现。

内容的提问来源于stack exchange,提问作者user1244932

火山引擎 最新活动