c++ - Qt::Popup 破坏 QScroller 动态滚动?

标签 c++ linux qt qt5 touchscreen

首先,我的问题只能在带触摸屏的设备上重现, 使用 PC/鼠标一切正常。

问题是,如果我使用 QTableView + QScroller 作为独立窗口一切正常 - 我将手指从底部移动到顶部内容向下滚动,从上到下滚动向上.

但是,如果我将 QTableView 放在 QWidget 中并带有 Qt::Popup 属性,则滚动会改变方向!我从下往上移动手指,它向上滚动,从上到下向下滚动。

这是我的代码:

#include <QAbstractTableModel>
#include <QScroller>
#include <QTouchDevice>
#include <QVBoxLayout>
#include <QtDebug>
#include <QtWidgets/QApplication>
#include <QtWidgets/QTableView>

class MyModel : public QAbstractTableModel {
public:
  MyModel(QObject *parent) : QAbstractTableModel(parent) {}

  int rowCount(const QModelIndex &parent = QModelIndex()) const override {
    return 100;
  }
  int columnCount(const QModelIndex &parent = QModelIndex()) const override {
    return 3;
  }
  QVariant data(const QModelIndex &index,
                int role = Qt::DisplayRole) const override {
    if (role == Qt::DisplayRole) {
      return QString("Row%1, Column%2")
          .arg(index.row() + 1)
          .arg(index.column() + 1);
    }
    return QVariant();
  }
  QVariant headerData(int section, Qt::Orientation orientation,
                      int role) const override {
    if (role == Qt::DisplayRole) {
      if (orientation == Qt::Horizontal) {
        switch (section) {
        case 0:
          return QString("first");
        case 1:
          return QString("second");
        case 2:
          return QString("third");
        }
      }
    }
    return QVariant();
  }
};

bool is_touch_screen_avaible() {
  const auto devs = QTouchDevice::devices();
  for (const auto &dev : devs) {
    if (dev->type() == QTouchDevice::TouchScreen) {
      return true;
    }
  }
  return false;
}

void configure_scoller_for_item_view(QAbstractItemView *view) {
  QScroller *scroller = QScroller::scroller(view);
  view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
  view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
  QScrollerProperties properties =
      QScroller::scroller(scroller)->scrollerProperties();
  QVariant overshootPolicy =
      QVariant::fromValue<QScrollerProperties::OvershootPolicy>(
          QScrollerProperties::OvershootAlwaysOff);
  properties.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy,
                             overshootPolicy);
  scroller->setScrollerProperties(properties);
  properties.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy,
                             overshootPolicy);
  scroller->setScrollerProperties(properties);
  if (is_touch_screen_avaible())
    scroller->grabGesture(view, QScroller::TouchGesture);
  else
    scroller->grabGesture(view, QScroller::LeftMouseButtonGesture);
}

#define POPUP

int main(int argc, char *argv[]) {
  QApplication a(argc, argv);
#ifdef POPUP
  QWidget *mainWin = new QWidget;
  mainWin->setWindowFlags(Qt::Popup);
  auto lay = new QVBoxLayout(mainWin);
  mainWin->setLayout(lay);
  auto tableView = new QTableView(mainWin);
  lay->addWidget(tableView);
#else
  auto tableView = new QTableView;
#endif
  MyModel myModel(nullptr);
  tableView->setModel(&myModel);
  tableView->setSelectionMode(QAbstractItemView::NoSelection);
  tableView->setFocusPolicy(Qt::NoFocus);
  configure_scoller_for_item_view(tableView);

#ifdef POPUP
  mainWin->resize(500, 500);
  mainWin->show();
#else
  tableView->resize(500, 500);
  tableView->show();
#endif

  return a.exec();
}

最佳答案

Qt 没有在可滚动区域中完全实现手势 explained in their own documentation :

Qt does not really reflect the system behavior wrt gestures on scrollable views (widget classes inheriting QAbstractItemView, QML classes) well.

[...]

In widgets, the pan recognizer is currently hard-coded to use 2 touch points. For touchscreens, it should be changed to one. But that can't be done as long as single-finger-panning is reserved for selecting text.

When using a touch screen, the selection in widgets is driven by mouse events synthesized from touch by the system (Windows) or Qt itself (other platforms). The same touch events drive QGestureManager.

另一方面,有一个known (和 old )QTouchEvents 和弹出窗口部件的未定义行为:

The behavior of QTouchEvents is undefined when opening a pop-up or grabbing the mouse while there are more than one active touch points.

可能这两个问题的结合是您问题的根源。

作为一种可能的解决方法(虽然不完全是您想要的),您可以使用 QWidget::grabGesture(Qt::PanGesture) 启用双指滚 Action 为替代方案。另外,正如@mohammad-kanan 在评论中提到的,您可以尝试使用 Qt::FramelessWindowHint | Qt::Tool 而不是 Qt::Popup

关于c++ - Qt::Popup 破坏 QScroller 动态滚动?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50491050/

相关文章:

qt - QML 显示动画

qt - 在Windows上使用MinGW编译Poco

c - 使用 execlp() 运行 C 程序?

linux - 如何在 eclipse linux 中正确链接 ffmpeg 项目

c++ - 如何在 Windows 或 Linux、32 或 64 位、静态或动态的 Visual Studio 或 g++ 下编译 Qt 5

C++ WinInet 和回调不工作

linux - 删除linux文件中的特殊字符

c++ - 属于对象的指针会发生什么情况,其拷贝被推送到 vector?

c++ - C/C++ 以十六进制打印字节,得到奇怪的十六进制值

在托管类中调用非托管函数时出现 C++/CLI System.AccessViolationException