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




