首先,我的问题只能在带触摸屏的设备上重现, 使用PC/鼠标一切正常。
如果我使用QTableView
+QScroller
作为独立窗口的问题一切正常 - 我从下到上移动手指内容向下滚动,从上到下向上滚动。
但是,如果我QTableView
Qt::Popup
属性放在QWidget
内,那么滚动就会改变方向!我把手指从下到上移动,它向上滚动,从上到下向下滚动。
这是我的代码:
#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并没有完全实现可滚动区域中的手势,如他们自己的文档中所述:
Qt并没有真正反映可滚动视图(继承QAbstractItemView,QML类的小部件类(上的系统行为wt手势。
[...]
在小组件中,平移识别器当前被硬编码为使用 2 个触摸点。对于触摸屏,应更改为一个。但是,只要保留单指平移来选择文本,就无法做到这一点。
使用触摸屏时,小部件中的选择由系统(Windows(或Qt本身(其他平台(从触摸合成的鼠标事件驱动。相同的触摸事件驱动QGestureManager。
另一方面,QTouchEvents
和弹出小部件存在已知(和旧的(未定义行为:
当有多个活动触摸点打开弹出窗口或抓住鼠标时,QTouchEvents 的行为是未定义的。
可能这两个问题的组合是问题的根源。
作为一种可能的解决方法(尽管不完全是您想要的(,您可以使用QWidget::grabGesture(Qt::PanGesture)
启用双指滚动作为替代方法。另外,正如@mohammad-kanan在评论中提到的,您可以尝试使用Qt::FramelessWindowHint | Qt::Tool
而不是Qt::Popup
。