27

QGraphicsView画面の中央にウィンドウがあるアプリケーションがあります。マウスホイールのスクロールを使用してズームインおよびズームアウトできるようにしたい。

現在、マウスのスクロール機能を再実装QGraphicsViewしてオーバーライドし、画像をスクロールしないようにしました(デフォルトのように)。

void MyQGraphicsView::wheelEvent(QWheelEvent *event)
{
    if(event->delta() > 0)
    {
        emit mouseWheelZoom(true);
    }
    else
    {
        emit mouseWheelZoom(false);
    }
}

したがって、スクロールすると、マウスホイールが前方にある場合は true 、マウスホイールが後方にある場合は false という信号を発しています。

次に、この信号を、GUI を処理するクラスのスロット (以下のズーム機能を参照) に接続しました。基本的に、ズーム機能はそれを行うための最良の方法ではないと思います。スケールを設定するためにオーバーライドされた wheelevent 関数を使用している人々の例を見てきましたが、完全な答えを見つけることができませんでした。

代わりにこれを行いましたが、決して完璧ではないので、これを少し調整するか、ホイールイベント関数でスケールを使用した実際の例を探しています。

コンストラクタで初期化m_zoom_level0ます。

void Display::zoomfunction(bool zoom)
{
    QMatrix matrix;

    if(zoom && m_zoom_level < 500)
    {
        m_zoom_level = m_zoom_level + 10;
        ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        matrix.scale(m_zoom_level, m_zoom_level);

        ui->graphicsView->setMatrix(matrix);
        ui->graphicsView->scale(1,-1);
    }
    else if(!zoom)
    {
        m_zoom_level = m_zoom_level - 10;
        ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        matrix.scale(m_zoom_level, m_zoom_level);

        ui->graphicsView->setMatrix(matrix);
        ui->graphicsView->scale(1,-1);
    }
}

上記でわかるように、私はそれを使用しQMatrixてスケーリングし、それを Graphicsview に設定し、変換アンカーをマウスの下に設定していますが、ロードをスクロールしている場合、完全に機能しないことがあります。これは、 int ループオーバーまたは何かに関係していると思います)。

私が言ったように、これについての助けや、マウスの下のスケールの良い例は素晴らしいでしょう.

4

13 に答える 13

48

このようなズームは少しトリッキーです。それを行うための独自のクラスを共有しましょう。

ヘッダ:

#include <QObject>
#include <QGraphicsView>

/*!
 * This class adds ability to zoom QGraphicsView using mouse wheel. The point under cursor
 * remains motionless while it's possible.
 *
 * Note that it becomes not possible when the scene's
 * size is not large enough comparing to the viewport size. QGraphicsView centers the picture
 * when it's smaller than the view. And QGraphicsView's scrolls boundaries don't allow to
 * put any picture point at any viewport position.
 *
 * When the user starts scrolling, this class remembers original scene position and
 * keeps it until scrolling is completed. It's better than getting original scene position at
 * each scrolling step because that approach leads to position errors due to before-mentioned
 * positioning restrictions.
 *
 * When zommed using scroll, this class emits zoomed() signal.
 *
 * Usage:
 *
 *   new Graphics_view_zoom(view);
 *
 * The object will be deleted automatically when the view is deleted.
 *
 * You can set keyboard modifiers used for zooming using set_modified(). Zooming will be
 * performed only on exact match of modifiers combination. The default modifier is Ctrl.
 *
 * You can change zoom velocity by calling set_zoom_factor_base().
 * Zoom coefficient is calculated as zoom_factor_base^angle_delta
 * (see QWheelEvent::angleDelta).
 * The default zoom factor base is 1.0015.
 */
class Graphics_view_zoom : public QObject {
  Q_OBJECT
public:
  Graphics_view_zoom(QGraphicsView* view);
  void gentle_zoom(double factor);
  void set_modifiers(Qt::KeyboardModifiers modifiers);
  void set_zoom_factor_base(double value);

private:
  QGraphicsView* _view;
  Qt::KeyboardModifiers _modifiers;
  double _zoom_factor_base;
  QPointF target_scene_pos, target_viewport_pos;
  bool eventFilter(QObject* object, QEvent* event);

signals:
  void zoomed();
};

ソース:

#include "Graphics_view_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.0015;
}

void 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;
  _view->centerOn(_view->mapToScene(viewport_center.toPoint()));
  emit zoomed();
}

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) {
    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) {
        double angle = wheel_event->angleDelta().y();
        double factor = qPow(_zoom_factor_base, angle);
        gentle_zoom(factor);
        return true;
      }
    }
  }
  Q_UNUSED(object)
  return false;
}

使用例:

Graphics_view_zoom* z = new Graphics_view_zoom(ui->graphicsView);
z->set_modifiers(Qt::NoModifier);
于 2013-10-01T11:02:51.860 に答える
26

PyQt を使用したソリューションは次のとおりです。

def wheelEvent(self, event):
    """
    Zoom in or out of the view.
    """
    zoomInFactor = 1.25
    zoomOutFactor = 1 / zoomInFactor

    # Save the scene pos
    oldPos = self.mapToScene(event.pos())

    # Zoom
    if event.angleDelta().y() > 0:
        zoomFactor = zoomInFactor
    else:
        zoomFactor = zoomOutFactor
    self.scale(zoomFactor, zoomFactor)

    # Get the new position
    newPos = self.mapToScene(event.pos())

    # Move scene to old position
    delta = newPos - oldPos
    self.translate(delta.x(), delta.y())
于 2014-10-26T09:08:31.683 に答える
5

多くの欲求不満の後、これはうまくいくようです。問題は、 がそのスクロール位置とは関係がないため、 の動作はスクロール位置と変換の両方にQGraphicsView依存することです。これを理解するには、ソースを調べなければなりませんでした。transformQGraphicsView::mapToScene(const QPoint&) constmapToScene

マウスが指しているシーン ポイントを記憶し、スケーリングし、そのシーン ポイントをマウス座標にマップし、スクロール バーを調整して、そのポイントがマウスの下に巻き付くようにします。

void ZoomGraphicsView::wheelEvent(QWheelEvent* event)
{
   const QPointF p0scene = mapToScene(event->pos());

   qreal factor = std::pow(1.01, event->delta());
   scale(factor, factor);

   const QPointF p1mouse = mapFromScene(p0scene);
   const QPointF move = p1mouse - event->pos(); // The move
   horizontalScrollBar()->setValue(move.x() + horizontalScrollBar()->value());
   verticalScrollBar()->setValue(move.y() + verticalScrollBar()->value());
}
于 2013-12-27T14:16:06.590 に答える
5

少し遅れましたが、今日はPysideだけで同じことを歩きましたが、同じはずです...

アプローチは「非常にシンプル」ですが、少し時間がかかりました...最初にすべてのアンカーをNoAnchorに設定し、次にホイールイベントのポイントを取得してシーンにマップし、この値でシーンを変換し、スケーリングして最後に変換します戻る:

def wheelEvent(self, evt):
    #Remove possible Anchors
    self.widget.setTransformationAnchor(QtGui.QGraphicsView.NoAnchor)
    self.widget.setResizeAnchor(QtGui.QGraphicsView.NoAnchor)
    #Get Scene Pos
    target_viewport_pos = self.widget.mapToScene(evt.pos())
    #Translate Scene
    self.widget.translate(target_viewport_pos.x(),target_viewport_pos.y())
    # ZOOM
    if evt.delta() > 0:
        self._eventHandler.zoom_ctrl(1.2)
    else:
        self._eventHandler.zoom_ctrl(0.83333)
    # Translate back
    self.widget.translate(-target_viewport_pos.x(),-target_viewport_pos.y())

これが私の目的のために機能する唯一の解決策でした。私見それは最も論理的な解決策でもあります...

于 2014-03-11T13:20:27.660 に答える
3

スムーズなズーム

void StatusView::wheelEvent(QWheelEvent * event)
{
    const QPointF p0scene = mapToScene(event->pos());

    qreal factor = qPow(1.2, event->delta() / 240.0);
    scale(factor, factor);

    const QPointF p1mouse = mapFromScene(p0scene);
    const QPointF move = p1mouse - event->pos(); // The move
    horizontalScrollBar()->setValue(move.x() + horizontalScrollBar()->value());
    verticalScrollBar()->setValue(move.y() + verticalScrollBar()->value());

}
于 2016-04-05T09:41:13.487 に答える
0

@veslam:s ソリューションを QT Wiki ( https://wiki.qt.io/Smooth_Zoom_In_QGraphicsView ) の Smooth Zoom コードと組み合わせると、非常にうまく機能するようです。

ソース:

QGraphicsViewMap::QGraphicsViewMap(QWidget *parent) : QGraphicsView(parent)
{
    setTransformationAnchor(QGraphicsView::NoAnchor);
    setResizeAnchor(QGraphicsView::NoAnchor);
}

void QGraphicsViewMap::wheelEvent(QWheelEvent* event)
{
    wheelEventMousePos = event->pos();

    int numDegrees = event->delta() / 8;
    int numSteps = numDegrees / 15; // see QWheelEvent documentation
    _numScheduledScalings += numSteps;
    if (_numScheduledScalings * numSteps < 0) // if user moved the wheel in another direction, we reset previously scheduled scalings
        _numScheduledScalings = numSteps;

    QTimeLine *anim = new QTimeLine(350, this);
    anim->setUpdateInterval(20);

    connect(anim, SIGNAL (valueChanged(qreal)), SLOT (scalingTime(qreal)));
    connect(anim, SIGNAL (finished()), SLOT (animFinished()));
    anim->start();
 }

void QGraphicsViewMap::scalingTime(qreal x)
{
    QPointF oldPos = mapToScene(wheelEventMousePos);

    qreal factor = 1.0+ qreal(_numScheduledScalings) / 300.0;
    scale(factor, factor);

    QPointF newPos = mapToScene(wheelEventMousePos);
    QPointF delta = newPos - oldPos;
    this->translate(delta.x(), delta.y());
}

void QGraphicsViewMap::animFinished()
{
    if (_numScheduledScalings > 0)
        _numScheduledScalings--;
    else
        _numScheduledScalings++;

    sender()->~QObject();
}

ヘッダ:

class QGraphicsViewMap : public QGraphicsView
{
    Q_OBJECT

private:
    qreal _numScheduledScalings = 0;
    QPoint wheelEventMousePos;
public:
    explicit QGraphicsViewMap(QWidget *parent = 0);

signals:

public slots:
    void wheelEvent(QWheelEvent* event);
    void scalingTime(qreal x);
    void animFinished();
};
于 2018-05-24T19:47:34.407 に答える