0

Here is a class i use for the viewport to zoom in/out. zoom.cpp

#include "zoom.h"
#include <QMouseEvent>
#include <QApplication>
#include <QScrollBar>
#include <qmath.h>

Graphics_view_zoom::Graphics_view_zoom(QGraphicsView* view)
  : QObject(view), _view(view)
{
  _view->viewport()->installEventFilter(this);
  _view->setMouseTracking(true);
  _modifiers = Qt::ControlModifier;
  _zoom_factor_base = 1.001;
  initfactor=1;

}


QList<int> Graphics_view_zoom::gentle_zoom(double factor) {

  _view->scale(factor, factor);
  _view->centerOn(target_scene_pos);
  QPointF delta_viewport_pos = target_viewport_pos - QPointF(_view->viewport()->width() / 2.0,
                                                             _view->viewport()->height() / 2.0);
  QPointF viewport_center = _view->mapFromScene(target_scene_pos) - delta_viewport_pos;
  zpoint=_view->mapToScene(viewport_center.toPoint()); //zpoint is public QPointF
  _view->centerOn(_view->mapToScene(viewport_center.toPoint()));

  emit zoomed();
  zooms.append(viewport_center.x());
  zooms.append(viewport_center.y());
  zooms.append(_view->viewport()->x());
  zooms.append(_view->viewport()->y());
  return zooms;

}

void Graphics_view_zoom::set_modifiers(Qt::KeyboardModifiers modifiers) {
  _modifiers = modifiers;

}

void Graphics_view_zoom::set_zoom_factor_base(double value) {
  _zoom_factor_base = value;
}

bool Graphics_view_zoom::eventFilter(QObject *object, QEvent *event) {

  if (event->type() == QEvent::MouseMove || event->type()==QEvent::Scroll) {
    QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
    QPointF delta = target_viewport_pos - mouse_event->pos();
    if (qAbs(delta.x()) > 5 || qAbs(delta.y()) > 5) {
      target_viewport_pos = mouse_event->pos();
      target_scene_pos = _view->mapToScene(mouse_event->pos());

    }
  } else if (event->type() == QEvent::Wheel) {
    QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
    if (QApplication::keyboardModifiers() == _modifiers) {
      if (wheel_event->orientation() == Qt::Vertical) {
        angle = wheel_event->angleDelta().y();
        steps=steps+angle;
        factor = qPow(_zoom_factor_base, angle);
        initfactor=qPow(_zoom_factor_base,0-steps);

        gentle_zoom(factor);
        return true;
      }
    }
  }
  Q_UNUSED(object)
  return false;
}

zoom.h

#ifndef ZOOM_H
#define ZOOM_H


#include <QObject>
#include <QGraphicsView>

class Graphics_view_zoom : public QObject {
  Q_OBJECT
public:
  Graphics_view_zoom(QGraphicsView* view);
  QList<int> gentle_zoom(double factor);
  void set_modifiers(Qt::KeyboardModifiers modifiers);
  void set_zoom_factor_base(double value);
  double _zoom_factor_base;
  double angle;
  double factor;
  double initfactor;
  int steps=0;
  QList<int> zooms;

  QPointF target_scene_pos;
  QPointF zpoint;
  int slposx;

  QGraphicsView* _view;
  Qt::KeyboardModifiers _modifiers;

  QPointF target_viewport_pos;

  bool eventFilter(QObject* object, QEvent* event);

private:

signals:
  void zoomed();
};

#endif // ZOOM_H

I need to add a GraphicsItem (which is also sub-classed to either the center of the viewport or ideally at its top-left corner. What i managed to get from this class is the center of the view (see zpoint variable in gentle_zoom(). However, it only works if mouse wheel is used to zoom. If i then move the scrollbar of the Graphics view (which is in a Ui HLayout and auto-adjusts), the item is added to the previous position and doesn't follow the scrolling i did with mouse. How can i get that ? I assume i have to add some event to the eventFilter(), but unsure of what is that. Thanks

BobR
  • 85
  • 6

3 Answers3

1

Perhaps add the _view-horizontalScrollBar()->value() (or vertical) to the zpoint. So keep track of the last scrollbar position when you zoom, then when you add an item after scrolling you add (newScrollPosition - lastScrollPosition) to your center point.

0

You seem to want to have a view-bound item that ignores transformations (i.e. zoom). One approach is to have an item with QGraphicsItem::ItemIgnoresTransformations flag set. It'd make sense when the untransformed "UI" items are shared among all views.

Another is to overlay a second, transparent graphics view on top of the primary one, and show the "static" (un-zoomable) UI there, from a dedicated scene. This makes sense when each view needs its own custom UI that's not shared between the views. You may refer to this, albeit not fully fleshed out yet working, answer.

As a colossal hack, you can use the first approach for the UI items for all views, but instead have dedicated untransformed item(s) for each view, and do nothing if paint(..., widget) is not the items desired view.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
0

Well, it was much easier than i thought. After studying the zoom class, i realized that i was storing the initial zoom factor (initfactor variable), so all i had to do for my new GraphicsItem was ..

item->setX(ceil(ui->graphicsView->horizontalScrollBar()->value()*z->initfactor));
item->setY(ceil(ui->graphicsView->verticalScrollBar()->value()*z->initfactor));

... and the item was added at the top-left position of my zoomed view.

@Noah Whitehouse Thanks for the heads up, the horizontalScrollBar()->value() was actually the one that lead me to the solution

BobR
  • 85
  • 6