QML中的Flickable
类型允许类似于智能手机中的菜单的滚动体验:如果您在停止移动之前开始平移并松开触摸屏,则内容将继续滚动一段时间,并且然后他们逐渐停下来。
我在使用QWidgets时发现没有相应的东西。
QGraphicsView
可以使用QGraphicsView::ScrollHandDrag
通过平移滚动,但无论我滚动的速度有多快,滚动都会在我放开屏幕后立即停止。
有没有办法通过QGraphicsView
获得与Flickable
在QML中提供的类似的用户体验?
我能想到的唯一解决方案是使用QSwipeGesture
,存储过去的移动,当用户放开屏幕时,根据最近几百毫秒的运动计算向量,然后设置滚动条坐标到计算的位置,然后使用QPropertyAnimation
以避免QGraphicsView
立即跳到最终位置。然后必须仔细计算QPropertyAnimation
的速度以匹配用户仍在触摸屏幕时的最后速度。
有更简单的方法吗?
答案 0 :(得分:1)
这是具有自动滚动功能的QWidget
的最小完整示例。它提供(演示)以下功能:
使用QTimer
并根据上次鼠标移动事件中的拖动距离完成自动滚动。
窗口小部件内容只是一个网格,因此无论如何都可以看到滚动。
#include <QAbstractScrollArea>
#include <QApplication>
#include <QMainWindow>
#include <QMouseEvent>
#include <QPainter>
#include <QScrollBar>
#include <QTimer>
#include <iostream>
using namespace std;
template <typename T>
static T clip(T value, T min, T max)
{
return value < min ? min : value > max ? max : value;
}
class View: public QAbstractScrollArea {
private:
int _w, _h; // width and height of contents
ulong _dtScr; // interval for autoscrolling
int _x0, _y0; // mouse position at left button down
int _xView0, _yView0; // view port origin at left button down
int _x, _y; // mouse position at last mouse drag
ulong _t; // time stamp at last mouse drag [ms]
int _dx, _dy; // distance for kinetic effect
ulong _dt; // delta time for kinetic effect [ms]
Qt::Orientations _scr; // enabled autoscroll directions
QTimer _qTimer; // autoscroll timer
public:
View();
virtual ~View();
protected:
virtual void resizeEvent(QResizeEvent *pQEvent);
virtual void paintEvent(QPaintEvent *pQEvent);
virtual void mousePressEvent(QMouseEvent *pQEvent);
virtual void mouseMoveEvent(QMouseEvent *pQEvent);
virtual void mouseReleaseEvent(QMouseEvent *pQEvent);
private:
void updateScrollbars();
void autoScroll();
};
View::View():
QAbstractScrollArea(),
_w(10000), _h(10000), _dtScr(50), _scr(0)
{
_qTimer.setInterval(_dtScr);
QObject::connect(&_qTimer, &QTimer::timeout,
this, &View::autoScroll);
}
View::~View() { }
void View::resizeEvent(QResizeEvent *pQEvent)
{
QAbstractScrollArea::resizeEvent(pQEvent);
updateScrollbars();
}
void View::paintEvent(QPaintEvent *pQEvent)
{
int xOffs = horizontalScrollBar()->value();
int yOffs = verticalScrollBar()->value();
int spc = 32;
// draw some contents (straight forward, without clipping)
QPainter painter(viewport());
for (int y = 0; y < _h; y += spc) {
painter.drawLine(0, y - yOffs, _w, y - yOffs);
}
for (int x = 0; x < _w; x += spc) {
painter.drawLine(x - xOffs, 0, x - xOffs, _h);
}
}
void View::mousePressEvent(QMouseEvent *pQEvent)
{
if (pQEvent->button() == Qt::LeftButton) {
// stop auto-scroll
_qTimer.stop();
// remember start values of dragging
_x0 = _x = pQEvent->x(); _y0 = _y = pQEvent->y();
_xView0 = horizontalScrollBar()->value();
_yView0 = verticalScrollBar()->value();
_dt = _dx = _dy = 0;
_t = pQEvent->timestamp();
}
}
void View::mouseMoveEvent(QMouseEvent *pQEvent)
{
if (pQEvent->buttons() & Qt::LeftButton) {
int x = pQEvent->x(), wView = viewport()->width();
_dt = 0; _scr = 0;
if (x < 0) { // scroll to right
_dx = x; _scr |= Qt::Horizontal;
} else if (x >= wView) { // scroll to left
_dx = x + 1 - wView; _scr |= Qt::Horizontal;
} else { // horizontal dragging
int dX = pQEvent->x() - _x0;
horizontalScrollBar()->setValue(
clip(_xView0 - dX, 0, _w - wView));
// store values kinetic effect
_dx = x - _x; _x = x;
}
int y = pQEvent->y(), hView = viewport()->height();
if (y < 0) { // scroll down
_dy = y; _scr |= Qt::Vertical;
} else if (y >= hView) { // scroll up
_dy = y + 1 - hView; _scr |= Qt::Vertical;
} else { // vertical dragging
int dY = y - _y0;
verticalScrollBar()->setValue(
clip(_yView0 - dY, 0, _h - hView));
// store values kinetic effect
_dy = y - _y; _y = y;
}
if (_scr) { // scrolling activated
_dt = _dtScr;
if (!_qTimer.isActive()) _qTimer.start();
} else { // store values kinetic effect
_dt = pQEvent->timestamp() - _t;
_qTimer.stop();
}
_t = pQEvent->timestamp();
}
}
void View::mouseReleaseEvent(QMouseEvent *pQEvent)
{
if (pQEvent->button() == Qt::LeftButton) {
// check whether autoscrolling shall be enabled
if (_dt) {
// convert values to interval of autoscrolling
_dx = _dx * (double)_dtScr / _dt;
_dy = _dy * (double)_dtScr / _dt;
_scr
= Qt::Orientation((_dx != 0) * Qt::Horizontal)
| Qt::Orientation((_dy != 0) * Qt::Vertical);
if (_scr) _qTimer.start();
}
}
}
void View::updateScrollbars()
{
QSize sizeView = viewport()->size();
QScrollBar *pQScrBarH = horizontalScrollBar();
pQScrBarH->setRange(0, _w - sizeView.width());
pQScrBarH->setPageStep(sizeView.width());
QScrollBar *pQScrBarV = verticalScrollBar();
pQScrBarV->setRange(0, _h - sizeView.height());
pQScrBarV->setPageStep(sizeView.height());
}
void View::autoScroll()
{
if (_scr & Qt::Horizontal) {
int xView = horizontalScrollBar()->value();
_xView0 = clip(xView - _dx, 0, _w - viewport()->width());
if (xView == _xView0) _scr &= ~Qt::Horizontal;
else horizontalScrollBar()->setValue(_xView0);
}
if (_scr & Qt::Vertical) {
int yView = verticalScrollBar()->value();
_yView0 = clip(yView - _dy, 0, _h - viewport()->height());
if (_yView0 == yView) _scr &= ~Qt::Vertical;
else verticalScrollBar()->setValue(_yView0);
}
if (!_scr) _qTimer.stop();
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QMainWindow win;
View view;
win.setCentralWidget(&view);
win.show();
return app.exec();
}
在Windows 10上使用VS2013和Qt 5.7进行编译和测试。
答案 1 :(得分:0)
ui->graphicsView->setAttribute(Qt::WA_AcceptTouchEvents,true);
然后选择最适合您需求的那个:
QScroller::grabGesture(ui->graphicsView, QScroller::LeftMouseButtonGesture);
或
QScroller::grabGesture(ui->graphicsView,QScroller::TouchGesture);