如何使QGraphicsView变得可以轻弹?

时间:2017-03-03 22:36:25

标签: qt user-interface animation touch

QML中的Flickable类型允许类似于智能手机中的菜单的滚动体验:如果您在停止移动之前开始平移并松开触摸屏,则内容将继续滚动一段时间,并且然后他们逐渐停下来。

我在使用QWidgets时发现没有相应的东西。

QGraphicsView可以使用QGraphicsView::ScrollHandDrag通过平移滚动,但无论我滚动的速度有多快,滚动都会在我放开屏幕后立即停止。

有没有办法通过QGraphicsView获得与Flickable在QML中提供的类似的用户体验?

我能想到的唯一解决方案是使用QSwipeGesture,存储过去的移动,当用户放开屏幕时,根据最近几百毫秒的运动计算向量,然后设置滚动条坐标到计算的位置,然后使用QPropertyAnimation以避免QGraphicsView立即跳到最终位置。然后必须仔细计算QPropertyAnimation的速度以匹配用户仍在触摸屏幕时的最后速度。

有更简单的方法吗?

2 个答案:

答案 0 :(得分:1)

这是具有自动滚动功能的QWidget的最小完整示例。它提供(演示)以下功能:

  1. 可以用鼠标左键拖动内容。
  2. 如果在运动中释放鼠标左键,则启用自动滚动。 (再次禁用鼠标左键会禁用自动滚动。)
  3. 如果鼠标在按下鼠标左键时离开视口,则启用自动滚动。因此鼠标指针和视口之间的距离定义了滚动步骤,即鼠标指针离视口越远,滚动速度越快。
  4. 使用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进行编译和测试。

    Snapshot of test appl. for auto-scrolling

答案 1 :(得分:0)

ui->graphicsView->setAttribute(Qt::WA_AcceptTouchEvents,true);

然后选择最适合您需求的那个:

QScroller::grabGesture(ui->graphicsView, QScroller::LeftMouseButtonGesture);

QScroller::grabGesture(ui->graphicsView,QScroller::TouchGesture);