Qt::Popup 破坏 QScroller 动态滚动?
Qt::Popup breaks QScroller kinetic scroll?
首先,我的问题只能在带触摸屏的设备上重现,
使用 PC/mouse 一切正常。
问题是,如果我将 QTableView
+ QScroller
用作独立 window 一切正常 - 我将手指从底部移动到顶部内容向下滚动,从上到下向上滚动.
但是如果我将 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 (and 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)
启用双指滚动作为替代。另外,正如@mohammad-kanan 在评论中提到的,您可以尝试使用 Qt::FramelessWindowHint | Qt::Tool
而不是 Qt::Popup
.
首先,我的问题只能在带触摸屏的设备上重现, 使用 PC/mouse 一切正常。
问题是,如果我将 QTableView
+ QScroller
用作独立 window 一切正常 - 我将手指从底部移动到顶部内容向下滚动,从上到下向上滚动.
但是如果我将 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 (and 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)
启用双指滚动作为替代。另外,正如@mohammad-kanan 在评论中提到的,您可以尝试使用 Qt::FramelessWindowHint | Qt::Tool
而不是 Qt::Popup
.